socnetv-1.9/0000775000175000017500000000000012542300363013276 5ustar dimitrisdimitrissocnetv-1.9/INSTALL0000664000175000017500000000177212525314416014343 0ustar dimitrisdimitrisInstallation ============ To install SocNetV 1.x you need Qt5 development libraries. Qt is a C++ toolkit published under GPL. Most modern Linux distros, especially those providing KDE as their default desktop environment, include Qt. So if you have one of openSUSE, Mandriva, Fedora, Kubuntu, Slackware, etc, you dont need to download Qt. It should be already installed. If you have Qt5 installed, all you have to do is to type in the following commands in order to decompress the SocNetV tarball and build it. Replace 1.X with the versionyou downloaded... 1) untar zxfv SocNetV-1.X.tar.gz 2) cd socnetv-1.X 3) qmake 4) make 5) sudo make install Probably you have already done the first 2 steps, so just type in 'qmake' or 'qmake-qt5'. If Qt5 is not installed, the script will let you know... When you finish compiling and installing, run the application typing: socnetv or go to Start Menu > Mathematics > SocNetV. Questions ========= For any questions, email me at: dimitris.kalamaras@gmail.com socnetv-1.9/COPYING0000664000175000017500000010577012410526714014347 0ustar dimitrisdimitris You may use, distribute and copy SocNetV under the terms of GNU General Public License version 3, which is displayed below. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . socnetv-1.9/AUTHORS0000664000175000017500000000053012410526714014350 0ustar dimitrisdimitrisDeveloper: Dimitris Kalamaras See my blog at: http://dimitris.apeiro.gr Translators: - Greek: None - German: Daniel Pinto dos Santos Debian packagers: - Serafeim Zanikolas (>0.43) - Alejandro Garrido Mota (<0.44) Gentoo packager: - Markos Chandras Arch packager: - Tom Tryfonidis socnetv-1.9/socnetv.desktop0000775000175000017500000000037612377612576016405 0ustar dimitrisdimitris[Desktop Entry] X-SuSE-translate=true Name=SocNetV GenericName=Social Networks Analysis and Visualisation Comment=Social Networks Visualisation and Analysis. Exec=socnetv Icon=socnetv Categories=Education;Science;Math;Qt; Terminal=false Type=Application socnetv-1.9/manual/0000744000175000017500000000000012542300363014547 5ustar dimitrisdimitrissocnetv-1.9/manual/manual.html0000644000175000017500000000244712534331160016723 0ustar dimitrisdimitris SocNetV Handbook socnetv-1.9/manual/credits.html0000644000175000017500000001367412534331160017107 0ustar dimitrisdimitris

License

Copyright © 2005-2014 by Dimitris V. Kalamaras.
Blog: http://dimitris.apeiro.gr

This documentation is licensed under the terms of the GNU Free Documentation License.

This program is licensed under the terms of the GNU General Public License as published by the Free Software Foundation http://www.gnu.org/licenses/gpl.html. You have the right to use and copy it, study, modify and then redistribute the revised source code as long as you keep the source code free for the others. For a complete list of your rights and obligations by using this program please read the GPL.

Credits

Many thanks to:

  • Filipe Clemente (bug reporting and useful comments)
  • Caitlin Matos (Debian packaging for 1.x series)
  • Serafeim Zanikolas (Debian packaging up to 0.90)
  • Vaggelis Motesnitsalis (SRS Documentation)
  • Tom Tryfonidis (Arch packaging)
  • Markos Chandras (Gentoo packaging)
  • Daniel Pinto dos Santos, for the now obsolete German translation
  • Andre Somers, for bug fixing and useful comments
  • Thomas Ingold, for betweenness bug reporting
  • Steven Orr, for bug reporting
  • Fred March, for bug reporting
  • Alejandro Garrido Mota (Debian packaging)
  • Martin Hohenberg
  • Victor Cardoso
  • Paul Johnson
  • David de Ugarte, for an older Spanish translation

References

  • Anthonisse, .I. M.(1971) The Rush in a Graph. Amsterdam: Mathematisch Centrum
  • Brandes, U. (2001). A Faster Algorithm for Betweenness Centrality. Journal of Mathematical Sociology 25(2), pp.163-177.
  • Brandes, U. (2001). Drawing on Physical Analogies.In M. Kaufmann and D. Wagner (Eds.): Drawing Graphs: Methods and Models. LNCS Tutorial 2025, pp. 71-86.
  • Brin, S. and Page, L. (1998) The Anatomy of a Large-Scale Hypertextual Web Search Engine. In: Seventh International World-Wide Web Conference (WWW 1998), April 14-18, 1998, Brisbane, Australia.
  • Dasgupta S., Papadimitriou C.H. & Vazirani U.V. (2006). Algorithms.
  • Davis, J. A. & Leinhardt S. (1972). The Structure of Positive Relations in Small Groups.In J. Berger (Ed.), Sociological Theories in Progress , vol. 2. pp. 218-51. Boston, MA: Houghton Mifflin.
  • Eades, P, (1984). A Heuristic for Graph Drawing. Congressus Numerantuum, 42:149-160.
  • Erdos, P. and Renyi, A. (1959). On Random Graphs. I. Publicationes Mathematicae 6: 290-297.
  • Erdos, P. and Renyi, A. (1960). The Evolution of Random Graphs. Magyar Tud. Akad. Mat. Kutat Int. Kol. 5: 17-61.
  • Freeman L. C. (1979). Centrality in Social Networks Conceptual Clarification. Social Networks, 1 (1978/79), pp. 215-239
  • Fruchterman, T.M.J. & Reingold, E.M. (1991). Graph Drawing by Force-Directed Placement. Software-Practice & Experience. Vol. 21, Is. 11, pp. 1129-1164.
  • Gil, J. and Schmidt, S., (1996a). The origin of the Mexican network of power. In: International Social Network Conference, Charleston, SC, USA, pp. 22–25
  • Gil, J. and Schmidt, S., (1996b). The political network in Mexico. Social Networks 18, 355–381
  • Holland, P.W. & Leinhardt. S. (1970). A Method for Detecting Structure in Sociometric Data. American Journal of Sociology 70: 492-513.
  • Holland, P.W. & S. Leinhardt. (1971). Transitivity in Structural Models of Small Groups. Comparative Group Studies 2:107-124.
  • Holland, P.W. & S. Leinhardt. (1975). Local Structure in Social Networks. Sociological Methodology<.em> 1976: 1-45.
  • Liu, C.L. (2000). Elements of Discrete Mathematics 2nd Edition. Greek Translation.
  • Page L., Brin S., Motwani R., Winograd T. (1998). The PageRank Citation Ranking: Bringing Order to the Web
  • Press W., Teukolsky S., Vetterling W. & Flannery B. (1992). Numerical Recipes in C, the art of scientific computing.
  • Sabidussi, G. (1966). The centrality index of a graph. Psychometrika 31, pp 581–603.
  • Schank, T and Wagner. D (2005). Approximating Clustering Coefficient and Transitivity. J. Graph Algorithms Appl. 9(2): 265-275
  • Stephenson K. and Zelen M. (1989). Rethinking Centrality: Methods and examples. Social Networks 11 1-37.
  • Stroustrup B. (1997). The C++ Programming Language.
  • Scott, J. (2000). Social Network Analysis, A handbook.
  • Qt Project (2014). Qt5: A cross-platform application and UI framework for developers using C++ or QML. https://qt-project.org
  • Wasserman S., Faust K. (2000). Social Network Analysis. Methods and Applications.
  • Watts, D.J., Strogatz, S.H. (1998). Collective dynamics of 'small-world' networks. Nature 393 (6684): 409–10.

Citation

If you use SocNetv in your papers, please cite us as follows:
Kalamaras D. Social Network Visualizer (SocNetV). Social network analysis and visualization software. Home page: http://socnetv.sourceforge.net

For citing the SocNetv Manual, please use:
Kalamaras D. (2014) The SocNetV Manual. Social Network Visualizer (SocNetV). http://socnetv.sourceforge.net

socnetv-1.9/manual/generate.html0000644000175000017500000001607012534331160017235 0ustar dimitrisdimitris

Known data-sets

SocNetV can recreate easily one of the following known data-sets:

  • Krackhardt: High-tech managers (advice), 24 actors
  • Krackhardt: High-tech managers (friendship), 24 actors
  • Krackhardt: High-tech managers (Reports To), 24 actors
  • Padgett: Florentine Families (marital relationship), 16 actors
  • Padgett: Florentine Families (business relationship), 16 actors
  • Zachary: Karate Club (simple ties), 34 actors
  • Zachary: Karate Club (weighted ties), 34 actors
  • Bernard: Killworth Fraternity (multirelational), 58 actors
  • Galaskiewicz: CEOs and clubs (affiliation data)
  • Freeman's EIES networks (multirelational, 32 actors)
  • Freeman: EIES network, at time-1, 48 actors
  • Freeman: EIES network, at time-2, 48 actors
  • Freeman: EIES network, number of messages, 48 actors
  • Freeman: The 34 possible graphs with N=5 (as multirelational), 5 actors
  • Mexican Power Network in the 1940s (list format)
  • Knocke: Bureacracies Information Exchange Network, 10 actors
  • Stephenson and Zelen (1989): Network of 40 AIDS patiens (sex relationship)
  • Stephenson and Zelen (1989): Information Centrality test dataset, 5 actors
  • Wasserman and Faust: star, circle and line graphs of 7 actors (multirelational)
  • Wasserman and Faust: Countries Trade (basic manufactured goods), 24 actors

From File menu select "Create known data set" or press F7. A dialog will appear where you select one of the data-sets above. Press OK and the network will be displayed in the canvas.

Random network creation

SocNetV can create a random network for you. At the moment, it can create the following types of random networks:

  • Scale-free
  • Small world
  • Erdos-Renyi
  • Ring lattices
  • d-regular
Scale-free

SocNetV generates random scale-free networks of n nodes according to the Barabási–Albert (BA) model which uses a preferential attachment mechanism.

The algorithm starts with the given \( m_0 \) connected nodes. In each step a single new node is added, along with \( m \) edges to existing nodes.

The probability that the new node will connect to an existing node \( i \) is: \( p_i = \frac { (α + d_i ^ p) } { \sum_j {d_j} } \) where:
\( α \) the initial 'attractiveness' of each node,
\( d_i \) the degree of node \( i \)
\( \sum_j {d_j} \) the sum of degrees of all pre-existing nodes \( j \)

if \( α = 0 \) and \( p = 1 \) then the preferential attachment is linear (BA model).

Small Worlds

SocNetV creates small worlds using the Watts and Strogatz model. According to that model, a 'small world' is a random network with short average path lengths and high clustering.
Given the desired number of nodes N, the mean degree K (assumed to be an even integer), and a special parameter \( \beta \), satisfying \( 0 \le \beta \le 1 \) and \( N\gg K \gg \ln(N)\gg 1 \), the model constructs an undirected graph with N nodes and \( \frac{NK}{2} \) edges in the following way:

  • Construct a regular ring lattice, a graph with \( N \) nodes each connected to \( K \) neighbors, \( K/2 \) on each side.
  • For every node \( n_i=n_0,\dots, n_{N-1} \) take every edge \( (n_i, n_j) \) with \( i < j \), and rewire it with probability \( \beta \). Rewiring is done by replacing \( (n_i, n_j) \) with \( (n_i, n_k) \) where \( k \) is chosen with uniform probability from all possible values that avoid self-loops (\( k \ne i \)) and link duplication (there is no edge \( (n_i, n_{k'}) \) with \( k' = k \) at this point in the algorithm).

From the menu Network select Create Random Network > Small World (or press Shift+W).
You will be asked for the number of nodes N, their mean degree K and a rewiring probability \( \beta \).

Erdos-Renyi networks

According to G(n, p) model (Erdos-Renyi), a random network is created by connecting nodes randomly.

Each edge is included in the graph with equal probability P, independently of the other edges.

From the menu Network select Create Random Network > Erdos-Renyi (or press Shift+R).

You will be asked for the number of nodes and an edge probability.

Ring lattices

Ring lattices (or physicist's lattices) are 'random' networks where all nodes are positioned in a ring.

Each one has the same even degree (number of edges) d with her "neighbourhood", namely she is linked with the d/2 nodes before and d/2 nodes after her.

For instance in a 4-lattice of 10 nodes, node 6 will be linked with 4, 5, 7 and 8.

To create a ring lattice network click Network > Create Random Network > Ring Lattice (or press Shift+L).

You will be asked for the number of nodes and the degree of each node.

d-regular networks

These are random network where each node have the same number of "neighbours", aka the same degree d.

Nodes are arbitrarily linked with each other other.

Web Crawler

SocNetV includes a simple web crawler, which consists of two parts: a spider and a parser.

The spider visits a given initial URL (i.e. a website or a webpage) and downloads its HTML code.
The parser scans the code for 'href' links to other pages (internal or external) and adds them to a queue of URLs (called frontier).
As URLs are added in the queue, the spider visits them and downloads their HTML which is scanned for more links by the parser, and so on...
The end result is the 'network' of all visited webpages as nodes and their real links as edges.

Please note that the parser searches for 'href' links only in the body section of the HTML code.

To start the web crawler, go to menu Network > Web Crawler or press Shift+C. A dialog will appear, where you must enter the initial web page (seed).

You can also set the maximum nodes/pages (default 600) and what kind of links to crawl: internal, external or both.
By default the spider will crawl both internal and external links.

socnetv-1.9/manual/socnetv-main-window-annotated.png0000644000175000017500000035007212525314416023156 0ustar dimitrisdimitrisPNG  IHDRkBLbKGD pHYs+tIME : v IDATxwxU93s[ )z (*ʢkW,(eu];b]u-kTPQQBo $H-$$ބ<}̝{f=g&9oΜbJvwB(a!ɘ #""""""D,yfv&ň$k3mحm\ Qg7t(Kz~O1Ifi+$jb$yˋ.v79jboV\4k4Q$YcBIK5LՋuADDDDDDY^P? 3 *Dd 1V/) !V +-X"0lX3•""""""N.mɭ%Kk'je&ɘ kLĴc jbXf`J!C޼@DDDDDDwre,aYzaPC0AVi qcuGM5Lh"\U'-""""""NΛ*VtTQIȘ k]L:F&FX_I`RhwMwUYU ^RIȘ k1&dvDzA,,OlXN&>MDˤM^!Pub,#XDDDDDDD*S)DMy3\T'b#JLkEYa2$FYwXmR,.m G+#LuVOŒX!S)#JUJ&#Jg$YQdfY1 FeBS!›׉4#~).kb^GJ˨NٺvoaGk#!&""""""괪^mBR\6Iخ7쪠tJKqَfj0>tb,h鲋&EѤ7(k@H)%c B=NT׀0DDDDDDDnUcBJEM(VJJ JJ-(UHs݀ [JܼE,_`P QJ0A<&a픍%Ʋ=e0Iɰ-d#Dp}B:BhND:>auRH$P|r4Q%ӕ2ʍꎛuܰvō 豖a93V44Ę' uBhWH́ܨt4Мt z!"""""",:9J9՜ru4vTJ)ǫ)Ҕk\7Y.k&a&9p,,"HĘRB~!EHjRi!j ,GHCpmNODDDDDDI])ˁ24Jʵ qcG)ub R zL_aSaw1&ǒ9ǒ`SE%Ƃ.x1HMMԅ#!5M i:i $huZaBI])ӄKu zSJM9ⵡ$ H^[@`:\w1=RJVQcnL̗eCM] M¶RJ R/42u@hB 4[Rٮ%-$""""""lWW ѥ*n+0(G3<6 )Hk[a庮u*vYn0L%,L2><K tCeѤTs Q=b, KhrlJhJJ]Bh49ʖBIK""""""NQR9Q]vLG-\.`D \&pUx: !aG%ɱfͅ(¶Qc*Ch(O.BxR>!#cVJ ` ͐Bw5J!=)!t:# d:'M Uu5WBAZa$J8P==BYURWt"){X@. $19H'Ty+XR @ا.>)B!)\±RHRJݭIٮmRz44 utGJPP:t!#""""""$4q+lؖTr]K渖eJM-tMP2q\^BPOOrTpCu#vk%G5ɱX !ȩeeRhQcR jI]HrlUBx4aHi!0c@J-9r:.@ن,ʴ64ChB ӑ+iW8BpRTʁ2rʤIDUZ n&19 n޼NGddJQZZ=jLJGHWRӪJ)Рf#Ƥ4 8Gj/=+!=T:#""""""ꤔ, SLMRqRӠ)[YDUBAJMȰ\WB!)23j"\N P{yr%oLcIjʨ_ Y!EG!pL!M݁բ4XR7M1&5:|!R~k:$4Frh±aRai+]JWp]HC9u-)tr]#u5qmMRWBD+ShJjcMi)KV}Ke0BhR!\WօTЕUs+J0D@E4D@B!!z!""""""]ڐr!:QRY.\7"]uQ-BJCHXQGFu*]!W( vkK0Lc% JY{K% >iIl)k&߇!<kHt&L@ )) v0DDDDDDDPʂ a@w5v=p%,uIQvhq}•JxD8ak&ǚhɶjE+G!pB""5WJKJ)uH˵I͑BJpÑ0_" LBu&Ro' 2e1DDDDDDDn _ݾJ8Jt0:aIa %4۵CZP^mZ c Cz(R6HkµPBI[hB RJ) F€PG󅫪v*B,jODDDDDD/ڞv6nݷߨZAJCBהk@stRuiPnzXIss!c Iu!ZXVD M‘Bpk )$$4\] a 4͢Uu]D"++a _D$뺬d""""""jUmme>)2CIhB@vuBjB^[ ZakD#ZZG~ Ͷ,qbZDWǁi-޾eYVVa=Э[p\M (Py3VZT #"""""֖}юP\8)|.X;v$|{ 'm[S΄Xp!)%g@W YW^ԶMDYE|0t@4U=!OFDDDDDD-֖}юTmY9_o ŋQB,]K +3YYYX|֮[e`[ThK/"0hin{E6|86m0MDD[ee%_2p0n&O~{a\;[<۲][Ƶ5aW˝D̲áPܲá c}T\ 1hˡP=_ii @eWA톤 VV"++4@$e¡ @,++Y!Dż_ɧɧ*eϜ5 k֬iӅ]/2d͝'MF(jr{UUU4x0VY{T2vC/b/q|h_|]{jj*|7~6k[$AZzmis|GŋnaxNuN?á*lܐ߬hSٳ?é~Exe_׿ٟ㑇B4?W_y ]=Lse(QYoEu>W5GnprM &>WwYϴ{p*+gƩ]MΝ={yorqϞC0kO<+V駟1ړi}?oHKKC>}bN6T%\QXX׋#F+&c%٨ ̘@ R0緋k8jJkKk9N;펻 5K6ْESh\˶L#XUYoY&*4"<(L{ VZ4FPUP}yOǟ`xmfax70o:zQ^=g-Z_y>3fRo#<_=_΅nM%_~W_|iz͛Y~3Csssѧa0s0s,-§?oK>ٳ?{?Q|'CL+ #^{M,Z^&~1{SOcQR|gXhQmzK3xے\|8ȣs7;6p!bUni.l˫˗CFj5_MItmisWs$֒E?oR[ z8Pƍ.@^~v~LqQRz8o^u/Y{>{4+DkUPs駟w8{VTF7'v:6lܸz#Gҥ0mǵyʊ`ݪ+C2q*Z5p)'# !Ƽu8䓱z;[FB^^z)ٲ3NDzG"q&.x^ ms}=g;HEvrduIÔw߫mMz^_~pyC7C*|6FM^Ïp]S5+tiF[S1{ڕQ^Zn$ϟÑ Xx -Z\XOq2ՔDזƴo eGq5vCs.um:#Ӱ/jFxi+#˰~}>^}u}цeo/#>>+bscӦ;\exO7 _ܹᾩScsq$;ּg>3g>E+w yIG믨@AA!^zt^}0- %%%0M ql8^?b ]C¶gW-υeYy 6hm Dqoe]8><#[QTTC`\+|g7o a4¨(+w܁ wO{8SJړO=KyQH Cxꩧ0У{{ *ѫGws}d8\qq_}(ڧ5]LdT[k[D"$,3Zoŋy}خ  &Ն\"|=y_|%&M Oo]?#5ŏ[K&{yxqWoryC&M3 li-((@TD"z1WޚYsӱmH)c Ħ:9^ye.̛7p?bO{ͪ+-Io>_m׋DsM]7vdM=&"j{m \tXH#r|_omVNm N$G>}AE| XK,A{{h~fi蒞E A׌F[&5 ,D HMCp][6cU(_ & 7wigOг\2999k|egcؾb7{w>֓|Ŷ?P #nnjF1nx>_<.ab +pĉp%\E8T4md&;#F_/ ~C/\ϿH1MƪK c xbX2%O\nz폎m_xqUW'&k*tzkLff֯_׬8$[ob4'6ݺueoQ^^ գu]GvfFDHSזDIt' ϯoN6.;j;CsFsmstkKDmh/ZZRykPQV-E+{eXbi+b.[=Xl +W| `&ǺAgj.CQy54lGvuشi3"(FVV233"dtnx4)c_4F_>? >/233q"A‚ QX iӰ{=< n݊[gώ}ѣSOi 6".rzǎxX`>>pV[v}ǟxA  IDATFqpW.égҚ*K" " ^10!!mX,vxsssQ^VPU>#uF4X^1'(,,SO=s9^kM.7^  @ -o`x1aŸbuܹ#PEaa!(/^TI66@ubzԨO#DUe%?x<QQ&ص9Rt0ض݈qIn>$:ZnM7OJMuZ_S}ђϱySaʎD"qHd>3a֭^nG4\snja\\/`:C܅`"F>R;RӏAj: /?aYY߀@-tK"Я__,Y?}VԄmp\Rjx<=̛7U6l(x.ɱ 1g `ĈzϽa3z4*p͵.{!FuKKcƌA$7܈*xw#a7|urI0 @uC sĉ[ž{{n?UWi6 pr6I)8֮]뮻?cڳwxpGƉ7!\l:HD7ǭݎ܅Bx<1doCu~-K"6L*&3q}?guJ\6`ɓce6ab?P={E_O}[n<~[\/w0n44hP'4&bH2kc+3pQG=L{92Mǭ!޵ 7ވoG<3[Ҹ$jw TIfs-m %ݦ+u]hAne_tWN"a…Cw ">pB#,>uĶ[/?pbۍ`"Xu~~ugYϓ𨕊yc B "/R |ԥt15] Mw4CҰ4ե/ φ *!ӤOt<2K*Ki䯏;l_P]2`RXv-5s4p0z25 YW]TZXz***0h`H^ jdc=лWO3h7ӣ'Rӻ@uPU[6,!5;22%(R{priFXzr꽆]2$#+{Kfx\HG{^ pyk+/!oCU ua!+VE޽qsG{PEUe%6imO={M! lԴttR eaIO~HIR PE ޚLY@ -r6u P5&  DZuf6~8RJXb-Kx"wqR+С?` B~-|ګUS(fkxbӘn (/+@Av55yhs~Ż4^?Ǜs~z_23&6l?ĥ%׋i$ι^ognMRm:^+bb+⑇_DD[E;JٚWzpܭ%閸U :!!׍xm7.\˵\ek^Ky[9خD rr|B<93j`-1>#H׼i`XUD#PgCXgoGɱG,+^}hڶҲ2lڴ [PYY H .RSS-'=z@fFFR uTvl{œEqWcҥx' x{wg܎[o˗-C$Eff&8!,oZ;f];s->^6=n%zλг{7i˾hG(ɱ#Q{Sp3'cPZnBgY>< 2+ T^I奰7n{u]Glt}E$mu>@כH3{m<.RÓ? `hlιTy+׬mk̞{a~?plUAlټy1k:ߙogwҎ~U\U "Q{JHa_M"l &MIU3'(wgQ=¬1sے|k#Ljvذq#6$#Rp#-- ]th=o1'"""‘cGC>QR)Z,0_Q+(C;$#"A)]c/08 Mqn?3:K? %$}خnGˈ$^֍O[*Bm:LT?QQTO1yKD)JK0bI2۶ 0c ƛ'qDDDDDLt~C@DDTMӴ׺#&,k5ka09FDDDD19FDDTiB c_5MC4ixô,#""jUjEV!uckS#G4O$""""Zm"LJ Mb&t]GJJ z),\=&FDDDDDcDDDkF !;V;Z0 xqk׮z< Q*j!.t]2qhu;ˆKzzl2""""⣝f'}NDUU+\m@Szt{QFa„ ;P\\ )%RbҥHKMa[1cbr:<㕸cGu˖mdUnơ9ڲ;ComWmn5kڿy>H\׿a5R/mݦ:Zn<pFY6l.]rٲe6t(<^/Lup{8{$D{ۂf?+_3^G.a|ӥOp]wzK/o߾-wjZ޽-lF4EYikpxGP\\,deeBMFDDDDDGupS܉ɷ ˲.~9p1#qmwVW~l.*++1|u;$8(;v1xF\}a 'bg~=?m:}6xgp̨qGbeowMš>o 8Prϸh%XxI5k3Go;1w9NtlK. nj:GN::R 꽩q'#kN]hgz^W_{͝i>gW%r[{쏅 ~ض ߛ8hb‚ /[ogf@EE/mMǏ/ٟaM0mч}/#^\:<7mZϿ'ܶIDN=$ ^^Q_,ᅨyߍ<Sd I=~)wdiO=ugq~.? |m[ͪDT[5db3+Vd@ n0p?8hāsQ'N-8O;)_}՟ow4ŗpmo߾ҥ &p=>&;hĈ({ P?␃;ヌI'Gѽ;&4ozddd0 dggǖ=(++zm 蛷5i{]>gu&֬]ߍի`u/ '- Lw2?sFy&`gFEEEt׋d I0b  diw=z޲4躎} ŝ݆Moֶb\7%לQ[B@>]f~8`ru?gb`r}yԋÃW50 O~הnGڲu+N:X'董bL6CM>opW_|w8PXX>}z>ӯo_&/q7fݻWl? ƌr?$ض !&\2ORlٺ;,Ƽ??kOi`0d KP4{ލx vǸe$Nsddd(yTi233{ NxmWf3a[kF䚳1jkx<zoarlW'Iy_m~/jo}=[޻W/|zo0+06l}&/?={L/OyV\իV^ԺЖ&N/?}>~l駞~3g}Q]$yZj*"N9>qo`F=4kpg4L$4xa:.p &떸Of8[Y8#\^^zլziι0ƭ}HtIaZKfzzo)) o5!4!.BhMH)kRhR74ԴfmW_\8f_^qee^p>ym4q"qӯ__|W9Xl޼y,r7?0H)qa{pZ3ۯDVffv pI'(@mGy@q]C?:Nff]svzx/[9FSރsݬziǝDלu(FDDDDDDmɱW_kv,==}n먎^8y>x 'p=27M' É'F[1ŗp҉'txTVVƞD9qɸϮ]_'暴;p7acARRq`'56ޯ}_1{aF|> <(9g_AD1|Myp˭b~ {%qOk*ty1\?&ӏR6E ;+ 7xCm%Nsӟ{>0{PJCC߾};n Ǹ:uLȹǝDלu(FDDDDDDm^1)c5#DGix B W?{OdnBnN&>*+O C ovO;*ƈvWgᄌ7M[%J-q#܅t,QCCnM]k-ּ *G8rl']Ӊ8 K q@B%P8Q;qOi/W1 uzUͽqʝ"Ѩx5;h,؛Y3;|ۢ[\9L`[9GѮ֒ m8*Y4b^y2n.k"Mh5tҐ؞.})}6\fT &}YPYJ]: ?/Sm A;~v;9DDDDDY19m ur={d &ڙÆ1DDDDDDD\iI N09^<*d,v&W"""""""E2DDDDDDDDbrv[Lns3sharڕR,Xǣð!ؼY [XxRR#&4M KQJ)X eW,v\QTpB 135mf:AlٲSnݺ!==o^ܡ1?eEB˵mg^';_)1R(ondيx-pHKKÜocA][upeKz:RX+W# gFTD\˶ TVpy B0 Z oXS@/TnณBm7DOѳw?D[t99i+(eݿ;G3A)X~C$ {"e#Q=^q a(% e6nڂAzAk~[ḽĽSb ؽEqoN蔉 NIb_;Yq c='O@2"swdsIȝt R9[kѽX|tբCr ާ_;U IDAT/o=subJ5/9ƶ|p,KL UMyE%G MH ZLU24]l`ٛkt<92fb)Afwu.s;'x  i:vp"#Q>9m'>xk 4Mj߬5M;1UU jVT_ @-{sӥ8 !Ќ4w'62WV# }N} t` (7vpeh|Ҁje|?/O{S KT/ÇnξUAUԪfki{c0X=l:<dA5.Jb{ b1LhpP\\̶m[+8ƙ#4!#.#gٯOK0tJrr/mҒ:_EaΣU}ytuQţ!Wi:q9qvepgD3HMMEUU4Mtb()-$K7M E* Ev3fEɤ`5(V%އX & f#v8`w;(N7E`F`6CEe%gՏD+EEY>|nwUM"Ʉd"%%P ر{=蚔zr9Q l6;aa!X-F˅`Zu]9 ̠vB튕+WrwRVVvº?oW^njt]w?w<w}7~˳=ǭ;QRR|?Gʙq~.ܼ<96 ԗdָ\s5^.zMkk6T^{#ü2gΜINqi%atOOEQno񐔔HRRf3VMa@3]cыM7m'h^iY=z",,Mi~,/oPxb!"€ /P:w]TgHQ1 & ͘#Qݎn{GqMD>طkvMWu_!Q! (5bul _<]c@\U[-I֪sle%~c̥|zTyJ-ttMvQ^j 7cZ:۷UVr؏̙&pyB\bԈaD3(L*Q*I[7}.saw88|0yyyU dv v^\uGpp00? 4Ztd8Eǎ`fsTAFTUPct;AAfWN]+Ky'n֬}<p뭷nlt݁=z4?san+/\g͵l: >Y]W'|lvm~9|m< r4muacՓGl4M#44 r IJ ?0GC]u5E`0ֹ\.Et NNXuB!DgH5kE@1D265XZW]c5^ϮZ_wpl~W=X9oF?f1.RkVT5W~ܿ *c,y<6۹iݰZN9v֭eǞ=ʠ'sfRpM1bGzza4X a*U6q.WɫLcܳ)+E!77XF8p  _~Ջo$44@jZwIVvv5JKI$H6vQ|})5UUh+nwd쁪*, %\\ѫ'ѻwlҺ4kz /9AU:ϡghUSJr2ͼtIMN '+Y~FFzJՠ={`O>O=w6<9 XT)o&^t16nj4M׍w 5>1ʫ8TՌo=Xロ0NySYֺΚK8{9=^***_{NjJ0YqF/_͸FuFL&!!!~?N8_R0ބ.dfe~z:,,Xn Q־O٢yWY[5Uڪ9ʳtc '+' yp]jjOnAֺwݾ'3@&1!gX"[-L\!xkUm <\Qx#k!IHQ\򥨎GiIqwU0!T>⍤$i:.bDu; pp8(-)a޽degӣO?5cndft:x<)9lϸ7~?_IaeUYL?G^$%%rp\ȑ#!22ٌil6q:`0L$%%a69z(~9W^>+.:]9s,jUn7ph>.#uծ,+cQ\Rۦu<0<3o=( FQ,^ȑ#tw}7{/eewȻᄏQ_: 07uV O *++}^^*[mgտ?1g/;׹%ۙYNmܶ2m[cֵ؂Y퐿U.@׌l`ڵ+'33L?NII v CQXXHn^>ojbЀ3(;^Ķc|S뺎V˷GӨoֱ{>bSp{%GqԬ/ ",<<]ר('""Iƌ͖~dO?rA.t2-'GaՌX!FFEjd{|73TgmM޵x4Mp8]0ѷ>/#11r|y&39+V+/:7""Œk-[|xF_l $$S9vZ:/}XpRR`=/;ad8cA_h_BB>l({\yZk$P U_u gTdvQ #kWK:8WZk k]XoS۬pm{wnBQ7c3,SZ9]suVrWղ._s6`jڝhK!s^~.ҰaC|(M9VSJ6Nw뜷ᠢ7Q^igЈQDpR+*())k1$EeFBnPTl Jթb5_{Nb{,ukbP5bi\.f3]tr+**(**"??CB9~8rpQay&~o Q5:vCQ`Rl;\W(O9={MOoτW5sj@髱Tl6BBBFf[ o*ծsAAp=hWU?Ύ0_`0Tu]k4zon}`PO>k/rjt&;(EǏؤg2d4yjrW_Hf5xaJo{ޝW7 !x; Pk}Z۶4]s(US{/@_}f:tįsLCvO7rñg4ꞕ9Xc5GVnﲚ?`*-wʒb]#6:7;E@aB@Yg*П. tk3,pRÍ5ѻ7kv>ǚ1Zdg۷~tbccZTVVb6)**cǎt:qݾf$H^JZ ),,xa)w种[ B6;111 ׏4á}?V)ݬX%%܄pm&O{K,K'ih]U>btlFzz65Npk^a3!(n'66!*ޔ/T_YxcǎѵkWE=7#Gҥ_9w\'g?獣f#MSScm(=z`׮]}ܹU9믿?P6Æ cVەV]&[JJޯ($ǐm[1`ٲed2HS8῟䮉ͺmܴ^Giƍ;WFl}26&?matLf+xhstMlsd";(Y' VP^^ɕS/Yu~5r#BB,6ӦݬR!S#:\7ڶZ  ~Fsm]߽_zr< z}ǡ^.9mخ=6l(e%~V=9ݿ1fwQoM2UnIFGSY^fnd?gU>Nh hP R;^| }15\ttFI%(Ȃ7&&M(--mp YhTJpX8C%??ݻwO?NRR`p , ]t!::łm#/Qvrk a߼rU0Ǝ Ҏ˥R+?X65Y%nG#S#v%7,ٳfaFg̙mZW_@4Z?O<3 G>U_~6cUvkDDz7V.?EU97,{u)III\xGxdC<\9e/Mjn|KƝ{.yyzBt7h;N}ijnqz3Ͳ^m0N?+“O>UW]u1W79s&ydfeOINf~}7T3{r?x<̤wFm3snd={_QfS n{i]-۵kZ;v,.lN 5Rv3iݒEfyy%f PQsBXDDi)I򿹩ٱͷo@U0?{vlkѾNBfPɃ{ o_\QUA}+5^tkta!\8~D㭙dmJ+L-M]繤|]ۀ;^OZx~~ko IDAToMMEr]d|J_Z0Z%:ޚc(5F[PAIadz iyN &K##Ki!V3F| 8 fNKbbr٤[ wIc_Ń!lfj%11Æ |#j9***ʢ]׉bܸqF 1-:Q-(s>u㇂Qbz7Mplk1xd91=ƠXB%?l^̑bTwS?]O6)k i7eZLkykRFOyL?z(>_q4v]s)V+|A*;>wuu| o:++`uyd$.4\JQq;wo>C+};d'͡A=I# /zPo1!B!7@?@OćO6CjtM1r_m2ճ|1]Z_p `K^rn$N_>4Ejf:ަ۷3ٽc ǎ,1l+ &iğ1CP(fNtxGm#s<.zݻw'jmBn9f ,8lj6pxnwLX@铻v=G;v,]tfCQ֬Y2!%%޽{ʑ#GnZN3(o1>d H翻򰆿xǧ3 ߬`9Ȯ@Q[1[wC.#'94r}=6Wi0{u" bDEQ\ZΝp:Zqpo;ɀfϞ=XuB!'YQ&W5"tM= 8~Mf=:*'| qi0*aԢEÆ UK|ÛƘέX헸eJzrb`hYcb&2:^(H h ナ0 bРDGEo ɠxGuP.0LhX(zGut Xif22tF?mɍY!#>N U~@%@YpY|Plv;,5F"lϽkd CWʽ9d?om{9&A~`i?_iZb-hViPa׏_w6voBT&N׮I~Α$F̫$:*0IMlܴ3z)!<,Mb6ygvJG=ǿ`1of2ۧdefj*>S.brر};{'!aRFp4t u/P蝑sΡgtZSnƔ""%vnv?gG7o;EUN~΍|uSND 6QZwو񀢴Yi="fmFwv\ &&6V.rA[S3N}2VY2U!&Nd;R X-6;V\l,&CNVzfw2s٤I٣YA@aѣ[Fb!=;Nl'++rbc ?}Ā8?)Ʉb (DZǛJb'Y:s_<:zbKspmJz/ilۺ6=N rq 믾zjWA{7mݺpΧXgud[2TWg)gX!8y$8։:眓v,ʥ\>aa`~uBBBNZؘb[⫪2vh2zb֭Jzzw)/+g=;#֍=?J(ħ2[k Au_rA6nEEŧg-**RSqή[J233sxժ&oCDX8))]"bmIeY '61T,Bt m&&&κV2fnRӘeAUU"iׁݥ|RS>k}ΐmBmlLB!i@ctf*B!< !DkC!B!BtZ'-2w _ ]]UP[UUPUUŠQUTUQ fj4T٠M`2d2YLl2Z,j1YLA!F%` }i`%EB!B!It2j",B!B!#scB!=3BKcLfVdB!P6dBhС -FV6Y !Ba\?kdB4CEyR}8Y5wx|_yj~_I(GT[I1 QU Fi0*aqT[WM6;5XUn IUQTa0E j:HB!FiqdB4( U@!B!BtVB!B!DŽB!B!D%1!B!BiIpL!B!┐* !B!BNKcB!B!ӒB!B!贌ӑL9~P211ф,!D;O[$B!ikA :#&]|.K}uRu߭Hf&RR$ShGϼN2A!Bv/?ɄFcEr.4 㑼k2b>crs(//|iPHLLB!BqZXtxƘIf4FPT >}QU[L4L4Cs4/.  B!BqX%-)ϫ騪Li h$-wB!Bqz~,t]GƚYkۍhB!B!N[d4)B!BQXHhG!B!XG悢 y%B!BQiV)ڕ3H&!B!⤑X7c:ǭ^ &.r=󚵯S}Z)77UsjJJKs8***NjZ!B!BxIpnV)?/ HDD_}#;92}՞rsZf P~~>wu7FaLx}AFKiZB!B!D$8IK?ѫ7{x?TkfЧo0M:Nm+?ZgȄ_]pROwAJJ2Yǒ1aDo iB!B!:@~z5Ͼ>?ֲlظK߰q#6# FsL㾩1׮|M:N?΋/>ϑY乓ڬrǎ̾~6ѫcF8o_OAL"6l跮45_猟+UӮ(+31YMگB!BљhD Ro7\βeԥ ?͚oŷMTd$s^W= >.tIǩ/Ď;zYdټ&lVy9#YClzd[w:-yYbccyX"Vwiw_y~sۗ99M:^eoW_}͛o,Kb"MگB!BљIͱ*КSQQdw}fViit2MIIIZqt_`髯qaOjͱx5f,;~tXVr81M&l2PoGZj*VٯB!Bq:X'1ܱbE듻vC\綊͛fNg#3on[nIͳ pm$$&0olܴ˯ʀA9bwJCjn_!B!t&*s7 ?8~8n'3}̛w/9dg3o޽̘1Q$kwޝ?yDEEGQQ^Ζ-[p9 ;l***Nh5Yr|&:ao?=^45>~oa޽6>>̬+B!BtvmVYŏ?#5K.OVr- r]}B!B!:;鐿ٳ'u&:/w~Miqrr?>e7_gOXXC>z}<[o "3+ JϞ=|/MM ,dmpwt1k(--_׿ܯB!BIpj悝&ay̟7ucF>=acy(zf5 ?vvW!B!$8no-Y!B!d|Yo9&B!B!:-9Ay[UJyK5+$τB!B$8&: HFHUB!BӗDxpݒR$8&B!4&`0#MBQt >!oJ2]N2AlQ B!BvMc+玓|${w$B!: 7+t/)!B!D"Y B!B!:+ !B!BNKcB!B!ӒX' K8h)B!B!ԤC..>eun{iuؾmdB!B!:  '22/V3h&B!BљHp4x?WRYYɐ!'j@ 4&B!BQIp4x|v->7qqqlذkջĄB!BIp4PW7\β{t&L8_2J!B! Cjj7!B!% U{ltI !B!ӑiYwu6\.%n۽{wV^nyB!B!4Yifcmf9w4Mw7HfBCB6lX.xͷYѿ%SB!B)HpoIUUMwyi$3B!BHJ!BѮeeeQŷ|~7_}Utgyfj7x+tBCC1rYgqM7zj<P!DŽB!DUW]E޽Xf l6\.|<裌1 &4ŋ׹|ɒ%ۗ_~\ФY]fϞ7ꫯ6)+7?wV7o̜9RQQl޼^x.#FHABIp[B!|{ Ċ+4Ii\e֭[WO?4֯_ϤIlӧc2|[ne ٽ{[VOުÌ?IcȐ!RXBtxB!ʡC (ݴi\__*Mx>׵k2m4nw@Kk ]klʔ)DFF{|())i<B!: !Bv[n!??o٘1c׿EVV.2ÛoILL /w[~_{5NgG1w\t=_=fٲe~j7lzjscp:駟 ƍ'V!D'UvpǏCfBq\.nRm$Y~=߲|ЯCP222`8Nfs}w(//ͫ9s|6 Xr%W^ye믓ʃ>4&M">><~x_j_M?V~O>$&Nĉu!: >>pF#*'inJK9pDŽͶb '`F5,?Ç3~x K698BEEo~۷M F#3f駟-{W<~gΜys_kT盟7o< 7 !8mHpZDEF̞{())&?/_2F >!(2zgNʕ\JIidvm-Xv߲뮻Yf>3rssILLlt'Or͞=tڤs9s_pO?%''$߲j͚5Mٳ{}/:SLaԩ\tEX,)B!N+L&gTɗMLٳg{nRDJUUi-hlÇx˖-,$$//o2<˗/oot\p˖-cf6_l޼F1dǎ;|_lsĈddd{뮻X~=}ofc,_N 7g-V!ZH,h pUbFi2T4zJJ\\hB***򛯫9_/_7?eBBBMixgpUW5ynYIo~6{F?u6/))_fС\veJB!1>L25:K锖+!hHhh|qqq߳w^eӧO1c?Ν;|[noo7|wѤ3ghAݻY~=7n;`6}2w\>;Ô)S>a+W2zh;&W!D;RE't]ʕL\ !D=jVZze&MBQ4q&k?ƍ緬v$$$0i$eյŖ.]+$,,줼GԩSy)((f~߿ JB!1>Z%MMRN\I\J!S^xuwyYihw%--YǛ={ ?voM*O{ fڴiY_oʕ+ !ФCӈi~su4 [>ŻJ)'LVTUEQThƐ]HBL8ѯ?gyQ+5M;jy۷͛7sg69MLL o6cƌ).6S/--eƌ2pϞ=;vlǢ"M7yd)B!:4 Ft]G׵SzN[!%?,g}x(L %,,AL&ůg ~޷[>^S^NvN~9 L6yu?իWs72l0bbbp8;v۷grJvEPP/ݻۢsyw wt͇~+r뮻'|ҷ>fΜ9(~hXQQAjj*ƍ׿5C%%%0\.{Gxx\!|9躎rRvBTr:1fm7thudy IDATϿ 4,?c6k}hX8K<_Acskr22p`b≍gșg#bu74.PfBVdZD>.2tl&,,ݻ3yd}Y>'|_`AՏ>uO=+Vh{;NC)~6<=e[UVQVVG}Ĝ9s8p QQQFI^Diwʏr94OYfϞ/<1{Xd [n h-=0r2r2y7-cӦ>K9B4c ހ |9r7( 3gl0f8p͛7|tRbbbJ[K.Demf2 !hNu-ˆ4W9T!i(8!i9Zspv{NǞ}<}?רuǟW &sKwckN/Yͷ܊[^t 薚F4,|zo?pMt*Ue$EQYf? F #<\xzeaߞfsOHdlݺ?zXw^wa_ө !Dk[ho T5jT'''sy-JHH`3gNUﱴʕ+;7RVg9_X>/~+o;j{goaz-~ﻱk1 O?_'k\ÿ?7mD<<ÝX=edW;S<G/)--iFTppM|:<ԩWy%yӮ{iX<~mꀯT-̘1Csһwo1 ̄ ?>6l`͚5M>IǬ]16uԀ\yDDDк Oض-㧟~͛_ʅ^HJJ UU %==K/%Ko߾:K!hdzj~_I(GT[I1 QU Fi0*aqT[WM6;5XUn IUQTa0E jSssx<ַkqUWivMd>[)S]/E|ʽt}HbAs`!b"Bvv.E0O:F/2p V^M~dȑ|DFF2lpX _ظg<ãn6=!HhЃDXPD"4JA"%t [ fMٝ ><;3wsϜ{_nJ+hBj֬-Zbrj֬ ŋ{I5nܘ 6PrewwwΝ+IcԷ$&&RAnߺe킃9v(JZMӠ W=4+Jŝԫ[!!j{Nͷ@ Ac_i԰ e1:V*].CCUR쐧t]ةd:NSkdZF' {Fh5:6C딤WtBA'a& (g]ZVk7=5>$m@8O\jNNTj"D}qwc8Us1vsYC@5:22Tlkn݄j|X VJ:u899ң{wXΈÉb h<[^:J;f}-?-7eݻTTt\rwޕ^tL A͚e S*OODrHh.J&0<822<~"$%co߾L>-_27{x"##tjf<͛ٳ[laxyz]%A W@ @P\{9*Ci:x_~|sZgm086G ίR9Ηڞ5%S ,}TU2;56Ȕi8T͞J@''V>7[1рaղiӧC $_ @,R`/ c8N$g9ݚt(:r8:zݓm+)S'GGJ_":>Q LV!%%_7':/m6xyz*W ݺN !""WWWL׿7pK2ƛo̙3|>IIڰӀ2x@ .ܾu 'gg.dU6QW%66ǨOI'\pi6jH֭QMgvmصoI2YmW>_-{IÂ@ @ ,h6+`pnLgڏ{NYcYS/bpI]S{.DŽB`˦MQF!11c7SaV.^5PڗڇMObVtz2l,K/(>z%&@ < > pOgҬbv,;8uܙc̮ 鎱eG%2:9fzr^^3׹BLq|!YMGdbW O5k?mԈvRZUN{]]@@ ]n`_lOql3ۯao>B~oJc @9e2d2ݞdϲD&z> *֫Zhe2T3 >/]fyTo߼!"ґW2nz<==itEH +@P$\zΑ#G@@M@P +kl?0|2/WL0O?py ̀JjEzxjA&C6To޸n-z\F+#%GA޽{}|j tЁjժ\"k%L]v,!U"a_ =a(faX]k sY=# H/`FoMs^,V<88qqBӛk ܁r1 Va2_ FN Cfq |lpÐKϬ}36ʰ&Vcp](iYn]WƏ ͛7Ӽ;v,D!HJJ|$''/OQF4i҄'N|r.\ٳgо}{{IOOGRT*9w~Ə֭[Ϗ:h"۲e 72NcO\v ???zɓqttڀ7o+Vի8;;ӡCV^Z/`޽ܾ}DZjehXd 7oޤz,X-[&L {.2m/k3}~җZGz˗h"Ξ=\.'55&Me,se„ tܙիW C UקھJɯRXh;ΝC@.]6mpl0-0s5zXHc'C}eKsKpZmg/y^Itt4* ,YbVXx1> `t钩ݻwIJJۛ~7u_*۷g;|ւz7xS͛7oQ̙3bN:e鑑aÇ5kV{7nlj,у[Ξ=ˀz$X+9x ˖-v.44 &pB:[^ơAW_̶mۄ*So_m/n&''Ӯ]\ .!l@8! :X*BXHG*B u{0aJ;@ѳgOd2^^^L4ӧp<15DFF}),99믿[oŊ+X|9X>)m۶)ShXlvvv[FIMMÌ7bvMrr2=z^ڵkCJ.05t9;w.;wVZ۷T"""/L6l*dnJv.:j"keN-FAjj*oeXo7}utRV^M2e %--f͚Ю];Ieر#nnnL4I¾>VR9v5bϞ=$''3|pӰKa[gKΡu@5" CK֫,aeݬK" 6``0ha2 _vQt J/ɨj Es9"##پ};QQQTVk׮q֬YCjj*Gɉh"S={dkѣGG+Wz?ȑ#3fLCtc5'/CꫀWG^{,[ S:;;.]T*MCS+2}tSiȐ!dcLω1:uhW[M_j -s$&&d>d,k֬@Wa_K}itd_*Up^~ea[gK=zcX%Üc Ücop/ހ2 sJ@ !1[RVwr\/3,1F8ݻ _-Q|yϴiӨ^:Æ رc̞=F[ QQQ?'''{9WիWUJRfbb"dffFxx8¶ ϮsL Yv-qqqh41|c1+ժU`ر$&&2w\oHJJb„ jßٳETfk}\vvѢE N:ťKXt)aaa?jVdd$*[n===ä˙l߾>AY^PKA||N,;j9)zW9r7^ ϼzb"jmfCN˅X<GRq'.e}HZ`SywY9iZϟo}SCN ¾ =/^$.. RRR3f K.%((cǎ  ꕿҨa) 3:V*].CCUR쐧t]ةd:NSkdZF' {Fh5:6C딤WtBЙrc'D cر!>J5kֈ@ *xL2իWg;?(#<X $&&+!E2FBO\e;=c4o\E }g!9V럮'޽k#qpBFF}e+|DX[ZБW\ڵHy|2'Oʕ+ԯ_XQ] <9ڟPaSwύxn9V֯y xx x9`3`#ġ#_y1+x& w_HJ65wW:]=:wd4a IDATdwߞl6w k22%Z0G~=#W;\rweI6 {ʜcj>C0c'.N<ʉQ{^ )zUiܸ1wÇd2|||hԨo6}y5k֤QFԨ-̙3g@777n޼),55UÃzOI%ύ@+o>@mצGG~jWV &9Qа5)^ZUzk=L6~_mY6e UJ ~;{7SMZ5k2~ *XTbtt [=R/FOR\*:u*.FşڵkYnvvNkGGGڧX (>\|d̙Ô)SLa79$\B͟zSmjI$3s#䟢s NxQҨ?2cפ<@NX.-oO|>UMΝ-SĞ=ϓj߳~F6o +sgACG=5w""/MT8VVÇTQB]<;^= y⯇TTT*nܸYprrbcW͛ u~CYf΁ך#|6m67n+WfwyqqqÃ]Zo~߶m...r<%˞={RV-P*/_QF)+SSR%>sjulK>ڵڶm˭[$^zd2vؑ- 6 hРtΞ= /3ʕ˗  FN̞=v g…TZGGGSoHFԂTkv%'RӰWuZ:|\Ƚ[nTRGGGPLe9{oxdQQX-1*[?IOʢ}fmew:g:M@s|۸ksH[WN.~Ӫlӗèל/1etSZӦ|M 鞞FD#^Eg*ݻֽ;6nm۶V㌎*K")11yNN*b 9eoN7aQG6jDdd$*TW^|#֭tΝ;GPPPIm$3?/|d2/_;c:)ܰaC{ݻǜ9spsscҤI먨(ܹh4?DFḞ1l0BCC%o߾?˗Ӿ}{u+V&ck 0t4w,xܽ{>|:t(+111xzzC"""cӦM|ᇦӧOaS nSٕHICJ^iaq3 "͛7333YdI-['l2v9M۪%h֯y >tUə?gg'RSSMK~^lټǏi:u)ȑ#>|[7oЫWO,\Ȣ ~*}zfY㏜۳k.SoQq}L:}ۧ/tzV3f̠r@e\\\MnYy@R;{qĉlas%A<ƍ+P*## l*Wh,: .]ȑӵKS ;g.*UT)5۶mv'J=.ԨQGG'}]Ϙ]z5Se([֏Sʕѓ5kגBӓ"d:z))%ĖHHYH@ףVr &M/ 3]ӤI[5kM7ee#,X@zzz1rRHLL4d 3ΧwaƍgPTܽ{,sچ`ƎK\\)))\x1ϴrb~#ݻwÇt!ېԜHeA`֭j_$99{ְCLKݩTwܡk׮DGGS\l=lMeԾuՕ~Ԩ,_<ݻw73ڊoD\#lj( UJӂx>7OaU1aJaR*lr h L:W/a(c~뷩y kyS) 3;J%8sTǁT'cF|ٰnUY^*{N-O`g#d[ػ'tn)QÇSb%]muekXV9yf)]l0֮Oo\V8jɒ%rGѾ<&-]&q߾}LS+_!K]TyyS233Vwnӿ:wLϞ=ذa#,_LRJUqV)(5o|rr0ܹv0q<'h9T l8pɓx{{Ν;رc/u1%7nGկ_piB`Ϙ1cRF =G1cƘ[r=s9rDC<2")) 777 ¢Eo/HHHݻgQ8K/d5¦̦ڲ_yS!%RVT*˳xgD:}nİʧfk;_/8 ̗knfL&^7Zd7Öhܷst4L~ho1W1jn[/h:""PJUlާRYtϟǷtÏ?/KQT(_ /ٻVxA{s+W7o_Ar <Gձ`,9%A Wŝ P(ۛz4믿cmڴa֭ԪU RI5شiΨQ(WvvvxzzRflPvڕJ*T*qttg&EhРĹaggGMsY{lܸMeʔ3ޟ>N:q b),gϦ[nHڵ-Ώ-,[_~WWWC.۠AL=}||5t WWWڴi*gAQajd6pBzgj|spjՊ05k#JJ*ѵk|]#lj(*߼JS[uh\lٲ8͓}nş?ð_~q}_aR߲Ud'::k Y_N [y?gϱaKOQUx375?Rbܺ}7OFzuٳ@NG;Aܻ4mM7oa>u2= sNܿfZjFZ2!,j9e-:Ka p(?/Yl7P(ظ~}z=**X;vlܰ/Kc6|5իWCpmԩm>sϛs6|/nС宻tJ*Uu&[Զ<=4׫sL Ac :kUD Xi?FcO=iu,iX@G CNd`aQX܀>+ng;^/HC~Q'>4&TAbKG.9(Nc)5xgVc1&Wګ_?~~~8o.IZz:,]F yK *+ ջŁEr/^L6muW ݺN !""WWWL׿7pK·De }3'9|}|>&N9#ҫ%kKTT$3888p-Ⱥ'))Z 2x@SW>_-)yx z%@9s =ZC d=zk[ -`#C2Ko][YMd wL])5' :kiz{_?yu3zD?O s~T(_>[|yڭ[9vs+ܺ̓5WODPeӦ|{Tz%td9&@ x=Ǟ> %zE*0NQE.V֭[g8t#(( eW z'gΜaԩcϟgȑ>|RK/Ė-[lu۶m?ѣ:th۵klj'HOOe˖/ؔϞ=|S/ȕ={r9bccINNL2ݛiӦe[ZժUܸqʕ+rJyN͛7R UVڵk͛?~ƍs,B<;l?qqqo2ydJ*=H-KIՇhߥSۻw/&Lĉ+WziӦBT VI>z#?;WJQᤲv kgrG :'Rj>!B+>D+Qy!zNppvvF.g{a yP A IDATcutEƍYB/Xp|b[Mn: K6X /pAl?ԩJ… ԭ[O:vg>ϝ;GPP%;wװ0s ܸq#ݺu3שS/;wlugMFR7օs2b0a&M$)͡C`,YYp!=z` 4C?(UVY&o>ZjM޽{ϛT'OQFV uR!ERߟe˖I \ΡCh޼|XMҴiS?.^ (>TWFFmf,干H}GH)KIՇhߥS;u͚5CLŇ+QCsP* J3:V*].CCUR쐧t]ةd:NSkdZF' {Fh5:6C딤WtBЙ=Pbg9Vt$^.C2\L&C.d \n'2^!er;;\a)2R!W(;(J^iP::(N.v 'w%%&JZڍ4j^ǃxQ(٧{oౡjQerTT;\\\HKK#55ggg1k$z= TX1=qiӆ={g1c >Sf͚E6mؽ{wq_ 03gT*yUTח_~{j*uMnhٲ%0U*Tٳgi֬۔… T*ƘVjj*2駟Rrenܸ!)PtB^ aÆSR%nݺE޽Yntܹ@yhݺ5/>c̙L2֭[gZj7n9ݣf͚d)~)S m֭J)yJOOg֭ooo.^Ȟ={ӧe˖%**ʪnȑ#YjU4Ú>޿ҥKSLKէ5?qlgF;BJYJ-ϯ>G.Rl^ǎپ};Æ c8::Th.1αG{Jc89;g5  \ >DPޜqK_NQըjQ~RYC< BFu,E7˖|XGZ} V+ -- |[iH}GH)KIՇhߥSsss#%%}p#D Av*W 5H0+FzfqFB d2L- 1wC~ٽ{7ƍԸĿrJ都=vbbrZHMٙmےɓt3SH-6!RN E~9ZW[keR)0({c9ۖ k y{S]b=c#ҖGv[^X}(]J9<#|@ (9&gs/:~![l!!!ߟm^㰴[V~͞Ɖ>,9ɬZ5Ì;8RRR7`Μ9šR{n6={0̟c)}k2TT7+HIh…3d\]]Yx1 mѤI[5k`*5| {"I)KN*T;wlV槬!EEŕ+WHNNfٞ Fe,xtd b \兵Df[(qw)KyիW`̙rG@bX5abH@ xaRIHHʕ+ׯW~MҤIN}x!(I.FHVi-/RRRlyA8w)Ky˖-޳Fa컠x!U>}ؕ/d+O#^rPFY2&&OO E AF>%@?Ô)Sزe SV-:t`n٤{nT*+WssGנALۤ4 ,YQ,57|ŋ[tuޝ72uTΞ=}ڛLXX'OرcSlYիgfٲe :Ǐ/ח$#['$$Dxx8 f͚Y>*)e^cƍU@;tAl8IM6lݺ>ׯSref̘AܪU+3f gΜAOÆ UK0sL6l`r六rHGQȥKcҤI aK7l V{$EW bk#b-/Ֆ?*}x]JRl^dѢEPzu.\mlQ@ FI9V8^oOGW7Wac7xu"hYrDGO@ LI9&dٵkgΜ!00+OA9s 4 88;v%yr]n߾M`` lܸ7|ڵ}Eqq^EإY `7$FbUL1 Fb%؈`E[{Ěb Ql`Jw wR|?3s37;۽]nȱsWfM ADDDFcƌa0Ș1c`nno>nVVVP۷/MDcݻwq5̜9w;\:v½{>k Ggju:>C\|YΝ;hJ%SV/^MN]WÈޠ `ҥhٲ%GZjKà ,-[nnn(S P^=,\0[Oc&αb`ժPTի;wN['N_6Boaĉ;ܹ'#\=z~ۺu??a)9sra ɓKzBmnM< ED&W^Ell,qu|WFBDcE\TTm߁>}}x+gmm Z… zyPpnݻw8Ν+K̝7~3gV\֘:u ~w4"""""""*49Vmݶ իWk=z7,okkӧOT7Ejd2e <{:m:WgϞEV[-]'iDDEɧI)SY#zSš5k0mڴBnF???TR^:N>a֦M5k0d!(4 ~W<{L7Wʕ*aذP*wqẾ_SPv]ٳ'.Zmwczy.]Ɲ;wp|2{{{lڸNNYDD$S|Γ2-'mN 6Rıp|x73g&L{ʕ+)$chکSp14mڔsDR&9Vݻ/^+ts~Mc߾ u d^5G||<@RfFΝΘ9ߎ=pFׯ %%  GD^FPBjT*T\]vEPPPk9J*tݺuUV!!!n݂<3K|Μ9HNNӚ v9ʕv6(W\C`&={&L']~3Q?666h޼^=N^V=CCаacL2; K.HNNFӧO]bʕ˗u[ʕ+lٲ@ k'N(21FDDD-?Z-z#F… HHH@ll,Ο?>C̙38|0~m~2QT)ԪUϙ<@˖-q!={hwPcXx"yM4}^?I2;t_Ns;ǏC3XQF j4l0e G.] Ko׮]pss9\]]m۶TM̐FAV -23^zN:JBj0j($''*977"LvzL 42 {# .`ժUXj.^XlΫ+W޽{Q|ylݺx كI&I::;v#o077GvzPPJ. \dZR2KjlMz7jҥKݻwuaNΙY)xaaa ԪU =zQne7>uLަ}tz1_˖FQŧ ]n˪df TR.S(Rf)S(*PUrFYR*rKZf0*d-rMb'OAg"`cc*h /^ݻc̙P8]ի(T\۶lAֆt ~ڿ^ C+L^?[j'Oѣt ;t6iD .W_}e2/;ߏ.] !!!puuETTߏN:ܹ={4r)%1G;;;DFFNK:u mڴѫK&رchӦ6H}Nlق޽{=^2.3T<)q1ɓ'cԩnKvڕU2#SĠA ï L///8;;([l[ZƚJ:TbReRi緭[f\z,;f5`Nچ/xWno ]b޽&CB&a͚5߿ b}ZlP{r=NkKpp06m$NC:r>uX)->.]Bf͠hru46))|V ФI4iΝ˷s Ѱy *TBJmiH({M^ Ib!V*SB I! P(eB*jdTn8 4uc9.d]v_bU{r=6$c[Ǝ$ 8ϟ?GTTޔ9dss4vKiS|wh4/+$&&Zw0;)|Vg6{L>m1"7c(ذaW.c=eʔ=$pssCpp0J.-Z`ذaf'Upp0ܠT*jժQZ- (W^|h]G@mRXEQN:!99-[_ҥK#!!QQQBtt4aaa6H}Ncbb`eekkkzh)qx)S* )))+]Rz`ƌ[O/qᅬQQ(m@/=xMϯjɺ}!'%OŒm1333"55UdPRDtt^Y/ "2uf`l{r2&t=Rϛiϟ.Srr0s{mXZZ"../^m^HOA^:>SSSaff,/pXÑcDDDY077'N:Ʋb쉐i?ԁMӧpa1BRTK.h4C&IO|*g)eҶm[/d~6H}nݦjcT^Vq1 7ےvIGRMlT۴n-c .yJۣGJ*'Q0 #G >>^oyv۶mZg}K.eىKnc+=Y';Mc뀩'|뤡6gM]**Ob$EA\:>@qHcDDTbQ$凑(HJJBXX6mڄ-[JZOF ,@dd$RRRCy[Ӿi QVUV~(L⭕DFiZB&znB;t:u^^=\~o޼9,Zb -ZشiS?+V!Cp ;F hZ{8~^ѣGѮ]Lu:t۷S鱐\.lj't 'd',, NNNYIۓ2ʯzs-R4n/^Dr)i7&&ǎ; "z|2,--|Z24&DEEyz?i{=9rd8xlH=g5JPqaΜ9Xx1K[oe3UEm={6Ə9sٺ.֜~32=R#i-;v?erJY?Ü\':s&Xne6)\eu7u|.\#Gĸq0k֬|;ʢ3qQQ)]r@o "J HHHOJ|<<==qQL6 gϞEbb"*UM6ŕ+WP(b:a IDATY3mYIzjQ 5 }ŪU0diGŸqpUhZTR 4}FJ) ~g4h@ob{{رcq]8;;cٺ?T쳫{|2?~ BFaΜ9z[_ųg`ii *]WfHLLđ#Ggggݜ)YIKNW)eVX?\wk}d^___c 00Çp@6FKq%'&v8}4f̘ݻw033C:uбcGIǒ1 )9ѳgO̙3۷ou[8wP|y9 cĉ °a0\_{oj{Gy3ر~~~vPb\]R:q6 0?Ɗ+3ԪU !!!zJQN=:oܾ}; TqXֿ ȱppZLDo\RR݋1"j*\Iˮ*.[?,`8|0^ 777 pss9v؁}nݺ )ѱz*<<<鉃8rcE}*-FDL&xQ1sss3Be߾}hݺ5Vu2dH͘1c`nn( o,&?Fx>""*Z4iJ%Ñc O*8 4ҥKc e aaaHIIA͚51x\=ᴸhժz ( ;LjހFb^O-99Q4h@N߾}uHɓ ixca* Hrrz09n7dDDDycDDDDDK,AѢE r |D1٨%Xq#/OOO'洌1IIIhܸ17nl: >3Ey\o$""""*@W9 pq[_ϾecGmORE׹u<)cLhh(.^u]ىInO,3QqQ7nʕ+L~y6{ ̰I&gdVcѣp-sTR^E 4L&+A(d2]66l ť@ciTF!m(.cDDDDD`׮](Wʗ/p8;;ޤ \&C#U* VPL}Ebb:ƒၛ7o/_^qu[d -[ žSոx".^Z]1FDDD7-)~D% o$G7Cg@RSbX[ AD ϟ? n߾ [[[ݻw3]ilڄK.MvիWx,֭ \uӧqUᣏ>T1w\ `ӦMHHHΝ;%;N,s\lٲؼy3J% CTb#Dz>qؾ={1gAEޏki?8rJ:A{!00jz|26l{߱cGy"ȕ>288nnnP* Cjp}ԨQZpuu+W/_DttG{Ƒ4RۆK{2e@R!%%EL&( BΝsu x0vq(]4+++DGG7;ۙҥKWL+_yrBq!^1c&Muĉyb+z8r(?"G1N""V^ Jmoc 4h7ze…2vK.h4C&I|HLL,,L^+]4 555S޶m۠VgҥKyNFzCi4ԙIDKA+> L\ZSN)e; )DžzgݮI1"A|""E8wZ->BNQmewXll,6mڔe7"66иqc̙3m@JԳgOx:v[n媾DGGcƌMHLLDRRRϗ+Wgݻz[^4iɯz)5jX`"##ɓ9ZR&BJNacckkk|8s O1*ȿVb[98#"" ,UشiТELl(I&͛~~~d7o >PJcd2Y>C̜9ϟ?Gn9=amm @.Oׯ_?9@R2}cǎ=z 5kM}-[V޴r*j[2e  F ;;;Zj:l2i SĠ_~j D`` 4 &bhܤa$sL!BK5jdJLb=!;v쀍 Vׯ㭷bPrhQFO[ئM=z͚5ZJ#w2oرcѥKܺu Æ wGGGT*jlǏG˖-ue[5 UVR j׮{j!z=zlٲPը[nީ{=ٳuԁJ v܉*.UoV"'=vJժUCsN))̊+{ІTѣG>>>w{?랍^b?!oɓ'jR2ڶm *:m'2ea&tZYa:th1Eb.kVͳR~f aXvm8HDDD o,J%j5Ԯ3kΘ;o.j5-[ѩ:}^=U9Rzއsh8}ny{ݣ!S. K\+аQ8W^AЬy wW_ԻX{z1SNIIS N6}RS5|Qf-ؕwm۷4-͛npr!C!&&`[vw+_{J#㶙* Xh1\jAХk7mtIJ}̙Z\Pj5tx3bӺ29"رcQSvsO?qEpu,?O/t)5q#$|1q/?z3۷Ǥtyk#Gby:O81l N<Q1w;wY[[CVcgqEcpvv9jj̝;Ɣ)&۸mۖ7WGƁqL6m 3QReTT~~~Xah++kϟ,  ++kL6AAAۢR|J 5jԔ\Gm3U~u,8;9C2ѺԷaF̞=N(] ڵkg4LiرYcǎ3DD%ʕ+Qtixzz?D""*F=1c[do؈6m'MuѹsWTQ MFc-bbbt>|g''e=@onth'O渍z3TJ1c ZrQC8::;;;ÇGnX#::Zghڬʗ]];Wƴ< ~͚7ǟ\GK)8;;q?~ {0N3g΀RR̙3J"tѬY3\t )))O""b6l؀w}`^˖-qظi3FϝzVjj*T*^#ԩS [ce2VXZc蘭61C#oҖ5hu;WZaapqqܽ{ժVի+cii&8el4Ԏ-[#ذaF|9.]\GצWTIo{STHNN;Lg_Bn܄m${ka6DT9ڵkѴiSȱHE$''#-L4{oF,{Z04 4nxrgg'8pIIIz 6&LDxx80?f BBBܸqd ÄRFJ7˗- 'Gx&L?h8_`$''!>>ׯ_7ؖW 557F||:/hb% Əǽ{w~ۏrSߌW %%QQƛ)۶mʖ- GGG\vcDDD% G0^A@ŊѶm>TT` 'ʕ+(W~SLwMĈ;v&N@V fp$= 777ۻ0pj [}l,XQ .OS}?W^-:tbbλmg e7Cpn5S;;\rva%7s~a{wMbp$'%knw :e ;|xxx`&8Ő)SM65k1a'QqcL2x,--""85lY~<d@OmybYLVJQe BP,e 6Y*J҈r1KVrZB%Cn)Wˬfr[LrEzG<}VkY'NB///!7nZFiQطg/!iV wDI*폊0.MDDrr2|||BDDD%;Lj9Lj1LDT`ԨQ '|€v[;[hZ@&*r94Z-2DT$]r>>>h޼9"""2cD׮\E}P9o= k)`Q3b?~hܸ1BDDDFs(ԭW!ׯx"-llʡv: +|||?` 1|TՕA ""*>lll3.]A!"""I9FDDDDEԩSfC |8|H:|0!"Bn! AEV6mon ?oooTP^ZfP(߰s(D< rL c)o &3eXv-ڵkǀQcQ~E "L&1DT <ooo <g@s( "]cDヨ(9sUVeP@s(1*8rŋ1aˋ!""7cT$EGGv8Q)S&(7!Z.sT""sϟ}]2 DDTq0Eaα(""s#x*V:` 53-$>2}9cuiN!'VV3{6iyLtt8>}irʥvm -Tm)Çܹsذa"""z9V<ntG.]>!,,o.m<+++I\+?`pزlnnny֧OY]qޛό卍7 .D@@BDDD)a͟;vIQreT*B.fFzQ^}̞V|{ݣ!S. ',]f[;z$| 4lkW>zZA/;Νzo߾;3Vׇ=z+;ww9PA̔`y@~diSRRipptMT.߳CGԨY v+w; /J[j5ΝGGGX[[cʔI]1ct8;;gm6h4Gm߱={|(qmܽzف~:Ƕmےx#"FƍFcmڼ~3gRʨT2~VV0337Ο? X VV֘>m: ER!8:JjԨ)fڵ0YpvrJeuIoƍ={6Qtkhرcaii رc QvoR q=mۖA!""Bs066x) ?zuL9;9ѣG"&&FR޳ghmO#8i)ʕ[}7ٻ={}[[[Id:tXoǏCD34ȓx{Z( Pc`Nx^]iml{34mLo}*`;`ĉXl͛w~[r_*8;;q?~ L('ifΜF^g7Q1`D$YLL ||| .r z9V´nw4_Jܿ߭aZ2שz4˯XzSTHMMr}WBE\ >([uO;vg<Sɓ'x &Oxz7ߌAHHRRR7nxw&"<<z;;;HJJ[[͛ti f4J*͛,g.R o=;7 ߔqc|>^^0q?~Ǐaĉ㏍֑ȑFrrpumz RSSиqcK!..VR>}0nxܻwII{. ߿?5 W^AJJ2^7SQVΞ= 777pssþSLwMĈ;vnpcFWu/FۑU]+VDf,ޢ @ӔDߏ?իWuˇhS3 ?83cСRpws-fjǷ~W\9L:f?С~_ dt 1ߠ}{SL}/޹d}@2tX!-oLyS 1*/^@`IyZ(DE(DwNNNHLL{QQſh /PݷoƩn"NhNEg&"w}-[`ڵx"""*69F9FTa"*ق퍯wa@aQ>Fd2`"H.Cբ-ATEEE)))v*TQ1|bcmkW[}(䜷Ap5ڔc0JyaƌXv-zQ1|R^]\EKrv)u0D% ooot^b@D`Q> Q#88۷oGݺu"""*1xW'Q!Ѽys>}cDDDTpQ t-f͚jd H#"""*aƏ;w"00[f@DmDDDD%Ğ={P|y 44cDDDD1"""bŋ(q"""s( Ett"E0TTX6p`a={6ѽ{w(vЛ+ֽAE։cڵk3DE̩S> Q!$2;LjIttvQ *Zi}v3DEBCC{nvn1|,P18`"00 B`` """" 9FD";Ȩd QpMnݺe;Lj1*5FT3C`` Zlɀe;Lj(#Lj ]v~-nܸ;(Ex'yeʔ+1/?Q,~׬;oM%! *d?ooo(Jܾ} PvDDD`hذ1Y4i^~n5lԤ@#+BDf,>/?*"A?,g\VXӧO󬮚\ ]KHT̚5 2dvɎ1"""0oR`Z 6`ZOF?GDo 0`&L  ;(7:~Y}}ŋX0^ޣO81l N<Q1w<>to/[f5nc7&  a8v8֬ vdlað| ]g޴cVsoz^/]/^ĩ'p =sgߧq?< ;tuy׬ƑG{.?w5 + <-]"fU+q۷ax"^%)-YǏG \F_03Wz8Xd_Xz5T*ڶmǏVZ  .`ժUXj.^Ȁ;Ǩ}ܹWރe8;;cشyslg{ܾuw8pwsl۶-16 GD=č73ڴy3fDJQRea affooo?^, ,#1}tlJBpu(*ԨQSrTka FR߆1{l8;9t2h׮x3駱c"%Ǝg`୷¹sjg1(DoL![ク/.V G sk<}4[do؈6m',YZ~[wGC=_QWT)<^L9W+VƥeXh1VId$v Z ''';w-Z`PXcz t:>k99ЩDD0pD aݺX)))tHHH?ڨj4o ?t:/:,t:6ilhnطo?p/&L 33O^Qamm xbjj*t:bҤ1"~GwP(0t_]oRjIY<~ޘ:m*n޼7o`i0`@}<>d7gϞEvv22ܹs^˙3QA&MQ>j5݃ӋԾ_?oL2/_FV=Rd֟/&3g{o./QIJJB׮]i&$$$` m`׽{xm{nR0vn ߇^o;9gaq0{,7~8 27u'B1&˟>S|4  `JO5ׯm[ ŴiڵkPTQɈ5km^};(~Gw +VD͟+ޒ(B|ak qvHKK5!s`|0|X$ GX'㣏>ƟQQP}!KfW.S r*G! >GlBTHrL)S$Yd+d*kLh%WJ62le*Nn! $U7VD:r4}!zĞs|S!'aEƔ)=}zA_gJdr9vڅnt/R\vPmoOA 0_>9s0 DT_3i @_FjjTr+a\+a^[/ )1E̒RE\eEBs\1W'sur\)]-:I?Љ9,UZ/܄T1RX(G)`Q>z ܹ=~Hj&*$^\v~gTVA!&*P|O%N>g$ )`r(G~DH99ae"zʕ+1zh] b@TDFFS^z="""#"䘑OUcDe3LD/::>7n 0BL @d L^ 0n88pk׮Ef"*uM6Jx걓r9 &LjFm3W2De(> {My̙3qFƍq9?:;5b 09FTLjשsqwNa@)_MyԬ] *7o߇-nܸ[[[N@ؼm|q^MLcDŨNݺ 5kVXk⭷b@h-۲v;w[N,-qgFLz=qcDT&=v }4m o#"1"""2[DՍ7η޽{ၚ5k1tP$%%СCpssc0̨ߤ ֭,7% Pq8| r1bUV  `D6 Dd` n Aؿo߾m:$$vvvڵ+8I 6Žl1`,U*|T^*,>>3'$IŠ+PN >&"1"""2G6mFG<694vvcߤ 駟ӧmoDDfɦMXiN>@@f/V;wxd"2zLY8}8&ntx)|$ rrЧS',ڿµk "//W ٳg[n .=&Ljхzɷ *s*:VZA5 2 m۴)41P9awꫯ^LIp=DGGPE9pttDΝpB^!"Q1pdr`Pup$DeU@~X)1CzaͰ  &Q!ot:97nd@19FTLRS[D2M۶سg7Ae{F% kT*N8 :k֬a@19FTLo%CZ^2rS&*UH%N>!"2u]tA||<ADecDI 1AFe)DD<`rD19FTD&ǨcjLcDHE&ǨlcMbBbq̜9Xmggӧ"*ޒ$[9,$ZDh_eHJJzi}b^?gƀLò-[`s'\I0WO>5k" %"""2!/cJ%d2ʕ+֭[ᛯ"4l;oP4inѱc'NG:PN=_z_w{WcW@;`p;tu.{ V ow+W%Ky[Ǝt$IX|7{5ۯRc- , A@(dee眜\|6c&\\ꆙf#7Wgߩ Ѯt/>NŋͽFDZZzײ{whа*8TD7:ﭰz`헨Q*:VBne YU[w 7kȀ|]* IDATYWqDcѠ(5)5=~#d2_!C0DDDDL)E¿n/}t=\ƗZmRmXߒ%Kpaa/bΝŨo0VŹjUzLE&Mh~ZX` @O=O ڃQQ#XoF1l0899a֬Y (cTt:ܽ{P_uFRCm9c*Wʕ+c֬ؼek4 ,--1h{HO5jCٳ^ZJ`_D:eؾjj,ZP՘>o_]~=fϙwwwXZZťnWሢ?Gl'6mތsRʨT2Ν6䭶SCEDDa_Ȳ WWW٩1k,ݻ7kQ*8{ %W(r[ak|>sCK} 7bpws+ؠcǎƛKeҤIbkkI&="SҠiSRXPAp^*,>>; \.͛7}v#-- P6md[$ 81cMDDDTFp13TJn胁> l `rwsÍ7 Lve=F0[[[dddzMHKK3ߺm^aU(t7oge(m$BT`…pDva?p.uww7\~=O_hHMM5'ݺf͛9R:`iXl/^vV>׮][UHo/7oXT4sƸ ??-n$0`dlڄ%6hժ<<[JRI2%VT2JJldTBf/${I.E ^֑ Q#&<\‚f#ѣW/L\.]Ej׮^ݺmZQQUvލ5kP?k֬hРA8uZ!DDDd6oXFO Q#Ɲ zIH)b*BL =,K(r+抹:A[J^nIzNgr%x&1B_M+Lj$Jf8L_FF^ygذa6lXmΝ3Ljժ1DDDDF1b$B(11Oe~~effںX[.~|ԩuVˠ&nj#*Ka"S*Ǟ{޽;K`1DDDDFe}{z@ J.en @ף|{M&KEҲD7fH+WZjJ+LjFm3W2桩l& D^S UUcyX9`hZԫW09FTLjשsqwNa@)_MyԬ] UZ1pwww}bxWjm3""""cDŨNݺ +Cƚ5kiӦᣏ>!"""*!EDDDf4+7l0ddd ==*U֭[JcDDDdrqsETTvލ-[ĉ|PDDDDň*lSأe=z4<<4"""cDDDdrQ*?]vOЈ^2&Ljlk|||pmXZZ_}K-c{ܴiӐp4l+" brVY{ ֮] 6`ƌի^ʇIDDD3$1D?CY{T p! <m۶ń @ڿX'2JD$A*3bz쉄899A.cҥ|DDDD@m:YYYddA!'IDQăYt ptt4{?~<Ǝ xzzBբK.|DDDD`rS@NNr9BDF#'7j2*%ɰd_ VVEZ XgeeF^w1TfZr!ݻG^Ю];hZXZZ=s*EΝ ///a|DDDDar̖V=nĈ;w۷`(09FDDDf+7|W\иqc9rA!"""Xfkk=[l߼e  ++'8q2./6"X > SM+1I7x+W,G^=op߰c{=(aE|DDT*X9ZƺuvZL:}7"""2iLx{owt|BXXXXãqqqJ€_,Z?̝3?so"BVcƌ쉈Tr,^^^8|0 -Z`ҤI ,&87774k;vm6pssͷK~zjm7WjhܤrsuE3ga@h4[вUkP˺ ))Jcݻ7_XZZbŊ &L@`@֬ z_}5F ,m֭p8Ac+g5cƌ_.رEӧ_}9;;ߏ\&% |DDT+'"==QQQUǠ`r4o &OA*hثthڤ)2329~GꐞIamm A,-,SxfϙL mCa 8w.999DLL,"87l,,,|r޽_|tK.10DDDT)0 xw|kS}Tg*T(3gϝ3nj 2Om1u4\z * 5j07w͛$_sc?fXOJC@RaH$$P>D""*QL=5k⧟~?]sj ILVZ")1I=}ݙΝ;sNիM+`@>4""*UVb~mXl,--1|L0!""2*(VtHLL vŠQ%V\-Bxx86l؀#**A!""21"""2K{sN̘1СCQcr+NJOvm۶pvvƬY"""2ZLYbX2dRSS *`ӦM {223`TDTz=rss! +93gDPPjjѲeKceܽ{Pre5d2A`PIQ7*X9Vʗ/7ɓ 3Z-*UQbr?@VV*XXX0DTDQBZl̄Fa JXfp1 ~~~7oCDDDc_Y-FDFC8ěV.ooo$&&Xz5BDDD12JD? 3SL;wpIԫW`PD19FDDDfc V¶m0osce۰aÐt8::b֭ =SŒfzaB>ސ%ׁP Yd) Jn r\#W\V*$R)uL ,2LA+%d2`'^* wr2DQ_e9>E=bbŹ*,,,i&""z3 sرcV V͛30DDd_}ypX%l( rTX[l_|ѣGc""2j'O ))0LQ&M&^VW_}ǏG׮]QvmL6A!""ձV݃0Lm^gMUC1vxܾY}Q .h =zM!bGIep&''CKDT 8ߘi۷aee[[[|W {w"3>a338{q]aPTxg'..1%GI'bm~vvv8}*666|DD\X9fNDaÆ_""2Vi<3C  2yΞ= ]eh_qhQ*d(WZno^а!CѤisWGǎ:>C:0B׻ȫ1ܫytY|mW:"&&j={AC/U]ʕ+}(behޢ%k |ڱ$aj<Чo"[OCF?ϱO;GOAyGO(3666Xv-6l؀3fW^z*CDD*cfJE$''#8X^k 9˗0i>[ԧ ϟ.֭[+WƟQQ8xg<3"""z'Nġp> k֬_jq|2=FDDop| x3? 4C0x`mǏgPTqX`r 9Uu7Ŧ[0 Nطo?z1Nw#h4P*ppض-3g@ʕQre̚5lӟF% zQF X[[w{-?j* ~}yaUhBVcOӾ}E:v=gaii WgYu˖;g6akk:9iT(Xr96oނ3gamXj ?DDJճgO$$$rK.ePTpX7g3tUHAc㗟A+єHNF ؏SU(Jh4g (7nnܸo[Z,ú-222 7{{{oJF60zo-߶oQ511../'MȀ@XF;QX9FƍCPP V.]00DDTb *13%*`Ñ``]ܩSq*ܱ 1 O*UWT=mi(ggg ?ׯE:1>oE֪Upvk׮c s͛c2 K,?e˖7Dll,CDD%W00GAڵ똿`!7kfاP(0t_]RND$&&O?bW??_L0111Afffϻ/>: 3y[AaI> N"Kff&|ƨ|G9|TmGDdX9F޽{1af`UUY(=ڴi/пVX͛s4t~}Ë~ARad@ `mm!tB @vN _>|-!~Dp5ؠYE>o0``_I`%=cΝ;#&&+WfΜI&10DDD&N0{ g۲Q Yr5@uIRV9 W9r`+lB+eJR'ɔ"[!SYd*D+RAf+S vr \%j;9/ Goo1\/$,\֘2e2!g׮^ݺmZQˀQ3gGLTb>WA(fY,@\1W \2WJr N,tb>Ko+< 7!Uߴ IDAT pRAad]Ьrb8y$mۆ6m 22A!""2AVIG3DDd222PbE UJ!<<#F@:ujVh"""1"""2;gպukDDDSN^:>3^@ GנAAhn:cr+EM>W\Cиqc9rA!""*8~H{rr!E=DI29AVoӧ+Bɉ!""*C33w܁-T*Kd2$I(Ɲ&09F/>;vE ""21s{J%TP* JxB,m "ȡVI,X9F[nC!Z-BDD "nx5nj=ѵ;r%ްO%KѸI3ԮS9VE,[-[AC/?32S}qyڏ=S}dbX!! DVVVg3f.9krss ;uvҥK}:/FzO epDI'DQļ?СC0r$lll`ccbZkábEڲ%DQZmدj*ԭW+³F tͰѶOQѺMTttD&Mp|dnڵk?rd|aD`1c@$\r"""`r,22@̹hѱaߪUq,vly]~~;|[nAE,^?Ϧ֭[X|`e|3>nr1$^} .̳K.ũS~b w$_;c)}׬ƁoqDQs湆vGw B߾}qAO?#Մx|k_cuذa=_ߏѣADlXW.aĈyWsXùj\*|WqD掕cdꂃq!];vٳg""`rZJ/N:mؾu6̞=nA_sk`|BVO>} ˗/Ö[1kl Ê!P(m[M&~U񈢈?}#66M7c٨T*Usb yþe! f޽{\ݻw wEJʕ+#%%%yj5 ʗ/u\ -Fz `ggg?t(2iDB?[[[L4=+aݘ:u* Ç#33!""c\5HKK3'&&ٹx]{^ ))ɰ_`b5ẋ3;n%%׮x{<Ԯk {Z}v7m/ad` ƌ.c <ΜŋaE*X9Ffiذa@FFuV_LK1轁4#={:yIdff"6<~?o(F)1 AF?0!B6oooL: 7n70u4п>]⇱#Ydee8{awݻ9~o| ܻ{R6lرc:mI eddd <<<~oJIT*Sسgzx,D掕cd̙h޽-[ĉ'""2{ ^?EFkaccM/T*G#!!֨Wnل>Azu1;Qg`Gaɗ*G>r09sư?Rs0>6|@TԮ]vBZ "7oرc` ыa=5,L.G p\KB>GLTb>WA(fY,@\1W \2WJr N,tb>Ko+< 7!Umy~X9FT$IdDھ߳kV*kZh188T@v9F *̙3j:t(CDDf1$ٜOvMׯw;*=2ƞ* 0uT!((ZZ۷g`19FTB4!CT*d2z=3dX9FT0]gΜAPPjbrh:zqx*("L45̖$IʂA 4C{nk.10DDd#*!5kX TXZNNN *&LjJ$29F12sppp` J>;vE0`.\QJ{5krt}Ӧ2 C.Ko)2*JʗWr=Ԭys+_^5Қ5k.z^̘1C>PˬٳV 䂞c@˗kСj߾ "I`$PtM6E۶ӨQ/)S+VT۶tmٲE_\/WЦ4}>>ѣ~uZUvݿ&IZK$Ԓ%K5QP:UpF~A.T$tҲW^yeޛ2]6mt2a^+Vr→rʗ@eUL3g/JxBCC.;ܺ=WHHԻw/ڵeO_O1!?p+;;[ LP$Pt[^:vRDd ]9NY, 8@Sޛ#GpZׯ> uWj׹"##t!zZZnjq{HgtF94w%((Hci*1+V޽{ݓ&'RŊreff9eggp\V{/֮s%'5\B*W3bfg䃥z'/zǝ[u]={yqK֩ӧrBu=4dƍ6mZԩӊyQtL41ObZ<*+$$D}[w}jРTti2u@1\60[/^^^v]W'NRvmsӭkWzaRS)5F0Z{twjr:qlxxVRڳ'9X$iժU8yR)9jzgSßVBbt)%&p xq͚5[oeee<Gzgti{qX-39jٲe:q{[R>2MSe) 9-Z͛բE hر@r Woe]˅%ISNWLLcu.5mT]޾ܹ 5kq=&m[p磏-jFUZMwu颕+VyX8:v줈H }xlܘ;w"۸FG^7N_jtiFJj["##VYs (9tegglٲ?>A\4fH.W5?]ִZDUeo.V*n6Wn3isXVӴ:$jq(es~VY>5f1Mݎ^}V]waC!sܺ vEnBFFLIWOH|8.ծа*~f~}tr\eN#Sg-Fmd;-l;LQ'<"9^z)--MJ. IDATfΜIP#9"0=bc s=T]Vի S$ǀBdF[VVu@QD1O3fмy4vXyJII!0P̐ //A:ta<=\)ӪUt}Ez #$ǀBt}w>MɰJc;C foMP 9s^?^{]rA*{SN*˕۴qӦΝXU(9mBeꭷ_|ɓ'M6JLL$0Pل(<GxCag:zh^Zl_xu[*wkZII;sNϿ\ގcJUWe˖驧R.]̙3c@!25gUVMk֒i*SJ]KgϹaQQJMMrҐ뭷ÇAs)Qp5nZ V@@^yE1xcVvf͚۷[o3g(+;K0}v+4$Ľwu?%Kn*V Ζbn'A)33SGQŊtRW1gh~wo۪ԃr,5ҧ{ʕ_ozS޽s qXy``-['N0 l6}j׮eDz(j&LM6iҥj޼6mDP*!9 O6uZUA 0PӦNs|TA:v6Հs XKiTVm.^=z*((Hcc7s@Q|c *hɒ%0a }رc c BtPKOǜ+a>wŋr=.VNtG'I҉ 4ĉβX-yn, ȡYfڸq͛jժiС=z4BB1)0 t^ڵ]\3gc]^@D1A>}tQl6jΜ9 11ץ=oWR-zq8Yjɜc z(NF^Z 6w}GP bX%PHrbIV\.s)Phڼy r7Pʕ wYB mݲUaj0 mez(fKM6SO=EPs ($Q5#1Q6Gk .`EDEr)PuE]tĉ奸8=C 1ըY U@1%œO>aÆiذaQtmV J%=x+;;[ LP Hc<ĉvZ͛7O-Z/BP"$Eo%=x}GZf>5h@qqq*]4ss xn&m޼Y-ZPHHƎKP$+IX,ZjAє)StRM0A;voF`h %֦M >\VZ/l2nZ:tozmڴI6mg 6$zgpX,Լ=XeX4gOyԡC޽[ժUfo/дi.4d6 ͚P$%p覥KeJژ.6efʔlx{kz2.GaJIIQ*U駟4MuAڵsxө)N'aE1P"l^^OmNz^OMSDںq#|x׵zj͘1C˗a5jasuT^EYZc֬k̘1fΜ G9 X(.W-@;z?WHH"##UB0t(a1PM^Cx.ij…gUrrtAa<[$P"QLceddvE7"'۴i\ֱ6Iyf <x(cYT="JaU(}qUOj 74=7BiiiEZ 1*jչXR*6'FE-P9}|˥L <x,cEY(gINN֎N}R.IJڑYfbt"̀myu\ٲe4{\Ϝ93fl2|T(AHy .ݮ0=pK|~ٲŽ߯Voh=>>>ǍUkTwvV vמ={rݺM[UZ]7t~GwuQ膪Rn~]ֶT[Zުg2MS'1Rv5edTVɱ}yN8t:TVլUG^y5GRr).M5nȨֽg: [oMvzcEn(111hpoK 0ErC4FnFIYbڵm:իS]7nҚoW+iGZjӧ)1!^_#* ZkHܮ:wqU]t9͜5[_J}T;kҤWs/33SwI3gr'xRuI}L_lѪԪƍ4mtw{S?>ԦЃsk9ot"~Lĉp@>5lP?|mgΨAxP$}щG \sոǪJ*VXs\g}.I>Qtfo%3z*T *hZp|EzqRԲeO:M'0jԨ| >pKohpx`$<Ё{_~@}\dX4):rAyA绮`?~ܽuZ-B{ﯓ'OT%<S\dSSSr>foRʡT9TG+5P ,\1G%=7aXa{>Ţk-A(%%Ž:飏>VmUyt-]圿0=ȣzxLJ},KU啒7OבPj܋/j* Α+sOr*U^\v^MxU~ŇT3g(iNx~>2=U Ð$˫@GnF(99YgΜ9g;}$؃_{tw撚F)55U9rz.ӧ>\۶t*==?'xJ ҩS. _|qp6j+W*K-r%iŢŭZq<xzPPP7n|zTZ"jO{/>_T^:_҈#Uz7hȈ}C VƱtGgEFG'ԯs^ݺjykk5l￯vCV(5k~bcc맻tC("4֨F_7V&u0 >bb3Z׫-5@CEb޽u|!s:Z`իG{bbQ2e5t-Crο4]ִZDUeo.V*n6Wn3isXVӴ:$jq(es~VY>5f1MvyJ|Zu]RBT.V'NdH2gyH}Uvm}dnl,6E%`6oެ7Jbcc~EGG,ßV2e_+V$(̛j)L=kReZFn8ʶde=}02Fdϲ[l#vZ\Nwytټ\NtrYLWfOtPfKD?bra'[vȑ#N9"S+::ZVA^x-[0M2Er nk_S$Ν3F?vEPȣS2 ٻ{n5mT3f̐7APH'`IҢEө|P_̙3@cX%@kر;v,Bp8ܓ@rÜ:uJGfj# .ed\ /P"0G||eZeX )0t9z䘧pCJp dXt:3@(ج6z3l%1 0 aU۶7eOIKK+]+qN@r ᡏ&M +СCUW@@6nXEy90Pr1">>^WPPWQQj&IR԰QVPkܹ2M}W&MUzZMR^^膍TZw={eijܹjڴUTѣWߥk75lQjykkdGDVmc={fj(VovnawUӦT~{CD].|m5ljg9T- w$P 7o#G)!1Q9~\N߬Z;5Z`_.ꫯbڑ] ?M6i}ZR^x]6-\HӦMQҎ}Jn]ۿaF}Jmߪwޡ3f*״ufuFz! Іjwiƌ9s5khvLФI˧N?X ?Ar}KxC{u駟QLl wyҥeU^]7ejヂp8Tl\ >slԩ#ݮ34{9WPPu=w+##C=ܭJp3F .rM6]L0׊+/ָqcTJ[r}K՟x0 xq=:}&NzM?Ι}޾=Ypp?^߻wj֬2X``233:yӾ}iii9RHHTyG1RJi#CaÆ\˓SRemXr+%eos~;w|ʕݚo7e{m.wyJs wK ߯UJJNN89sFUV)I%'hQ٫g{#ӧOkݺrr:|XKI&z8wԩ5xP*׼a@;dCR=~\5Tt \cyfyedd(4$D}Q>W>>>2dRRR:uh¿&^>}O111K Gh#.Xhт 喠:{۹goݶM }.{b>\zXX,CzȵjԿU~/'"9桂x|SLl٪ӧOk-4~K`'$9@acX%MZHns{nޛ2U1MT="J=zʱo[[ZHթ[_CGW ;KM^h=9.]aXUR[[w'T="JjmvUͷԏֹw6RjjߡIdvv"""4~K|-g_m̝MZH5i#NS^yUuWIr\lқobUVmٛ?X 9+ꫯ֊kGv-\0Ϟ=GV%=~^}uBY3+iGfbcc5f̘{S͙;O߮YEhͷe&MzmܰajoU;3kںeYFӦMQ-rmڴIW}֖ 5YɯK].vhE6mv$VHM6][lї+W˕+i&M1:u~X>`6^ (HkLAAAr8*[%;}3FU[9dULyG'}sڴze)00P?VXc$ooosPdd|}}u=w+>>>!!!Q믻sG}?oS,Ϝ9_UO?:ok˹gΚ_R:ud%Kj TuP:-\t,ZXu[ %}α!hV,wVEEVbYVbZ,65 IDAT/jXve6al6yy;o{{{>޶R>|{{ڼKͬQZOi\{,.98<pްܤ7 Tff{_'Or[^^|QII;wNggݺuر{{fHL?5BBBxz3 .{ϑl:[w}Z|e]5JM=Chu]//wsTzz:t@`'(lcǼBÇ?#oooEG7p ~Ae9wݥ[>yw#4zU^]}RIR{GC UJJ|}}UN-Z8@iQڷo|}Q`jV:tH_|V`2dCR=~\5TtiwAu ;8+L?];w_{e/???ayv=C 6\9z(9Ǿ~v.p)!qBC*RZ שG )~޼Y!++((Hhݺ2e2A.KrYYYڻoԮ,эxflezKP,7W !`U`p 2p,㨑i2N-vYO2Lo)ٳ#6Wm0]6/t9]F+Uxa$Y.A%2'XLJZϱw [;9ֵid(]ӥ/Tjm,o]gv:iuHN3vQ0[},6/kb6kQ( } JI^Ӗ-[ui]{mYܢƏ<*=T:<<1x,cX$Hc*LBP"9Er <1x,cX$Hc"9e'ixO1T^J c*xڶcPPLBP`}/Q݋\de?Q Ĝc(3gɱt:cGRmS =PNmZUPHTp:o׫~;5iА9N?erӱ?T2e]zrڳ'Yg2(,4T6lobQ@SַߟW]UPj׮ժ髯W{)!rayqԨkБ#GdΜի߶ ׭SdX׭WH?C9k֌w߯ѣGrt:u1><2zከR%\u u땑qB6]USf 47kt"#CC˕vJܑIRxpoOpW{jnk7kt v)SFڶ@!"9*U А/Ytc/~X,ծ]Kk؋{9{vykuEFF~PHʠ -^0?$$ 5«c;qϲ5k.ڭ{TbB|}}=!AO>9\6M}=ה˖-ZG=iӦI6nܤ[wޝߟ+Izg5stթS[+cر?UBWXQGͱtҲ_&N|E's-^DcFV TB;Z .ʱO``|||޾CRոq 9auСz8\:__&OG}ڴ駟L.HջvչK7=kgufϞn xG4ue*##Ct*{\KB!jӝwI'?pzԣj~ҵ;7^K.UCz;OHd15jLԫWO=cfϞNS}g{>^>v8}ztK/i4qM#Yc4j ۺ[#95 Yg-[|qvک Htkznq^y =3fL|)SO*cOuglYgiӦ'|^JsҟpǏW'LԸqcÂy1sf>Ԕj ՙ9&CO 5i9b5kf͚jx^]wݡnzw^7Y=pjժ4l0M2~͟@HDmڴWÏ<;FHDݫիn;C6E~~$MPqrl5j֬f7oLWs׼yrVegg+ )++k:4hp8ƍ'Tg2"IpXsnm&6\ٯ+Ve˖ǭ[ү+V\J-Zut8T~ч5r3vqnw=+P j#zjݺ/_.IrG.LcnrrԱc jUVUV_r6lXnŊ.˖-ӁTՙ9&0s 5S:`͛7Ӳe⏗,Y=7?s=pJm^>rZ\77֣&b Zbu֙ HZpc38]3g[o;:m?:㌌ UXG8pz:EÆ t鲤Fuu&s $ǰMp`l9l 1R+Wʕ4|H7^~iMhܹ}_x@8&͛?_hT?Aѹsg__P6w A$F"JRnn|ܹsuͷh tEFω}*^ze򔗗i„ 6]n4yrunZS"reZk…zw4xu 趡CdRjYՎLPXm"33Sw}nР˵.//ͅ^.$^~Ek/ES׮]7_8PktR+;;[_YÆ 9ըQc }X}| }>cF?WF"vUGKj٢@\p~m1cǪ{nưuVګ4et衇jk-n2f;n6TCݮ_7X:7x]ԪU+h&{gz?M&﫯RhTWn:_~㢺:9Tx|+7YV-v"^ r2/C a7[7,␛L\nE܆cZm|>f1vk}eLyeD:ߕY3f߀ kZFVzznr.{,_Z쵗4h|ֿ4s,4' @kI>j_ݾޘ4IGVͷ5΄_:Vj~n?f)F&/9!H ECN󽔘ͳ |Aa7Ѡ(H[-SۚV6'$mL48 mcuTzm9S矫uѰ2jh͞=G}yѺ{8] ɱ:vaǵXƿ2'; &6P 9@Er u1Y$Pg@Er u1Y$Pg@Er u1Y$vlw$Pg@Er u1Y!BP7}?gr&OfLM@Ar]>'>yPc,! LgG+77Ww>mjDV{xFNNNcQS}N4vGSm;@iÏ8JC^q"9syveff_RvUq_dϛy{VӴbJ% 9Q!bxPuTACA<=ܥl^(1FO>z8\:__&OG}ڴ駟l.HջvչK7=kgufϞnjc{ƙg]O:SKʕB!jӝwI'?p쬵zԣj~ҵ;7^K.UCz;OHd; 1ԘݻWz*,7Y͞=[ONWJ>||q/fAŗ^'Ӧi GhԨu-GFs4jԃnlZg=:Sj#z%A/f7^״܆Azf̘vSLU>'TۯJC;{Lo 1/8<}' n zuD7F?,?3g?CM^NPɜ/vcQ~EmV6i9b5kf͚jx^]wݡnzw^7Y=pjժ4l0M2~͟@HDmڴWÏ<;FHDݫիn;C6E~~$MpBBRjjm7oիW'egg+ )++k:4hp8ƍ'Tg2 $r"IpXsnm&6\ٯ+Ve˖ǭ[ү+V\J-Zut8T~ч5r3vqnw=+P j#zjݺ/_.IrG.LcnrrԱcbܰa[mVX &e˖Hՙ`G*j\NuO7oe˖m۶Jn$g=S .ATa-K/ZjUmC{W&oAflo?.C>pԻ i=?>O護">@$ IDATt|ǩv_>*jذ.]/~TWg2 e&naƎ{s}+WiU>b/4[4w\rpnr͟h4͟Kܹ֯/p_CP.T]+I}_sM7ߢAoZ(]>'x[kU\\ ۇ%|\7@ %KP3gΪL|0s Dff.ܠAk]^^@]zI.1F\7-_ի]n,pRSSujҥJOOWvv&:~ sfQ~ |ƙ_˖\m۵kWut/e8]pׯƌݺmu[j cdSoԴiӄcwUW*_]_ׯL|8 sN_9\βEnaRhuDx8^ aߺaɏtMeҼpSL/6znukSmW7ZKH$C]YY5c 8/p6aK[\~~zhh!7obԠA뭷3gi̘9iu\Z6WMhT˖ƤI:׌o $u&Ա\U,ݼN`:զ S") 9&fb&;ARb6^$mD m]̶Lmk~X!d듴18|@9VG5j&OzlN>EZ zNӯQFk9*,,7Vϣֽ z$v7y{`v;BL㝲__y!?,cH"9:,cH"9:,cH"9`{#9:,cH"9:+DUܤɬ 5HQrSg;o8P̬<CUCz(J퐄+m"uEr [yUmV{4N&9%1U$\צVW7Y,DϟzJM6UVv'JVff]nP1fB_ }ݧvm$v[3fԙgٳ/^^?}_?:F)x3*z\Sk^|%q컟t~MABtӁګ3'RCǃu\6Y3<[w}ڴɧ^pMÏ8JiO? @%HAzg_H{={޽ P#m7Fs^|+gƌO2U}Pfg5{l?uޟ:E_+= l񟊌?ANqS5c̤kq/fA+ɴi8q}15jtBuWjczk {-GFs4jԃ r YBS 9M>]W^yxb1 (gfꭷ[n͛iٲeK,՞͛gddbX}jذ.]ViyU{j…k/Mqe?-^t IzՕW 7 $ I袋p UΆJ1Vi钤H$> .҃R眳#r*\JGT~}ݺu)SۺukMľۆՒ%KUXX3KdUuiMhܹ}_^8P7IW4UAA_/{'kbZ]ܹsuP$)33S~~ 8p!-Y}n>\˗/WjJڶm/ׯƌݺoР˵./O_r/e^/n2f;n6TCݮ_7X:7x}~]}ՕF׿rsW.P]sߴ|/WvJMMW_K*==]ٚ j_ݾޘ4IGVͷ5΄_:V-K:Yml bN~Hna1E))Bѐcb&fb|/%flE~PM4( lԶMɾ>I dVcuTzm)'|sN=/6~FsTXXwoG{g@Cr:0m'ǿ2pC~]$Pg{xt;mV KgPn_"Yk庮!(6m1***ɱ:QÆZf P @5"j-cuLzz 0cH"9:,cH"9V c^` b1>icKv!Bk+..ڵdQ(cr׭SQQ 9 s]Oi%K*/o0Ab1b1ݷ- 9zc$`䧟]ɱ]PN ]ބ_"v8cz]$${NH"9:,cH"9:?3c;1`X{ͷ v![ +'_/_wkqt٥jסSϛS̱s釅 f}|C[kMmJ+kӲ,IgW+WJIIQfuYUʕ'+aÆpP.8ouUQt Z企駟֌ǐn\դWǫ~r*N}7JOOS?P߶uo&;vjKv>n}+u_/q vCry`hr_vo{RL:U?,\(IZOfI:|p+F;AE]9TIw{}:uK]+WƷ7hϩq'ǑGkõ~xyթkw~tx2^.jEb=:աsWٳ.tepWȞݩz{Ec.U=lzY:#ձK7JaGTuU~KڨnЩ O#!G{\A$eW\ǝ.+qHN:4똄ګXJjס~y$iѢծC'ti gcqcl:v7 )wjUm*ɞ^Hi7/v:iɒO?\:t|KdRI9=‹:ԩkw Z2jסʪ;DȶյUPPoMw;Dǟtr-{ۯ$w1lCoIPH/z{.*͘9+:K~眭&M3(ywxuN]wB!9rxHK.cO<)dٖZj$y}V Paaޛ1FTVU}1s\Zk.U[ҏ+//OXLSڵkF5Ou=%~emT_B/i̸gn]4K/'gЪ~Sqq 4mtਭjcMZ(uǓFk?1Z_~U,Sy)d{u ^5DM4'_2OJ~璤s5r'zmqcW4dk|i?ՃF{n曒~I浩mkɧֿ~GA=Vw 1VW׶|OЫђ9}w1lsjQÆu4L*/RsKfz 8\լ/Ԣiv`}2mV]NPԬiS>lgݺ|X|*;2c?Oƶ8w%IÇݦF`8DmU2{OuqKdښi}CVv 1VW׶|Ob$I7{AZ7ޠ_I;W> 8 @?a NaC'(_՞{Y I%귤|+k'[jwH/䯺*Dկի(??_YYY?0I*)̌?ˋo[$¿^?ޫgOd }$kzW[6{=8a]V6ǿDQQ$)++++kkTM}pX'G>%WD.=UuHJ0+k#jUқ=zHn]\zz$cUDR]Z;hѢըaCu!_dhFt!%?K"Rd{u ^ %fy"&nͷjYZt=>dt}:(;[{ݺFꮱ9\]8SԄW".hحT O2Mm[][^rzz"X1Td[]]= 3>\|r׭V쳷$}ׯ_ߩyj$I۵$y}ZlѨFq$)}-s!miVy"^?A:M,\rU~`Lf$/f??'/?4qzޒgsP=??7յKgI9^kOeUՇ?W[޸q˫c"m'cTd+:YL%nƌ1sfsM&}\&ljDe},y)'u݄ŪW"$gVU[ۏoT4>=ս&$z͗pj6\"1_ն̸:z5moIK.בd]y c=Vz 1VW׶|O_$I5;1߮^=8J:wŗ^.I~%U&yWءffV۵SAa~jyMO9Uv_N?S}>CPH^}(Sg~t/>gqVNՋ?x-zf->=6&oozu~v6x$)+V.А[n$ՖPw)nnPZZjNO}[W^}$cM>&֨oJwV:^%IgVAk+$S^kw{t%7G/MO?==T4誫ձKU]ۇoY5֏oԄ^QƯo~_/d)'T7uc=ta=o9JOK)'ͯ9K嶡%_q<}K5mz1kЕWk]HumufIo}x۞+S]յ-|q=:dg@`яՀ@ÎtԑPzZZ/v-oJ.xAOzj:zuG(~},Su->,B!9 ܩnzp}c ϫqz'Ol98o[|]v.|5oL)##CZ$ӁH$TuE}get[ղE ytT捩VnU3>ƍmu6UUJמ :uՕW$\^Um{kT~Mwj6m t=>>/Pztuknǒ˯Co6 [nֱVjj"#1zHnʒjҤn|pniU&$s'>LmG{nUmɌˌ챓O>qC¬$!vgivW\~.R$)w1Ov߲o?rӍZ/hבں F-KA:;o`ec2c{"$I%K/6*Y;T?s2\IDATi'gr-r 뇝ECㅼ ꅜ n K~8䦦n/m=Wndz؆ss֭˕5\>H3fҿx=>cg7𣏕{Nwn}(YweK+G-y~J?&0] /&MQ8&j5uU~n]N`:զ S") 9&fb&;ARb6^$mD m]̶Lmk~X!d듴18Wp.,j_p8Ge͚) I&:3uӍs2SFc Bj޼.&0΁]?lu8($"eRJLI0s*q]{:g}~, :7UW "PK&֭]Swq[zK3f`!Bs )V}֙jukT;*wZ~[aYNNl1Cowr;U$ *[Jɲʝ3v\YJJr;c; c$5%VslgʝV"c .)UẔ3v(Pw,'S 9VCBj ҬN $c$_gqY7d22 nJ@ݱ\X(V w" @E9"|#"~bQ`51G&:&d#JaEEE*EiB*@Hm sGKh(Ee^`m,(V6qǗ[hZ9:MYKΟL%%tTͷr9q =D/ud<'dlJr ^`Krl($UѶ7OB?|/5i TuMK6RDŽFeB3\ ^̓ј- IzBn'\ @"9"+g 1[]/sL3!7lLD"&5^Qk)I7̽X!ۤ'܉ 9%^ii"׆ҍ\:-ʦZ[um _ &|kLLQ5\߆#7#96pecrݰc Zc&fw o\WŊڨ6qhr UPdb_6*k%c ~VԞ '$LU,1.Ԡy{hS?l#^XbkZǵ6 RXakB51Y߱rC6(tƵ1Wq 9!GXq -t}[$c }-62ak|X?d kܘ5aXk5%u*H9r4oosVla9ɱ!{Nߍ)38)Ʀ40v͚ekMZ` /QWux^Q 2+NgMq2A

What is Social Network Analysis?

A Social Network is the social structure which facilitates communication between a group of actors (individuals or organizations) that are related somehow (i.e. by common interests, shared values, financial exchanges, friendship, dislike, etc). For instance, your friends and you form a social network. But, social networks operate on many more levels, from family relations and disease spreading up to the level of company strategies, social movements or even nations. Furthermore, research in many scientific areas has shown that social networks are important when we study the way problems are solved, diseases are spreaded, organizations are run, and the degree to which individuals succeed in achieving their goals.

Social Network Analysis (SNA) is a beautiful blend of Sociology and Mathematics, composed of various interdisciplinary techniques for the study of such social networks. SNA researchers conceptualize social relationships in terms of nodes and edges (links) in mathematical graphs. Nodes represent the individual actors within the networks, while edges visualise the relationships between those actors. The result is graph-based structures which are often very complex.

What is SocNetV?

Social Network Visualizer (SocNetV) is an open-source project to build a flexible and user-friendly, cross-platform tool for the analysis and visualisation of social networks, targeting primarily the social researcher. The application offers an easy and intuitive User Interface.

SocNetV lets you construct social networks with a few clicks on a virtual canvas or load networks of various formats (GraphML, GraphViz, Adjacency (Sociomatrix), Pajek, UCINET, etc) and modify them to suit your needs.

The application can compute all the basic network properties, such as graph diameter, and geodesic distances (shortest path lengths), as well as more advanced structural statistics, such as node and network centrality and prestige indices (i.e. Closeness Centrality, Betweenness Centrality, Proximity Prestige, etc), clustering and triads (clustering coefficient, triad census) etc.

Various layout algorithms (i.e. Energy-based, in circles and in levels according to various centrality indeces) are supported for meaningful visualisations of your networks. Furthermore, random networks (Erdos-Renyi, Watts-Strogatz, ring lattice, etc) can be created with a few clicks.

SocNetV is a work in progress and is being developed in C++ and Qt, an open-source, multiplatform GUI development toolkit.
You can run SocNetV on Linux, OS X and Windows.

To download and easily install SocNetV, there are binary packages available and instructions in the project webiste "Downloads" area.

Installation

The latest version of SocNetV can be found at http://socnetv.sourceforge.net. It is distributed both in source code and binary packages for Linux distributions, and executables for Windows. Mac OS X users may run SocNetV either through a disk image prepared by us, or by using the Fink project. See instructions below.

Source Code Compilation

In any Linux distribution, to compile SocNetV from source code, you need the Qt5 and QtWebKit development libraries installed - most Linux distros offer Qt5 via their package manager. At the least, you will need the following packages: - openSUSE: libqt5-qtbase, libqt5-qtbase-devel, libqt5-qttools, libQt5WebKit5, libQt5WebKit5-devel
- Fedora: qt5-qtbase,qt5-qtbase-devel, qt5-qttools, qt5-qtwebkit, qt5-qtwebkit-devel
If you have Qt5 and QtWebKit installed, download the archive with the source code from the Downloads menu, untar it, enter the new directory, and compile with the following commands:

tar zxfv SocNetV-1.XX.tar.gz
cd socnetv
qmake
make
sudo make install

In Windows, to compile SocNetV, you need to have installed Qt5 development files, and a compiler, like MinGW. If you wish you can avoid compilation, by using the Windows XP executables we offer (see below).

To avoid compiling in Linux, we also offer binary packages for Ubuntu, Debian, Fedora, Mandriva and openSUSE available from the project's website. Debian users may prefer the SocNetV version in the debian unstable repository.

Debian packages

For Debian & Debian-derived distros, a (not always updated) version of SocNetV is in the 'stable' repository (thanks to Serafeim Zanikolas). Add the line:

deb http://ftp.debian.org/debian/ stable main

to your sources.list; save it, then type in:

sudo apt-get update
sudo apt-get install socnetv

Ubuntu packages

Ubuntu users may use our repository. All you have to do is add the following lines in your /etc/apt/sources.list file:

deb http://ppa.launchpad.net/dimitris-kalamaras/ppa/ubuntu "version" main
deb-src http://ppa.launchpad.net/dimitris-kalamaras/ppa/ubuntu "version" main

where "version" is your version of Ubuntu, i.e. gutsy.
Then save it, exit the text editor, and type in:

sudo apt-get update
sudo apt-get install socnetv

This repository is signed with 61AE869C37A4FCC5A73FD02EE088941209CFE071 OpenPGP key. Until you add the PPA's key to your own system, you'll see warnings that you're downloading from an untrusted source. To add our PPA's key to your system, open a terminal and enter this command:

sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 61AE869C37A4FCC5A73FD02EE088941209CFE071

If you want more information about keys and repository signing in Ubuntu, read the official instructions.

openSUSE packages

For openSUSE and Novell SLED, you may download binary RPM packages from our our repository. When you download the RPM, become root user and install it, like this:

su rpm -ivh socnetv-1.1-1.i586.rpm

Fedora packages

Fedora and RedHat users may download binary RPM packages from our our repository. Afterwards, become root user and install the package, i.e.:

su rpm -ivh socnetv-1.1-1.i586.rpm

Gentoo builders

Markos Chandras (hwoarang) added SocNetV into the 'qting-edge' overlay, which also houses new Qt4 and KDE4 software. To install the qting-edge overlay type in this command:

layman -a qting-edge

Windows executables

To run SocNetV in Windows, just download the latest SocNetV zip for Windows from the Downloads menu, unzip it, and double-click on the "socnetv" executable. The program will run immediately. Warning: the Windows version is not properly tested. But feel free to notify us for any bugs you encounter!

Mac OS installation

If you are a Mac user, you can download and run SocNetV from a disk image (dmg file). Head over to project's "Downloads" area, download the .dmg file. Once downloaded, double click on it and the double click on the SocNetV executable which will appear inside the disk image. Alternatively, you may install and run SocNetV using Fink. Fink is like "bringing linux to Mac" - you install some base programs and files, and then you can install applications like SocNetV. Please note that we do not maintain nor support the SocNetV version in Fink! Anyway, SocNetV is in the unstable section of Fink. Therefore, you will need to configure Fink to use the unstable. You'll find useful instructions for this here: http://www.finkproject.org/faq/usage-fink.php#unstable After that, you only need commands like these (I think!):

sudo apt-get update
sudo apt-get install socnetv

Development version

If you want to test the latest/current development version of SocNetV, check it out using this command (you need the git package installed in your computer):

git clone git://git.code.sf.net/p/socnetv/git socnetv-git

or download the latest tarball from the Sourceforge git repository. Then, type in the commands:

cd trunk
qmake make
socnetv

Please note that this version is not always stable.

Execution Options

If you run SocNetV from the command prompt, there are three (at the moment) options:

--version | -V

Displays the version of the program.

--help | -H

Displays a short help message.

file.net

The name of the file you want to open.
socnetv-1.9/manual/analysis.html0000664000175000017500000006505112542274070017300 0ustar dimitrisdimitris

Analysis

Once you load or create a network in SocNetV, you may use the options in the Analysis menu to analyse it.

The first option in the Analysis menu is (Symmetry Test). It reports whether the network is symmetric or not. A network is called "symmetric" if for every edge \( (i,j) \) in the set E of the corresponding graph \( G(V,E) \), the 'opposite' \( (j,i) \) edge also exists in \( E \). In other words, when the adjacency matrix is symmetric.

Distances & Diameter

The next options in the Analysis menu (Distance, Average Distance, Distances Matrix, Diameter) etc focus on basic network/graph measures, such as the geodesic distance between nodes, the mean distance between all nodes, the diameter of the graph, the number of geodesics between nodes and the eccentricity of each node. Each option is explained below.

Distance

In graph theory, the shortest path between two vertices of the graph is called "geodesic".

The distance (or geodesic distance) of two nodes in a social network is the length of the shortest path between the corresponding vertices in the graph G(V,E).

By clicking on the "Distance" option (or pressing Ctrl+G) you will be asked for source and target nodes. Then their distance will be calculated and displayed.

Average Distance

The average distance in a social network is the average length of all shortest paths (geodesics) between all pairs of vertices in the corresponding graph. $$ \bar {d} = \frac { \sum_{u \ne v } { \{ d(u,v), \forall u,v \in V \} } } { n \cdot (n-1) } $$

Distances Matrix

The 'Distances Matrix' option calculates and displays a matrix of geodesic distances between all possible pair of nodes in the social network.

A distances matrix is a \( N * N \) square matrix, in which the \( (i,j) \) element is the distance from node i to node j.

Geodesics Matrix

This option calculates and displays a n x n square matrix, where the \( (i,j) \) element is the number of geodesics between node i and node j. The produced matrix, called sigma matrix, is used in Centralities calculation (see below).

Eccentricity

The eccentricity or association number of each node \( u \) is the largest geodesic distance between that node and every other node in the network.
$$ \epsilon_u = \max { \{ d(u,v), \forall v \in V \} } $$

Therefore, the measure reflects how far, at most, is each node from every other node.

This index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs.
It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.

Diameter

The diameter of a social network is the maximum eccentricity of any vertex in the corresponding graph G(V,E), that is the maximum distance between any two connected nodes.
$$ D = \max { \{ d(u,v), \forall u,v \in V \} } $$

Walks & Reachability

Connectedness

Checks whether the network is a connected graph, a weakly connected digraph or a disconnected graph/digraph.
In graph theory, a graph is connected if there is a path between every pair of nodes.
A digraph is strongly connected if there the a path from i to j and from j to i for all pair of nodes \( (i,j) \).
A digraph is weakly connected if at least a pair of nodes are joined by a semipath.
A digraph or a graph is disconnected if at least one node is isolate.

Walks of a given length

Clicking this option asks for a desired walk length (max: n-1). Then SocNetV calculates and displays a square matrix where each element \( (i,j) \) is the number of walks of the given length between the corresponding pair of nodes i and j.
A walk is a sequence of alternating vertices and edges such as v0e1, v1e2, v2e3, …, ekvk, where each edge, ei is defined as ei = {vi-1, vi}.
This function calculates the number of walks of the given length between each pair of nodes, by studying the powers of the sociomatrix.

Total Walks

Calculates and displays a (n x n) square matrix whose elements denote the number of walks of any length between each pair of nodes. The algorithm is based on the powers of the sociomatrix.
Please note that this function is VERY SLOW on large networks (n > 50), since it will calculate all powers of the sociomatrix up to (n-1) in order to find out all possible walks.
If you need to make a simple reachability test, we advise to use the Reachability Matrix function instead.

Reachability Matrix

Calculates the reachability matrix XR of the graph where each \( (i,j) \) element is 1 if nodes i and j are reachable, otherwise is 0.
This function is based on the Distances Matrix; it checks whether the corresponding element of the Distances matrix is not zero. If it is not zero, then the nodes \( (i,j) \) are reachable and the XR element is 1.

Clique Census

In social network analysis, a clique is a group of actors who interact with each other more regularly and intensely than others in the same network.

In formal mathematics, a clique C is any subset of vertices of an undirected graph G, such that its induced subgraph is complete. This means that every two distinct vertices in a clique are always adjacent.

SocNetV computes only "partial" clique census statistics on the active network. The clique census report includes aggregate counts of cliques (up to clique number 4), along with disaggregation by vertex and co-membership information.

Please note this computation can be very slow! In general, the clique problem (the problem of finding a maximum clique, or all cliques), in a given graph is NP-complete. This means that no fast solution to this problem is known.

This is because the computational cost increases very rapidly in large and / or dense networks. So, if you run a clique census on a very large network or a network with thousands of edges, it can run for hours or even days :)

Clustering Coefficient

The Clustering Coefficient \( C \) of a node quantifies how close the node and its neighbors are to being a clique.

This method computes and displays the local clustering coefficients of all nodes.

The clustering coefficient in a directed network is computed with the formula:

$$ C_i = \frac{|\{e_{jk}: v_j,v_k \in N_i, e_{jk} \in E\}|}{k_i(k_i-1)} $$

In undirected networks, the formula is:

$$ C_i = \frac{2|\{e_{jk}: v_j,v_k \in N_i, e_{jk} \in E\}|}{k_i(k_i-1)}. $$

The method also computes the network average clustering coefficient (Watts and Strogatz):

$$ \bar{C} = \frac{1}{n}\sum_{i=1}^{n} C_i $$

Note: The clustering can be used to determine whether a network is a small-world or not.

For the ring lattice the clustering coeffient is $$ C(0)=\frac{3(K-2)}{4(K-1)} $$ tending to \( 3/4 \) as \( K\) grows, where K the mean degree.

Tip: All the basic network statistics, such as nodes, edges and density are displayed and automatically updated in the Analysis tab of the left dock in SocNetV main window.

Triad Census

By clicking the "Triad Census" menu option, SocNetV will examine each of the triads present in the current network, and count how many of these belong to a certain triad type.
Some background:

In any network of N actors, there are C(N,3) triads.
For instance, in a network of 6 actors there are C(4,3)=20 triads, whereas in a network of 10 actors there are C(10,3)=60 triads.

In any case, though, there can be only sixteen different triad types (isomophism classes).
Every one of the C(N,3) triads of a network must belong (be isomorphic) to one of these sixteen types.
A Triad Census is a method which counts all the different types (classes) of observed triads within a network.
The triad types are coded and labeled according to their number of mutual, asymmetric and non-existent (null) dyads.
SocNetV follows the M-A-N labeling scheme, as described by Holland, Leinhardt and Davis in their studies.
In the M-A-N scheme, each triad type has a label with four characters:

  • The first character is the number of mutual (M) duads in the triad. Possible values: 0, 1, 2, 3.
  • The second character is the number of asymmetric (A) duads in the triad. Possible values: 0, 1, 2, 3.
  • The third character is the number of null (N) duads in the triad. Possible values: 0, 1, 2, 3.
  • The fourth character is infered from features or the nature of the triad, i.e. presence of cycle or transitivity. Possible values: none, D ("Down"), U ("Up"), C ("Cyclic"), T ("Transitive")

In the seven rows below, you can see all the sixteen triad types (classes).
Within each row, all the triad types have the same number of arcs present:

003
012
102     021D    021U    021C
111D    111U	030T	030C
201     120D	120U	120C
210
300

So, when you click on Triad Census menu option, SocNetV calculates and displays a vector T of length 16.
Each vector element (Tu) is the frequency of each one triad type inside the active network, i.e. T003 = 3.
Furthermore, the order of the elements of vector T is the same as the aforementioned ordering of the triad types:

T = [ T003, T012, T102, T021D, T021U, T021C, T111D, T111U, T030T, T030C, T201, T120D, T120U, T120C, T210, T300 ]

Apparently, the sum of all these frequencies Tu is C(N,3).

Centralities and Prestige

The last option in the Analysis menu opens the "Centrality and Prestige" sub-menu.

Social network analysts use various metrics (measures or indices) to calculate how 'prominent' or important each actor (node) is inside a network (graph). For instance, we might want to know how important is a person inside her friendship network or how critical is a power station inside the power company grid...

Although there are various metrics, focusing on different graph notions and applying to different graph types, they are usually refered to as 'centralities' collectively.

SocNetV follows the conceptualization of prominence that Wasserman and Faust as well as Knoke and Burt use in their essays. To our understanding, all indeces attempt to measure the visibility, the importance or the "prominence" of each node. But we distinguish two types of prominence: Centrality and Prestige.

Centrality metrics attempt to quantify how central is each node inside the network and usually examine the ties attached to that node and its geodesic distances (shortest path lengths) to other nodes. Most Centrality indices were designed for undirected graphs (symmetric), where the relations are non-directional. For instance, SocNetV can calculate Betweenness, Closeness, Degree, Stress, Graph and Eccentricity centrality indices.

For digraphs, where the relations are directional, most centrality indices can also be calculated by focusing on "choices made" (or outEdges). But due to the nature of the directional relations in digraphs, the social networks researcher usually needs to measure the "prestige" (as known as status, rank or popularity) of each node, focusing on "choices received" by other nodes rather than "choices made" by that node. Prestige indices focus exactly on "choices received" to a node. These indices measure the nominations or ties to each node from all others (or inLinks). Thus, Prestige indices can only be calculated on directed graphs..

Centrality indices are calculated for each node (node Centrality) and for the whole network (group Centrality). Thus, when you click on a centrality option, SocNetV will calculate the corresponding index of every node and the whole network and it will display them in a new window (a small text editor). From there you can save the analysis into a text file of your choice. By default, analysis files are saved on bin/ subfolder.

Degree Centrality (DC)

The DC measure quantifies how many ties a node has to other nodes in the network. In social network theory, this index is often considered a measure of actor activity. It can be computed in both undirected and directed networks/relations, but is usually best suited for undirected ones.

Mathematically, in undirected graphs, the DC index of each vertex \( u \) is the number of edges attached to it.
In directed graphs, the DC is the total number of arcs (outEdges) starting from \( u \) (outDegree).
The index can be computed in weighted graphs as well. In that case, the DC of each node \( u \) is the sum of weights of all edges/outEdges attached to \( u \).

Along with other metrics which are based on the notion of distance (closeness, eccentricity etc), the DC falls in the category of reachability measures.

To compute Degree Centralization (or Group Degree Centrality), SocNetV uses the Freeman's formula for unvalued graphs.
$$ GDC = \frac { \sum { ( maxDC' - DC' ) } } { (N-1) \cdot (N-2) / (2 \cdot N - 1) } $$ Note: In valued (or weighted) graphs, GDC cannot be computed with Freeman's formula. As a measure of degree centralization, one can use DC variance or mean instead.

Closeness Centrality (CC)

This CC index focuses on how close each node is to all other nodes in the network.
Nodes with high Closeness Centrality are those who can reach many other nodes in few steps. The idea is that a node is more central if it can quickly interact with more of the others. CC is also interpreted as the ability to access information through the "grapevine" of network members.

For each node \( u \), the CC score is the inverse sum of geodesic distances from that node to every other node.
$$ CC_u = \frac { 1 } { \sum_{v \in E} { d(u,v) } } $$

This index can be calculated in graphs and strongly connected digraphs (that is, if there is a directed path from v to u for all nodes v and u in the graph). If there are isolate nodes in the network, they are dropped by default. In not strongly connected digraphs, the ordinary CC is undefined. In that case, you can use the Influence Range Closeness Centrality index.

CC can also be calculated in weighted graphs although the value of each edge (u,v) is always considered to be 1.

The maximum value of CC is 1/(N-1), when the node is adjacent to all others. Thus the standardized CC index (CC') is calculated by (N-1) * CC.

Group CC is calculated using Freeman's general formula, in undirected graphs:

$$ GCC = \frac { \sum { ( maxCC' - CC' ) } } { (N-1) \cdot (N-2) / (2 \cdot N - 1) } $$

Note: As with all centrality indices in directed graphs, CC considers only outbound edges. If you want to analyze inbound edges, use Prestige indices, i.e. Proximity Prestige.

Influence Range Closeness Centrality (CC)

For each node u, IRCC is the standardized inverse average distance between u and every other node reachable from it.
This improved CC index is optimized for graphs and directed graphs which are not strongly connected. Unlike the ordinary CC, which is the inverted sum of distances from node v to all others (thus undefined if a node is isolated or the digraph is not strongly connected), the IRCC considers only distances from node u to nodes in its influence range J (nodes reachable from u).
The IRCC formula used is the ratio of the fraction of nodes reachable by u (|J|/(n-1)) to the average distance of these nodes from u sum(d(u,j))/|J|

$$ \frac { \frac { |J| } { (n-1) } } { \frac { \sum d(u,j) } { |J| } } $$
Betweenness Centrality (BC)

For each node u, BC is the ratio of all geodesics between pairs of nodes which run through u. It reflects how often that node lies on the geodesics between the other nodes of the network.

The BC score of each actor can be interpreted as a measure of potential control as it quantifies just how much that actor acts as an intermediary to others. An actor which lies between many others is assumed to have a higher likelihood of being able to control information flow in the network.

In essence, BC assumes that communication in a network occurs along the shortest possible path, the geodesic. It totally neglects the possibility of communication along non-geodesic paths between actors. If you need a centrality index which considers all possible paths, use the Information Centrality (IC).

Note that betweenness centrality assumes that all geodesics have equal weight or are equally likely to be chosen for the flow of information between any two nodes. This is reasonable only on "regular" networks where all nodes have similar degrees. On networks with significant degree variance you might want to try using IC instead.
Also, BC is very sensitive to network dynamics, i.e. it changes a lot when we add or remove a vertex or an edge.

This index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (u,v) in E is always considered to be 1.

Stress Centrality (SC)

The SC of each node u is the total number of geodesics between all other nodes which run through u. When one node falls on all geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality. This index reflects how often a node lies on the geodesics between other nodes.

This index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1
This index was introduced by Shimbel (1953).

Eccentricity Centrality (EC) or Harary Graph Centrality

For each node u, the Eccentricity Centrality is the inverse of the largest geodesic distance (u,v) to every other node v in the network. Therefore, the EC score reflects how close is each node to every other node and therefore to the middle of the network.
Nodes with high EC score have short distances to other nodes in the graph and therefore are likely to be near the middle of the network.
Nodes with low EC score have longer distances to some other nodes in the graph, and therefore are most likely towards the "edge" of the network.

This index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.

The EC is also known as Graph Centrality (Hage and Harary, 1995).

Power Centrality (PC)

The Power Centrality (PC) is a a generalised degree centrality measure suggested by Gil and Schmidt.

For each node u, this index sums its degree (with weight 1), with the size of the 2nd-order neighbourhood (with weight 2), and in general, with the size of the kth order neighbourhood (with weight k).

Thus, for each node u the most important other nodes are its immediate neighbours and then in decreasing importance the nodes of the 2nd-order neighbourhood, 3rd-order neighbourhood etc. For each node, the sum obtained is normalised by the total numbers of nodes in the same component minus 1.

This index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (u,v) in E is always considered to be 1 (therefore not considered).

Information Centrality (IC)

The Information Centrality (IC) is an index suggested by Stephenson and Zelen (1989) which focuses on how information might flow through many different paths. Unlike SC and BC, the IC metric uses all paths between actors weighted by strength of tie and distance.

The IC' score is the standardized IC (IC divided by the sumIC) and can be seen as the proportion of total information flow that is controlled by each actor.
Note that standard IC' values sum to unity, unlike most other centrality indices.

Since there is no known generalization of Stephenson & Zelen's theory for information centrality to directional relations, the index should be calculated only for undirected graphs and is more meaningful in weighted graphs/networks.

Note: To compute this index, SocNetV drops all isolated nodes and symmetrizes (if needed) the adjacency matrix even when the graph is directed (Wasserman & Faust, p. 196).

ALGORITHM: In order to calculate the IC index of each actor, we create a N x N matrix A from the (symmetrized) sociomatrix with:

$$ A_ii = 1 + d_i $$ $$ A_ij = 1 \space if \space (i,j)=0 $$ $$ A_ij = 1 -w_{ij} \space if \space (i,j) = w_{ij} $$

Next, we compute the inverse matrix of A, for instance C, using LU decomposition. Note that we can always compute C since the matrix A is always a diagonally strong matrix, hence it is always invertible.

Finally, IC is computed by the formula:

$$ \frac { IC_i - 1 } { C_{ii} + \frac { T-2 \cdot R } { N } } $$

where:
T is the trace of matrix C (the sum of diagonal elements) and R is the sum of the elements of any row (since all rows of C have the same sum)

IC has a minimum value but not a maximum.

Degree Prestige (DP) or InDegree Centrality

For each node u, this metric counts the number of inbound arcs at u. The index is meaningful in directed graphs as a measure of the prestige of each node. Thus it is called Degree Prestige (it is also known as InDegree Centrality). Note that in undirected graphs, the DP index is identical to Degree Centrality.

Actors with higher DP are considered more prominent among others because they receive more nominations or choices (they have larger inDegree). The largest the index is, the more prestigious is the node/actor.

This index can be calculated only for unvalued or valued digraphs. In weighted relations, DP is the sum of weights of all arcs/inLinks ending at node v.

In unvalued graphs, SocNetV computes Group Degree Prestige using the Freeman's formula .

Note: For valued or weighted graphs, we cannot calculate Group DC using Freeman's formula. You can use DP variance or mean instead.

PageRank prestige (PR)

The PageRank prestige is an importance ranking for each node based on the structure of its incoming links/edges and the rank of the nodes linking to it.

The original PageRank algorithm, developed by Page and Brin (1997), focuses on how nodes are connected to each other, treating each link from a node as a citation/backlink/vote to another.

In essence, for each node the algorithm counts all incoming links (edges) to it, but it does so by not counting all links equally while it normalizes each in-link from a node by the total number of its outgoing edges.

The PR index for each node \( u \) is computed iteratively by the formula: $$ PR_u = \frac { 1- d } { N } + d \cdot \sum_{v \in M_u} \frac {PR_v }{DC_v} $$ where
\( u \) is the node,
\( d \) is a dumping factor (\( d = 0.85 \)),
\( N \) the number of of vertices in the network
\( M_u \) all nodes which link to \( u \)
\( DC_v \) the outDegree of node \( v \)

The PR values correspond to the principal eigenvector of the normalized link matrix.

This index can be calculated in both graphs and digraphs but is usually best suited for directed graphs since it is a prestige measure.

The PageRank prestige index can also be calculated in weighted graphs.

Note: In weighted relations, each backlink to a node u from another node v is considered to have weight=1 but it is normalized by the sum of outEdges weights (outDegree) of v. Therefore, nodes with high outLink weights give smaller percentage of their PR to node u.

Proximity Prestige (PP)

The Proximity Prestige index measures how proximate a node u is to the nodes in its influence domain \( I \) - the influence domain I of a node is the number of other nodes that can reach it. Apparently, in this metric the proximity of each node u is based on distances to rather than distances from it. To put it simply, in PP what matters is how close are all the other nodes to node u.

The algorithm takes the average distance to node \( u \) of all nodes in its influence domain, standardizes it by multiplying with \( (N-1)/|I| \) and takes its reciprocal.

In essence, the formula SocNetV uses to calculate PP for every node \( u \) is the ratio of the fraction of nodes that can reach node \( u \), to the average distance of that nodes to \( u \) (Wasserman & Faust, formula 5.25, p. 204):

$$ PP = \frac { \frac { I } { N-1 } } { \sum{d(v,u)} / I } $$

where the sum is over every node \( v \) in \( I \).

socnetv-1.9/manual/formats.html0000664000175000017500000004035412542274070017127 0ustar dimitrisdimitris

Supported Formats

SocNetV supports the following network data formats:
  • GraphML (.graphml or .xml),
  • GraphViz (.dot),
  • Adjacency matrix (.sm, .adj or .csv )
  • Pajek (.net, .paj or .pajek),
  • UCINET's Data Language (.dl)
  • edge list (.lst or .list)
  • Weighted Lists (.wlst or .wlist)
The default network data format for saving new networks is GraphML. WARNING: If you create a new network and press Ctrl+S to save it, then by default SocNetV will save it in GraphML format.

You can load GraphML files by clicking on menu File > Load or by specifying them explicitly at the command line.

For other network data formats, you have to import the files using File -> Import.

Please note that Pajek and UCINET support is not complete. With regards to UCINET's dl files, SocNetV supports edgelist1 and fullmatrix. Pajek files that declare *Edges, *Arcs, *Arcslist, and multiple *Matrix are supported - don't expect to load event-based files or vectors, though.

Colors
When loading and saving network data, i.e. vertices and edges with colors, SocNeTV uses the list of SVG color keyword names provided by the World Wide Web Consortium. For example, "steelblue" or "gainsboro". These color names work on all platforms.

GraphML files

Each GraphML document is written in a special form of XML and defines a graph. For instance the code below, contains 11 nodes and 12 edges:

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<graph id="G" edgedefault="undirected">
<node id="n0"/>
<node id="n1"/>
<node id="n2"/>
<node id="n3"/>
<node id="n4"/>
<node id="n5"/>
<node id="n6"/>
<node id="n7"/>
<node id="n8"/>
<node id="n9"/>
<node id="n10"/>
<edge source="n0" target="n2"/>
<edge source="n1" target="n2"/>
<edge source="n2" target="n3"/>
<edge source="n3" target="n5"/>
<edge source="n3" target="n4"/>
<edge source="n4" target="n6"/>
<edge source="n6" target="n5"/>
<edge source="n5" target="n7"/>
<edge source="n6" target="n8"/>
<edge source="n8" target="n7"/>
<edge source="n8" target="n9"/>
<edge source="n8" target="n10"/>
</graph>
</graphml>

All GraphML files consist of a graphml element and a variety of subelements: graph, node, edge, keys. SocNetV understands all of them.

Nodes are defined by the <node id="n1" /> where id is a unique node identification string. This id is used in edge declaration, below.
Edges are defined by the <edge source="n1" target="n1" /> where source and target are equal to existing node ids.

GraphViz files

This is the file format of the graphviz layout package. Unfortunately, I have not yet managed to implement the whole specifications of this nice format. The features that are recognized by SocNetV are displayed in the following example:

digraph mydot {
node [color=red, shape=box];
a -> b -> c ->d
node [color=pink, shape=circle];
d->e->a->f->j->k->l->o
[weight=1, color=black];
}

Nodes are defined by the "node" declaration. In this you can define the color and the shape of the nodes that will follow. Each link is denoted by an "->" for directed graphs (digraphs) and a "-" for undirected graphs (graphs) between nodes' labels. For instance, "a -> b" means a directed edge from a to b. Moreover, links can have weights and colours.

Adjacency Matrix files

The adjacency sociomatrix format is a very easy one.
It describes one-mode networks and contains a simple matrix NxN, where N is the amount of nodes. Each (i,j) element is a number.
If (i,j)=0 then nodes i and j are not connected.
If (i,j)=x where x a non-zero number then there will be an arc from node i to node j.
Again, negative weights are allowed. Those are depicted as dashed lines when the network is visualised on the canvas.

This is an example of an adjacency sociomatrix formatted network.

 
0000000011
0000101100
0001100000
0010000010
0110001000
0000001100
0100110001
0100010000
1001000000
1000001000 

Two-mode Sociomatrix files

Unlike one-mode networks which describe direct links between actors of the same type, networks can be two-mode as well. Two-mode networks describe either two sets of actors or a set of actors and a set of associated events.

In the first case, which usually is called dyadic two-mode network, there are two sets of actors. The sociomatrix codifies the relations between actors in the first set and actors in the second set.

In the second case, which usually is called affiliation network, there is a set of actors and a set of events or organizations. The sociomatrix measures the attendance or affiliations of the actors (first mode) with a particular event or organization (second mode).

Two-mode networks are described by affiliation network matrices, where \( A(i,j) \) codes the events/organizations each actor is affiliated.

A two-mode sociomatrix is a matrix NxM, where N is the amount of nodes and M is the amount of events. Each (i,j) element can be 0 or 1.

If \( A(i,j)=1 \) then actor \( i \) is affiliated with event \( j \).

This is an example of an two-mode sociomatrix formatted network.

0 0 1 1 0 0 0 0 1 
0 0 1 0 1 0 1 0 0 
0 0 1 0 0 0 0 0 0 
0 1 1 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0
0 0 0 1 0 0 1 0 0
1 0 0 1 0 0 0 1 0
0 0 1 0 0 0 0 0 1
0 1 1 0 0 0 0 0 1
0 0 0 1 0 0 1 0 0
0 0 1 1 1 0 0 0 1

Pajek-like formatted files

Note the 'Pajek-like' part. This is because real Pajek files can be much more complicate than the ones recognised by SocNetV. To be more precise, here is an example of the Pajek-like form that SocNetV understands. The numbers to the left are just indicating line numbers.

 1) *Network 
 2) *Vertices 6
 3) 1 "pe0" ic LightGreen 0.5 0.5 box
 4) 2 "pe1" ic LightYellow 0.8473 0.4981 ellipse
 5) 3 "pe2" ic LightYellow 0.6112 0.8387 triangle
 6) 4 "pe3" ic LightYellow 0.201 0.7205 diamond
 7) 5 "pe4" ic LightYellow 0.2216 0.2977 ellipse
 8) 6 "pe5" ic LightYellow 0.612 0.1552 circle
 9) *Arcs 
 10) 1 2 1 c black
 11) 1 3 -1 c red
 12) 2 4 1 c black
 13) 3 5 1 c black
 14) *Edges 
 15) 6 4 1 c black 
 16) 5 6 1 c yellow

Let me analyse this a little bit:

The first line (*Network) declares that this is a Pajek network.

The second line (*Vertices 6) declares the number of vertices of the network and identifies that the following lines describe node properties.

Each one of the following 6 lines (3-8) construct one node. Each node's line has 7 columns-properties:
Column 1 denotes the node's number.
Column 2 denotes the node's label.
Column 3 indicates that the next column carries the colour of the node's shape.
Column 4 denotes the colour of the node's shape.
Column 5 denotes the proportional X coordinate of the specific node on the canvas.
Column 6 denotes the proportional Y coordinate of the specific node on the canvas.
Column 7 denotes the node's shape.

Line 9 (*Arcs) identifies that the following lines will describe arcs from an node to another. Each one of the lines 10-13 construct one arc. For instance, Line 10 constructs an arc from node 1 to node 2 with weight 1 and black colour.

Line 14 identifies that the following lines will describe edges (double arcs) between nodes. Each one of the lines construct one edge. For instance, Line 10 constructs an arc from node 1 to node 2 with weight 1 and black color.

Note that it is legal to have mixed columns in Pajek-like network file. For instance you can have an node's specification line like this:

 4 "label" 0.201 0.7205 ic LightYellow diamond. 

Also, it is not necessary to declare X and Y coordinates or colors and shapes. In that case SocNetV will use the defaults, that is red diamonds scattered randomly across the canvas. Nevertheless, the first two columns must be valid node numbers and labels.

Note also that weights might be negative as in line 11. Negative weights are depicted as dashed lines on the canvas.

Colour names are not arbitrarily created. Valid colour names for nodes and arcs/edges are those specified in the X11 file: /usr/X11R6/lib/X11/rgb.txt, i.e. red, gray, violet, navy, green, etc. You can change colours of all network elements from inside SocNetV.

SocNetV also supports Pajek files which declare edges/arcs in matrices, like this:

*Vertices     11
     1 "minister1"                              0.2912    0.2004 ellipse
     2 "pminister"                              0.4875    0.0153 diamond
     3 "minister2"                              0.3537    0.3416 ellipse
     3 "minister2"                              0.3537    0.3416 ellipse
     4 "minister3"                              0.4225    0.5477 ellipse
     5 "minister4"                              0.4538    0.1603 ellipse
     6 "minister5"                              0.4900    0.3836 ellipse
     7 "minister6"                              0.6212    0.5038 ellipse
     8 "minister7"                              0.6450    0.2023 ellipse
     9 "advisor1"                               0.6488    0.6031 box
    10 "advisor2"                               0.3212    0.5515 box
    11 "advisor3"                               0.7188    0.4218 box
*Matrix
 0 1 1 0 0 1 0 0 0 0 0
 0 0 0 0 0 0 0 1 0 0 0
 1 1 0 1 0 1 1 1 0 0 0
 0 0 0 0 0 0 1 1 0 0 0
 0 1 0 1 0 1 1 1 0 0 0
 0 1 0 1 1 0 1 1 0 0 0
 0 0 0 1 0 0 0 1 1 0 1
 0 1 0 1 0 0 1 0 0 0 1
 0 0 0 1 0 0 1 1 0 0 1
 1 0 1 1 1 0 0 0 0 0 0
 0 0 0 0 0 1 0 1 1 0 0

In the above example, the *Matrix tag replaces *Arcs or *Edges. An ordinary adjacency matrix follows describing all links.

SocNetV v1.5 and later supports multiple *Matrix declarations in the same Pajek file. It will parse each adjacency matrix and load it as a new relation of the same network.
Below is an example pajek file with 4 relations/matrices and 5 actors:

*Network "4 possible graphs of N=5"
*Vertices 5
1 "1" ic red            0.221583        0.644042        circle
2 "2" ic red            0.233094        0.351433        circle
3 "3" ic red            0.696403        0.328808        circle
4 "4" ic red            0.471942        0.197587        circle
5 "5" ic red            0.726619        0.644042        circle
*Matrix :1
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
*Matrix :2
0 0 0 0 0
0 0 0 1 0
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
*Matrix :3
0 1 0 0 0
1 0 0 1 0
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
*Matrix :4
0 0 0 0 1
0 0 0 1 0
0 0 0 0 0
0 1 0 0 0
1 0 0 0 0

Another possibility, is the *ArcsList tag. When SocNetV finds that tag in a Pajek file, it expects each node to declare list of its link to other nodes. Here is an example:

*Vertices 9
1
2
3
4
5
6
7
8
9
*Arcslist
2 1 3 9
1 3 4 5
3 1 4 7
4 1 2 3
5 1 3 4
7 2 8 9

For instance, the first line after *Arcslist means: "node 2 is connected to nodes 1, 3 and 9". It is very simple.

UCINET's DL files

UCINET's DL format is one of the easiest to understand. As of version 1.4, SocNetV support "full matrix" and "edgelist1" modes. Each UCINET file starts with the "DL" mark; then the amount N of nodes is declared and the format (i.e. if a diagonal is present or not).

Afterwards, after the "LABELS:" mark we read the labels of each node line by line. That is, if N was 100 then we expect to read 100 rows with arbitrary strings-labels.

In the end, a DL file declares network data ("DATA") which describes the edges. If the declared format was "full matrix", SocNetV expects to read an adjacency matrix. If the declared format was edgelist1, SocNetV tries to read a list of edges of the form:

 source target weight 
i.e.
1    2   1
1    3   1
1    4   1
2    1   1
2    3   1
2    4   1
...

For instance the UCINET-formatted file below, contains 4 nodes and 7 arcs/edges:

DL
N=4
FORMAT = FULLMATRIX DIAGONAL PRESENT
LABELS:
On the normalization and visualization of author co-citation data:Salton's cosine versus the Jaccard index
Caveats for the use of citation indicators in research and journalevaluations
Should co-occurrence data be normalized? A rejoinder
Home on the range - What and where is the middle in science andtechnology studies?
DATA:
0 0 0.158114 0
0.201234 0 1 0
1 0 0 0
0.1 1 1 0

Simple Edgelist

The "edgelist" format is the most simple network file supported by SocNetV as it maps what edges/arcs are present in the network.

SocNetV supports two kinds of edgelist files: unvalued and valued (see "Weighted edge list" below).

In an unvalued edgelist file, each line declares one or more directed edges by vertex numbers which are separated by a space(s) or tab(s).

The first element of the row is always considered to be the source node number while the subsequent elements are the numbers of the nodes outlinked by the source node. No other node or edge attributes are supported.
For example the line

1 2

declares one edge (the directed edge from node 1 to node 2), while the line

1 2 3 4 5

declares these four arcs:
1 -> 2
1 -> 3
1 -> 4
1 -> 5

The simple edgelist format is suitable for unvalued graphs or digraphs of dichotomous relations.

Here's a more complicated example. This is a network of 37 nodes. The first row declares that node 18 has outbound edges to nodes 8, 10, 23 and 21:

18 8 10 23 21
19 11 21
29 5 9 10
23 8 9 18 11
4 7 6 8 20 5 21
5 4 29 20 7 6 8 9 26 21
6 5 7 4 20 21 8
7 4 6 5 8 20 21
9 5 8 23 29 20 21 11 10
8 18 23 4 5 6 7 21 24 26 25 9 10 37 20
10 18 29 8 11 9 20 25 26
11 19 23 9 10 25 21 36
20 4 5 6 7 8 9 10
24 8 26
26 5 8 24 10
21 19 4 5 6 7 8 9 11 18
36 37 11
37 8 36
25 10 11 8

Weighted edge list

The "Weighted list" format another kind of edgelist format supported by SocNetV. It is suitable for both unvalued and valued graphs or digraphs - no colors or positions are supported though.

Files of this format consist of rows with three numbers:
a pair of nodes along with a weight.

Each row of the file declares an arc (outbound edge) from a source node to a target node. The first number of the row is always considered to be the source node, the second element is the target node and the third is the weight of the arc from source to target.

For instance, in the example below, the first row declares that node 1 has an outbound edge to node 2, of weight 4 . The second row declares that node 1 is out-linked to 3 and the weight of that arc is 2.

1 2 4
1 3 2
1 6 2
1 8 2
1 10 2
1 11 2
1 13 2
1 14 2
1 18 2
...

socnetv-1.9/manual/socnetv-main-window-annotated-2.png0000644000175000017500000024662312525314416023323 0ustar dimitrisdimitrisPNG  IHDRkBLbKGD pHYs+tIME % IDATxwxUE9B/ .UW]lkWu-vvuWײvAE׶vT[ r@HMMyνw9̙d̙u#!fuBha!Θ #""""""d,~f&ň$3]؍7m Qo7v,Iz|ZN1Ie*-$Ŷ$`o/]ĺ """"""kmN\-IeLu:&d:[XXd.YDDDDDDD}Iq)%ʘ$4Ltf&If -ɰE5|&\uBDDDDDDe 5hr@dٖDYI2&:18-IXNc q 끈 |yC,Me.b0)$kkO3DҨ ^%Y'DDDDDDD?[itiIƳȘ \LtTXKŶ$ŢVX!R'e@(v-C+A+G+g'Ǽ8)ω"Jy JW} 8:֥?XJX5vX1(1i4aNܔah!]ۑ҄m!m""""""^ lhijmPgil) )GKw%B, L D @8 v @$\kRhX)3jdU̒A۔0+ҰB*[!! 9 CaB W9R@Z9K""""""̅PО)-a{`yZkОa\Dړ>:QR^J{*;p0L%,L*?t ,LKy>1e3k Q?c,$ᙶ GJ S{ - -0}+ Cv!0dҒԞ!LI@B.0LL-BqHm:QP!f+ݸFAN`nc)`r JuXi(-4e0KXJ+ [)aXRt,C aJ噞RjHW(HNBiLҁ0,G (Q'a6}B;uR!t)4],]XK*.Y.i!0DV@HD eAxR CN4Ę\˕gh4e93=)-K K08s0Oi%\ԎV1axqliS4>OtR)H"0<.Y.f>pk%gɱ<=2c_[$0RISHO C{0])}>CXRZ<'dAyLaJ 3Ljz)i*)xv-i8j/j0'!]Wkaji %!³ !LN܈[0ZJ c1iȠ* &%Aa&$ FR Zt) Jy'-i)LQqʊŕ!}5O(HJ>W⭕"&ZS*+ ?`0B(%߃4t\JKJC Bj%L-lR_)ȐI V""""""ޚn0 )7ڑ0MTL*[yZHi JᚮN\jqS(m !nzi>2uL´ǸqR)R L!<0lڎ>ÕrOB]3iH-0 MDDDDDDԻHLex4|#Cy0i᪸'}T@(ZDcJA(q4k&ڠtƶH!BŅ0ELJJGJ)MHG90I!--,(,I)ӄY0DM{XD8u0- SEu^ ݏMiОl/s,--G [haʑ xBZ^\B+"Bk!jc[s[lYwl,K^H nBY>!,¶0'9rƷ]ucoXJ`@\!BjH!RڀU t=̉kѺ~ZkhQ`Qv8x|mHi SV %4BZJ\!@ W5+7^U?{챤$C9ܘH\#* ` PB@BZZkhHf۶ݦ}^l:RbU@"A)J&"""""NՕc^m&`> CH<)!Ls %!A83pX70\ 8(m<;pmFCOiR@kҍr dfò,VuXWE{BJ+x-B0|X7U-_~УFp\k֬ؓy.~?; )%g\ҾmFUM{LBܶ]?!=327sJ#"""""vʱhO*uD<U…q>/Yŋ\B!,Y ׬E%p֬NLPZhv<۶~E7~<6n1MDD[mm-."DQ\]mnϮTcוqcQr{;+Ǣ۳h$h$1tX2󐓓GBuu54¨Q;! "KtUJekke!,BVv.µ>w8={ '⍙3ng̜9+_{5/IZQQ8Vg6?&_=D_ /i뮟um/[aRBJZLrTkrƵ$H{#}Kg]Q4ѿ_?qݤ)PQQ"mǓ~"u4x**ʹ@?Qq`1cֻawcOc딲gΚUVusŇ}?6? \wdD"VWWWQGc _ո՝リ6n.#6y=c3vcOOOǜϿ@0dGЅq$H{μ;*Q\wkwJ4-))F$Rda6E[Ξ>;8+&*?O1<ԓxW^1t5zh4b5a!֘q :E藗 ! l23ch]! !\yQr*PUVJPW[<7czm_꜆Akxb]wۧ4}]n=wǑ*zmb1ᄌ75 q]waʕ=ztkK,AyY98`ěR{ۦ[oMMyXhARzV\j 2=@8${kEqq10p@vD|g(,* qUWbvj8+Xnqe{Q\\ZJ <=|-QYY :IFZd1KpJJJ1a\zj_{F^^"չ3f  _1駶k8jM3\_ǟp65K6ٞ"N*ǐKo-&#XFMu5nfXy;Dqm<mig;5^G}tƹ/2>t,ZEE^D~yatseSoG@zz <`sbL/ÇCVf&~AMrׅ`c{'z &3JfQwAaSb ضx<0׬-[+y+Vkot̜5 ?0%̛#z𠁘xaϋ/&Yo /`G!`ƌ盼 q0SMg}G8i0~qr|w~<۶;=sŬ̌)!x1otʩ9s>|;{zx'#>~.?ŋ`8_fK$\SX,Jx9dd7f6fLr|,Xf-ƃ~gwydmƽ#ޤۛ%^z .믿{Si;=ǰ`1~},X6Z%<ēqQCxAxɧwlvaҥ K~%\K3Z;$[:ofH1$@{%'X7܀H4n0n:kbi,O|S \x{) 6vlǶȿ+/?/wMڰ=o|8ʾ555(..o㠢0 xLjX4PpYK 8Nm>ˇ %%L6 o3<=w߅7x#JKKa}EPZZٍ7"`Y_0j*q-nSnkZyy9N8ئ?R{231a}'spQGÏ>P016\H]-; >~&L|m;ga4xnyJ\X )ǎ7Ykm…x򩧰vm!`1lSSے+PZZ[o|?'CM[FDYYYk]܃[o/".2מnonˬIێo())i8~X,$aK$b\Rʆx[bS??"̝;*{poӦnio5R}$B[撵d]swkmDD]/XԶpӟs9O<$*PS]զh/2(zr@U5z'\u5X|I~ͷ3^ĀpA>L{愔>tcqc1jゅXl-[EaԨQm]P\\!#Cr?4 defc `63"uul?GVf6arJ7b(.Z. BQȯ]%VZ^;|i׆ >m=KmŲzx_pf&Rxͷpyw(18HxgX<֬G:a# En^za : l_{ ,^p8p8~|pWgHdmcŒ+ :0h^rU$bOoFQRRG} zZ6Z\Xl v9F7Z+WEᵬl|ŗb3:~())A<… ;\o,Tc''N< <(8@ǣ>C1aik븣Z[Zl)^˯z(k~d~Zk;CkK IDATq6Lj6(/|J5MTv,K8ƍb.ēNe(\\Wxq=-=iO:EiuH{1oHZdFFE 0Ép ܹsQ[[q"##AWccΜ9@(„ 0;ƣ8u$TU+u9tɨqbQWWw}WAu,8ݺA"s4,]wĺ1c4kM7ހ{O'?Sool4RK/.Exj4Ģ)4|kc޼y|8p5"Z:Hdō7݌ۧ܆|>k =Ӌ&\[n;tV6 .Wbc v )%GL@t`}q V\=D<?džX ٙ5zg <em!=3 iB)6m(8B_d*+Q!BGvN.a bũw6ɐ< 4\A+X,5330``X4իJKðPz蘆5>thPW[ %qS${8${y?=#e8XbYJ$5fHޚJY@,D V͕mF# \m܈V7>;m ǖ|>,+XR%t2:v,F H nxe-ƪzKD5?DiɖnuD0QQz%gK5.8qJԷ^Sr}Y> @ X2;'6o?ĥ=EvR9d\{ٸ/N֦|I}=f<?ÝʱhO)0M : ͷ^*lUbZA=GԚшR1biB9Q+<5ky\/sż`ƨ/ ɱ< *oMuQYU 6`Si)jkk)8p rSZL ka)Nlw=%f7t3.YX<p_Huvo;|m .-ކA3DHWE{BLutPX] w߻l&!++ Æ C,0M@Pi6YHhyZyWܺk̞|z \ualڸCuvo;|m󯾂VK֣A$N .ԲiEwíV>˂/+ bp(% hޒh<֭]ݭ;CGߞ2 K0pDXWE{jٴq DDDDDDDDg19m_"""""">n1""""""""곘#""""""">1""""""""곘#""""""">1""""""""곘#"""""""1n=ns 555xQc¶ғ뷷5X6v^xn wǁMe]_ҪKЖzj]Yϟ}Ǝ }Ej++ZWeV\)m-m"""""br{Xdi?[?+L|l{W߳=3N?-avGi^b=a>x]|4=\QTTԡzc'"""""No$|])Ǟxhpn߷]GKKZjku4<}zt0?`.]xQs}q)ms\OMzKc)G{O<@\nsޭšM: ~80oqbE Zǟ4i$y[f /kuf& C'b}pY`maa1H$Y\ޟ:8>{Gg ?HOEN=~]xop1~>8c^:i4>Y>T4Jc=&ɀ"> L{q3 (IJ5qӽ~\_^{`…x3MѴgŭ7݄aÆ!++ \u%>V{„Y<_~5v=_|U7k0p 0_{ ^ ˲װ'BUU5+:5f߼_Ç#== nUoC A)'UW+Wa5?G5 J̷*1s&t`I'Ï?AMMMeee!l5$eYX|,v)a>`@6 ʪ&222`&v7t6MJ[qgwk-L9QG09 Xnnm6#m*+ǟ0nlۯaq9~+.>d?%%%:tHg:pa;=flܸC uͷጳOu4\ׅ]x6T >3yWyW1bO`{ ^laONv6p1H$Y\z;^|{{";; vyto2PcZK4=}NGcDDDDDDL{;~2fg;M<9@y[v,kFvλ6RxaH#GnaРAIO[oò˻<&Ɗ+Szo8tk^|1s=덆'w,[̜&&M>,< /K5A y|FR i-./ qoHT31m1s:?WXX!^r5qgw*94QWcrakPs֙ۍ7aemhK-kx}aǛ,=CpOLzNwމ 7bƍpʤ#;BUUU3N 7݂ŋ65RV PBX>_v4qgđ3b>aO1n}}O>fў{w1o|D"I㲥~s|K=mq%_´gEZ<=ʫkO|:a;p)T/ֺSi^bDDDDDDLĩE[g^}  1v _sՕuT\;o7v,4G^| ؜ QGb3裎 P[[$ʳ<^Jٯ_?}`ʭtʹ7_φRW^}-#-- ?MƖǵndL[@GjxS&`'4\OZ\7ƛ1~=yv9 ?wu7=Vh-Zk7_CJb\n}*,\\\{U {ba?$ Ӟyj}֙8QR/:Si^bDDDDDDC sc!v Q[.#hRن)2azqԆ%-iZZ닛22B KK YOBjC{hO„QP[[ z`Zlcݞ~S1FDDDDWAC<-*<[UVPa&d4T`ڦPr q 3|=׋\e{1/X1ji1t~/A5ToUuu8hضp)¸Q\Uw*{D5fc}OKάl= .M`W9gюƙcDDDDDDDDgqX;~r>z+&ǺM""""""^nРA B7X73v8` &Ǻ  """""""ɱnFk>ƕh{*br,&Ljc #"""""""~n?,XK3qɘ1ظyy:XpB8056 C ;+Ѹ5ۆv+0xP.,z h8< |Fc,,LV^p8M6vܿdffb#wh,ָ[>&L}޷~RZQSJC V4QX8*J^A4i}XncD7{0 ũ ϗ`vHh\tm*.Aˮ/M L1rHɐ֬OPScw PlF - @qBJKjEY{?8W_o~ VYQ#GjDVWCnfN&_O0GfFN0OvgѸ?fqiAMDDSv30l999hgs> oZf >v=h@mu5מ{msً. > 6ͩFeIe9}wH&rht wPB`ؐL,.Xy 7'b~-IƱ~C9la"=3eB `P*ښ0J*0 7] 8á>3xqO{qiFZά  ׭6'aİam۳B@%1?Xdrq|Xڲ;:!yn(01{ /x _]矟]_!#vP0?5kuJr+kjn;.K `&<Q[>~QSeaM5|0vi&>^K3j_1f{[EL\Ԇq/ֵ'~! 9u0}od[ٮiHtRʆk>1y{y m?LAѢ%K~++aYPSsqt@2cɒ(~3G3ʫ*CU-9z8F 4's`ߟh )LC eGa4)\p/[eZ0!jhv=8AJ J=h@F֭_=vw`f+(+F8BA~XH$󐕕xܩ)p J*00?>e<\bCiv1hㆍ}>_)\] siwm{mxi 8ϲZ!9<1u]{cbIAͶK`c$">ɱ`K}]@_<8222pa㪫ئ2 h=bرjJQiw! 뺰mׯCt`4 kbZ-9ƶ6qۃ#'7pNrŲodD$Go`{\cͮdc+w} 2""zKoA&7h i`7ߡ騪X _es~ !^[FQ[Z DkS~6: ǁ#\[!+; x7Xϔ fƖ I$Bkg , oYAĪ#O<*,lPKVuM FӨ_0$LӬOi 43- Fm!3#ߏHim?FT@rTUS{. ;^Jm?~-M; P2!;v>׷m~A@~]Ƙm#`.>VYsV)Qybl˿LcrhWμrKN5$׿@Vf&D[\\ (u=X|mc6\ 1(7'y86n¢s|(Gm,֦c1 ЦV[+}`y N [f 2 TTug`}ڵ̄2`lN؎ںH 7ڨŴyG4k_rL6ώyZY>l&zσfU^yUpŽ׿➻C/ֈ.B΄bs?}'{0B Ps3"-`u+YsLJP(خۣ;SK3Ț''Ȉ2&Ǻ/~gx&߷lwsFD|'/ȝvaH'h5qՕ8Eq'²,(.8`ظ ~>1QQV_ vK4|6%Lj $-Y>K4:Ⱥ=8"U\ a(SO0b]Gハ>^cbqX̆m}p\w ' 0 YZ넳c.]?׭kv11{lyx'6Oq%@}tΝ뮿̞ݵC>[},kwܕ.vwx͙HKK?/>eTWWoOu5n6L YA }&a@.1:nC.6U@|> l\Ёغu+f̜z>\! RN'ciXZ<>mZfϚz f=ի1ۼwGc1JJJ]YszEVFvN ((cn z6 4tq0a=z46nئ5j;?~lgb5xGj" :TʲJxxy!2* df3`͓ZVL&TҀwVXT8t&{U GxxC̐$ $[\_l<3E^^ &JsMF{x 44nH=@P@Rd2k!t:tZ?offF+'O xMlDDD u qС(+-ݬMcF]@sW4BG$MFꐗ}Z8ZdU8(4; !Y9E]T{vP:SPD FHU@QK$$0ʀs#5- :gϞENNNB0LpwwL^\epuu3(..A>qPjޞ(/ sY1*(Bad9y0upq6뾒TwG}7Vɓ۲y ^}5aX &9ÇCe>}iigqQ|vݞk YncW^y%&Od7rnvpuնslz:N;H;{_{&̛AlQ$gP*ɓ<%}KՁI'aIMW~eYF^A1<|Je pHd lj5=P3( 24ٖ.]:Z9 F$W?\/8VRTBb6$+DP7?3%Ȳn۪G P(cF `0 hz.NXcDDD"hZkJYL]҅ƒl yHN8aj #??{DD7܁|!)#y$ njv":*|("4j T ,+\DN\ZJ|p  C1pss Ά?1`G\\z//K 8v"23[mCDhΥgBCd9(B+ݷwbGCfE?~gӣ;dYBll ΜqXDD~m7vG( v롇$jկАLzkHxH7?wY3&Jݣ;j>- z +^\ XcF\yms&ϱnMWq"ͦjg޴i̙̚5 _}ޑ%Wkճ$Ix5n|՘3w>***^{߂4Z3&QSJ* nnnsB[E˂:滴8 +ʩOGys [$syY{Vjzmٮl2^8w޹ $a27jқL&P̋ c`,^У׮La5=L[ HZK0$hУ9g ^^GiI p.3ѽ0dpWp$3UUU0 0pwQ$ x˨eXIYF  {@NU+R9}:{ ((FF(">>ϟ7j5$IBUUa0PRR*T*AV#++ ;~܉qwOtEZy$<+ FLT|N?~YYrmv׻q[%%%h$Z KJi;lRLI˗㹹sA{ヒx]1<=x2̟\k+%0Z9eаZy3L։k&Y7tM'{϶QBjJ 8^z Xte5,Y5e^)Y[!R-,kzZfF$CQT};_]MŚ4$;=z!)8bzc;:s=SX#'O%ӫ޶c^$`kL"k273(+EspQhPRRS'Ou'dN2sQVff*C9#7;*e2})p9h؞m݃#0,7cJ@W Z N` ##(((@II t:еkWiii(,,DvN.rsZeV}{GUUubjY!UϷYPQYvũ Y$ ƜX/<==QUU YPQQ///]sp9|G™3g0fXd X:^] ssgĒ%T*T*d&ˍDeq9@I5f .wGzⅅ/a-7[y3̟noq!!j5wXq*? ZOGX&_3lPTTt4Nk?ŒE ,4^^^sعY~*qqzmodMBM5i677W\9t]ۺ9IڽWyBa~>B$^j5=luv'Yຒz}9 :$Xu@L`Y_Nc0Nz=*P\蠮0L8Wa 펳F䖢%%%(E͟!!!!o R:dY]QWV[?e6 (Uooe{Vn+IZ@F !77zBBdff h4`4 ]'zC)* 2tz=R߇ B^^>ТU'<G>ܩ2qXDDN%&a@8u*4a6uo0leGoc/UWˎݳrW*hnQ7%wcVDʲ &_XHN> 77aNs>>+FUUPQQT̶Trs^^nukgR!WtEvnN8}.hRwCBQ=zwrp: X~j_h4LoI|8ۆ*ZkWe9,pXwX{ݠXR8~&""bpi*Ͳ%&UOe`"Zxe %d ؿO,Ty"15z:aAB| m؆+[nqjݺJ/O{*Qï?Baa!\\\0rHDB@RY`L&t:a,s5cJJݑ"** jZFQQ dV퍐uOBګ5yxb((,)8<] tU:a@\|} If%Gi)W#=a5J*K&Jͣ.uqXz5V{=s5Mcjl_AyYfrӁTww7\7*$|}oAu:``nrw kV Dƹs 2{```vn4f?7ϛ׏@eU_9ocAGDGGɓ/dIEg~gUhr"GW$ɨ$ˣ˽vQ#!"0>[1p]wbeXd1`7v0g:d#<<eEŚUMUJU4tmCSukᣏ>Š+p^5|Adg ܹZCCBpĉ&n9SO ^"*2f`cN jY|QX\$X&j Apsumggۻo{@kŒE>4v5"‚VV^u#"/7!ݚ9BV!edY?' yyy "4U6;.1k׷:'DDFe95UZG8V4WN*E3,q"!`TllF o~ۉ\5t2>>NJ) 9L4 J%0PAT**9/znݺ999ā WWWxzzK.pssVJNCnn.݋Kգ0K;==kp > pW 9rTkxm2=2ShzhX[6l:wEnnnmV9brrrOc˷ȔPVV; 8dkyˋ`μ }xi xe2;wZCsl5uߖz'gtz+Mv6mg֬xᅅ(-==<1kFG-&NV3g#=#..ݻ7>x-`X*̘5G-~>^(.-)RYo2l즣='RR(=Ʋ٢M0wck֖x6>""":^be-X2`4Yz dN֣0|(TzȒ &w?hcYYJUCi2u ۷oƀ~}q˃к\!B!kaTi5 (3 j5N@hh(uVW^?+jzTTTܹs(,,,#T*QXXZӪKgIq^ʂ^>a8? S]a@|9qGqw^Q7C?O˾,-*6-ƿ,U0{1>KO*RY3Ŭ=av#F8um^o|۾wWV͛U۩0{v| oAq`8_oV~~떓;p{ed1:ób>'_Iq|&RSеċj{a{,6oLjU2t,Y( :qY0\ T*\#е@(\ܡVa邬c8~*"##նz){]6;}JVٔS=s ~N>-[| QJ!XV!m0 wwh0],E 8s&'/5bb9OR qw`PT֥%IOttA1p@t{=!/7ݺϯh˕q&/*9g8tm@v}>F>^r{ jbαBq׷BCBA.] 7X&;sGx}߮rA;]"tı#Gѧo~D$;z ^mzgʽr+k`~v4ۑv1xyx/lɶPHJJ#KT&""x؇1l-B !6B.YT jVbPB0JCP(zRVD2ɢ 0JQ*Z$Jv!zZK}++D e\V] m##+]/.777v`0#GBEDEEk׮(/+ǩD&Ġ[@7/[$4<*\\5C3(//GQQeY>>BDDX*lF*+wL~~Dhh+ܟm9V#f3O#@T@RQ("0(ӏ7 f), fT( R(+!VUJNctҠ$d&l4)4FL6+fl6&`֙]Jrxrțƅn'=Ǩ៓`A4@V#,,aa˂(GDEF\rCzdaP೭sѨU#""08pT""""""S:-LjbpN IDAT:-9p1"""""""vCXDDDDDDD!C ڙ׏d!]*ccLiq 2P2$^pB~""""""""#"""""""N1""""""""#"""""""N1"""""""KU QuZ QdPgr0 w0U:|@DDDD:ǨSHȀJ]A s&  2~;32BV5I,""""lSBX{d&;;UU(//g5..(//[f3 Q۷,XIѳWo`C2$IFZj -=$ I JDDDDDD-:ʪ*k`d2@kJ%" l]Mlms? \7g0(`ι :j1L&K:Xy/""""""˕=oL:y]>|ms|"/lk P`dYQ-Ϋ5+7F tR'xғV/_DDDDDDD3щ* 2TҶXx4a6ۧ^V Y櫹/""""""˔3Vo@JjټWi<@*Yqq1zCyyE=, =b ,#7p. WVP4O0Vo4XMǰkmk;;|v6c] <3c&d1={9}ΑdMJ""""""9 pCm;  X}=j GYU-,נwqH,Zm̀V^^^C yQD}{z: z+ qzBٹVIDDDDDD0Gcqa"~YXbgpa~Eu:{X\7B7[aY;YU-_P{<<<7pXf ki@:|?8|}}޽zFZ{Y_]saАسyαm7 w 7Թ,U got mwqDDDDDDDt;o6pL `76dCYU-;q7bsswFٌ^_nTԓӡPXb0igϢ=cco "š 1=/tL:u0u8BDFD`6y~ܹ_} e#9sO &&ju) j]nݺ/bߚC~q\4Mkǝۆ`dgg9t]GʍrHϱVop'XXڹ@\8mپtXzvo9wWD[ ~{t;ߎ:uc&:i}mbuݧeK[X %\SO=0e#ͽ0̰ h#99Ć,VoT*8~j 1ݻ;u}-GtT$%xu9:9GcloPU6a:X&Oe}T_gMy @+coZ&?? ?# Xy|Zijqĉ8|CJRp*1j QQQv,^jлb޼صk'; L{|5i={nD`FV&4d(~:7=#QQv5tݚssY_:Gcݍ7f?8 @ CZ6`+,1,[jﰪZ9 Bhh}xX_eʇ3_~Gƾ};^7 T\\\0{LZ P/cSgAFz:ֱαu5_SO'tMKs0wF]lcC%mn&z-L @˰JoW 5*U<\ 3VSc֭ @Fƅѫgh7 n"* Fѡ|8s+ufG0w~ epr(᯿3g`4PZRk?B\\o@ۿ )))s$I+9ʪ5tm#Gh0`p܉&`֬9HMMN68d*YÓON65kQTTٌtyb%Fvv6/YqеӮ]tH;{ѴM ?܉JL& 0-*gU'9nCpQ,^W-BDTwL)9ox=7 c cs"84K,vܧ~ W]u%n5axlM^J""ñϱ򭷱fZK.aH<(++c&N<е͝_Y9E>}L}^\\1ǎ+,c.p%aEvy=uQnQ8uNaAu{ۛ}R0~rjDDDDDDDcHdd$VTb̙5s㶫P}M}n6~m_} C.̈2X(aeFDDDDD_mp&8v+7A nBQ ! (*DAT(BDQ*(*RT(U*QQ+jB*jhtAey9dYj2[!K[uWDgÐ!"""""""fQ3!0A{A9ڼl]8e(+""""""""{$YaXDDDDDDDdAos,'buȚtbko;A ڭ+~۳CNf0k{ӕ,[bnoAubJP+ RJ٠P Bi֫B%DIUIWZWQԚ (TC ^ d_Y!W#;fsm^^^g ː$3 ^7@NYDDDDD~bVف1CmvL`!Q" """""""Ί1""""""""#"""""""N1""""""""#"""""""N1""""""""#"""""""N1""""""""#"""""""N1"""""""KBf QuZ QuZ QuZ \cp:-LjR:JBP@P@10Kf݃BDDDDDc\^~><<|3~g/|Zi2/[>I'>,pW]y%F0oYyǞ={ݖ8| 6v֭/~ $VXϞV^Sxl8wn$'%nĂ/KfZ`ΜlDDDDDD1˯x+ D`` ,^/йaaaj08}S5s~iӆ|RDEE70!|c""퍗_~ZBBIjtޝGx{{CbʔpZyg50Z:7pHdf6=[F݊(\1hFSUTxW1ye٩Blt]X ŠRa6(P*P*QeE`R땢U&H. .BB-*WVэ> d~۳dFNN.||P(X_Dtəf#00|5>nC`⃓.:a,{?i$$''#)) t{믿[n_~ uގM{VkZ냈O1p@&) t SǵoBA*trlʕ*%I1I:@iP Q2JF`6\&jI6Ujd0.F9LCJ<kM_C*c1"")0LPTvg8uǏ wy+0j(`̘1ӧOL䭭e 2Z>|8֭[צuYS!!!8vXi-I&!>>~~~ |sĉpsslڴj/0a .c Qf4k { [FKȲlIT==3 T*cԨQCRR"##!DQ믿ެvӭ={N= """jaaaxꩧ_/mqq1f͚Hh4Gm0EEE曑d{(//?=z@VK.q!ǏTVV?O?EUU`„ @#a6o"..6sM8jjٳ'-ZT}NJ+0zh7%"""#"vmtGtt4 77[l(x{n& رcFUogݻw#>>iiiPTX~=fϞ}US5F?^| :oΞ= ш ;:t(i7xiii0 DNN<\}<5tPZZaÆaҥ8}4F#yf?ty~XNNj/3g"!!UUU(((햫BC#11 .?*u=fϞ~OFUUJKK]DDDDcDDԮ}Gɓ'C믣[nҥK^j|||cDFF8ɓ'M_[~bر}Se+f~^c׿Ng;v,n6h𜊊 |7>|8<<شitϟo: 8kɓC㫯… vss7ڽﳳV^nݬ~~~`ٹsyf\{ .goJJJvssCEEːKFS/?lذ;v5v}r| 8q(}#331b5mXXN< ظq#&LٳVpSNmVnܸƍCݑam DQתZ-["""sL f͚`[̾+ j@{ﵾ/uۀʃ>&?իdpBkR[l|I@/Z{{_|њW^۸q#v ̘ͦ1wyc-~B"::O>Ym]N2ZqqqkѻwZFn IDAT !C8]eeexЯ_? ."QWZuhiW z9v m;?ģ> W&tz-!~b~4{fϞ1c Jǧ~Z+'Dhh(T*1m48pִ8px@R!00111kݺuv 3 ::j...ݻ7ZiΝ˗GPTpuuŕW^iwijon s=2zj,[V^_~z |||0`V[ݴ5uKDDDqX%K'Nh2M||s3g=ֵkW\+Wl s=d~Oӧ>jXp!.\t;Z-uKDDDuZ QuZ QQd!Q;*Hbb"mmHJJ`;nGll, cp"66fͬw, 0=!mV,xŋu2ٹs'/^,#** Vˆ#`iҤIHNNFRR X ,K"""N1"NnPv{=z4$I;v|}}Y-gXDDDDcDD@CfѢEܹs#-- }a%#s) #"""08FDts6(V7;ݨt|Im_{Ι9s&k.SFDDDtjDD[駟nXUUuϏLDDDDD-u?`A `Ŋ=ٻ󰨪a`dQAd52KLTpK);?L\rAea`MyxsϽ9O ...044{=L0ׇ=ƎǏ;n8[[.R)LMMѧODFFb̘1prr\\\`Tb gggd2d24ifB^^^?;w3,,, Jaee={"""㵽7r ssssE#33SN QNZSASi\Vd K5>ѵkW߿1.&"""ʪB$AR&M(Ǐ7͛7}Ê+p;wu,_\ /_߸q'OD"APPP튍ŭ[qqqFFF.\MSK O{vB4}/xB^LHW֠A$&&ШQ#$$$qB{4֓SSS>r>SSS={6vڅTWJM"fii))Q{Arr2#4hPqΝ":m.mL+*%[I577ӧOuqI%""c-hղEI$ԵqWk)*Q"WQ)P2 ~T+s\=2O)%y BW)󕹊E+T|Xl@nnKJ#? _=XZZ )))/i-/˴GYbZ1,MMMAƏ ?ɰFP@z*S?D˖-add^z7nݽzk׮-sM4lP6!++ S]Sҽ,1&"""X )rgj>+W,Xs ޽{W?z3+!J_W8vҤI z{M6k{.tRdq5;0h ܼyM4X,.STDLuiOIĸ@`` ֭[QF""""*+kQEk=~ڵ۷_ϟuhEÇGXXX1̨tOMu'OC+Mx7qi֬Y y_իhڴNm.mL+*%ݛ.%Ekddl!33?9VmUO"beec̘1T*-F .)+Wb̙pss$ ,,,вeKTծ]ϟѬY3d2HRԫW=zp-0~x8;;C__jBӦMѵkb4yd̟?...J044X~Y"bK{J7]% " DDDDđcũԑcaM~#"""Gka֭U\==C*j\bqnsEIUk.8r£xJLMMVVV/}{=ڴuٳ'jժ>}YfUOj: #""#Ǩ¥Kwpq3 DT#5h))){sRRR\U~j'5GQM‘c5L:!γ=&O>*UJaEV"*JRҶrss4nϯ۽V L0o HÖ7A$g^xA] ]FUF2jFA__ڵ+߯V^@M /^ www m۶޽{ujΝ;R9/~EJJ &LGGGcǎǏ<{3d2d24iYf!//BFS;ƏΝ;JBϞ=Ql}prrH$X,Ʒ~u'> %=eӦMݻwO?qDD"|G¾w Ǻ3\ҔYm333SN QNEDDDc5T'4o7~*+J,_mڶC7w;B}>{3;pm׃γ=A6jmOq%ƍة3;*UrRbz򮛀7o"//iiiG}Tu 00x={"<<\y'N@C*b͘4iRujۗ9u# 8r={V gLOOG0w\ܸqyyyx1Ѿ}{;w?brP;vLif<{v?Tpɳ .S'iӦ e&}+#>:-]Rlb FUvߋCZ\\BC]d۝"4tt>,̜9SطxRյyfa;!!8}4zJmƌy& ܾ}3fx^QS3r>|X_xlYYYDppPv)))CSk%=e>8y$IQdZnɱƢi%ٳqemې-$Z[l[:ĉZA$aXz >^mZp|e>EgIL %"""zU"^Ǿ߲]t>jSݽ-qw=OxT*ոs~pzxUڵk O<)e˖!88h׮dmm-l'''x>-_Z^'JR2h"ϧ&\.ĉiӦMW< ٗdIA #lll;v})sKzvtakk(̛7 ݻD'''tڕ?}:u B/Rknߵ f8u4ià&ǨJxGOE"&a7JDDT8rT]G|TQQQv`` ѬY3=XGEz*z0ղ P""rcDDDT<{Lh?zlذٳgSD8/ iqn O,,p09FDDՂH$PLI }}}cرxuy{{2 2 M4YS[ǏΝ;JBϞ=U[47nz-ԭ[Rӧ"##1f899...X`T*kXt&&&055E^/SShBCCѫW/fffxþ%5ƨ8eImOMMEPP `BBŋCCCXYYm۶ػw/aX_}j׮ H!CS'FDA_D"(4E$LeGkN$"jx7qMa߽{b 8pΝC:u/66n>!88Xp,[}O<Ç{o߾:>gdd_~/ƍ>`Μ9j?y$L<76mѣG<ڶE\1;a`i  br.TJ\vD&MDDDpU *J*0c !hxyyn߾3fh]8|Ϟ=nwVV233,\;44x6n(|[.mٲE111HKKC@@ ;;fgϞ-$ƶmۆl `O>]cHMMņ 4wG׾|fc}Arr2=z%Kl޼YNHH@ff&N>=z?ӧ{$$$`ҤI000͛7Ds۶!_Dxc*j69|˰ؾ}DDDCP_bB||ևnNT/IjE="W'2IrJ"K|X 2CXe-Tf IDATe,D,IŖR%[-s6Q(uԟ( <|H$/"z >M څN;h<._S?RiJ4hBrQFHHH@ƍ{iUWVVfϞ]v055 JcRRR,--ɓbӴ}05}>r>SSSYaggWsNbbbtQU1XЏ͚5Cڵaiihт?0|,^aaa_I\v#Lj*[Ъeɿ H$ԵX"B%JQ*S94%Jۺ ۧT*|X0 666tMi*Jʢ4}Y\ܿ_1˖-Cpp0|}}Ѯ];aTTyk0RaÆaԩ_1o޼WڞׯO?FJ?DDD&Lj*];v\.Gll裏  55 ,[L-!J5p?(= K,(HϞ=CRRb{%l>ubҥB۾,Nn݄iӦAjj*.]yyyaժUغup"j 6@*k׮8q\\\^yZlfҥKQDDD/`rC ۾dpwwJ `հիvvv Ѻ}ئ.]hKaDǜ9s`ll65"~4mTxa|1 ;WWWd28;;c̘1.Ueqa*;Pn]XXX`)4 044pa[Ƣ}p >*NJƈ4`rhɘ?>\\\ JahhOOOaZΟ?1cR9r$.\6V\3g FFFH$@˖-֯_Ç~h۶m?DDD{=XZZB" NNNx1nܸrѣ鉺uBOO2 ͚5CHHΝ; X[[[DEEapvv>jժMk׮n.Ueqq 2֐JB055EVrW>*W``  %K` Q5ķU2VـG|[%UoJ"꧟~?O'2 DD$*HK>d2n߾ sssJ"""""-̝;-Zѣw^&ƈ^LpvvFVV3(DDDN$"""" //{.~w4lؐA!""z qX5񈈈uݻwGdd$cDDLk#Ljuue@j#""rrr?}||о}{Ԯ]&LAaժUXv-BDDTC09FDDT EEEFxx8\Rܺu+Ξ=vcZٳgLMM Q iDDDU'O{l֭ѳgO?~ݻwGf*{|fxallD0(DDD5cDDDU֭[>VZ^_oXf гgOJ""""1= '''r$&&21FDDDLQ5vXt666ׇZlYf!++KP ...044{=<33SN QNj( ,^044ڶm{m}ǏGΝ HRXYYgϞ(E"1oԨann]b/m^=:/_ӦM8q"D">#aݻwc݅)))0a{{{;?Y-,>>NNNDor9 9sرc5kBDDD7dR  ďBZ9 gRD&'I$\D$Sȥz*T,KUb)/ bY>$RbLd&[JD*KDliGIP(%6?'P*x,,!H_D) <} ٵ :vx\=GMQ7o'NܼcwSN|KL6 sy۷cРAS}%%݋}{J*1>r=mMsN 8P/CyJ1{lVTppp{`hhǏVZؾ};  3c書yK;99ܹsSNjǏo߾HIIT*w})իWcĉ lߺZ(8D:6k[P>VD)\e2G2C'ԃYRcrDChL@ESb ]Qc7|+WH?#lN`ܼySmJJB^^Pl2ڵGEE>] rc[ׯ:%ߐ!C[#GӧOѸqcL:Ĉ#%Mѳ -ZT*G=%X tM`Μٸs.n޺%T*^-ZR;V*}=ܸqCJ%/_6mۡ;Ǝ"۹|RL??1w| /PP(d24h Fk^+W ##CX4vչzxrܺu /ݱcj*lݺUcbEtQ0⨠r)WTr ]t|*#C,p1@=`gg'Wm ,@jj*,X ݻ6qD 2zq5̙3Ѿ}{bΝj߯DDDU'9V L@k׮Gߏ?T*/6lN( ۈߏ~BBu,YHDE ~ W۷{^  `Ӧ8y{3BTa¢t{/)>ҥK<ȣp"֭oA5kԩSy_8.EGaȑj"z7oSSS[`ll,,^YfABr2 jK222U(6ljС¶/d2qJqy^bThs-/J 7#ZXX`;;;V\q50(ȑ#pppJ½{vɱӧؽg8>4`h j륔~ص{yyسg/Xn=/ 1sf~;t:'ȑ?Rَ?bo`cc̙= ۶mʷ؁aC'[w/M{ҥ `ll={ɓpss\[[[DEEapvv>jժM>BaggT SSSj +WļytOW'OT CCCxzzʼ~EŨ,`k|DAi666'ѧOpy3Fx~lmm1rH\pAmڥ.̰tRϧN8? |-Aa8ydo%"""VYm˗# f͞s33?8:9#z, ^:V㈱~p8+6n|>2 jJzwNuFG_of!=@b[r9\4߷oj,^]CDکUQͶrJ|ظq#>SVYmG ش{\vتUq&ںќJ%Xz T*֬]/vbgggNM&ժUK5}?__oo?4h@x&mUhj qqqfb c5ȁpePrR"ڴm~1y׸}4-C#''Gmao& 0~D֪7==2?a01W"77وn_}5kש8L$$%%a`x|"0pbbb/L-Qlڴ +V`@\1UCek\au#?C\Azz7o̜)SƌE-嗓5ݳ[mŏFĝ;ð!{4ǞݻJl¾#G"=#]<_Kl0|!ARaĈQ{7ʥ=DTI?9F۱c+W2 DDDT!XUښcKf#"HLwޅ֭7B&1(DDTmpͱ#Lj ͛qFt֍!"" 5Lj蕋@ Hp&ƈp2Gjj*Ν;i"""cDDDDJ,_666߿?>9FDDDDŋG.]Q &LjҌ5 .\֭[+iDDDDT~ypcDDDTepU۷oȀ>BDDDU cDz7SLa HgӧOǶm]2 DDDT%19FD7A ,]A "oǨQp-4&LjH-T9BC!tbeA Qdee!** 666 Uy\ DrR/"bɒ%qA&ƈ`r +jDQUqy4oCZZ"""V8J% .]Ž;ЬY3%#""""lٲRmڴs#""j#Ǩ\h ]f^c7oބ3WI"""8rPJ-.LxQjOy=zY31FDDD &jÕW"""*ѯz7no͠kɱh0~"4cpuu_B!+ ,Y =Z¹QcvR+Цm;4qsر㐙Y8>-aNow+ѽ{;8]qjڹQc4h˖CTjnφU6wD}\T"z%RSSѯ_?Y1112e BDDD%&j7==Nwxk׮åKyGqE[A(_f-N:RtFvMq,8ٍgR…nOqΝ?Ǐ!!:ʕqU ꏩӦ?jH\y7|u#"~ѣGpfvM\\BC]amc k[ݹ+BC!.."Jh"899aС8p֭ˠkɱ*(p"/#f666ٳmv|?o.6t u{GcP8::3gCJݞXXX EFF\]]ahh|vln :a9m:RuY. (('CrR"q1Օ"JsY4mIIIx)ˠk+PR ._My)᳓A틬ÇhVWh999gSdeey#޽[v.>s\r{BDDD5GUJbV-߶~}ܹsG|mW egggN #`qRD-UHTں&""6o X OOO9s1"""q&%֬]oO0mz0iӃ=S3 #--M|?a01W"77وVk|ddd~Ґ}Q#G\xt ǏB"""8g333 \|Y7rHgdKk /r?(U*1 w݃1ڽP L#q0eͱgRg3v<&[*ս{ پ233aooa|ʥDDDɓ0tؑ!""ɱ&9)}>BI"TӦ~iSXX,F#5D" ˥=yOZ_ۖڭ\AAA|.ѿ#"zł'2DT}c R1UcDe+"jqqqʀ1"zqc"j(,, ÇGXX""""-09FD/2e @DT\~pssJ DDDD`r4i80[ J"""jh߾}]6իk׮11FDDDTJ9FDDDT?󃞞`iiɠGUχ;Q9`r;y$7n4CH<"z-q+0w\j G޽{#J1j@|Ph+``U^ k1*-Zb gggdee!)) >BDfͮ]N,ʧ*kɱ}qY<|)oucC;g{LuADDTベ3gǜ9s"TQQQh#\ DGG3DarZ;\zFB<MDٳB\^CLUJK1aDi,yWW7̟ B(W(Xt{s5@|RWMvh掱c!33v}#7󀝽 n.T2 M4sXmmQTشi3ڴm;{Gxh~rφU6wDݴ֒j,&&ڵCtt4rss1tP^M2X '/^|ut"#"2(.\u7k֬ũSp\¨#ߴi3E=q̟P*UX0v]p:8]L6]{g(;u[l?lۆ͛p 8~ O#"~ѣGpfvکUow k]qqq Q55a <Vڵk"zڶmzz>D7x$c5TPD9_*۱G̞ lll`cc9ga۶B;0\4ltk׭Eptt9f oi.sssd2CTTekkwavB4ozzz077N HRԩSG:K*i@Rꊠ@8~ IHNJĉ]aݻw|2$*uR#e˖ $vI*b)/%k |vrrBbS3T,]Xz5;DTJk1 ޴ X歔կ_nTX|9{l?#'" KHIM%:o 1A H.9VZaRmؼf۷SPQ#aڴiVE6`ц x"Ο?դIJ3 4nnn0`C&"_ą"$te,΅3g0⣏B\kVQF> +H?+++ >aQ///ljժx![2\"{,@2"֪U< c106) -5CR X Zϟ#::PDD ;wƂ x[%=LjHp"@Deư>H2_0LBrXq#!&Q>} B/ 2"gl,C 2e(;[c~61aDDtY5 Cʕ+=Lj='OfDTf\pn&&ObNƸxΞCDTuwaDTje.\@DYJ7""""*{X#""""""""i7FO-I& bqL_QQhذ!nyc 0H"""21"""*?D"?wW͚ضmlق-[v *=V Tش fF0g^X[wQF1P"""21"hl,C 1*38u :u˜$cW[X#~x ݻIDDDT8FD9bP EHB˂Ȑt~FnC33B~*'D"76Ɖ#GPA,[ W v [[[L8_5C%"""*1" 5~n5h/ӑַ/DlbD"J1D"(T!<֭[/^ 33UVņ mjfff9s&&"""*%X#"c9uQ,߰J [NB1zx`kT*_VVVWY4mQQQ|2O3f 44! ÇUTq9d2888 <<5kDf.Q)7B&xsK/٣v4iSN_{nLDDDTJJ҉ *111hѢPn]D"To>ѣQ^=8;;㫯bDDDDc+W80=z\޼yí\\kbܸq#ߟצ0׼EK8p#z7WjDDTz͜9;wDձe޽bqjrJ|Xv-222ԩSfDDDDz1Ǖb,qqwУys4k =zΝ|mCV\ܺy7o`?A$ᣏ0 ׯz;Æ Z֮]_s۷o3l""""=pLL ^ }7JRXT*.Gz Z>yJ_F£'ƍիWCP9°0aw;4}W^011Ι n $W5hԸ)j^}/',4xޡڵm浼GO?^a}@TRRRP|"ٶ':ÇSN3ΈH87Ŷfб. Ӹ|ٲt=GXGa?,; >Okym݂Q'R1~};~zzo~Op1 }ik𠁈׮[6`OwĩSQȑC{[6mj=111 E"oDTt9nݺ!66...D HO8fpa˯Y f 6l(,߸i;N033Cǎ}~X5QR%̜9iGRRWyo~ՑX,YfW^_r̟uB*RJmFFF6Zh0wwwH{X$Ѷ]DTT*233all\,7nj5'Ia2220^߿GGG{'''ܿ#888GyOfaaQ1rʅ:ƬXQvlk]VckdFDDS]cC&A&!<<ru !"""*3`^^ Pvؙmmꈏ08ھ-4z=NGv?w5?\m6غm{lيm Wh4022juh׮]_reܹsGڦ6Tq xOvBpp0Ç#%%'8fr~X<ۼ>덩_OCBB0i׷| hdf*C`B@ *222Wi``֬˖/Kl9֭߀ 6m{#˗/b߾HKKqyZFzz:bbb0%x*vڅI=˯ .RSS>f^@~"Qϯ: ::ްܹsyb2pׯ/\"/KYGC`ĈPj9 w*T@&MC#F~;(oZu-ۿS m v6,ZFͱn3[7A066FÆ^²3c)3vԯ;x5j8±3T*бC{k|1HOG^~HNNFZ0X1לڦ6dػ aÆaʔ)Fxx8ÓDDDDTD jϧ!~\"M)Nh$*'1fH"T̐HEf"Tn$UKFb#Zl(ʥK&bT#u1fbDyGP)y'׻7T*%=z JH$<_…?aooʕ+իWغuNcժ1ZYWxXL<'Y-%u-(J$%= nތ֭Zj\7pO03g@&z5Gasr96mʓEDDTJl\^ 繞D"AjVpRT%*3T4u L+)ĩ)*UBH3"U*S)3LKRRNP+r U2Mi"S`⦊yuPo SH3>viitRRSaiicy DP`C *B96mڄӧOcرpuu\.GժUy҈t1"-&@:Ѕ8FTci֬Μ97VZ9r$8""""JXPP`7f d@DEH_;կ_? XQv9UVpy[...7nf̘ ODz'.v_ݶ+mQ s]Dbb"D",,,vZ^DDDdPX#"""eȝc>}:pa4l'N`(DDDdx[%,veWR%ϸx"d2U\[[[CDDDe;􉞼~/Grr2իWT^z ] μ;4ѷo_x{{cҤI , УGz`F"xٖ l*D2(I_~ Ulڴba,oz½{`ii ccc,[Q☁y ||bE߷coahެN9._ * Bޟc7qD|/_P`qK0}45kքO={ws;8Ƿ{* 7G-O'vC5Ю}OY.};NXVOkШqS;@^˯( ̙kݽnJoL;ʕ?;w",, ]v͛7 z,b:ĉ_bq#:aRD=m[tITj̟mlG؛زuy8nވAN,A]7lV!-?S eq%=zG|ŏEAHH(ڴm/6۴mPzDT2X+www۷cǎƌJS4.kٲ:ѣla233kooQnH$}ʕqe`[:ߎ-..Er,DDT29[!!!8y$֭[v J LP` ~Z3/^D( ܹTT {ׯM6޽T˗ٶ Bƍ#7VGZZC0! }HIIիtr\ ė_MA\"**P|YoLz_OC}9=xv鞽=~7̜9СC"K7>bq8;;at2ZlN.ѳ<(3wl̝;;>>>3f  !""#,a.]ƍULMMPcDDDdP T*ʕ+0؍=HHHoPJcT,ƌ>Q 7--  ~ ni?v}QkAHHN64_w}a6m$o"*:ҠJ*X~=,Y'>Ç QmT/_ĩ(²eKO׮]>}8|o߆F j!˿OKKCRRRܸqw!x5MZcޕ nu+VCzZ<{j*Axp9a Z-N8Tٳ¶uVyQ8FI&MpIl޼^^^2dΝ`tcT좯\[itX\(-]qoի7o~tԩ?{߉vj\???q7>l(߽{V)kaXPܹ3\_W0k֌ﻨ<7c4- Yfy]_!!Ba? $4yp3gaGDf[ݺ$o"*:J޽{#!!(_<~GBDD#, ˛bз޾-L}0=j89¼ q´0` ,A(h޽>ך5z)ig[O KKKTZNNNEdUfff0^&;˳Tw^Wľ}tɨSLJ'\]]ތImo^P o!tչ&cf_}={sb(DDD*XXX/^>|r\q/11Dܱ=_4Kl[& JPǬi$ŶdUJa:,ӷھy~0ɓ't=0An݊E\\P  BpA"6M^Ϟ=\QsJ;SSSXW^L&\.\.3!""*HXXX3O1ߟo`mc &&/]\rEX)RRS166ζvӳ]\W^)Egc~vn.+jLժU?~;;[<~ fii)L Bd8;;cx6mܻItD"RHw9FeEڵqA޽:u/r9!""o$ӱCa: HOOG|||;soC؁t܀JHokbFFkymƧI֭ӧϐK}k{*T/l}{QZ[<}}}`mm ѽ;*W riogtŸHw9Fe]xx8"##j*t1['9OrV |ǵ!rݴԊFriT,H% Td&HFRHl$6RF\TlR^,6Q@e*1RWCl&6Kʉ+KDjjh͗>{*U|?I JG¢$ 8R簱͛ѺUK]tb`T" N:a ʼ#G@&Yf, «A<K$R7*n)UOjQ2CJS'zBR+Ti4C*Re2U 2S!1TT+% RL-Pe(Ӕ/2&nPW xH3\0"""2$#CҡCDGG3gC!""zcDDDdP12DÆ ׯkXYY_~a(DDDbq ;Ȑ͞=عs'7og2""2x,AajժaӦM رcѿ{ J<GvTjs\]]gL8zȑ#`bq[ ¼?,ӸÄ">>CM? $44}- Kn|~ٴQ{nGF1p@jj ;x7®}nĠ@ae+f&wň} ?^y5n3ggΜŃ̶uz x^ϻ簰4[7cp#BCB[>x9ͱi)pRcDөS'\z^^^077 J,Q)_/>K[lľ}tɨSLJ?:%wFŵ&F-,o]<<`ff e¼Nd<%˛bз޾-L}0=j89sun'OOOImo2/o2c,l=Fme]4 *bŊ{o"q9Zj?بc1iiixlmmeBDD cT,,>lӧOi[7c{'OrI_b%{.233$,S*J*ZS/^>|精0]={ܺ8`_؁7Q166ײ.Sl:jrpFX#*:ùsej .\`(DDT*8FzZjOxq,ς}܎ҵdeU@Z@IL̽PTϳ:]?7Yyl=L^~I_NFrr21~ vvHIIIo:D"zQuR }Ee^0&'3OF%*x[%QѪ^:6oތyaĈ4h 5; Ӌ,Arr2/Y"9~V"bI_jj*=z3g vZ񸹹 W>_[oܾcs#ct@@bcclj-ZN8ׯ/b1 R۷o't1u@u>Ƣ2sXl s#GWcǎ@҂cDţe˖8<:u̘1bqR`` k wXgoE2(0 f}ǰw]QAC;vP<|]M~rpqqkMǏc gm7oˉҲU8pw25_666F},uТE`XcQdsXl ssf 0ܸqHQ15p@$&&B$RJ H/UR{PT^Rma4B*U H`ll ww bTT@sgluڶmJ*A, hò<}1'''HRLǦ0|08::&&&Y -[>+WXYY oHÇY:Y0ԯc,,s ma~nrSbE|Lo޾9O;LjJH4lP&"""F Z? {uV4HL3bD*QfH"3DL7%Fb#B-6ҥbb*Sb3\RN\Y"RWVKUCkٓ='׻7T*%=z JH$<_DTJ%[7oFV-5we8Q9s&j5o"*A/^L&Cj ""}qZx5zbUZ"F-"R-JTfUidT/WRSST4c* fHELU*S!Rf*$ƙjRV* eEMjW޲o "\0"""2#*y^^^8~8 oooL8Qbq 9F?z{ ʕ?PD8FDDDcD'((_Ftt4<<<~BDDŊ1"""2#OFFFXt)v܉0t7oDDTyXɅB%w~ZPy:mSXXVp}KyZ:(QV _Deƈʾƍɓի0ydBDDybq ,==] Ē?ޞ=oo[>m*,--Qj899i\˛bPa^9RݻwZF2W|y 0֭})QNL0o`ffZ]c opŸFf9m7s4TyZ:(ƈ G޽sss/_+V`(DD#Ǩ|0}=&QQSynS~%$<9 aŋ97iҗXx ޽L$&& ˔JΏ+븲 ӷھy0ɓ'tbgOU-v>uyZZZ8{aEW+Wh>/nnosSR=8=֭[ sO!99N+}X,U}Y^d xa^NvH5BQyZ:Hs$~m; wAT")9lllufnRz/]F*VGXXey2 aaa?~<!"ٸn-s=DU"RT%*3T4u L+)ĩ)*UBH3"U*S)3LKRRNP+r U2Mi"S`⦊yuPoٌH3\0*zJPԩ9sEp۷}ع36 m۶AJ aff{{4kaY^F ߷z%R)UgggyΜ٘77W75X,9<==~ZWaTR{Pa?|addԬ醖-[:mi3wis,QÆضu Zl011Aƍ*TI9Ƹc<NNNJ055AC//(C$vQZx899a׮] @s,7#*S?7YZBP`˖<%Ю][i#*v>[n}0"2cDDDd9FDښ={6sN4ogϞe(DDecDDDd9FDQwQ{ْt(]@J/z({<"A@=b? (;]" %H・<%$lB \ٙg~S`<3SF&Mշo_C{0p!'Mp&7n˗CS DQa@ 1 wݻw+::ZEOQ#@1iڱcuei (1  .cj„ zԱcGmڴ@C8\cΖzi޼yݻZj~Q(@+s vM7iƍ՛oIQ @1#<@ u֩z>}:E|p sEo3gjj۶~7 M p[%PZ5M6MWΝu})99@>B8\c5\_U 4Pb/S'y/Ȳ,|> OTRRۧkʔ)8k @~K/iʕ2e7o+WR#x(&&F'OV||v9rJ(Aq8s @~֬Y3kTjU 29D8{PSU5n88s @A2x`mڴIW\\/^LQ,cG1MbjժUz衇TL1B+V89{PP5h@-RդIߟ@.#=z(:w-[lٲѣ) 1pޣc=cǎi͚5UfϞMQ [o/RÇ 7ܠ@U8լYSfR߾}աCG`@6@x]պ<˒t=uq}JIIr.\7wRUJ5ns,a?p.ycg_֭[:u(::ZEl C0uuAy^F~;OI9s֮]e,XPݺвeuqOLڵkUxq"'9 ~رC*UNQ cȱr1BNWIwuզMkEEEm۶Ufe$}Uڵ]SG 7yW &UV駟( oJ(rԩR.::Z&~|FXzMh\Kj\g9mbccgiѢEݻׯ#GhѢcr]RRRhd@jٲVZVZ)66V=E!Ym_+RUjּ Y~ζmlZ]\UԮ^iӦzz5lt*VV4p ۷?[NqSU5jS[4{S m-Z q۶m U Wи[m٪uڝپ`ujԼD*V%[Z`aˊ)_e>ҩ-RV=nK׫->ݱ~6υkuwzZR:)a /Y Nk1r1D@1nwwСC  GQ U"ڼys焄ʫ:z^~Uz礤$8p ]o~Ю6m}v}}\͘1]^X*vNȑ#Zt,]~Ko)[mk׮F)IZ{|scBŋhX":ݙyア;p,X ?rtdxYtihxZlfΜZ5k1BvKv5!!Aߨcǎ]lym8rnfoqޥ\쐧9}Y=Cz5rH9RM60\c8kvyM?6oS414~ڴiY~O&.N h_uVZ(h߾~3[۷$m޲Ef5/5҆أqc~'m8IZ<5X|o.IJLLҼyMؐ^ _GBvX/?~_=okFc$΅pKv c={ޮ?~_͸G7x]?|BǍ$(.sJ* &h߿v;wRE8}BӶd&9994ܻݺPh$I3g |j֭<7{*] ]w1aÆlN:V$)>>^4ԇ[%IiM_"IUVgTvBuABC֯_gdxV-)RD>^;wV 4`ǐ (m޼Y@@Mm;^ݵie9sx{+TH}SŊCv[Y_FJ8@ÇmnY~}PvWڜoQBBRt;ia}Lə/Jʰyչ~ζ߿?ed]YNOZ\\V 6{2dHۅR.]c+VLzw( pC 1BB@S ?s҃ 2OZrԩsR@8\Uɷ}JNNֺuN۽ uhN/|>Skۆ5JқN>kN*Wf=(uŒ=[N*ئMP3u&$%DڵKCy64nQ{Wλ:vx=Pֹpjׅ}9ڵK.ꫮ ?qmܸQڳwV|=F.,teY7oE,DEEi̘12e^z%u!/|dQ,ϭ]][V6'I,"Qvg>;3^/#}BOTPNo {)≲y#<%)i _~{d9.]8vڭ%9ăG^xS]`j֬q~It'{=j /P˖ƫzZ2A_ o߾y˖S+_f͜7Mf=2If۶?B?9TZ*Yvڥ^`0(INnٷٶ=}vWFu}*^x3{;cl 8߯7v ݪUvءuЎ;NZOO꧟գ;nʕZd|A 0@Æ ( ӦMC=kĈgf:rej/ *..7qx5ix*Q/>Ų)~'rQ<')2$IZv #FAcĈb'чRTugv2LAeq#gF 7= .b|>EGG)A 6yQU^MxTX1թSGzKhW_yY=ztWٲeUtqoN˗S…Un] o xAJ̙3t]C+WNzݡٳfI {}ّݶoNʕ$ܱJ,)I*[l荃+4Fԙpwީ taRzT54sP0\۶&2IZTxTHUUW4?[¹}ɒ%5kt=êU"##t*Aм1113gU\Y>OQQQ)WNW6jąy]eYټ<,>\eOnԾ}{%$$jժz1bD3gԻロuxe>Z٧m֌0|cYdm:pbbb4udh,9ejѼ޳mu -Kݒ%)d#\رcUlY9!CpP(8h\A.ʢ8͚`otEg =?W\\xa $z]mbF{miƌU5}rP`oY“nhIM6i 0@7nԺu}v5&׮pmʕ<**GJUV>PBѣu KY0_ m۶RRRnݪٳgp0cƌ37mjР' wMjÆ?a\aRR^-YD[֘G+""3.8,K;w1Fmkoc֭jF`ybO)B.p5y3giuW?dCÆ 5rkLj͙3G1114h&LSx¹m,^ߨgǀĥU6t2lRj<އNӏII9-iMrׯ/Izww^~ yp@8'5iǮ.zHn4O#oJزt}: oިI/_}D0#'L{T.TжmBr1tGޡq[n ۲USue㦪[YkRNh٧s1 {E5nLc+v}ZfM==Xo+l5/QtIֽ,X4ltE7n$mڴ)4+=p,۶u-XPݺЌ33ܞfMO_g-[<4U}ݓ IDATw֭ }v}] ڻw_СCj׾~{lݪ@ ;wjϞg}YK.S7j֭|z} N=ro \^~^3G#"e`[xR(7Oux pzcȑOc˖H˗%II7o~ԡCSfOڐ^;ٓi~M֨7qCz%$~ {kۢEp%%KNUpW_ӦM$I۷of_ؼe^}\ mj˚z١g}Y]vUX1Mt5e7عc[wװ&'kY,EE~߯QQ,У%&f Y0ĉU^= :ʲ,YRJ~p 9RNUVU/Ii-˒$}MjRAt*T(ZwuV̄ B_}Uh>*CNBuAB_>y۴ijwK;>oϧ֭[IfΚnݺzjqgɕN>=4XTAm|>j׮}-W||֮]5k(::"5cȱN7,IZ>Ag( ;l7(!!A :u9(Q"4|Gyܹ+%i{ )U6\Rjٳgk۶^p7V…%I{B[UX14nϞ=R;vcccdYOxsLξ3ѠA{コ{U?n݉O>D͚5ӵ^/):1X[:b:*URlŊ:~<eY|p,3i˖nmڶuslύ7~zM7|NZ2eBûwI .v߿?ۼcǎ8N-kR>Y.;;m099<6~x}wYx  W!*WfMJ/^,Iѣ<u*ڴiY]W_uUh_ǔdmڴ)z%I7n>/ -Iڶ7GҡCQq\pBcyWǎױcjKgKڻw:_~%4>m[Asܹ$iEY> ,;m*<> %''kݺCaސϨJjzs5Ki(ބ5iDcǎUdd$EpFnpddztNwm9^OWLL԰Y|Q]ٸ>5(׶tҺӍꪫ҅P>*ukSZ$I˗c ۧ7<Vj͏W%I_|^ZO5kVG Ӵiz-^ʖ-+IzWd8_vnS勪U6g?^rGĉ%I)#`Pw7nqq pFڷorInQ%K$-[6*bE]QvhΜY{Tre~EEEFj֬in٩St?*UR3gwVT\9ufϚ6k۶O&U*^<)JjJݓawEiܹJ.-ϧ2eʨJ*y^}e]e˖U.;e]ѢEsHJ}#萡C3/;m}@>/X>OQk@` nvEGG=9Q"E瞓1Fݻw p~ZbuRʢY0&T[ȳemNÓXoEx)>y*by}vg~_ F$a(9G8"kZcp-1@8"kZcp-1*1\p E8"k pZcp-1\p E8"kZcp-±Psp E8"kZcp-1\p E8"kC 1\p E8"kZcp-1\p E8OJːcp-1\p E8"kZcp-1yCp= +l(|#۶))A~[0Tbb1x<,8#q@0@AcU2eo>%''RyƶmٶeR X5udp / Syp*R0EP5 O|WTkcp-1\p E8"kkcp-1\p E8"k0sO(oM{Z]|3m׹. "±$ _W]aßi _w}y6N<ծW?uϜ5[n+m=vд#_WW6ѝwߣ #IWia/`0ԮW_}<1gbϞ9\Vuɍu]|>'mlu;A+U/^N=?쌷9/eu.deÆ?GtEfjظ<6선#+@f͞r5R{q:z)nܴI}zHn_y$6|D^/4`,ZnР!CC֩wnj֒E ܣvaUbIڶ*Sf&u9rr[VuC7oޢkxzc,R=syfu.dU;[955EEFzj谷DW_{.Z^y=}>6h.o:2O7=c5s!3~[ݻvѝwT…UpaunYǼ=29mLj!+@֬E /Pgl{Z~YӿB+ƫuz]nwao)S5Oƈq2} ò%z1a-Lٹ%KCv_4w&5W%(D>C8V۷Oeʔ>|[ԲI /O1SmZR9ymZ_u.kmoc Mk-_a=+k#F)]ZMSOrJ*\lJ\Z )66VԫgO]նmhg_|!Or7>8Q~KO\uO?jݚ>svh\ w'?m:x{رC+V\)6V;v9k.UP>eٻW׵DrTp|Jkc}.(T(˗TTI[mq[seWRuȑl/g*Qx۸u6ըQ#[5?2ϤY )^v:_CΝ_/fM~.Ry2~~?5Q#>x ݰdGn\Ǜn԰^]s. {euchz.hgo) >ߣ[W 2T%&&jg.YmObou}8.-˲ :|~7 yYWmݧONN=‹5g˭[89renzf\xs}d}8~=#Gq5e?/4_\\v)oT)V/PRrrX/ApC"n4F|' ǮjZOo'3O?5@O歯Ri3{UL,9盹ztNjc?e˗+A}=3-c?P˖/WTd6i M駺gӮ;+EUN_ ^:zhw\]u=Co]cOjBkpݻ)2*R>6o٢hվ};a%ԓƛz'@=?EmݶMQQQoנkAǎW Խkurkm:>=Wbccՠѕm[eʔV5yD-S&m^wߣÇF*RHAic ΅Jb5n{zmhctEtxR>C*"2R.44_ГNh9_* 7V%2 .-fyY$[(otcy}^;볊X^㗂>OT!'*('7=DYż^˔4^υl5)11#~r}s|fFy^3g׆Y3υm{WVUD =zT_OWy)[)[h Qqe;{mcSN99b>y;NRdI|)> 8'@0G퍰ډA'NLκ2e~2Y'Yi\9O~ xqvY f̜;tNwz'B@u gozKk~TT)hTC?y)[)Jz996[S91p IDAT˲gvUX?֫hac8` Vʴ,yg)RfJc9Os,غsO2U= ceI>;xQ&]IRP c31r6'h)*b-c6FǗx&h_قpd<(㳣S3(±0ծ}`EbI,rZ\1#c9>ccvd۶&h8%-Mڽ(*%q&hm[ۑm,91r|rd< ,))I3XމDf!29±3W_c)/Rԃ^x,9VPA+h^J1'ёhdmNx^gV yh}K8gDN9jdsd%˓u+hVPA%k#c{|6CP|$h9Luʔub\dQ?c<c#Q,{S9#mco+LOz^YA\䫹X_7H2&I29}Zǃ!+%:q,c<~IIv""c;171)8Nj0Y1inL:2XN>L_!x=cI&9E&RxMx<;A)A8'J^O%-K [~<1'Hv:n'hbR1 xRlxlN2Ɖ0A_8`cMEF'EBX1^b"T|s?E&WD1c4ƎˉtHqlq|AdzNx<1l#ǒRq-R|SM ˓ DO$qAO#'m-}ql8ql_=ǒz1Nc#l(_~2,? ±0=3T.'o,S$hE&:&cH1NmˠڧG8 k˨tce/Jk2A88^d͙`EnXXiCØYK?ѳ曌ِ1OzyorTroJıZv#I5DL۰̰e4K)]֥+xBu*LpTf6 YIi-l6exe?5=q[)JV*u|[+27iPc9JV6!w~:NeQ.,)[m9J^M9+deUp[~c,~d;lh&#"bedkjٶΧcB.ıKS~>V7gm5&/fViyo.YJI{'j.ym$B]fTE}4˘)16-OؼȈZl^X5S>?JV*oɱ4=oԘXIZ̤YkEHo\u65+e跻܇bS3YT#XpzI>dNM9 "}ʈ\V݅0}<cBc?ʎ^ܕ&||OmtYfK켕Vaw>UZf+(mgъ22JT2*ikcqaP}qrkGLDbcl9myܼљ6sŴs@y?vg2(Jˬیbc딟sS#7sV|ܧ^6{d}lάuZ6|.-XR2UegtU9zD?)w%bYZFNc7ť@&IoXLiOglm#̗H&?I2p=9 _ bQ |idy(,u.`b_!rE$x/ؗb/F$C랚'?#bIIENDB`socnetv-1.9/manual/gui.html0000644000175000017500000002733212534331160016232 0ustar dimitrisdimitris

Interface overview

SocNetV has a simple Graphical User Interface (GUI) composed of:

  • a menu bar
  • a toolbar
  • a dock
  • a "canvas"
  • a status bar.

The menu

At the top of the window, there is the menu bar, filled with commands and options, organized in 6 menus:

  • Network: Options to load and save a network, export it, create random nets etc.
  • Edit: Options to add/remove nodes and edges, change colors, filter links/isolate nodes, etc.
  • Layout: Options to apply layout methods, i.e. reposition all nodes according to their centrality.
  • Analysis: Gives you tools to analyse the active network (density, diameter, centralities, distance matrix, etc).
  • Options:Allows you to show/hide edges, edge arrows, turn on/off antialiasing, etc.
The toolbar

Below the menu, the toolbar provides the usual icons: new network creation, load a network, save, zoom in-out, rotate, switch between relations (or add a new one) and display help messages for the menu options.

The middle part of the window is occupied by the dock/toolbar (left-side) and a virtual "canvas" (right-side) where network nodes and edges appear.

The dock/toolbox: Controls

The toolbox, on the left of the window, has two tabs: Controls and Statistics. At the top of the Controls tab there are 4 buttons to edit the network (add/remove node, add/remove link). Below them, there are two groups of options to Analyze and Visualize the loaded network. In the Analyze group, options are categorized in four submenus:

  • Distances: Here you can select options to compute distances, geodesics, eccentricity and diameter
  • Connectivity: In this menu there are options for connectivity, walks and reachability
  • Clusterability: Click to compute cliques, triad census etc.
  • Prominence: This menu allows you to select and compute one of the many Centrality and Prestige indices that SocNetV supports.
When you select an option, SocNetV computes what you asked and displays the report in a new window. Note: All reports are automatically saved in a directory called "socnetv-data" in your HOME folder. Of course, you can edit the report as you wish and save it to another filename.

In the Visualize group, there are menus and checkboxes to apply visualization layouts to the current network.
With one click, SocNetV can visualize the network in intuitive ways:

  • By prominence indices. Here you can select a prominence metric (i.e. Betweenness) and a layout type (i.e. circular). Hit "Apply" and voila!
  • By dynamic models (i.e. Force directed)

Finally, at the end of the toolbox there are options to toggle node sizes according to their inDegree/outDegree and enable/disable layout guidelines.

The dock/toolbox: Statistics

The Statistics tab is mainly occupied by informative LCDs. These display statistics for the active network (i.e. node and edges counters, density, counters of inLinked/outLinked nodes, etc) as well as the selected node (its number, in-Links and out-Links).

The canvas

The canvas is the main area of interaction. The initial background color is set to "white", but you can changed it from the Edit -> Colors menu. Below, we describe how to work with SocNetV.

Network creation

To start working with SocNetV you need network data, i.e. a graph of nodes (vertices) and links (edges). SocNetV enables you to create networks with point and clicking on the canvas or load them from files.
There are multiple ways to create or edit nodes and links in SocNetV:

  • from the menus
  • from the dock buttons, or
  • by right/left/middle-clicking on the canvas.

Creating a new node

To create a new node, you can double-click on the canvas or click on the "Add node" button. The keyboard shortcut is Ctrl+A.

You can move a node by left-clicking on it and moving the mouse. Right-click on a node to display a context menu with options to delete it, add link, change label, size or color etc.

All nodes by default are accompanied by their node number.
If you want to display the labels as well, enable the option in the menu Options -> Node -> Display Labels.

Note: In large networks, it is sometimes difficult to locate a specific node.
In such cases, you can press Ctrl+F to find a desired node (by nodenumber or label).
SocNetV will highlight that node for you. Press Ctrl+F again to undo this.

Creating a new link

To create a new link, middle-click on the source node and then middle-click again on the target node (or press Ctrl+L or the toolbox button). By default, all links created this way are weighted 1.

If your mouse doesn't have middle button (did you try pressing the mouse wheel?), or you find it difficult, you can right-click on the source node, then select "Create Link". In the dialog, just enter the target node number and the desired link weight. Alternatively, you can click on the "Add link" button from the dock. In that case, you will be asked for both the source and the target node numbers (and the link weight). The keyboard shortcut for this action is Ctrl+L.

Link Creation Example: Say you created two nodes, numbered 1 and 2, on the canvas. To create a new link from node 1 to node 2, middle click on node 1 (the mouse pointer will become a hand) and afterwards middle-click on node 2. A new link will be drawn instantly. If you want an edge (double link) repeat the process from node 2 to node 1.

Remember, each link you create this way has the default weight 1 and black colour.
Right-click on an edge to display a context menu with options to delete it, change weight, color etc.

Note: When you click on an edge, SocNetV highlights the source and target nodes for your convienience. Click again the edge to undo this.

Relations

The first time you create a link in your network, the application asks you to enter a name (or label) for the new relation between actors/nodes.

A relation is a collection of ties of a specific kind between the network actors.
For instance, you might want to label a relation "friendship" if the edges between nodes refer to the set of friendships between pairs of actors.

Starting from version 1.3, SocNetV supports multirelational networks, that is networks with ties of different kind between actors.
You can add more relations to your network by pressing the + button in the toolbar. You can switch between relations by clicking the previous and next arrow buttons in the toolbar.

Please note that while modifying a multirelational network, you can add more nodes but you may not remove a node from the network. And each time you save a network, SocNetV saves the active relation only. You can save another relation by moving to it and then save again to another file.

Interaction and Group Selection

As mentioned earlier, you move any node by left-clicking and dragging it.

If you want to select more than one node, press and hold down the left mouse button on the canvas. By moving your mouse, a rectangle will be drawed. All nodes inside this rectangle will be selected the moment you release the mouse button.

Warning: in networks with thousands of edges, the group selection process is dramatically slow...

Node Menu and Node Shapes

When you right-click on a node, a context menu appears. From there you can remove the node, change its color, label, size as well as its shape. A similar menu appears when you right click on a link.

SocNetV supports many kinds of node shapes, i.e rectangles, diamond, ellipse, circle, etc. To change the shape of a node, right-click on it and in the context menu select Options > Change shape to...

Loading a network

The easiest way to start working with SocNetV is when you have already a network in a supported format (see Formats).

For instance, you might have another program (for example a simulation) creating adjacency networks which you want to visualise. In that case, from the SocNetV's menu go File > Load. In the dialogue that will appear, navigate to the desired folder and select the appropriate network file. SocNetV will automatically recognise the format and, if it is supported, it will visualise the network.

Saving the active network

To save the active network, just press Ctrl+S or click on the menu entry File > Save. By default, it will be saved in GraphML format.

If you like, you can export it to another supported format (menu Network > Export To). Note that some formats are supported only for loading - not for saving.

Note: Each time you save a network, SocNetV saves the active relation.

View the adjacency matrix

The adjacency matrix of a network is a matrix where each element a(i,j) is equal to the weight of the link from node i to node j. If the nodes are not connected, then a(i,j)=0.

To view the adjacency matrix of a network, press F6.

By default, SocNetV displays the adjacency matrix as integer-valued only (although we do allow float weights).

Known data sets

SocNetV can recreate known data sets. Read more.

Random network creation

SocNetV can create a random network for you. Read more.

Web Crawler

SocNetV includes a simple web crawler. Read more.

Printing and Exporting

To print the network directly to your printer, press Ctrl+P.

Keep in mind, that SocNetV follows the "what you see is what you print" principle:
we print what is viewable in the canvas, i.e. if you zoom-in to a network, the application will only print that specific network portion. So, you might need to zoom-out enough so that the whole network is viewable and therefore printable.

Except printing, you can export your work into raster (BMP and PNG) images, as well as PDF documents. The latter are vector-based, and therefore offer the best quality. Again, keep in mind the rule "what you see is what you print".

socnetv-1.9/manual/gfdl.html0000644000175000017500000005407612525314416016374 0ustar dimitrisdimitris

GNU Free Documentation License

Version 1.3, 3 November 2008

Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

The "publisher" means any person or entity that distributes copies of the Document to the public.

A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

  • A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
  • B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
  • C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
  • D. Preserve all the copyright notices of the Document.
  • E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
  • F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
  • G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
  • H. Include an unaltered copy of this License.
  • I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
  • J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
  • K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
  • L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
  • M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
  • N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
  • O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation 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. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.

11. RELICENSING

"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.

"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.

"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.

An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.

The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.

socnetv-1.9/manual/toc.html0000664000175000017500000001211012542274070016226 0ustar dimitrisdimitris

Table of Contents

socnetv-1.9/manual/visualisation.html0000644000175000017500000001217712534331160020341 0ustar dimitrisdimitris

Layout algorithms

SocNetV supports two kinds of network visualizations: By Prominence index and Force-Directed.

You can select visualizations from the menu "Layout" or by clicking on the checkboxes on the left dock.

by Prominence Index

SocNetV implements algorithms to layout the network so that each node takes a position that reflects its centrality or prestige status inside the network.
These algorithms supports all prominence indices and a variety of layout types (circular, level, nodal).
In the left dock choose a prominence index, a layout type and then press the Apply button.

Please note that these algorithms do not take care of crossing links. They are only meaningful as a visual representation of the status of each node.

Circular

If you select circular layout type, all nodes will be repositioned on the circumferences of concentric circles of different radiuses.
Nodes with higher centrality or prestige are positioned closer to the centre of the screen.

On Levels

If you select the "on levels" layout tupe, all nodes will be repositioned on levels of different height.
More central nodes will appear towards the top of the screen.

Nodal size

In this layout type, the size of each node will change to reflect its centrality or prestige score.
You can apply this layout type along with circular or level layout for a more meaningful visualization.

Force-Directed Placement

Eades Spring Embedder

The Spring Embedder model (Eades, 1984), part of the Force Directed Placement (FDP) family, embeds a mechanical system in the graph by replacing nodes with rings and edges with springs which excert forces. In the words of Eades himself:

"The basic idea is as follows. To embed [lay out] a graph we replace the vertices by steel rings and replace each edge with a spring to form a mechanical system . . . The vertices are placed in some initial layout and let go so that the spring forces on the rings move the system to a minimal energy state."

In our implementation, nodes are regarded as physical bodies (i.e. rings) which exert repelling forces to each other, while edges become springs which excert attractive forces.

These forces are applied to the nodes iteratively, pulling them closer together or pushing them further apart, until the system comes to an equilibrium state (node positions do not change anymore).

Note that, following Eades, the forces of springs do now follow Hooke'w Law. Instead we assume weaker logarithmic forces between pair of vertices v and u:

AttractiveForce= c_spring * log10 ( distance(v,u) / naturalLength )

RepellingForce = c_rep / (distance(v,u) * distance(v,u) );

where

naturalLength= vertexWidth + Sqrt( screenArea / |Vertices| )

c_spring=2

c_rep=1;

Note: Repulsive forces are calculated between every pair of vertices, but attractive forces are calculated only between neighbours.

Fruchterman-Reingold Spring Embedder

Fruchterman and Reingold (1991) refined the Spring Embedder model by replacing nodes with electrically charged or gravitational bodies.

In their words:
"Consider the following analogy: the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces on one another; the forces induce movement. Our algorithm will resemble molecular or planetary simulations, some- times called n -body problems. Following Eades, however, we know that we need not have a faithful simulation; we can apply unrealistic forces in an unrealistic manner. So, like Eades, we make only vertices that are neighbours attract each other, but all vertices repel each other. This is consistent with the asymmetry of our two guidelines above."

Again, only neighbouring vertices attract each other while all vertices repel each other.

In our implementation the attracting and repulsive forces are computed as follows:

AttractingForce = ( dist(v,u) * dist(v,u) ) / optimalDistance

RepulsiveForce = (optimalDistance * optimalDistance / dist(v,u))

where

optimalDistance= sqrt ( screenArea / |Vertices| )

Following Fruchterman and Reingold, for every vertex v we compute repulsive forces only for vertices within a circular area of radius 2*optimalDistance from v.

socnetv-1.9/manual/socnetv-analysis.jpg0000644000175000017500000046701512525314416020577 0ustar dimitrisdimitrisJFIF``C  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?E,|-E,4!GGz~[&o!EINbl~Ss\}vi"< l%o yd^n6yCҼD0=}gY 9*UCz |2&5H`X6X8LVtlMϕT.ݟ„ئ_~3K;_We%GsW$2\J$တ" \3+ >P =sAz67WQТ$!Ge,IS.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.o˿5?.ozLA&ξ7 Caw$!6̻U!$ K@w++ $$K˿5fڔcl%e(݃+dۭ\3[.$zYhw*Ia#&߽1ҥe}#0xcUxޤ\ W}]o.D׶vLvoch"6Au ԥ #/qyҾ,p 2\˅ǮsX?c\I[g 722T&3ãcYpF{_-~MBx2^HPZB2IRhP6[fi RMc(^ ^%i#)v8ՍFK'< j@9 ր.Zj3%ih>e}/#I伍d+0xf\H6Hθ)mCms2o4f,q݃*G($u]n>uco=bkkZHab0Nx7he}4˗ > K{i "6'aӊ];{ܽE/9W_1J@w.o[v+IwzvF܅$zcU4\HùrycԚڒ$-æ@J9|3 קƴq*'9ǥ!#vutpcv5\\}WkaW\f7/\V=1N+$p_:>G#'ԇQhXo^HKOe}a<6<؞@ 0<g9e|I $-3)IQր:O]٦q$k$w:0 =5RLn;ϛVWSFpq5-Yc.$r\2qU˿4}~fr2ꮯo溊PI\Ƈ2.?뻨OJ8%idyLʌ)-WFu]ǐԟl\-j,Qٮ LIt9\N3NhRap,H.Xq"hqQK5ܪd! -eekUxSH98IYQ#^jW*ƫr3vAcoqtp?5'.oً).E͵n?g[}"|ʠ*$gtek-e´r Y k*g(C|ÐpqӾw?3fm=u6gMkٱC 08m}[~ljS#]wonww@.o˿5􋛙/ iZydg`@@ @#94I12\2m#K!%$ +asJbyLn}0-Z^Y(-1uF9r.Pi\Ms<~b3W={hFMhi[jR@e8= cX:I.q_}`\UY5?V'3M#۶*?r^à<zK;$\F3]M٬-4%h͒I kdq Uy!Dha%*X+ ~:hxϭy20یY?uF[ Dt=hwVqVG$\<Pÿ!E1X?ΰ>HR?Gk:̉lrPr[O]7i~MVBYerH^N~Xķ8x#'Ib(c_'*FUe*F=5u/!%JiyQ99G'P|m? Yң[k&d}UT xPM17}-%)V6$VfAkAPDC*g_^i6zqeK#x]LL (ñ t]oMX4"BwN:iQYZ<;[\ja ͼ7ijt tCmyyV5PfeN|hj+?ĺO]Idӭ%b$h]@'$}hBDey{UfU$4SHݷ ggh8ֵ'I[%?-B2י9mtR]E`x#>VtM5w־bRUV;=VV~[iM+I]!\A,ĶOl,8$qkX4KEֵK6ݤw 9!AbNz56-Ău*e/GvsZoFvybJyq^wc@UDԴk_Ū2Bűn(U0OLFO Yxca]iwHP2DvЪdxjhKzs:}^_FEÅ*Oc/^\]9\oIANqҀ5(/rJV:ܑ=71ch{Z}m}g0&+iVHA) Ҁ,EgZ}Y]N\L 6̣r V|;/}~Tҭuh-

ʛEHg䙤1sqA&i zTtPݔ+;OSNifFifOKң3*$3GOiy2 ,tPPA"3G"ۇVLg:>TP%7r }ic0A!Σ$4Ic}}i<|+/9ٸQ@YeY#1H;.Ŏ['9>(GEUye^T$6GyǩcM ( ( ( ( ()|(;cr?ֹk'FwIwX(";Wahcin|+iw=2;S.OmÿߑԞM] ̽д뫆hV o5EUӬ-4LVjNX}IjPL% sȬ}85~maH^:NjC?-]U!6c?to\4SV𳥪\ZzՅngzkv̍Ǖr>xGǿbuffʹ랕#8xuf{ .Hi[Nо,YueaCeIuٴ^x]RK*եӐ ;dځ]`p| m^][Zۛw͞\YX$TSg~,{sYj;_K %J;+*aY籬 cNcoD-,ḇ1,P S'ʙ,,:+?K~?*_U5yg Am2G"Z@ }29ƣw}n'RuAwS%oy4 "Wnr#O/+ߏʀ"Ny:.CV GW$2H$ ~OI_ }VMCݵï̒eڀ8Gҵ)#c/DVZFiBR@ȻcH4}\4:Ժ \2x7y2E1[{>o"v+}O4nV7Lu?K~?*i<=h6^{tc@OӃ]eKVeo_P! OVHRO-Mws=*ׁ<i>]>[p\UTdDQ/<{BI a/uH-űrlۜ-U% '+v͹|͟۷gvQ+ߏʏ/̴_q-;ĺ!KҪ5)1W;L\XKy4Oэc ?7N v?eo_QVE\o'> t=[k5O (D$Gq$B?K~?*~ |2~ e?a>Mws=(cVPմPemHx/mओHNpJ?ni~,"} xw@Ycpֿb](?3o=JO>6.m-tmͻ.c,WJê)3veo_P[Fּ7j\gk..-Q`t]"ڱKpq4zG'z9}I6بv$bO!ߜl/+ߏʀ}V|A,n1g\Do%ll~LI,_S:DWuΟ}a[1V[qYD>M[=+ߏʏ/*]eNu7{%#ţ.BV8bn}zﲷ/쫊WVNV$gf Q9o[k_ٺ /5]LiLpjQ`08b 닚ip}nt.!gk+<3#P|Lj$v'WV𶖺}g1V+%a_/|K(+_OfX,nD$ rA V&k& .( ?kU_,)F*#M)r^k~?K~?*j{cӶ{ue{ib{.ny2e9xk{A5 O7 Wm&Kmwn?K~?*><7(N>lr}\I"[+hUޒ vk+ߏʼXj歨}g{>tv,́8$r/xI̍Ol{=-#ShpRQ|Fx34ۏ.n& >~n,&Q0p<z'Sgamy Zeo_P꺇=.:k:M4/%۴0R޷eReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@QReG[~T/[~T}e@^ )t)f[v$>Ĥpp#*[ͦpz@pHg\ӡҠ/$^9WlU*D be/\1iA͜`'#8fpDo @e8 aj Y[D41ϖ #{{CDOygiޜKg䪭UJ|&U)8涻GdHqPy"֩gѳ{n\$*'YBߴv>xnu5b-.˷yBʫsw!\M6Ǟ/ԝ-Syp fP N3KkkC>H7%X+w~15& mkyQpf9\f61מ;-Uwiʌr͏i ONtd|4o7W< }G[5 t]V+{K)p[oؿ6V\`8,0ka\⿲|K>w7'Ŀshs^}Bkg$ZUˁE?YG23|^ƪ-Zt{Ӧb\cqPN>n5Ӿ#hڍ^7NegCv P˽~aĆ&\ *L;SsKW9x^7.=Ilt{뇛R60EHect0Ԕu>K zd$v"ʭ:ƬQ@al(Z^2t0iV7Mɐ%Ҟsy<`Ͻ/T[wZwFn!Asp?1e ~5zsimau^]ww#Ȓ/U]<Ý rhϽ^Ǟ/ԝ-Syp fP NL񎁨A4]\G\Co,W6S[ʏ31Ȋ30pނQK*v+Ѥ̛{r=pn&}ϽhɡG@{/yǫ(Z' HT7E}?f[yDǴ,F\ 2_ ˰??DU>&/49e[)60E1Ix%_WBdKgoA/o;G#lq^/|g|惪\gZ[}6[dh-|(n$$4`Oxuލ:}Pܯ%,G=H(27E]#lYU>*jӆ:EśIAH.$t?jLA0_/'vBYls巒%$!||qE]?f94mmCH7:mRxt3)#I p?AWᳪ[xwMcb/ncP4 dנ.`j5k /ؙr}3~Fy;Jf k.'MQRzKiZ_iisff@Eʅ3ـ$( [8nW5p=sY4sOo'm{\4r$ֱ$00@$-U$ok/Lc;{Hn."5p"0ʲ .}fYմtou)t*[Y6r!6[ XKF&iU"qwWz3LF?>gޙ3@ϽfF}陣4џzfh?>gޙ3@ϽfF}陣4џzfh?>gޙ3@ϽfF}陣4џzfh?>gޙ3@ϽfF}陣4џzfh?>gޙ3@ϽfF}陣4џzfhl!Ez?t?QLLyn o'{;>C9#;;R:4?5O:i./.aud¯~M՜ }JkȬ!72$\ܒI2MkJw2ۍFc6v"i-ncy&D-#T) gۧjf3Q99Uc~=Kp.,j6YtyO˶;XKq`d$vZm$,MX܇9G5Aimqf;a=ZUU5Z;^ðе=^Qqgf&pi_I=uٮBjnhg::QeoykjΔ4 Wcrk88'һk{I-nYv7F¿_ ?Uq5#^O ib%tĘ圊'_UkgDL[ 3UOW r?] vp$D6h0Su*)MK3XB%e 6!ѵ8.,loI!yH9l^iAobuA #kFk$ZmRW:Q{3oam3ˑӟzJ8ּf6uGkRdž#P[v]_mtӬlTTBPD/)K)4R3>#FjXdžuX. ;*g8%N|q$$EԢR{qҴ쬭,wFe SXsUgAӫ+hgur^FNI :u1]wI\3}lil֯).-OSMA#K3FclFa!z{av͆ky fi#!xZu)SQ.f5 V,iG/}w۴K/#f6y\wq ls/= K;UΡ.6ۭR7Hvb1I'wʽ^^6 GԵiuN(--~΢Cyks|ْFf% fSZ^WIRl{6Bac5yWp< ߸ PyVo,l򼟴yc󝛺8/y~tyWp.4#+;'5<1ZSqVG8+[]5J+{,$ITĬN#|U?8jtퟴiWۏ7{w3ZqF?L״Kyk{5 ;Rgyi&]4s\֝i/jkwZyln%FYWj 9zhq5o ?iWiipymt6dM,-BF3u xzNvxnE[Ihk]YAAo^3@:3,@\4:9?0shv PjR:Yd ՙX.5hVZͬEyhq sM14jxDxo RGD!X9gU;畊 2z?5ߛ7GAoV;+??y~m?ͿbmwQn$mW v ָ6PƚrPÉePBם/kuƙXypX/Y:wv}+`r_QJZ>(յBH6WBrdHU8aÌ 8;$94ǁ\Gdǧ]YUؤxgHsЇºr@}7NVFI#[ǒnC(i;&y'i(Z{8.6uK^I4jv`1WExcV<ˬ+;v7OofD$/$`:%:m&wK!dcftxSl^˼mL[ CqpӒclÜA3@l0*x ֖Ieg%7&2c8OњPdEj?_E@#dFN&h ~MjC??LTi7[ھ\Yy7^lln#8*'oy=r  g :z O*7NO,OL{Sy~~ >n1:ӵ,Zŵɧ Jwa46lmtlp' d^U??n*7Ex Lִ 0ڦg-KFyL-rO\{A{ʽpyX.$úv!]^eVA9,!2w њҕYRw_?M&?4f3Y~h34f3L3Fh4њ~h34f3L3Fh4њ~h34f3L3Fh4њ~h34f3L3Fh4њ~h34f3L3Fh4њ~h34f7o@& *0u;h$.Ļ;n$Q0_ݟO]0Cwwq%pĊeāXnݪFAKoSt}T]^熙oƨ熙oƨ-?MKoSu{']g']g ?d7G-?Mwxijwxij(T]d7Wu[?u[KoSt}T]^@$i%ے}c"hRX0+"KoSt}T]jOw4}?KoSt}T]jOw4}?KoSt}T]jOw4}?KoSt}T]jOw4}?KoSt}T]j TIkiZ@  ݟ%gݟ%gݟ%gݟ%gݟ%gݟ%gݟ%gݟ%gݟ%gkkKTr9UFbI@T]d7[Mt;=7ӳ d7G-?M#)MtY[v ݟc{ynGuS,?-?MKoSu< 7G#)asݟ%gw^zo?{yn0KoSt}T]l;=7< 7E\%g>m*o.?׿瞛Ow^zo?.cT]d7[Mt;=7f1m*o.[v GuS?׿瞛O d7G-?M#)MtY[v ݟc{ynGuS,?-?MKoSu< 7G#)asݟ%gw^zo?{yn0KoSt}T]l;=7< 7E\%g>m*o.?׿瞛Ow^zo?.cT]d7[Mt;=7f1m*o.[v GuS?׿瞛O d7G-?M#)MtY[v ݟc{ynGuS,?-?MKoSu< 7G#)asݟ%gw^zo?{yn0KoSt}T]l;=7< 7E\%g>m*o.?׿瞛Ow^zo?.cT]d7[Mt;=7f1m*o.[v GuS?׿瞛O d7G-?M#)MtY[v ݟc{ynGuS,?-?MKoSu< 7G#)asݟ%gw^zo?{yn0KoSt}T]l;=7< 7E\%g>m*o.?׿瞛Ow^zo?.cT]d7[Mt;=7f1m*o.[v GuS?׿瞛O d7G-?M#)MtY[v ݟc{ynGuS,?-?MKoSu< 7G#)asݟ%gw^zo?{yn0KoSt}T]l;=7< 7E\%g>m*o.?׿瞛Ow^zo?.cT]d7[Mt;=7f1m*o.[v GuS?׿瞛O d7G-?M#)MtY[v ݟc{ynGuS,?-?MKoSu< 7Y6_23@t>UO(79c۹$N?ɿBf=O]8uЦoΤƳ׽5"usI Id[ uĶ6pci3<Xx85? sh t=z-VIbҧv 7I[Yh5-lm乹o.4R ׏9'>,^xᵆn[ oja&bL/>7Wš՟l4?-ĒKxy8nX^5|%CT@ V_|>Zhej$ʣeIGpJBҀ=H֭u=CXYVM"l ;[8+:q?SĖ^yvzk4)ma\FH 9 8HS:jMh,j+kSm4A$ܮA75h/W^5h/RNKRfI%XO1Um#_еkMUv$Frgz稬_i_6ڦg ,@XTu rFRVog鶷X:2IJK- ` 1T/.E֧>&{>`DXH0$VAc5|U$?,l~{ii%פ!xSVS! C(H\U>!{ElHKs&ȉ&*wg<{` GCT.*K$n8'2$gTi\3:95hO\[[j+im=;7 C:oizo -\mZ jWf[$&7̨QB{mn|m{NvL'UDl$a Bw?u:]Z}VZK[g(J6 Fx}k7K$閤|Ei/Zҿi\SA1QHaEPEPEPEPEPEPEPV4o4&JV4o4&J3* ( ( ( ( ( ( ( ( ( ( ( ( ( ( (/xDO D*" O!ƿ: $HMʄ%9(]E|oCz썠Z%uw,'r77Z{JPi:K!NDI-zP[gS[38Om}v w.}=EGiqowkݤ\[̋$RD#! RWyEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^_*'++ZEK)FM]QǓC1Ν5C)?toY>MKUAZy~lrƫ2 \`$3z[hj(0$WNSwΘ9 O86,_BYFMv$G; A}\),G3Y״/Y=7WI O5NT)\ZT4PQ@4ˉ-Kyl5$P"8RB]}u/ Mu4P-qԿo&ӎ35@ۿR>N:SErnKfhw8_7]M˭]o&iVIeHX}nO?Mi=G&kO?Mi=G&kO?Mi=G&kO?Mi=G&kO?Mi=G&kO?Mi=G&kO?Mi=G&kO?Mi=G&kI6 YCIg*.IdZ7Ww%X_|[7Ww%wQEQ!EPEPEPEPEPEPEPEPEPEPEPEPEPEP_"֡>Ai[ZEI].TQ6>;jY l آP^FPM|Gi#uԴM\ &Ʌ;g;29afs{"ܷ{voQLX#Af8W%}?)(۲GuQeiSF1Z$A LR0r~+O/N5Pl^[rC+&)GNn f{E F=#\ZĜ--͛nu/ھ 1SHΪPמs^v= <((((((((((((((((((((((((/i\AO?"!E& ) ȿ:|}k^,t Əpw"w,\#G S0Au?toP铛kJe `8/(4j\x{[^,7-KuH.onRW\׌X+| MG×GIk_h~,4GjlFzg' n%Sic tk".?2I|9_ZyԴ+p~u,21i$Zuu@Tk".? 6({_%^ qƨEck".?_K5@V?/Qu@TEck".?_K5@V?/Qu@TEck".?_K5@V?/TZ*crH쎹V@G@TV|o"g?M@u{H:Ej5{_%^ qƨb׺E\?}j6({_%^ qƨb׺E\?}j6({_%^ qƨb׺E\?}j6({_%^ qƨb׺E\Q/TſJɥw2TޠZf *^#{5inn[BA[|mƟj>/7ojڼm%v((5}gltGF%T}cɎpWAχԺ:)_$W ;]]r~ y I[[Ϩks.Jcs{(2*'#h|4Lo:a;|]?I*qf .K@dI*:i31O5뗸ù[ Za.%@M2+4@>ƗTN-뚠j}8)żSF`v1`pR@ Ew{uW 澳2h~fk~2I-2Y\+M_NI u;Ve΢zu3ƭμ;9;}3<x_#y%hW]iڥۙdPl21zJ]xg~i^#ѯW Ь rv6qx^E]Lf5]OP,eH[; 6NrƉrz ot7rX\hSihdtB#! ൳i\;۾[4;*+T)cn2R<z<zE'ߏsGvCUbېHMM-ZB}/-zV@~d"M;hgK,0ޟ#k-XJfп#Ҹ ^sZh~"Tb ;dASY_ iΑy}u Gos3ieGDuGbFp\}_7|<;6e޿oe'rNpoz4O&QբUay}lt86ӴX$+ 3# ׁR|]N.wWVo?ޏ?޾r5=N妵+i4FD)*A.*u<[M_WFww74k2ҽMUi>g= ЛNFű.kj̒6 2 D.s""iQ7>+dž54ltcnGBs;Hp_O[C[NIgufT2HvpYgN&#l.QѠ:o jYqI!da& xgkb;%ђYw֭,DrSVv<G4-4r>ym5$0Jzd[恩cmel;y&%0m68k~X}cw{esy6G9c:>|iϮ+oTmf"G0o,Ϲ$%՟;I'h%ӼO$MKLWvPRMIe2|>&fQm/pG( r s\Q>4;mu}m_p{9.73hDP; s aev~ ,֟%4쭻}}2|>~kgeUi} )M+RN85"$Q2x7ĝ* bnH;ؤ.+H< !V[4eIJ8RX3ݝO}{|q#\;-l`F"w?AmŬ#uK^ee9#cE5x+^sX5+%kۓ$Kp;]=z-xcsK{Y=ƫp1uJ7l0E}sLׇfmtL݀rNHZ5?|J񕗆<;zEğf8Ԝ0rg ˼uk8t.X]\yOɧynm#̒8pB+v'}_Zُ9WOew<HOWYWeԴHu]62SIeFuӢ#qC1iieFJ.NsdʺS@=Y|:ӦD[ H8oj _A(k^/GRS&[iVOby'χtf6 )!b}f?}cî|;pk :tp ?Q@:OVujIY"9+&reB2k|fo-I.]KΆ!- 9NVaAvo2Ɛ^ 3u߄Au~~ ^;ėBhW&ѯӇ 7kB$ ix;T|GkkIxXMը(8(>u_x!0p$ Xd{A z爼ig&Q ?WmGdqI·0iWl2'-$eI ŷ(Mۙ%b~A_ ϶DH͵r3r29I$FEWFeaG=>xTK^MCzshdoi 2sr}k2g }i_4=]?Q00tϥjxgⷂItyoҵ>`IB޻74魦FլX>phR/VZLJ?yNKN#7r@<_៓~څ;1q@EaC}ν/<30((((((((((((((((((((/i\AO?"@& (?ɿB77t?Ϊ\lj?f[??띟5_X[f]MDoF@J헿㕧E1l5헿㕧Eer Oo//4 [Mz"6 HiHQp+I{kc{@k9G/ }+Ne?I;^{< q*;)l)s9|u=*SVK"$ʪN7u|gv⫛HǾ>㕧Ef}rJ|F-Ervopx/[) RyNzTot_\^O<^+rHΐCR NB)xY: Ğ9$]mcYOܑƨ `u\hTl5:(3헿}r ϶^Q_pӢ3>{@k9G/ }+Ne?l5:(3헿 @Ǿ㕧Esz[Ȳa˔׼-si$%YIeGm_?-~!i-.@mDdCٔGQ؂ ֕oj~kԠub m2 /R}<> dmiki4k?eogx>况dcp:c&ZLF|VeR+"kxKR%pfm2m3g[.2@ d+kzijW;uҠ  b(nci/N ~|[$iլ>_0Gޖ-J,z~1|4[Y|[c\H5U}>LmT9klg5]]xIS56eVP>Uj٭`sFO+P[ { ⧮K6uuJ/a.}w۲5|G\xW{:MWypu?cwTg@>x;K; YjR*:N'ns,݆|Soͦ#м%6C5ڪG{%ʈ J(/ƾ. H̢k2G:$|)J(^xKO OMY##䷷X;%E|H. D[L%nn15utw60>8oٟT֚\G%FtnDž;P,/:7DŚ9?p}V=Bu5oOCzz+G_G*d<=Ŵ/6}e\* =OsUgF?aƶY*C.G4rwQ5^&_p%h|IhvD.'ԕ@e៍/[;xv')!u ?cA( ,b95+J_!}+p.\RS]v~5ԌZtiz[~_c)Y3COkxv>xs6+.B{/?Pj-m5I/X)hccIx%˶@CZ'] ]YԡE7 ,SbV [_!En'2떑?S5ɏV׶|La+776!_-4+!fV@@Ey߉o4ɳaI591v{6QEQEq/g^NG[m]9UkKb&zGab|X>-4a=Q.]*(>0hv71xK| |^!9- )Cx wG,N\z8"{{i-x%RG*W}EUQEQEQEQEQEQEQEQEQEpMyx3:4L4- yϢyIZ[~$}>Ri<68c,{5#qxɛkm'FaVoZG FަX )9U{1_@Go?ڲ仜wOg'Fjobq7\ҴBI# $ƍK9L>ȧqHzTxctUKYuxs‘/b5gNAf{]BYd#ucb+G`P(jRJ״h~1>u1GϜh䀒1*dCѾ,xVo_VNza- rlg$ re7ƃSؼb^$z]H:e9gq~pnEx/J< 'IY7jiޓ;hLs-b / Vl΍k4>HqExV_lx¶.[@x3뵹&(5y/|5_\ϯ)u+ym*kms"F|%Yv1@uxS_5w4y֊-m',C% O'/:Y^? hGղZ%!n#0vL}qGim )on 04M:Pd s{姼pԾu |&ig~#L[\Klf 屟A)1] 3N+X .fxI DHtMx}'3xWյİ@ ,r*M)OzgL\ہK뛛*F 60rּJ k[%YizbLnzZ;g=X4|'osM\յ.MK>o11%DrnC*郑{yk-nio-N"K) 0py9<ی|W} gh׿fx;POXs3[|~4/ kѵl/%` O 8fsi/Gkcxk~_M7E,֓y#"+}~(-ASA,-+9+dQ1*Ne(<-!JGo-Y$_}?_ҢOYeikA5 dVk1xI̴&udT^O$SXtau 5%k=F6=\c'цF'Qw o=cg $!euG '5V0h!ƞT:A!Octdʞ=b ( ( (  &"C#Vgk+ǁ'j a_4OBdșMKS}kd9a\^0YNAEQEQEQEQEQEQEQEQEQEQEQEQEQEW? 'AV?RGKǛ <2oUC*~Mu_OKב~ZHbZgis}keeP5gwI'֏缿R{1ۺOl!?Ə'?W'h 򤸐2VrVlO@jo y ;WOxZc5dW1Rx 9GZW-buk\ y?_w+O;4G̚=\G<1/;y{aw|&m?*i'c6K!c#tf.RYկ'?Z^AG缿PoA[OwI'֗缿Q/;VheUE1KCïOZ <=€)kg¬x: Tiy{A@ۺOl!?ƫdKEԴn y?_w+N*WqwF5[ffv"tQrp3 Ȗ+.5% ,VU`z <=ª(Qtգ]Mݘ^j{w4<2\'J8%KӼ/c_Gnoyz/؇qg;f? y?_w(vOiq[w HZg s<ڨ5&Z}"{XYA ̸-J|= y+X~:;QmեQp'|/m:\[ɢ2y$V_:A$#vNMoy{A@-mcch m@]7Mhn? <=€3t B\҉j$xO/ y?_w(-\&UUQEj2i_L k|##*]FM+wQEQ!EPEPEPEPEPEPEPEP\GƏ>&ApVĹaf5!>]y g|FmR\xg~]Ы>zMO:InÌӤFɲ%$^o=3w;83:in\M37]/'oRb]!x,ѧlR|_ףIAPuT-=R2o!uqrF[D?_SxB>%|0bՊmp $Q9VÞ\"ŵ$sId62IkgqH󌯤by"W∍a@%| 8>X𗋥L-I B$  )lk=Y[9~ͮR)V xRoHT-xG oMn+n.!/hݮO~ mVfrAlm>XIFA%&@Rյ m"];Wi{#I'2YG*w6߈VvV: QVX/oX *JXpV{} 4o|;)%4|܂rHGd_ u.5 \GxIgpѵr€^XUSCeSjZw.V+vƗ bɥ-ܨwXD ~;Gf˴ћ贽-80ȁ7FÆwu\< { ;KJ5;+M:7k"ʹw+%H=i?Xx^+y>A<` gHMbac#Ť[ TOqtTkZ<*+^8o_?ce9+yhO\}w߄>a^]}W tꇖO,d}q_G|v-<ih-״}Iy 6((w9j~W~W^6v7y˟ yN^xzVl"2un?&9@aإy_̞5D_ 1B' f5[y>7!4b0ɞJg1UQZei~ P&.q.|) h#_|2'X4ۙ"]J{_(g{U z ^f֟ {x+ns?bTfP  |H+uq\mwulz_i_ x_VPܦa(-o0/ MOBŕľ}Gsg{k&$+duᕇ x\{E jF?׎~9|ς;cݜL1aVCE{^o%iz/t}N5Mr?6+6ºxMFuZؒ1e|?2IОI8-K.Jԯ^+~W(t۹VAc׷|5NK6 _Nݟ)?z̍pyV_چ^"-[Hm+Q:8H,6Ŷ: OxUv ?5νS1Kk!E54omwbկe֟p>~Y8<|dMۯIjNh6?'Gj= x#5e#9o<*6Mt\Зsl`4tWoY>]K(.04Ӭc|<ޒD%>[y4tW#|Ny^5Ч Dx؆He yw(iAsB5|#Ѥk^%1%liӘ%/(}2g_x*?د]O^@=^R>[  ^u<"+]Ms~4d*€:J+̿ >;mZzi^*SrKIJ>,6D?<%D _i*PQTM_J4RԬ.-gYco)"EPEPEPEPEPEPEPEPEPEPEP^_*'++ZEK).mB4/oP ʾɿ?ιoxS~"֢/#POrȑFrKN3]M~MuF + J-.!3ƭH6sXdFM$>h!_xCN.AˍfT.mKI4s3̲g G^)uz6wek%۬j&"HPe5z.S4pQ<23) A^XwjesV͉ΉK>e܀xyV燭B>lK2g&ٟQ <3 zƖ-u]Zo%ڢEa|Ŕ+^¾ CBÞM&Y<,Vn7'3xkEѮ.%n]B"y{$^a7lUw(嘜H6V~ ?X֢Ӵ`(%/q)?ta#n- nKqݧo! ƎM&b2 ۙ#!A*m3^, rUo!4 EUq'֯Zg#ci^j\`8c,@<}jQQ}(D?P2Z*/A=h{G} hG B%D?Q?Р h}(Z*/A=h{G} hG B%D?Q?Р h}(Z*/A=h{G} hG B%D?Q?Р h}(Z*/A=h{G} hG B3"2 c?4>vOZ6\uQYZxw8YZtpGʀ}zGcl~^]"k.C.~N7SՅu;T8SIX*mpĞϲՏ M8^ng;ҴW>ꚍс g}F+2\w#@to o_ˡvv?Bћ0:LHr#kx5Kz7Lj-otThcE9*v ֚ĿT7|#rGEkcM xse>hsď"h&KMRXĨEC [..>0|p QbV[ aw74NAź['^:Ծ7|>\xR;X.'MhO +cq2815Vn[ˣ[35c8"`@nG h{fq$g0ʜho{[KhKDߪmO[Kfi۠<\y&\և?|9]dC(];Г(LsF^ |A@zjJ`.>)t+xTIpխdE2r;Uđ~iBVM {P~? &H xm/hHcD[\c~)C/ "N o<@kn$OF |sGC>gDž`oG)0XpfcF2B`!>MFx][s!?nQø;^bʒ5lW ĐʽOF kg<9c7 "b0ñVAR J|}GmW5^bt>CC(z/>$յQӮl<;%*NT\U/gF&7^$2LsWq*U׼3i^%,;>1$2qr<Gھ3ͥx+đ/_]iWY?1^Ey,}fX[aԢǨ<'8*k/ '[kiW-WM>LmT9k{hnaoO(ul.tkJ)AXQ?宇 qg5ßXO czm U_IL.GM?[E FM_CZ̜\7sMu/ oOi?(麐)Vrx*ZԝƵڠ<=WUx[H$`FЯcA&9[n&\xgZ~fI?5w74u*TAwʇ Ͻp_?߆2ռ2t˒u 4e;Ҁ=6/N(((((((((IUpOWW? R:] <h_(3*?&|:hA]UlPv6J3{Q)h!0=LAFQ)h`z Z(0=LAFn ՕJ}sT#xɎs֮`jͿ"AFOQ=6jp=??5G|,!`z {Tmڧoƨ\Q*oAST`jͿrAFOQ=6jp=??5G|,!`z {Tmڧoƨ\Q*oAST`jͿrAFOQ=6jp=??5G|,!`z %uD7s`iM+ IM@&P#Rb҈j}FM+?z/(ѿdҿ)QEDQ@Q@Q@Q@Q@Q@Q@q|Qwo6ͮ3&@lyeyve+ufQX<:w]&<\O+~o_0~V)7Gƚ̫qtfo[$J $Rvm_N%t/tmΩso?w|o|_t'r pY=+c⯏ xg~ $mlo2$(uJI<W*"5 8-ÎRHg2/;?-qgokX˽YH`m.-k_KU^>mo^@7 ^Ѽsuޓ'%0x6Y)6Eq28c]#o?GC$sD"du9  WZxKU'C1ig'<3y< c2B6 px";% ĶM_η7qb[r1>X'2оX;/?$66$R~i~Ua4?j{xGQ+ȵ2Je5N#]wzP{e#XO*LȡT ^s W6[ RDaF#ЊZݒxflh~8ӣ&p? 1e=hG_ɣZjQ =<ؕd)Oz} x‘gxrdn4C;&O$8 tn|=sbu] ?(B㏑QwƹQŞNU^ZP96 }x{q=}Χ}o ]ťx2NwrmTr1~y >ΕmW^F%x\2HU w]楯CF[y1kyugس r6st։{TyV @"4(((((+Ïx̚׃4ىϝ%~h>?]U4[?ԅ;} [ߴ5{2b9]cUVѭ6f H>`yb2/=vWn6ouwۉyr=AX7t36y E[Ԫ3^HK*`!MьII&ke#?όO x߷͂F,X T/~ c]ϯK㛫liI.^AEWyAEPEPEPEPEP^_*'++ZEK).y (eUlPv6J{UC)!m v}+XQKk m-mg>i&| j5=^IYk3,چm,13c/9AH3T#Ia}*t]HZIxEBkPٍIe|gv>//c-#P}9#[vgfn6(;O3KU5 "^E~Z2|S0͇入:mΛAʷ2gWGuV¢6P!1WhӝX緶1YR^iDHRԦd@YI`IQ~9/u[k m.UҬn2JڴȌjX[J~f|A6Zs}i]B`.gh&b4&euiy7yXeI i r9f` ?{tmdԴxu]cZMg-Po2C^cZ|5^ZZA7my؊3V88[YiEkZ<^v K=+YVH6#͌Et7CMZmg B!I?&989UBѴ o$oZw].1!XK0_xZphZoȖ S@PďOmXڮp.3ͭEqx6#IdF#lN*mkǞ$Y_]<ۉ b8یH>Я!T:C k@U&/7j0001U<akjj΁yg-3 XgiuhFnQc@ʢAGvr`戺]繒{IegFm@1ZjyyGY~jyy\ď:τ58a,VvTY1=:?ޏ?ހ-WjgtXo4FO=@׭coE7"^YaQ#h#ZEWFJd'o4 ! ee,#Qz{[x7Û3z}Vzh>KU5_>uzwf629WLJ| [BOYNOgO;F$271ހ.z[+~4 i6ޢ gu^|=" md]u"^b?'~ {/Oυtv/݊(#x%$8 |Y5eXlϴ1a1SK0 QЧK\qegIp $v*;ϧxM>Ĉ46r&9KgފNWnɋçڤ>c}XFi,}ɯhXfO$p\mh0q{mHrv<|AWwBW .gvBecpn.z2YV^?dI+gU!;Y|ok z*G{eO}C  DC"JHp#hHf?|?~1 B0,. yGlAo èwexŗA2I"5 PQhPC{?<oOj3ja[=̰ۣ9DB[Y4x):[|R$մxpɀLǵz%rt+oŸhBo1DdvE/!k(92+ 2dr;2"5^U>aenm$T%-fP$Lm20_≧AB׹ /恩oXH%R9Pt`Q@>x>lC!&+bu! XpEm:m]iZ77q47JDaxV5*L֣)`-H"CPYfpPu0y`+;yḷTT.·Do6dKrÕu`0Es_|MAMʇĖ1yvGZpp$A[ :]f>M#zr*LrNZGXT˂9kH!76ŞPxq Ra _jȮOޒZ￲"߻>&x=Y#d:ح 3@'IĺxAvr|cş&[kOMerbx[R̚[\K& À7naL?c#H#ǛYOA#Q=t/>'oú-QKBنS2d{HI,} Zi? Z 嶉~GYImqyuϪc23\ Fj:'$m].m~_.|x !򡶍ċcH# $-kz|PЦm&]t9-slnKa&¨(;5~ jww~Kf2 hz$j~\ҷ5/\Mt.5K!sx'GzF ¾a'.iwtkIFEhqE&ERdRPEPEPEP^_*'++ZEK)&mB4?oP ˽ɿ?Ϊ\lj?&|:gsѲRC7袊b ( ( (g).d1LUڀ&+kh4ҸDfcrIAdr((+xnbMaAt AUO{?u=.[L$3\TqZ^1dz?[^^ dz?[^^ dz?[^BgR;6ҴU78MAf 'X.dcKz_|]nax{Z_Gax{Z_GX.acKz_|]nax{Z_Gax{Z_GX.acKz_|]kZiҴ[GV0Q uVVR;AT^ dz?[^^ dz?[^^ dz?[^^ dz?[^^ \k`p[TP0U> iHEa d}Eai>e4䷍j7%yKJ!4wLjn&JY>E]ӏE'U#|z=h =5z=h =5z=h =5z)O .R\꒔]vBM Z_khwŵG+u)SoCDiOGgÑx7Z~'77!̌^iO;1;Wxzn JHI.wdoc xX9$w&/]Do [x{c q?֏`#m5BhV55LFF}{YY<#_JֱO[ 7Koa`@_ [V@.;${\i:vj 鬭?ְZ}^;좏`#j4٣}[^kSKn29/Ϡ{ë GQOkI-qi2 :(X;I'dMʿ^ia|c? l4Hlݧ*M$dC`V?@=k}bp|4GHIAh =5~*xO xN[/%kdR3Ȁ4{"5x?}Էw7jƑwo` Yw1Qڶ!Ӣx@R Ot@`Gqgy :ǧx"9#;dO=:uh`"_N? 4mb;>-u'06pA ȧzşZZ|-%`[QA 7C,:Ƨ}yᵦ5M* UnlsNS.NܟWVDHb2[ˆVF+$N)"0]H0Ga|f/ßxKSդ$Q sʈ?<.Ѩ%H'5k/Sm#zxPd}3XZB\} x_ Vܫ%k戈Ki=*΄ 37,k-մ@Xkz`H\cr`A{dwoy VJ9&D.[@Z^񄺌hi!Pn-d'߈jxl^;i?td+Я_3%~X=l 2w7kMHuHu#?5x'֓ωl<GftK[]aV۫ʩ?#nֿkῴދw^:_#Ozb8Z3qGa푹=VVOM=t;q&ʜ]\D儹Rvܧ u~xO>6;4oִ 6YfVxhu }-EGJ-2>b Qm?N>6iWQ:MI2Z͒^' LO*I0/+:Ga'\O [I#M%N¹ R>VC) #oNJ|ނUӠ#}\{dz|SEHn#5;|-ōvHI/ueo9Y15ԇ~?/<j~}=s(v3Ga1׾/i~?[̸6n}ڧxg?b{e,lWM*84Ӿ!k_}n5j\z~k? `i|U4-W _( =(i|i?8o%n7w{ؼlK<}ę'B$˂?45 iw4?Yݥqp(e6}^Q$(ީnBP0df9E&[h$yV`{C]^Y%M}367 EDWIk?jW}@*`=Gs>j/e-Γ#]zm$c٥+Q{dj ԅZ`Ե)^!i]|KAO} Ϭ^wb6q'?OcGyw~/?x`6ƽpTquDտ,-[#_@Ñ#gf< zQ{dp_%˨ZŨx Ϋ{1:YenbPgqW~#|DI|;t;{F> o;?^^ c#crO`#'ÞǯY vPJ˩\d$m>7 3W|9w5=~9nKF6Vnb" guit~#T$0*KQ`dy矘o6)=>#Ƕѭ?u'y?`#Ca/,~#x~`B E1+#Q5)Կ9qw,PL,fះ Ò [Q@%`Qzh^h $=BkW>D*F-Z71qo"=9fey- z/7޼/KY|-XFjZ T׫ڵJ$f#AFGY?k}z{dkyyGǭFJ%Z|w@:,dl+Rς\٬%FW%EAaEP^_*'++ZEK)&mB4?oS@̻ꭟ~FVo;?%J^p4/‘[騡9s0/ SecUIo]oS^Xi)4uͼ;[g=0;&燦A%i{elFPA&IyuM>dn9uFb<Ú߉?z{-_ĺ|,V۵dA&l':v:5/ۣ[[<#>T຺l$RNXzz^։\IuTysȊx͓,9;?x^:kx{IWll#$uwZ>&Rj~)!as5*it?#nsx mJ'a[jxoml.ˆ0ȭ,bZsֺޫkipjR;HiԐHw,i~0izc s [Y3!FB(̥$:`yw,*jz^KzZKE$Xú!b[9!K,ikzZ'c###hVLϒ퉱o i(I{;X[X^=ݽ2di0'p  wsVY bŶqff'']qMo4wBKm A&*Y d\,y? խ<-i3i1d`w# W1$G/kP}n!4ֶ, $L"6l\ BġmȪ-.Jl|3y٪PI P`pYW/%Ɨ_bx6.6~\ qE]H𕎅w[ê5,lz@n. PĈNA,41kW;\(욫h2#[mF"'tȁʫ #m 4+oϤKP:F>ն*YYNMc59\E-<귖:^ftw?ievEʔܗWUI]CX׾$T) ڕO49<b,:kYj=apr0yX|k궶PBWQ+4H$;X =(X|axB4 /_3iBDi#V|6|w¶8g_>u}J_ j_5Gk$*JM4χɢ&UҥΒAn wKp9xe$ hjM">Rۯ$6I=;i`4trXjwYr <9o?޹]*:NϣY4$WrI֭n?A?Vh+c 4wI'Vh G\A[OwI':?޸?lVVҲqo40jp7?t BۺOl%?ƀN 羸D[Pz^sK?wRs 2?@MADhp v؅12|E! ?lgT~n-o, ʊpQ8$sU|Aщ]&Ӯ c~uji^F*II`}ؿ!?ƍ݋kkkS2gT8;+o[5=Gų28ǾkfUj~jo_F 5Xe`+R{pI?)q߃~?go'ѶBA0^IlxP*s}(*?a"}݋h}ؿ!?ƠWa_F 5}~ʿ3?ȟmb6v/O(UfDo'ѶBAE_6Rm^=>wkP68-5CցYn3L䴎y (~?ٰgKVz,k5y`Ʈv}I#W uU[ Y,`޵ҋQĉ n?ֽ">?_l?r.|E]OIiױܴf#:nU݉Qx^ .'g{K{h1qH Š>?_l?y |CO ]Ei"i]o$B ӕ"ceqd uhg[; (~?ٰg? \Vֶ\_-n#"cb6f= յ[-/5e~ruzG.iX7,FOzi[zygU_TG0͇?ombC hڛҷ1 gt>kͶi>"0[G17|f4{ R $U'-ϥ6\o59McO>uW(ǃG0͇?xL}2-9 H%cy^[1Z?3_xX]ORow]L?C]}j3鶺3[G:~5okĚ}ޕX }\f nk_a _63|g=kX׭V$\`3JB<))5AikTPDkhX SںdIiUy# "v:v=p}*ִa S#")瑍ٝ9Ӛ>?_l?y_KC{WӤCyrz#}i:/ugŭG1OJu^Eu-\Gg?_l?to'{ImkILܠd`r<2= j3鶺3[G:~5okĖZVc+\$g34l\Fv7UfG7xC:"n/c=m-VB6{VΑŤZ M+ME-}~ʿ3?ȟmbJmtkKe+˛#V^WarMj-p#1 }Gs?_l?CihtZvZD>H5߯$'\-3!]2m%Kbwӿ|G0͇?# CTuH`y ^y;/*j7mU3 1c[wQ*?a#3ź^YEyqo3طyxž1Wv 68[emWG0͇?ٴF_jFEŋy4?_l?`xGM"L{xbT\r4zSxCKm)Bg[C ܋[ۍ*Q$]RfDxN3AA*Xy\QK)}~ʿ3?ȵBo'}[YDžnMv X$^pX Q,rA58Op=ޟ0·?m_F 5VWأfNA¾O[ n̍I )1Ueh~?ٰgB:;ʙv^=A) ,@G,"xՊly7#m$zei'Jچ)՟$0 ~2z[spp+ L[vc]Y4%t\^ԧWQEO?"BIUpOT(DF|!E̻굟~FVoo;?%$3~+/t+䰾gc=ܫH ;*x#z KYKauhe~ƵUIu(m"I85 ''w-EjmepY۔uYnxaqy#4hwdlCOn;%<9a9- }"D}m1[Fd*N$+&Bc =Q@4pi 储 X=}A=SE>V)h?ҟ?Z N cxSB1-03?20 2#,Fwۓ|O}?Tm[Z>c=֟? w3ڳleİʸ 7qAs?nO?[qQ=SEts?nO?[qQ=SEtg\kx+Ș$8'9nvn,Wۓ|O}?Tm]7U5fEѯuN[ss)R\h' .b}?TmGۓ|Oyy?TmGۓ|O~]彳Y8nJ;$^&#:VE~ܟ>z n?z<z~ܟ>z n?z<z~ܟ>z n?z<z~ܟ>z n?z<z^0kmEK=ʠzS&  u2MI6doKs  |ĭX_FeOC#AcxMC?|r seq6~jW6QM%Dmfq9{zUϭu{ayƟoHu;8n9qF.mʐK8=jZlw/)ja<ɭxLx >'|Gi$gE*<27r@tEзњ"7O-MU!G`'$v$*Ѣ((((((((|ma-mnαmqnf%`^&عqC- [oXZi\ï{5 }UW@J&nas$6db!w?l]qu|[M>eue4pEJ]#9(.{{_o]O<3@s,)CM-2YjU݆?fUM2*2|.pê83A4#=z]F+&CX\Gj,ǂHfxBԬ5;].Kn omyNެи.BD]ۆ6;(,nj$>{;͐݅J26 ];˹[]FYǒ$n<ɞTv0rpNEpH:iS h,рo3 pBA ) njl,nbw//4I,DxMf#&qC? ڟo_{7gv1!Xx:RѿLJm){yM}cDB"B_o(~uy_i۷oqLzT4Cs[jI;vc-kq%b(i^57{1FY3ʽ;-;'V:"K,]C1B(rs7SQ@|&]SIԠ5GXic1yR4m`RZ:֗A ,~ Kde*, g Q@-)u)m.,>ҫ= 69joi~%i]>[n|^B>-gb89(((((((# ޹-igN(8N UA"k8â#4ZomhvlWq-UNࡏ.Ź-I$]?hq!\dTjQ-s?jBRۙ>Y B~]@$N<R mY%fKFcAPې9>+c4@8ͅt+$YH#?7XBMֹwj6m5>lw `{c1 \V-s+JAtmIOMt%u`ŘoL[vbiukn۠KiN:QEO?"BIUpOT(DF|!E̻굟~FVoo;?%$3~o xėZAsiyaMy`eY 8$>i9bo}fC1G(_'>E!Sc$mkwSTs>^{Iu9@.MUPErZ^=k~ݯ^Z[^Eiʱ#n$ }E0<úMwGcuΟi, edtyvr+FֵoFcVnHT^T+*YIV'kcj~  ·t/(t&eٴClFcP{Cf!f[|9>헿}rΗ]KG׉L–QJդ6;$R c*a9&7!ҵMKTqch'M.Y'd1d\+ȹ;Y=Mtl5헿\,y.k7 y inloVJ_ {l`H $pqC(n]GyjfvtkL =ݫ!d ! *)b3]/ }(e? KE֬妥>"뇌.U!C.8^Q_p.<^wxOvOmOMmđ$")^3 ˅.-^~$ԼOU[s\4mY으8ơc=+헿}r -cӭΩmm=cps!pEv~b}r^PߟGXl5헿'/ }(e?my_p>{@k9@~p4m5&UD?z78~{@k9Go }(ogii| ok4?SX?z חǦxbB}mnm4{RwaH]ubWIFu;7oq?SOnDh5}FP={x X-МW7m<_ҟ kƑ;[\M).0 VAݫoŞKx'^[';m1ُd1DxWα}tG󔵒6 r-yNUc_ 0APEtꚽhl#Fh!{,[4mKNlZBC 2\'wixgS֢$j>[i#YЬM.o &BK_jhxuXmWS\[#&˖8ÒI?w!LOS񖹡Ig%jP" i|#䌮 sx \m`Umt$/[icr"K)H(@rzWAZGTmƛη7vrY̰$ @v r#4'ukfIsi7pg&u ~/]h |Eei6Y{wǒ>fu D;oĞo+[8V]#qaAt_iimku *CUfC*2+ U i2$2"I`cPǏg#w l<+Zi QUee+,8$Þ7;\:Zzb!!$G:aWxW_jzޑ``BUX#G3kyѴ>+!-d!w.|*>P [GARKWipK,49ݎL5(((((((((((((((((((((((((((((((()$SSk]$I]X/㯟qf~h>ݷAXgZ۶+lFaSN&vQ@y~ OP/i\A,tz'z7 (?ѿ?QN̻굟~FVoo;?%JExzm>EKYW0L3 O:E1x5z|h]OC(}R7KtFMN7-=Бn&d08#?W[Fe/.jkXiwk7Hu6776qlYP hzƾD>$ַVmeigr(BWpە1Pj>?𭍖y%0Iqlm,g-e1ES", |c4Yyד\5^}XMv 1 ZMk~ѵQjEq2mHpYJD.W4 (ַ֗w[˾K) cIsȇ##PjE:O3Yr|J;jv˪%P )f8YUȑ+wY!ixd%F $Hs)4/Ϧ>:O3PxWi걗Ԭ$mнey^>V9%xB&ɶnŜ"lyM3敠 /x}4/Ϧ񚭭Z.^W8C&idfT!BsMon.ɱY5"3򃲺w9 bi'^ M/5SJ=y;;ۃzO3BeHtUčӴkV֎aYFM3J#ؼA>_Ih?K?PkY:r}$cm-6[HΒxKo KGM .%{Ym#Ȫ덧3^ M/4}tf}[7PeEs@zS~$xFQT-6yA&dpPؼA>_Ih?K?`t+okQjc p?5CiU^Ӧe_QId)*HEeSЊ/Ϧ>:O3H.ѓ 'Rg2E1 s2݂UXx:GMψl#RIdi eHK|F@j>:O3GؼA>_Ij-z650J[8#lFU4'`GѵbogwEc(bQ27)G# @:O3GؼA>z_Ikcg<)Diw%i3S2C@dUIk=8 ^c3CKh#9IP:ȭ;T5 7wA$LS`ϸ_GOX[®?:?1+2X>c_ubWWvH,w#Oэ!Lj|R[&Qkoy'=9&k=AfMɿU[A5vME5{(M) oeLd~IٞUne.Xc WI ӳ[&Qkovj˻{8|ۉ)!Tu,Ǣ9$C$5oUfMjucb2r`zbѵmR3t݆ 11 !`g+]XSsɿT}y7*< "=S0xI1ˁpJT.L< Zv-?[&QkoMG:5{x!,B!_SM'PklJjWLqׯƴd]OA5oUfMWֹk1_ Kn>zzWYƭ9REjqRo٭5oU8n?Á3ts95C~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oUfMf~m<?G٭٣4߳[&Qkovh7ɿT}y7*3@ 5oU2UPaCɁ{'sK'J|#0wFƙV >ݷA[b7#ҝMJuq3(+ZEzy~ Oe#ѿ?QFB7k??띟UPv6JHfpNMyaյR8%+L~Rú5<i~9%k/oao iX21bkwtiuЭo`sJH*1GPAo^E08ox?Rֿ8$ix_1l~ۖ|)[OC?LOS{o÷qm9kϵ<ۼ-3gΜm5f(GQm}.DK\_\xwxS -#}jGY,9n]09!>KcrG,2y]r=b;uekqs__ϫ[MI.DKRřu=F;Z&x{z54t&KZ%LAlfv_;Tɮ캿>˫,y6ėWZz݄ M|ӛs|?[G!8_Ë{WP5n,.GH]R zH]_ e8=ԼK囻YMiaHXG2"2 #mj~ԮO sكKam+|9$ \캿>˫,sZgu+SٓT&AuW'8gX⸁/tm_j:)R,*J$W;Gu7\GvmJҴgӯ> bmNb?!TD!0TGx~NZ\\̳4"Trdַu7}W;@Xm~ž#]|7:ܢP2- 7EZ-o4MBo7裘nRvk~˫캿1 jv7H+Z$/"L)ڍ52~ Zkw~.GŧZg_\{IDyuST Gu7}W? =GOXVVͨZGT`G2Q䥽v$H@0+z[U|Aщ]=3\3n o'{;1JoQ],W}Etc~|CZ/spFd?<}1b6ק3MyǨZwq,MTU-zOu,tl5 `򆺕gl"yaxYQ5mN88o11,pǕ=k|Gr?IKH?ɫy++CH-V{B3V$iWw; ַ[6M b$yvW9x'OJj jWKtb(>D}:nFknLeԥD.3m\8WU^{I6v zv?Ou9S_խ},IuI^m.A9' ^}kc5!FmX: rO6v "hZ5Cx+1ce;sօ׉=4, 6יPSI\GfjA,Wh9Y.c UVeQ˗s)(/{amu Z49_}F]^k5ךז k#qS=5V  QHv?uIjv}?yp8׷Ni3XfFi4њfh?4f3@fFi4њfh?4f3@fFi4њfh?4f3@fFi4њfh?4f3@fFi4њfh?4f3@fFi4њfh?4f3@fFi4њfh?4f3@fFi4њfh?4ۯm]$II.?'J|#0wF֙V >ݷA[b7#ҝMJuq3(+ZEzy~ Oe#ѿ?QFB7[RQcEhO^X它y$N]OafdAXZYTgRW#<!!O T ƾ/*Z5}q%3K+"B̋<Fk"kh2tz`H.@$wNqژ>|?2̃̏:Ŀi?vZt$X(2YwWݷ1s@|?.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓'?E_KO_]#ZOo>.F|] O?Ə-?vi?ݽ?{tX.q?Z?55vkI`iikWhm֓5uk[Bmf*,9-?Y25H!y0rVb.^qJj2"l? 22"ßĥ]ܠ.Ŏ9<8y|4tZgZ۶+L[vk܌?[Ju5)Т(/i\AO?"EF|!E7zP ̼?βu-6xY`2e8 V}u;E"@q(gs~a%6emP[;WΙ# c vt45y$kY(+@B\yj<S}ƺtZl3?c2ɰި@ V}mruNL& SO[{?,.͊L1' {7?O֣͟xS[/im<30m,[]ZVGNb L|/Nox#\wNLz>mjBGHne,AbT77j<OEA[EG^u;*h[EG]Xφ?<u[Pw^%I7:6|lC~h Pc,+-?t$7t,\b.^0Fƈ#Qa7H{ѶW (O&_ ObHK;|QI cSM}-t4HofeA[rrKŞ"xxŷտ_Ph= KHn$cg@țHr7^ @ե$ӥm&XͶdan&$~<I+v~UǍS#Hϋu%ۭ'w2l#sQ(g,5=ڜ-l'ND-/Ɇkx.!Ғ ) k@KneiU 'Y8ks6~0tws,8 -Dg #~>xmψkx"rDem޸4-;#➥\J5݆麂-<{Œ9 Z)VUcbᯇ𷄼=$Od}>q*2! V1!`Ft6%*I1 XHwB3I 0r?y}# xXӒi;V:KS!} Ų]wʚ9-f?s]#x;T.4m1DJ6\[&ߌAÎ\q~i0sNjtz}k,kw,b2 0`H+0Fuo:_LoAeҳy2dܡ En4/]3G֪q1..zdkt˯Y őZ:L.fVi`ݽ΍[P];~&MJ}A*Op2g 0 ]<i?n/Z`gu-&9m~P0A Ow{}ȗvo=ȩeF*MG#nn|?OiU0V;o)$2\wGBkrDV#y&v676Q #);HwlF(xBu2x"9leTrD1p@ jĺ6TWЖ6/M|mT e8\%>,4\RX퍺>"wxS&70uŒU5k'5bt[Y !2"f@Xsx4GOl47GHۙAxUBI@8(au}ky=-\夒'+1cDS'{ij>_޼ù H$ttQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW%o.YM7͒[XH?91< x7PH5lOy G' (989}Mm:NGֺ[}MsqOѕӂ2Ga/掋L[vbiukn۠qKiN:QEO?"BIUpOT?QF3o?_;?%Y?ΫY\l ߢxUn|It<;rڅ.xg&8 [[ :`,Ar(4VI&"2W}Pqwgo5kV>$ Fn5HBXd,.O4\Mm)KnX[immf`Ѥ.ΦbcqBxkgH?wwy^v/;7n>%B ,QyyE&&+*Dl,Ft7DZzd9x[$oKy:_%Z6S 3ȑyAʺw9_~!>/ū>$xk,hl"nIRq3s?K|oY?g.tQucO3E ϫ_WZgg5kQ 9z?5zuT!{u" f dh9eaw9h3Cg+ky'vfiDaf@U7AZ>>IL5m"IJ_YR6dR˕C}M^!;sEw&TBAs_tkVSmu//WNQ UMʠ6@DҵnI]fonW[xmLrr[`_-- DrǨ[Zβ $Цd3!fWi}sJ{ D*nʤE&crh䊭|8ӵ?Mm6jZţDг\y99tKYUa7awccse<gp6Ɍ0[ `}AI etK}rt+kJo.L)ڿ!ˌXO:]iďy~l+kNB?'ˀÖᆙi^?ٺD:+\YwiDdd|:`]汸еmW7V).Н"d9v@u|`c^:τ5[uGyvI8f}&ƖmŶugϥ導U}E8X=kVݛOmi*,#$V}cB//% !awVq,N xtWr:cnlj$Ӿ`Ԓn3nߌ Fs3p8/B:nڿ5ֵ,M䟵Nw8w).;e<6_RK߶y"GC# cGBycGNM7GO᲎9ЩY7q1@]s_<=xbKIԮnmZ k%G$Gn!'*`hmN].S։$I\Z@]0yLsɏ12]'Wcu-?TYtmF9ż) cu*(LTZCG53쎡gO$5~6&GEo5ȭnY1mnFS$Ro-nR̽6̞`ɷ;O8B Vs^%|?=z{@2EfY+m i!Tp1mwJ6kZ, JMT˞o]>4xd$7v]΀q&9ߝ/MMuht Y_ing=̚V٢>_G%-8Z­PГE57Ie$,nQ]R) I&6炴_2jkvZ]~Y~![;d`_]TȴOI67CsQLBc3ʩ+/5{[-=ؗz%lD/o7u? xF-tG[fY[ہ!Upav@ ÿtUSt3+NN }. ?;Yxi}oNzF],i$E~PMD@G!A3ޯxB45YU ^ a܅D\JIdvg<^7懯j`5Gdt\%p,!,cWb~ ogamS9'S)o g%=afd0|~p5м)o}[GCo.h#L(XR\QxKm.c}]Xjjwq"ndٚ\(\yr n*n"f֖Q{\k{gwgLp#|8)c?tg׵Ү/&7{.\2[6'Y=#O w1 4i7;Ь=-+6,Ze68B*<'JN^To5I&qǜ8#QPd@::( ( ( ( ( ( ( ( ( ( ( ( ( ( ýzA[{!C4Tr/)__2;"Tu+e<_/V >ݷAZ7'ҝMJuq3(+ZEzy~ Oe#ѿq[*'q$Aq:^(CwRVC*}KuZ?ugdfy_^%%h>i5{n-5-4%~D?yZunG\W2NHcUB xϯQվ/R F.4׫se$K"4Ip< fPWpNj|#񖍪]]Ecaۺj74IjBY1 ћ|g56Hvhu[idkGr\36rW8C?jtGIj_ۏ7˷wy_3q4tVI|Qjmv~_kp8+` j ˽RNY.ZGSI>ꪐaz~n?ƌ~/q^5-߈H/cRӴm$$Y.2 2D7#᱂9o5ڎ;L# ޭ)k(}:פfhԼjjzƝ>o#֬!E":+7R'*ai=ċRmoisV2$ I2<)5w;|}VxsIpAܹ#'n?ƌ~/0<^*U}hhkr@_yB6YeV޸dqX>Zx:jZ\Ճ]Bvnvg$^n?ƌ~/!vM.45 ^ FAi_f*,vMPfh2z* ~/_7ߋ?Fn?ƀ'ћ| 3q4fhz* ~/_7ߋ?Fn?ƀ'ћ| 3q4fh*CR!2,Ơv/ ,[UAщ]Ɵysh~(,sm7լaELϣڲMa1*Ty񷌼Ik>,ӴuO3òjA: "Hd){ h:/oYcqwn nj|]oxtho_j:4!n(巊MU3(V@-T Mvx7zWu2kyq5ҳnr|ȚD,Ij 수Z_ g|M'%ܲo_ @'F>l=b/ݧ^L6*[Exo|p:u{2VOs8)ne =Fj>X'>$׭$ƍKlm_ ,d,C0{23j<ˡ*<ߞqg.̞<;uy= ]V|0Im#8K|ZmKW3h1hմ3*2+M$)޻_tЯlI-{S nKam!R"BH<{fB@qu]Mլw`P!B3(*voψlnu-m&0E4BbQhQT$ $Zm;RԾ:m]\[`"]=đxCHJp']];# |$GCE!16mopO-tMV? iWVRrE= YqU7NGkz,:.Em6|o2V/ 10}zOn5{yw}siI9O-vW!T|n$ݎ8\+oZ~h^!t['JIKGqʲ1 c@uí75U:r4@s{41D]¶ N/?7^垫+? [\ysM{d# .M˕D| ȮXkseu%KY voÍ;WzΔu,:ؘ1ujLo6) Mu`]|BfzdӵdKhӭfؐddiAP'|sO [xo𽎱{!2#S/|/%cRPW WgkXay+-gU[k9 FUaYtf@1򺃱r9 44#NI!Zx[Hj$##!#=>־2kk-dp;4idT=3x&ZƊdZ/1jo}}\Ǹ =}+cndp hELLY !Sبx>dMCX5Kۛ "6w <#X0T; ?2jq]YA4Zp w ,{H|Oe.up&UQ*|ж :tעx^(- H #9Hw20A̧;Ag.xWmV.EUC6rO^hCM&UUrGtOeEUPJо!jڞᛓh<;kL:kQk=J:@qF[PxvS6z5ܗg;F[K,,">f!?wo9|;e+R-@Oҭ?DMnHVwi%FO_zSi6rhoex"\m&n-TdF#l+'-߇DKս6"i͋`ៅmf°Rq]L}{\m;FM$wX~SyBM U2oC]ut} H *ڹW`b9 o7'ROft#]-R?Z&&+0%z-K.x{ĺƻ܋ xz гG&d&Lx'n1V 浪^3]ܘB,nżx$].CFOimeqq-@hZ;2,KݓN@)|+Ǎ˝JM?OQ Q3#y˔<ׂ|eK Y%4ЦfŪi.Uh11! m@G0Ti5SXu$)\;^<<ښ6^ij3<"+'y,dgUw`8^ ɡ4R\j?i|^o˻pl|vRh~9m5zFwƣ]ǨA[Uc#p\`o4efKkYf4X#$$-< anjƏ?I6ѵ˗Јęfw8geP=+;Sm4ǒ64!W8xayɷ<ס:fg&kQ]r+f@0Db>f°Rq[Mjiq\]v[E%y 7J ?IŅΦůkt:L2Bmet?H*:rqz]ivYqu=Ztxc:E#dZMr7 ~`LtK-,/ou[5Č>95\|VX<"WPMub;W8;>mi lF$HH9 CS#>iiL>ZZO7O:9Uv%sd\οĚw%.MM` MĐŐ$mi2Jr| A^OLJ4;[ C&$b#Wv Iٟ-yƶz'-^XKHa*' q AӴGfFM!&HGN:{ 6_!avݻգF(}_z|MI,w֖>6㟹\|$\zΨu;!FjE4CD >dVuQEQEQEQEQEQEVjܬ;פ1sOѕWvzXPgg:Wº0__??ukn۠M3mt#rp m)ԧW;B(IUpOWW? R7tk{"G6y7;,p'(4o?(/k??띟ԿUPv6JC7U|P!Vwum+۴'}8 "Z g(`-Su-pzh:k`-D!d@A`~q:ÚiY71$b4ge 6aN#,"`-S}ϖмc}yU/hc Sg}1/~}7ztyx{ "7:ܡYm̧<7Xq/<,1?TlEl"hZ&kzmE6I,HTeHy⠇~C^ĺ4L k侌 m(\>Z g(`-SroJ -~oq>ȯP[!m{J}hWk{5$!;N{s _l"?[?ZZ|9X[jt ;q1##c19⤓zzޚd\:IawNq`>Z g(`-S?eV^jcfA :F'8$ozU.^1H{`x8ʱ,*}ϖ>?TlEij~-ޙ[iZt-B8$ U+b,l`5|q(od'\uG3E_l"?[?V`gzyQ{:jQ4vŎ`LqHl<}SKa4e``ix< ?TlEl"h|7q}i:ƣƥ[K@92*o lNCDFP`B3JKۨmyf"($(\~?TlE>rfdмM8`=CMgu+ixJ`H9I5[^[?p2}©ˬW$SXʷ]wv.lxn泖[.!dc:Fa#ǁ%Oy_O!jO&f (Ģvxm@$T/LzOb9,5G1E]|+J4s\^>.>=qZ++TbOLo\𾕭kj:I>2KkrG,MJc$#nF8H'㖗wk-u>\x@0^9(d Χ' vī[鶾'𶕬jrT/cıw!"cbs5dk|amGJɴos%%4V?ͧ#KkqkoFa`^j}ggOOl1yݿvyێk{Nvھi7vKqtbd Gm (9W_?9>l3wm4s.O[D;\[k~y6`H 4N+$u|oqX&%ޘ!yGQd~%U\6cpT#5 ܍u-9l5nFP6ڹ$xWQYA]gQݩYO5o cwQ*['ƺ独"x{FŖ擩\_޵ J$H%l=CNT &5ڜ]TIn&Utt)eX`YFbdqOm ǮZ~ڍsCՁyS6UQF?iZ.kՇgCG+Cep?w 7I8|k'mOIMf":kZݝb݁iȍպIڥZ iT:m.t}w +# Hcqdm_ju}+ĺoWOh;U4RmWrm$*N-x TuKGM݀Ȓe&D W#;h:]uss~5)uI.}UU& *6 u1h'unQ<((לH$s@>:[3Dd@5-Ql~џK $U'`v x5x\h9auŨu \SCt QZPee6+^+Fak}y H 1JUygLEZ[IaqZwX\<+[;IJ1(;<@>/#į?n=w>6}?uN}|:|>סOڞ<Du";dŵӑuLK0"֗=MMm/RVu B\TmHQ,O57Zίx] Gtʡf@*MPI:iiVږ&;ƶ:!m <Ҹm<-iz m^C7*F2j89>^rQ:Ήk:%ē,5^ۈ [+w<`/89:~խWuyޮ4wLYh BHU yKI?ōnyݽ^!{F4sqJd@P6 ݗߊw|AAa_]MCvEXy1\r3x;He&Sj1kHYP *yc` ]AZ[]R&3BRUx&Y0%utȼmJ8m7yuD-Ds s;iJ.S/ׯ^ 񇉴 u.BŚ]!W04ADjc(>Ijƣ$1Ou}?$9 7+/^oovhhml~\c;8~jkj~[ ]*Mx|,퉮~#o5[m?NkƵط\ nӴ>-ほ{ohi`_kݿ3;y~6X/_i'H6jg+VT}/1'ox#ȏ@:OQ5M7Dдxu]cQy0bI@FX%@ ]cVzse\jxJ)qgsmln-dJ6|Yxu#5-RщQ$HHI R0@5E/,^>cwe]hBFZB+g#|w1~'|Uki[o+KQ*8%bv)#,q|r.~i~ףͥFmw[]0^>*ݮ4L[Öw`Ac/zOb9,5G1E]|K]?~"} [A7MboV%tFYTȜ|rpi\𾕭kj:I>2KkrG,MJc$#nF!𿆮+j֢/*MA!!%wcs`@ԭ3}6|n߻<5y*W̸ښݖ>_{_j/~rN K'ؾ׻fwnm9xėˡ)mC;KT1;&b,$@H-[^ ɡ4R\j?i|^o˻pl|v]Ë M^-45Huidu8&A5vTu㓛x7zWu2kyq5ҳnr|ȚD,IhB]?4 wy-lێw}9·oYcqwn nj ߇~,{JעdHR چV`C;I5A߱믨i^%tIƗ@ֳJ͹"i;dGPI'I Xڟhyh>Fy^oݞv񷌼Ik>,ӴuO3òjA: "Hd)zW_?9>l3wm5 Ë M^-45Huidu8&A5vTu㓐}5`,k#E*uȆ7,ͷo4|B<1"1eue֌:hQ!\)xm;{]}CJ.ZMvo.4VmOH%:I<MQ־iֽqkjUސV$yUe2XuދvvZ\Zw%b7Yc6;'Wg.W1Q SE]nWҼK~{͆teP̳E Vw&B$` 6_x-m/UKҖ0- ۲id?;HBj|cҍmxֺ=ykmveop7u"u[ufӐmn%XVFd #+; _E+$MLejjoS X_$m/yq&סqhf"|'p6ݼ?N?M 5:CQOXb~]ۃc۷`o4efKkYf4X#$$~Ǯx]-&7\ Z+6'̉@A$ &2tm-o|;ok:^Ũe{7 BbPk38M>A, 'MU[lfI0?JB+:P`F_.ew~i+|d@S$]G±Z:t!+SwHFd(r3@Oam>kkKФfGjHd涁L QGħ )[^ ɡ4R\j?i|^o˻pl|v]!ZW#_h7۝vq<=u +ĺi5ټhiY9>dM"l $I4'G隦hZ<:VWp|Ğ~խWuyޮ4wLYh BHU &=kT񶱤Z#HMEVlBT'Eɐ UhV[3K-o 0 CNKs@Q@a~ ýzAC*\Wm>]OW6]Ilܙ% +R8y|4hT[[Uħ۠5z^,׼0,LUr|̮~-zUAZ7'ҝMJuq3(+ZEzy~ Oe#)|!E&3_;?%Y?ΫY\l ߪo*WO?@ZmZCLƗr˸.# dlq'KIl[\2P1A1## m8Z(>OI~q Np-Eia%] q{_I߈ENֵQx|B[ct.$ݺߍ2F ky<_ qֶ1~ x;Fm!llmXͫ]"Go&r`.$ w*tO~·Z-ȇn6gQxe#D*bjcoh|6{kwYnܖy_{FȉN8@}rO'o}n?8j#W;RZ.ErȨQ<(zrv~OU?uwV0uy׈:?1+|WQWN38?ĩN?g`ּMt{ k)-okH&d(Hx~n+Fo4^F>z.&S̏*7/0iXA}]Ɠ $/V"Y"1FcywdW0KV孛Bq4/ÿO Ǭ,DtLD!<89vGZ ^Cex%މd4Űy[pᶞ+~ŭE{7{yww &&"DAƃ NI$wjwqjzծei3#[iVs ϵX4;Gyk/x7Ows\H˱%#䈲 W-C7Gwn[Y5Y_-D`-[TF$q7xODׅtX4U6P<"@Xl(+r/w[ҭn.,-x1dw D`2FŔ$VCƭc ,z`}WMFՒmUpٍR ֮Xukg,m,T`Z4rvmꉀ0sk-k:nè@ sKn<h> jW!a{e,7 B+- 5_zSi6rhoex"\m&n-TdF#l+'Y+J/O^{^bK 2NY?}ou-4gh% /;j_3xŲu$^|'<$k9 ;qWuinQ5{HRCȭ. EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVjܬ;פ1s_ѕW9˯]//q~hcj>bӥX؟eBZB۠VJ|=}߮[gwTߚ>zmS}ڶj^1}hqnN;TSR\L ( 'AV?^^_*'*YH4?(GSB/k??띟ԿUPv6JHfWO?V*bEsnm<7i-6D^'*J>lYx5k:4#onkFŖ$ cm$2Y֭rEڎsOu:Kpt]dtUo6(#i#i._"Pv~f]44^ڥ]ymc±`rČIIj /ٶgwi>+7s9k[}3W rDea`@ A?+5G#oƫ?zrֺ$i6c0t" U wԵ8oȷ6{g+&caSk>n>?;~VjGuOW&ėi1hڞ%ͥ UI) N++u CP._]]ZHShRcN̋+2I&PL>=~^𥞩iry,2HɼG(WU}0k #oƨ|wT[ JꟕASUo5*>?;~VjGuOU?ޏ?ހԩ[??+5Vz<zR#oƨ|wT[궩5ӧ\{pR>~?+5Tm?RdI<2I)FA@;n|7׾"kVVVa eA $tk[I>T?}©ˬ[®;J_u TU^ |įEw]_EE]8ς?<u[Pw^%I7:6|lC~h Pc,)fxvV}mglmҙ6Nbx?᭟CYyg#QM2l~hLDݱ ]wL\ip4 k4ۜ2&Kux$Z_MYiңyki켍s$nӃF<W_C^Z]LJZ;d{֎:m V@d*,5?ؽ~D^R B[5K֞4(HUp1Ň6lm{{ !PXkyxMѕlu-ZmZi:qMAi&-v2Ҫpw;;norZͶau[[˭YxHW"خ9clG {ZҧD[Enh  oRC`F4Xxzeĺ֦=@YdY ı/GxOIF] HKQ3k-Xػ"I|'8Ϯ_h^+M_XҡԵiw>(Y]Lqf'2CH$YËɬm׵" rjCޡ;>*5g*ilƥwBdI 2AG"Ri+Pw#5f֪EUVUDPBJH:o4qq:ͷqAky$p9 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ýzA[{!C4Trk__2;#blѕт2Ga/掏L\}`~KY =XWt:gZo<>614M^%AI`ceԊ8DT0^Ywk*o,Gm)S$Qbd"@]j-|OPCdaH1?n*|4 cw6 +vEbH |OQ>Z g(bQXKScF6׎MnEaiBŝQ3rI;ےzZPG+4o&lgqSP+GA[ORA[O{J'(A[O{J'(A[O{J'(A[O{J'(A[O㺆Yx+yU6ܠG~FJE'*UrOU?uv9ubW;a:_F%z'/agqSMǺx;R _B[xi6s!yS(1lr{_H!Ғ ) k@KneiU 'Y86<;֤,/myMiNvcG _>ƽk_â [mNhu?LB&g{W|Iܪ@^?jzLh{4qV\E NDn2N,)幽K/ -C}?-ó?&z\ߵkw:]wl7 *Ve)Ы6A'ٮofUkMY7,8dcWݏ3 *~ BNˍT[܂e?2^Xs5޹RCs9m(t :g?SkfVskbH;5!3Ð&<~9c' 1k:ng"NInyQq0Up;Z],~0YxxZi)E=ǔiy`$y*qZ Ǯj:z ldAkk MQ;}X Ic֯Ej+o<V7 wFBSdJwQ~hY_:6oxzMvo*캌|tlsCǺ׊P]66hGKIsqf;I5?_u9/m/u[(nFܾ !T8P*w^s-RNe3d9T0#ds=Ʀ{-]A-dD~fv& |e-SU-4hnleم&QF(ц, ssY{_W"n4[q& eٗ1.G9╿k/:S˦9âFǗ c6 ~`p@e&2%J\SxR?̗Ϫ 4`6to<]jS[k_-WI##,-5m{\ӣ&աfxEWRN"YΪp0ϷƽC0 dhN~;7ߗvxZĚ%핽-%IJ[]=UE&'`Dq9`o4efKkYf4X#$$ַK]NI[2[}ėCw$@9_Xڟhyh>Fy^oݞv㚡=G:5d,K+iνs'F vcB½?hic/cgۿfny h-Iwe . 򇕀2Iq?g[ Auٴ8Ԍ 2^=<*<+#r:{v|S4Jo+%R-Lֻ1ېpm56Jx4Hq YK(r@Humugέ%s$gDB:֭_JtegHou]iGv%Qq(;bc H7~eΥ&ŽEanwGs3}6|n߻<5C$zujYIVӝzO A?|{ohi`_kݿ3;y~6[-t :[9&)oXAm7\8+dm-o|;ok:^Ũe{7 BbPk38;/5{[-=ؗz%lD/o7u? x{?iV^]u7:}VyAv6I_=MV-OW|:dkm;x*PT~Vd9f°Rq]L}{\m;FM$wX~SyBM U2oC]ut} H *ڹW`b+|+Ǎ˝JM?OQ Q3#y˔<|k8Y43FT G>oYcqwn nkakI S_y!wh1Ƽyy'A߱믨i^%tIƗ@ֳJ͹"i;dGPI'I ϊiay3hy H\\?z$P ݌Ao {OI&y)u;d%hbΤ3 -̹茶iO?Pu,hJP4۴5? )7 C~#mKY.eα{yVX'Xs/Nќ|uO&<ߵw߻|w^$]IKm i\5X Va$G2AoË[.쯯GZLSKŸDcS [:OZiji0 9pk[\^ڛGRB.H^x\\nĺbhV~$9dՄ72B,9yl @<^9I~xsƱ鍳\:FV<ryݷg#':l{H==1h⽂ Ɂ^FGR<a~i>ll *l#1L $qr+!~\}fzkơh`;tQ iacq h((((((((((((((+bx5q?pO̧sC\o4KRѬD qXVaGZ|#0wG[O&K۶|zqG#ϫB&vQ@y~ OP/i\A,o?QF3_;?%X?Ϋ\lx"Ut_7A>(((((Nb6 .{y*Aٶi%1+\f` OCGI%v5XDZdYO$'I30-XMփUtŝM[ S$c|CYCWw5{9m)B\Ka?@ Ӥ:u^#77ױ 줅ڳ5o Vi!ETIQYtyĨ}O~6-%0+Sx%wz]n'|b#_uq =Ӆٌ,G[d^>gv7(~OzUϴ{׆k oiVȶy'Y'Ί>H, i׾@XJ__x-8i*k[@QHaEPEPEPEPEP&E'*UrOU?uv9ubWx7b?*:ubWx7b?**|>_<{ڇ.Mi %g0;@U2f' O4#>/Ԗ;t{k;cnφH6ɰFpk l<MX"V`Baa38ci^M"6S ). P^&P~E(="}ZO|=yqufEK!ɾ[1'װ 87u Pj -nrn6l}D/uo_I4vS$ %BT@l1Ak;Ez6$[؈rȄ-9ۑ!}j8W-5c]Bi 6>y8L:%<kCm?,<+jF[qpbDRy"doa|1xv;HY^7Ǜ 9&|;gzFEѼAnڜ5˜Ll72GUsҀ'ƽ~&h#ދ&-[dYS]sz6_g[ID~Z1|gL~L"_ju}+ĺoWOh;U4RmWrm$*N: +?\8dnXq&ɻf=>xzͬj^]64[ ,.9fuE>$ÚdwΩo*Ey,`iN#[q|]/: 7R#Gan_yB~cQ([opJٕ1\(bxIzEqf Ma˩Z5ՊvB2fi|?*I$9ֿV/,K^?FϏmKsZ (唝w$c;L`#m_h^KiwGR?"hϴ|W6cH[{e6E=LcD cUvf[je/ggsm2ȋidWX¦Xdk}BJѮuO(Ӭ.s?ƓF۰ ky$%v ڕ$`(eN[b ;vLn%Dgۍ9Ρ&u_7He4ivBp>r3u/q,_J[KQnk buB ݒ VzhzZ۫fƁzs^y?B]?ں$ڮd+spv+zQm9Go?brP-;N 񇉴 u.BŚ]!W04ADjc(oz' &`Jr!w ,n1y.mvi_a}{?GCnvgsS o4efKkYf4X#$$NR mtGW4{ ص,qfLJYmf`Uߠ<\~0_h:e{7jky-#HWg@ ӳVk^n[s=ڼolh#jd˨V;k]gUn1[Ejb1^H؀ rFh_oYcqwn nk+Js fݽw?s<ߗ3~Ǯx]-&7\ Z+6'̉@A$ &#+:fk7.uhۣ*+3'45-Zy*#0j.2+-gUIݍ͔HN;1&0@l\|0 4;K׵'7HEk9Yn"]@ 3@ |y{M e}bG6i'G!i˓a o{u Y𕆫y.0 2 c4!c,pϢ>yn4-[Uŕi˥'}}ș&D!rvxX=kVݛOmi*,#$P#ԾYH[IKFkcݼx2*DJLFV+Hl:oswkwf1F!%ە$m ('[ ^^L6W_h܂Yd)оo.N6/|8Ӽ_u}q.GQΓ-V|b>d7^9wXբԯ?l3* ȗr6zi|Q"&;$"X0B`Zyj5X-Fv ȿfcqB'?,'ҵKz֩.[,I)~rάĮ훘 O?IoÑw)^G sS~NXdW?U&մ_éh6^ܷ A/)nC:uWm{SZֳݽef ;7ލrvl0C|<ޛin-5C"-"[O/*p]8>Uバ _k jm"&hVucm|}~W<#Y`> =m}cuȠ1lwo׶ԚܗW{{B]c/WPN~l L6h-<1c1P1/5]OAύaӠk3[Z-1ljYQ+N+ԫϣw%Ɵo=ۚq{qhF&8QpK1A(((((((((((((-vUFW89i1<5xkNk9,`ÎH׷5/4$>ǩ%cHۑ8y|4Z<[wVDe彍Bu4k91zq\Wx:DW~ Cue u-%Mf*`c^_,e^ 50߃^n  Lb,9qOIEG=Sσz@Vag[;t-r C$5sσzGO)<_ \}Q=S ~F??v#W;W|?kk87w"Dؠ IڽUʏσzGO$}Q=S (?TyTJ*?>|?σzGO$}Q=S (?TyTOU?uyn O=jv uWE-*s |įDo TU~ |įDo TUӌ!<*|S~( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( |U#+ל?W_ƌט]ks1)GɯN= r^&L^v˻Z24\p#|:8y|4|4xI MH\yW@_SV|3>jjix#բ;=7On8u%w%g/ZkN޻#^?PitdXY SFlTG6(#Q>SqOcu;ɾ]oq>z|Gwas/K'oKod|= {FG!K!Icv'|@uil=4m f @ |Ȁ8|9{z݃sA{J@wmB k$r'9;,C]/2ns#mTnAlOk).G$EE ,{sI$uWh툴ՓPLmt'Pp̅è#0' nt뺳jvWpEho=L|To66P 8([\񵆙/ŦZqkom2U\ȬpK]ij?kY_AKwl~2DD7J]B */&hqB5-we/.my<űG#+TRNo~#yj+֒I7)%1D%flU AEONO;Tnjۻ߳oͻF˟.5}/UXu7J1BnkiYq|,TW/%/WZs\x]7T<3Bᑈ pMw (((((((((ȟRjW= oKs ψ:?1+<WQW5uc>|CJ/|Ad <6gwHV9HLmgŽ26\v/?>3}6|n߻<5fᯇ𷄼=$Od}>q*2! V1!`FI K'ؾ׻fwnmH,4\RX퍺>"wxS&70uŒU5k'5bt[Y !2"f@Xsx4GOl47GHۙAxUBI@8(|eƠyup%ʲ$?,j5 (((((((((t_-5?KdnM͢,.Suioq-R@J9]~9ž&`ݎc. M-g&+ [s$@w^$zꚵC-m<$[iY`#9~< >=F{}=mleAiawVtr2GqxHѠ$_Ykޙ7,&EXiVH`),U `IKSTsF X¨8Jo|ZOxrI]>M=@ ":H99Kt.)+JHuvP!$U @(((((((((((+W^pre\oeC3CTl ]lr?*z;4[M*QXK4qԒz|#0wEk:x?’yy}rivZ܄N?(nkB4Lvdv$-#坉$X~Эt4=f.e?y۠qNc?1|>9WU_Xmc)?v_3 CE[iq4~(֟vТ(/i\AO?"'= (f}|u^?ugd_/{??띟^}/sKOHlaмGr(G9j`($x5X^y=q ֧[k&M牖pY#DXrX@*6֗?_K״jMay5IVpI# UF#SВO]+m~_3>.rjZ;m#xY#GV|0@j.| 7YBQ^]jfޖ HLl5ppP~_lr7m{*o/?gǓ_wg2:֑=GKi5vIR;eVGޞ\g9|HweW(/?@ՎoGtY;gd᷎F|9NN#kPKjzR6K2;Ȇ%D2\}Mwחk:^ 2/ʇ.!7k((((((( =O\]x VFPtI&uС!p19H88Te\]Xn+ۛ{x&f̑>BH sN\ڛ\kI5iE<6^Z,qXڃ-sZ:%*0YIrF3d-r0WS<Z=0kXZjz.Ϧir[]M$ bѱ#p #x^"ީfTK"ҭ4߲q4vԙC3) 6W:OK=Zm[Ktk-ayeSwWȠ6w )<}J/Դof߶yywu毎+-u{o]\YiZXxV2d_(֩}\hZ}e/ KE3JbXdNku/P~ojhvuH#wgllIiC׶vvt k%9^MiCO%oH%/o~"\O,|^\g `S+'W:dn->LʿdyS i'4_\Х@|K0wWmxU ~Cs_a'/=4_~ѻ쿽n>yxJ}h6mm^eJ2j%,G 9e 75?if^kqd64.ٳV0m2͵ثGiz$KM9#ݳXlKX!KɥK.nk2jq},xѕ6yKU<7iUZl:moycVT1m2I wys7<*z5RM>(AIW rv>yW^o|?i߮Yd+dQ,v8guMF冋k=n"-,༇# YW,4Iѭv[В)b|]Z6cp!iV%ŕ[y"-EG$V NpӚ+WG֥Ԗ{`h`Ek܊S:C ( ( ( ( ( ( ( (2)>T?}©ˬ]05oy߈:?1+<VQWN38?ĩLOH{{xY}$֐¶w &5;2v_ݝ=wUx񆿫|Z;iKof9ĂpGW\QEQEQEQEp|W/ZLB(ScE!U |*۞Mv:4ֿ$Ahxb4t6kj:4 *ۀ <8 ^|9e}GlJR+*C8`ē궓gԴ J?gdU\3e0 4h% 4iGf`vqŸj GE `tQEQEQEQEQEQEQEQE v3]m O ~'⧋4mrm?O )% w#,-П^7s^!kNQ]k[Y5K9 ?M3ozkFLnmBGЁ&F sh\$l^&"4g(JApcO0FqOQ֮ln|UxĖ[JW[;|X^'ep gWl%`xShWwj_,Q_j ǻˌ,HI]k~ ( ( ( ( ( ( (8Crik>YjKL$mL0 U_k[+Xc[IO) ){ĻC,3kgjMsun#{f-rFL&y-jKC@-|؊6D^o6:cĵ賶yZ=aqoeF&.#%Y&B@nߚ3Jmt_W"y.5Yo1 $Q0 ?378 :(((((((((((+W^pre\oeC3CT?ѕt5J=!]8//q~hϺ?xdR_XmPy-juk/A_EtGL>Z7'SN&vQ@y~ OP/i\A,oi??QF꽟~FV._;?%$3~]?E1(((((((((((((((("O]cp*'*UKs :?1+<VQW}5ot>|CJ/ *2Z#k]%,VdDE" q(xg+k_xZ.}A'dgU91Knos=pQEQEQEQEqxZjIyos`Zܬ?M}_mu>/Ė3rͺ695:b4g+r.t|o#۵&9ѾzJ߭Xɧ m.رKb ነ\g Z| |C% zHΥwA`$΅_;PLF OBL-o{u!biBvr @st7ڶ6kiw2yvV2 +`nr(pĭu|-K S@4t]-,YC" |~jY5=>u++,#*PT$}5RSZR[T}Bh"̖Y4PcT,Z|%\syrWkJm0 PQ@Q@Q@Q@Q@Q@Q@o}wPoW:ֱ]j)eJ" "(dˏ1F#'=~❐5_>Y[Ieoe%K xy>a2x`۴AFŞ#n汚y[(_0Ā6Qv lkM__Q5fkn!H *ѿLwq@u ~!֯E4wJAe~q!@X I/a!JzEsxPK}-kQ[y串h2"Wl# {]QEQEQEQEQEQEQEQEQEQEQEFW89k7_2yI硪V|ʺza8y|4tgZB>ọumsPz&r_in,n`_v>J]v=(qMdSR\L ( 'AV?^^_*'*YHJcBr|u^?ugd_'{??띟~iV'^EqH 3ߓ՚gr:,E7Qaj:G4X߅ ,#HxEH㻸DU حIW{@%w2M(F F` `y(]xCY[_0K$sސf5ft۞;{Y֧j:ZQ 7*+n9䀖 x{[|b >n?EW-yLx68Dqkt; TԞ/nm!pDRN@ Ȯs>:t=wHEume$_dHcRs<=YWѵkk-JxԬ,LGDHE <Ɗw( u-3םSVv XִK;O.$mT?dxn` ZP#E\cpCa^j6.ye0܌Q޹4GchWڬ~Ս}YJ a L Ó>CkkDG–kZ^iD|bLu*糒ox1\\%}@a)"iPʼnV\x/*<;i7w1Y:YYCh9* 8a\|WQ@Q@Q@Q@A{:M7P$6(M )CjBbr?{pp:_KO?$W YEHT Wq6٭;p[[jmqkڦhm$է(yhAbj 6Oks{_kc8-[$,9ZSUVt>o+-]ñ3O"f&Fiv(wEuq{kI* @>$ᘨn5EPEPEPEPEPEPEPEP,/SAXM=&onmidPYv]F>rͺ695:b4g+r.t|o#۵&9ѾzJ߭Xɧ m.رKb ነ\g Z| |C% zHΥwA`$΅_;PLF OBLF5BEX2G%C.Ё6NM\🇭;iuW7&զت*lEQEQEQEQEQEQEu|Ck5^T2" b"Lbdx:gsѲU꽟~FI ߪo*WO?LC袊(((((((((((((((ȤSXʮI .?tR®;J?[EE^yJ?[EE]8ς?934j#Rcwqi[ &8[y b+ $$2WGW5&6{:۵C |#%3Q@Q@Q@Q@o}wPoW:ֱ]j)eJ" "(dˏ1F#'CCouڕ1魤oوb]S [`C x ^M}7sXcou v3]m 3xsxN.uS.xSУ>[o +Y1\J95o_C5+M),q,,6%Fe 1GgӼ CO5{ȆaE {7n9/W_-݄q]E;i8bes|> gAMWVմۈ.^~,l񘏕0S#;~pk6ho\iZזl/*6>j lO_]jƧ4=ȂB3c26#$k֗yik{ K\s!e;tt\"vʓR(((((((+f{xk7]ЭeaFσ3eIFgxŚ^GifY4RG˰@>l|p=Jxf(L- i|E9he %`а) 0'$6OLC]qgźg<;kN[ս[(|!xK!B̩]y4Q~\P_; ] [6yZe2,mp% & 鐜d++|UkΦmf{ ;[ټYF#1:K@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@q*Z쫍W^prcFyjz_72rk?Ol7$A}]8//q~hZnq_Mv{DG]A\߆,Ӵ=>mHc쪀?A]%AZ7'ҝMJuq3(+ZEzy~ Oe#wJcB4?(B>dx:gsѲU꽟~FI ߪ}\D-p(`0ufܵo~5b)"+MAqN¹$(R8F3+kU>+>]h̷J$FpGzծ5:#Ds&FA{9\PڽZAx$,qc88ؗ(n5֞jZ핼Ϳ^/O,ڏmS]fVQ,1LvH [V:ԗ)kqr纮ɐC#| d #?g}3z7}9M ߣXH^-)} xjOa%g UR*A=z* SV(au V*8N}:j;+4ipKve+$ŎjOo-`X|썸c;4vqMKyA'2rx]fa+D/)f6HJuwx,B',6f%D92R_ E.g[uj%ICB{xyvTgtlۍ9sZt(QEQEQEQEQEQEQEQEdR}©ˬW$SXʺ)naWc_F%zX,z1l=_F%z_EE]8ς?9/^5M(_VlWU-Ys @i%JEzmr^ {]ʍjYXS.؍p1]mpQEQEQEQE |Z!Q@g}=$Aksq gRی0b QgB/~\]#XG}0d;fW 97nbp9 xK#N|ڭeG،KtePJrr1}xKkx;Ek4 L> orsSIguY5+ tȭ.Sjr>f¨ m|c;oꖚOQEE$+ :*c(ww0sڴ-Oh:T۹E74$aPqYB׶sjqZ<%ԕ$%Vm~a5>$׭q.m OrF5`@>AT >|,𖷪OCDb,#;a@,IzWMXΕi^!4d*h6Y,((((((%h3kWmͼq<B .˨YF88/Ė3dx:gsѲU꽟~FI7G:# <bb "QE}1qȿbg)m%(.aKis8c k) \r/_ ܋?F.?§ "QE}1qȿbg*z( \r/_ ܋?F.?§ "QE}1qȿbg*z( \r/_ ܋?F.?§ "QE}1qȿbg*z( \r/_ ܋?F.?§2rSb*R *C]0_wV<_F%z_EE]8ς?0| g񦵮dIf[. +0[pcA2)]fxwEᵒy~}q{+yg#>UUFI<:= ((((xzͬj^]64[ ,.9fXVS=' +{I,줳idi`crg?+\l{ro2)Co{sZK @HKy<EPEPEPEPEPEPEPEP,/SAXM=&onmidPYv]F>rͺ695:b4g+r.t|o#۵&9ѾzJ߭Xɧ m.رKb ነ\g Z| |C% zHΥwA`$΅_;PLF OBLTLmZvj ^j:<1}wi%r*츕#UR|>U]D᭜4K^8 BHFb4F$}Ėi/h0x.~Sp ;w|3 KsgR_%Ի|d bNҺj7tCJa[%YU @ٲ@f'lEPEPEPEPEPEP,/SAXM=&onmidPYv]F>rͺ69|$I{@>,Ք]M$L*wiݚFs+~>c&d~Ёwb-+*ArlA(fOks{_kc8-[$,&kS4x'|2^F#d~3~mˌo9i noi GMDhWQM RL$v=񽟇=j62;K(9W|1x4[ˍFTR5KBD2yjI$h$2zQEQEQEQEQEQEQEQEQEQEQEFW89k7_2yI确C2 Y|3~s&4f-J^zwW0LnxXQ9391~h3mtȒdu :zݶ+\FaSN&vQ@y~ OP/i\A,ngKsPUPv6Jsj~FI ߯5I߈|gmg|o|AH,xոRf3r<20+ҫ#_м3jOnwCh1U? b<ڎ-]Նs $u 34CA.@9|W?[:Moj"Ss~商CkuR^,QP7\]t_ jVZCso@*\8G kZ潦}i-Ѷo6WkVg%IZh{G} @IEG?Уh@(}(J*?A=h{G} hG B$D?Q?Р (}(J*?A=h{G} hG B$D?Q?Р (}(J*?A=h{G} hG B$D?Q?РLGTz%G} q$V0_sV<_F%z_EE]Xς?<5x+MCW//l+[cUQ~ɺO cC ]jzYXGaǰaj)#Q v(uzAEPEPEPEPx^7n5U5aZYD/\w 8 0)jQWU<"ᝃ(]ujcu5=dP &֙-?taFۍķ0[:h0A(8$ Z>'[kB_gdl! F|=0O{vMS\nox$m!Rsolo5|Iqic*$Hhe#;fpk{OkY*`K~v`bnN:SR\L ( 'AV?^^_*'*YHi?ҊnOWC*|I;?%$3~ 9p_l[rBN;FuBD>^@+/.m 2SFC28VV*A#5mG=&`z o>z=IJKjxnNF,bY=*{y+^]b`mݷ6gvݜm@uqmkx`HHAw`=ٙT95.5ޯK\xX`btXTVb]`6ccN?u?ect/[$YJ.Ÿ.B *.mn%('Y-Tp'kNSف\WŽS[ԭu &V㷒%6>ՃD>}Fj7Uu$pVK_NeC,2]AeD] AF$P-%YkMI<'_5 0s98ncQaF|Yac& *Mb<ϖ'lLg`z 0=q^3cZ_-C9J#0#r?<~wټ!{qke6׉H"Ew~F>a8#0=▩y{0oa"V@yyIX299sJԼm'/[kx̀U#X(fPUAܒX(Z'nm|3-ޗ/]$2u: bS-Ώs}6-J-;cdu!u0bY8`z 0=yO߿UJ7 /85E>h07Q,+RФ%ɚDU3 RF _`z _/=zv:F}<[]nd-p f@b Tz AFAE`z 0=P(Q@QEd2(ATwWE-*s |įCWտ訫i`crg?+\l{ro2)Co{sZK @HKy<EPEPEPEPEPEPEPEP,/SAXM=&onmidPYv]F>rͺ695:b4g+r.t|o#۵&9ѾzJ߭Xɧ m.رKb ነ\g Z| |C% zHΥwA`$΅_;PLF OBL-o{u!biBvr @`9cqcL䓁^v}#ÖZM}qz)4aFc^24\QEQEQEQEQEQEQE F vkѾzJ߭Xɧ m.رKb ነ\g jZ Ǯj:z ldAkk MQ;}Xd֚xrNwJAe~q!@X*$0N#5]O҉k+fu].mG0L rT($WU$W"ֵK;[;!)%v;ǿ5W/~VZ: QK+aڪv((((((((((((7_2y]r^)jzSjmoa ,1BJ|Oyǭ&4e⮧wiFmk2AH0IrY2&<3ywMѷcv15EdxcW:燴RQd#pl(φuu7:L7Wxj(;Xt=f[YNds%Z8!`v#TZdž<5,LJFԱYG*bB\ ҵ KKP hwqXFY:ۢ* 0ZMCMvMy4m9uy{jm2cqc=F1_PQռ=n.H$'ZdŮ]W_f%1{W)崈*XeٱݽݴpEqRH@zUMC4;6t}?Lv,$(IJ5EbxKº}均t[KkasĊSQ+z}u-w-ӡhp f ilYҦ%~QxTS=Xz+ ?4!$l69N0CG$OrG N%vHௗUH\g:?1+_'ƺ1܇90 >_Cþ0e}!գ] DX GfPZMNJ{!47ͺo5iG &dqʌJM{Eh+{Eh+{Eh+{Eh+-O洗P{6ζAd&3)f |Q'cOݴ^ dN#x̾C*) XwfQEbv/sM"y7۲J~]vcbz:*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E _%Z')?*?MW)E  ũ?o-u6Ft$]cLȥĿ ;p(|OVti`/-]S,7Fdmr oQp [9gVoˁ(rSyrݏM/XE=NJ=6Ms* ;"X8ՙ6 0'ha ׵ ;XլwRtŎ/n!&bUظi*,Ð=G%Z')(cD_7G%Z')(cD_7G%Z')(cD_7G%Z')(cD_7G%Z')(cD_7G%Z')(cD_7G%Z')(cD_7G%Z')(av7|M.&tHEVY.fbX <uYڿT04}N{!qڦ| hR؟+nb4bu8>"Z?|V:mXjngU֩3dGnH[CHw:ꎥ d|7.5_VQ4+/2A6f9rc|%kmmV[MM!k^Tm:pSqI7mT\(f#(/i\AO?"0MsP+UPv6Jsj~FI ߪFY~JpmT $# M?mY(*CTIm/-!VV*A#5WmG=&|Ϡ|!q,ڼӤ7o5OcJ`"$׿?g$؟>D[|y;m|͟ݷg{\%(#i08P]".OvfU MK^;k_]TeM>#HLme&Ɲ~1Uk+M}^Ilym#]y?z>\ A @=jYd˝Qhm;YNfq?MoGR/4;d]ZOKl'V ;Vj#HtVMWĐZiOY.e}:і9 w;;9v3('2HAIQCw *7g{ 4aRlnQ|8;d c=Rw#O`Mw:Y\IkᾡͬoV3'~ѷs!Eyiğ BK?,,GZ,g~PD~v>> }RUfer\gV*Sp]]Dl$RNX0=j ]D'/GX-,f (KCp2]eškZ}⏰y Ca۴G0Ts6 n?+ =]O9w:W:IlЙeY r_ |Iqݧo! Ǝ &b2 ۙ#!A((QEQEQEQEQE I .?\O]cp*襹]wLs3V5fA*э!Lj|QQEyǨQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ@FsA8=}Qxc˕py'Ip}wJ7fn2qA۠sQ@a֯~((((((G MVEͬVp~)Xʴg8'ޓҏJ>vr`T}OI}WQQGGX]ꏻ9I?>/4KkxlnYV9BW#A_OX-[L&> |/ց{n= ?P!g5nj^)754szӧZ'*~k\ּ*77F i%(* r ' }Sú Z(dngЬӕkb_\ÿW]ku$yg}xa\!GPJrpeJBo>Mƻڜ ]qĦhN]&7~Ǩ$'kMC > i>MΡ|=ln'_Zg?i#.q4/o ѧaѧIOX]Yaz7v>ICmcAq eKO__AO?e}zQ>[o058xsQ?|G7T~ҏJ>vp$Եi>*h?v}@{36ҏJ>vr d'w$ue}zQ>ޓzO꺏J>=(T}OI}Q ?']GGe}av>'ޓҏJ>vr`T}OI}WQQGGX]ꏻ9I?>'(ҏ.Gݜ$`U}zQQG`_zOꏰI?>=(( }Qg/ ?'G$ue}zQ>ޓzO꺏J>=(T}OI}Q ?']GGe}av>'ޓҏJ>vr`T}OI}WQQGGX]ꏻ9I?>'(ҏ.Gݜ$`U}zQQG`_zOꏰI?>=(( }Qg/ ?'G$ue}zQ>ޓzO꺏J>=(T}OI}Q ?']GGe}av>'ޓҏJ>vr`T}OI}WQQGGX]ꏻ9I?>'(ҏ.Gݜ$`U}zQQG`_zOꏰI?>=(( }Qg/ ?'G$ue}zQ>ޓzO꺏J>=(T}OI}TZx}zSZ ;@`^*8bj5pԝB9PKEQEW? 'AV?RFޛ(MsP2|I;?%X?5^?ugdH/{_.-*waWj=+[wh ,#:3UV1;J0=(aA s0 P=()(OF9Hs0 P=()(OF9Hs0 P=()(OF9Hs0 P=()(OF9H<@TQ)b*n;Q@Š(IUpOWW? R6?EiG?Q@W?'ƫ\l=?5~FITJ]m2rR:*C1@4\ ocGxoS#팳3'Rz ٢o7h4~.|_9cw`ip"R*2 R߆8->#QҭmSND{;k4_>ƁIíiv:Eݺ`FEGx@WC/a mshĘU4_>Ɓoó˧M>K&X;M\m&01<,.mb [Znjn[d^0p@d]٢o7h4–h$p^G2ffg˴1fcd׃IҠFH4(т@vȎ]76&/f@Z7|7Cs'M% SOƋiiz ;g.랦~ƏEo - DXVFӭ,n ˁ#/N)/ LK4۫ %ykTxGrbN2I'EohORK[ Y<1=bkZ9&1I5Ih4}/-fG٢o7Ih4}/-fG٢o7Ih4}/-fG٢o7Ih4}/-fG٢o7Ih4}/+}Kk?4BFr?JBzP?'D56#&]DPRCSh2i_LC* ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( 'AV?^^_*'*YHӿNPޢe+k>[جL)i!11I$,2pG_Zи_'ƙHc,ͭ?RQe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(Xo%_Qe> NQe> G%_(XԵxuXqǜ`5aqokOI* '(X? 7Rh?)?Ƹ)V;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 G$ O..;OH4 \ OTPƕ ;Az4(?4}?4@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQH۶ NB:zu4?QKeב#)̮K h>joҴ1Uz:נ(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((($ =zBF#r[=~tPdx$`} WMGQVw`Ɍ,OE; socnetv-1.9/manual/bugs.html0000644000175000017500000000150512534331160016400 0ustar dimitrisdimitris

Known Bugs

SocNetV started as a hobby project but we keep improving it. In any case, if you have any questions, just ask or submit a bug report (see below)

Antialiasing is on by default. To boost the speed of node movement, you can disable it by pressing F8.

Report a bug

In order to send a meaningful bug report, run socnetv from a terminal, press F9 and repeat the steps which lead to bug manifestation. SocNetV will be printing debug messages on the standard output -- the terminal in Linux.

Then post the bug report in our bug tracker: https://bugs.launchpad.net/socnetv

socnetv-1.9/manual/header.html0000644000175000017500000000125112525314416016673 0ustar dimitrisdimitris

The SocNetV Manual

socnetv-1.9/manual/socnetv-logo-manual.png0000644000175000017500000003736112525314416021170 0ustar dimitrisdimitrisPNG  IHDR=AbSsRGBbKGD pHYs  tIME < tEXtCommentCreated with GIMPW IDATh5>DA7z f(kA7g)7րmgTMS/( y!|5N2^W  5#& ZjW=06(B FA9H79'` DpJ#%B_ X($h8+? ҂7 K-A  iD6PR: 4f  j!V ?:1>eOJ %R2 Ha+ T'%  -"7 .V"*#t )  @)2/4%"{Zd ( !f SyM6{ >*v 1 #U,E "2+F(pg' L NcAm:I_ ]$ V'^0\ W#iG$>[    RR 0_ i> 8uY 'G5 w 5  (#pF;  8'6RBDM(= J]*V'*^  '`/; bH @!Ch2Q5(1J%> G+0Ee Qa@ ,{ZK-n# Xz5R2M=F)MS #E4Yin-u;"-FlQN e;lR ^j:=H573>$ ' $.   & rM\r<6]+<)h# &'P3: 4 Q 8 4U 'c(! #  &  /#% L۞&<  *7#@IDAT R ;0+ }$s#! > ,3Vz7I-3}dH*eA_OIn9mWP'j/kV6#dg |jGJJT/a Ph 0  k Xs/8D( 75 k~HA4d  g$9\H,0"|3 lsDfH1PL9N @C [&L1$& h)?>0 h7&9 ;j B!V[| q#&nZmO#2,@H T (0O0'vPWOQ31* 0E K -y 80,s>860=x f E> y6%T N$M V/BLr@0$D#[ ) V \9  ) $'TO_8)7#1@D #+ 43+ ʾ  PBWp *JI&l'o>;< E  MzDF55ֶ S0I nr  _& \*E6%89&G5YS/^P1`bQ YJ<"bY/ M1N/J3H ^"a] Nq)P\.9:a"JoK3b0   ܬU`*e ݲ 9;7:;O$C$f(k`+aUZ.PD7~FS^IENDB`socnetv-1.9/manual/footer.html0000664000175000017500000000042112542300240016726 0ustar dimitrisdimitris socnetv-1.9/README0000664000175000017500000001366012525314416014171 0ustar dimitrisdimitrisSocNetV -A Social Network Visualizer ------------------------------------- 1. Overview ----------- Social Network Visualizer (SocNetV) is a cross-platform, user-friendly tool for the analysis and visualization of Social Networks. It lets you construct social networks (mathematical graphs) with a few clicks on a virtual canvas or load social network data of various formats (GraphML, GraphViz, Adjacency, Pajek, UCINET, etc). SocNetV enables you to modify the social networks, analyse their social and mathematical properties, produce reports for these properties and apply visualization layouts for relevant presentation of each network. The application supports multirelational loading and editing. You can load a network consisting of multiple relations or create a network on your own and add multiple relations to it. SocNetV computes graph-theoretic properties, such as density, diameter, geodesics and distances (geodesic lengths), connectedness, eccentricity, etc. It also calculates advanced structural measures for social network analysis such as centrality and prestige indices (i.e. closeness centrality, betweeness centrality, information centrality, power centrality, proximity and rank prestige), triad census, cliques, clustering coefficient, etc. Furthermore, random networks (Erdos-Renyi, Watts-Strogatz, ring lattice, etc) and well-known social network datasets (i.e. Padgett's Florentine families) can be easily recreated. SocNetV also offers a built-in web crawler, allowing you to automatically create networks from links found in a given initial URL. SocNetV offers various layout algorithms based on either prominence indices (i.e. circular, level and nodal sizes by centrality score) or force-directed models (i.e. Eades, Fruchterman-Reingold, etc) for meaningful visualizations of the social networks. There is also comprehensive documentation, both online and while running the application, which explains each feature and algorithm of SocNetV in detail. SocNetV runs in Windows, Linux and Mac OS X. The program is Free Software, licensed under the GNU General Public License 3 (GPL3). You can copy it as many times as you wish, or modify it, provided you keep the same license. The documentation is also Free, licensed under the Free Documentation License (FDL). 2. Availability & License ------------------------- Official Website: http://socnetv.sourceforge.net Author: Dimitris V. Kalamaras My Blog: http://dimitris.apeiro.gr SocNetV is a cross-platform application, developed in C++ language using the Qt5 multiplatform library and tools. This means you can compile and run SocNetV on Linux, Mac and Windows. SocNetV is Free Software, distributed under the General Public Licence Version 3 (see the COPYING file for details). The application is not a "finished" product. Therefore, there is no warranty of efficiency, correctness or usability. Nevertheless, we are looking forward to help you if you have any problem. See section 6 (bug reporting) below. 3. Installation --------------- You can install SocNetV by: a) compiling it from source or b) using binary packages. In either case, you need Qt 5 for versions 1.x Most Linux Distros have Qt installed by default. Windows and OS X users please go to http://qt-project.org to download Qt5 library. If you cannot install Qt5 you can try the 0.x series of SocNetV which work with Qt4. Please note that SocNetV uses QtWebKit to display online help. QtWebKit has been added to Qt from version 4.4, which means you can't compile SocNetV in distros with older releases of Qt. a) Compile from Source Code To compile from source code, download the tarball archive with the source code of the latest SocNetV version (you probably already have this :P). Then, untar (decompress) the archive using a command like this: tar zxfv SocNetV-1.X.tar.gz Then enter the new directory and compile with these commands: cd socnetv-1.XX qmake (or qmake-qt5) make Now you can install it using: su -c 'make install' or sudo make install If everything is ok, then you can run SocNetV by entering: socnetv b) Install a binary package or executable (Linux/Mac/Windows) To install SocNetV from a binary package for Linux or an executable for Windows, check http://socnetv.sourceforge.net/downloads.html and see if there is a package of the latest version for your operating system. Please note that SocNetV is also available in most Linux distributions, although not always the latest version. In Debian and Ubuntu, install SocNetV from repos with: sudo apt-get install socnetv In Fedora, use the command: sudo yum install socnetv In openSUSE: sudo zypper in socnetv Mac OS users may download the disk image of the latest version from http://socnetv.sourceforge.net/downloads.html. Double click on the .img file, then on the new window click socnetv icon while pressing down the meta key. You can also find versions for Mac on the Internet, although these are not supported. See: http://pdb.finkproject.org/pdb/package.php/socnetv-mac 4. Command Line Options ----------------------- SocNetV is primarily a GUI program. Nevertheless, some command line options are available. Type: 1) ./socnetv filename.net to start snv with network named filename.net loaded. 2) ./socnetv -v to print version of snv and exit. 3) ./socnetv -d to enable debugging mode, in which snv prints comprehensive messages about what it is doing. 5. Usage -------- For usage documentation, see online help. Or, when running SocNetV, press F1 to display the SocNetV Manual. There are some example networks inside the /usr/local/doc/socnetv/net folder. Just press Ctrl+O, go there and choose one file. 6. Bug reporting ---------------- Please, file any bug reports in our bug tracker: https://bugs.launchpad.net/socnetv/+filebug 7. Note to packagers -------------------- Packagers: please note that the SocNetV manual is copied to $(DESTDIR)$(prefix)/doc/$(name)/manual socnetv-1.9/translations/0000775000175000017500000000000012542300363016017 5ustar dimitrisdimitrissocnetv-1.9/translations/socnetv_es.ts0000664000175000017500000060476512534331160020561 0ustar dimitrisdimitris HTMLViewer &File &Open Ctrl+O Opens another helpfile &Print Ctrl+P Prints out the actual network E&xit Ctrl+X Close Manual &Back Ctrl+B &Backward &Forward Ctrl+F &Home Ctrl+H &Go MainWindow Welcome to Social Networks Visualiser, Version &New Ctrl+N Creates a new network New network (Ctrl+N) New Creates a new network &Open Ctrl+O Open network (Ctrl+O) Opens a a file of an existing network Open Opens a file of an existing network &Save Ctrl+S Save network (Ctrl+S) Saves the actual network to the current file Save. Saves the actual network Save &As... Ctrl+Shift+S Saves the actual network under a new filename Save As Saves the actual network under a new filename &BMP... Export network to a BMP image Export BMP Export network to a BMP image &PNG... Export network to a PNG image Export PNG Export network to a PNG image &PDF... Export network to a PDF file Export PDF Export network to a PDF document &Adjacency Matrix Export network to an adjacency matrix file Export Sociomatrix Export network to a adjacency matrix-formatted file &Pajek Export network to a Pajek-formatted file Export Pajek Export network to a Pajek-formatted file &List Export network to a List-formatted file. Export List Export network to a List-formatted file &DL... Export network to a DL-formatted file Export DL Export network to a DL-formatted &GW... Export network to a GW-formatted file Export Export network to a GW formatted file &Close Closes the actual network Close Closes the actual network &Print Ctrl+P Prints whatever is viewable on the canvas. Printing This function prints whatever is viewable on the canvas. To print the whole network, you might want to zoom-out. E&xit Ctrl+Q Quits the application Exit Quits the application View Loaded File F5 Displays the loaded network file View Loaded File Displays the file of the loaded network View Adjacency Matrix F6 Displays the adjacency matrix of the active network View Network file Displays the adjacency matrix of the active network Erdos-Renyi G(n,p) Shift+U Creates a random network where each edge is included with a given probability Uniform Creates a random network of G(n, p) model by connecting nodes randomly. Each edge is included in the graph with equal probability p, independently of the other edges Connected Creates a connected random network Uniform Connected Creates a connected random network Ring Lattice Shift+L Creates a ring lattice random network Ring Lattice A ring lattice or a physicist's lattice is a graph with N nodes each connected to K neighbors, K / 2 on each side. Same Degree Creates a random network where all nodes have the same degree. Same Degree Creates a random network where all nodes have the same degree Gaussian Creates a Gaussian distributed random network Gaussian Creates a random network of Gaussian distribution Small World Shift+W Creates a random network with small world properties Small World A Small World, according to the Watts and Strogatz model, is a random network with short average path lengths and high clustering coefficient. Find Node Ctrl+F Finds and highlights a node by number or label. Press Ctrl+F again to undo. Find Node Finds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node Add Node Ctrl+A Adds a node Add Node Adds a node to the network Remove Node Ctrl+Shift+A Removes a node Remove Node Removes a node from the network Change Label Changes the Label of a node Change Label Changes the label of a node Change Color Changes the color of a node Change Color Changes the Color of a node Change Size Changes the actual size of a node Change Size Changes the actual size of a node Change Value Changes the value of a node Change Value Changes the value of a node Change all Nodes Size This option lets you change the size of all nodes Nodes Size This option lets you change the size of all nodes Change all Nodes Shape This option lets you change the shape of all nodes Nodes Shape This option lets you change the shape of all nodes Change Node Shape to Box This option lets you change the shape of a node to a box Node as a box This option lets you change the shape of a node to a box Change Node Shape to Triangle Change Node Shape to Circle Change Node Shape to Diamond Change Node Shape to Ellipse Change all Numbers Size It lets you change the font size of the numbers of all nodes Numbers Size Changes the size of the numbers of all nodes Change all Labels Size You can change the font size of the labels of all nodes Labels Size Change the fontsize of the labels of all nodes Add Link Ctrl+L Adds a Link to a Node Add Link Adds a Link to the network Remove Ctrl+Shift+L Removes a Link Remove Link Removes a Link from the network Changes the Label of a Link Change Label Changes the label of a Link Changes the Color of a Link Change Color Changes the Color of a Link Change Weight Changes the Weight of a Link Change Value Changes the Weight of a Link Filter Nodes Filters Nodes of some value out of the network Filter Nodes Filters Nodes of some value out of the network. Filter Links Filters Links of some weight out of the network Filter Links Filters Link of some specific weight out of the network. Change Background Color Click to change the background color Background Changes background color Change all Nodes Colors Click to choose a new color for all nodes. All Nodes Changes all nodes color at once. Change all Numbers Colors Click to change the color of all numbers. Numbers Changes the color of all numbers. Change all Labels Colors Click to change the color of all node labels. Numbers Changes the color of all node labels. Change all Links Colors Click to change the color of all links. Background Changes all links color Transform Nodes to Links Transforms the network so that nodes become links and vice versa Transform Nodes LinksAct Transforms network so that nodes become links and vice versa Symmetrize Links Shift+R Makes all edges reciprocal (thus, a symmetric graph). Symmetrize Edges Transforms all arcs to double links (edges). The result is a symmetric network Strong Structural Nodes are assigned the same color if they have identical in and out neighborhoods Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods Regular Nodes are assigned the same color if they have neighborhoods of the same set of colors Click this to colorize nodes; Nodes are assigned the same color if they have neighborhoods of the same set of colors Random Repositions the nodes in random places Random Layout Repositions the nodes in random places Random Circle Repositions the nodes randomly on a circle Random Circle Layout Repositions the nodes randomly on a circle In-Degree Ctrl+1 Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Circle In-Degree Centrality Layout Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Out-Degree Ctrl+2 Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Circle Out-Degree Centrality Layout Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Closeness Ctrl+3 Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Circle Closeness Centrality Layout Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Betweenness Ctrl+4 Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Circle Betweenness Centrality Layout Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Informational Ctrl+5 Repositions the nodes on circles of different radius. More Informational Central Nodes are situated towards the centre. Circle Informational Centrality Layout Repositions the nodes on circles of different radius. More Informational Central Nodes are positioned towards the centre. Stress Ctrl+6 Repositions the nodes on circles of different radius. More Stressed Central Nodes are positioned towards the centre. Circle Stress Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Stress Centrality are situated towards the centre. Graph Ctrl+7 Repositions the nodes on circles of different radius. More Graphed Central Nodes are positioned towards the centre. Circle Graph Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Graph Centrality are situated towards the centre. Eccentricity Ctrl+8 Repositions the nodes on circles of different radius. Nodes of large eccentricity are positioned towards the centre. Circle Eccentricity Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Eccentricity Centrality are situated towards the centre. Remove Layout GuideLines Removes Red GuideLines from the canvas. Remove GuideLines Removes any guidelines (circles or horizontal lines) created for the network layout. Ctrl+Shift+1 Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Level In-Degree Centrality Layout Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Ctrl+Shift+2 Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Level Out-Degree Centrality Layout Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Ctrl+Shift+3 Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. level Closeness Centrality Layout Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. Ctrl+Shift+4 Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. level Betweenness Centrality Layout Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. Ctrl+Shift+5 Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Level Informational Centrality Layout Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Spring Embedder Alt+1 All nodes repel each other while the connected ones are attracted as if connected by springs. Spring Embedder Layout In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. Fruchterman-Reingold Alt+2 Repelling forces between all nodes, and attracting forces between adjacent nodes. Fruchterman-Reingold Layout Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other. Zoom &in Ctrl++ Zoom in (Ctrl++) Zooms inside the actual network. Zoom In. Zooms in. What else did you expect? Zoom &out Ctrl+- Zoom out (Ctrl+-) Zooms out of the actual network. Zoom out. Zooms out. What else did you expect? NodeSize = F (OutDegree) Alt+3 Resizes all nodes according to their out edges. NodeSize = F (OutDegree) Adjusts the size of each node according to their out-edges (OutDegree). The more out-likned a node is, the bigger will appear... NodeSize = F (InDegree) Alt+4 Resizes all nodes according to their in edges. NodeSize = F (InDegree) This method adjusts the size of each node according to their in-edges (InDegree). The more in-linked a node is, the bigger will appear... Network Symmetry Shift+S Tests if the network is symmetric or not Network Symmetry A network is symmetric when all edges are reciprocal, or, in mathematical language, when the adjacency matrix is symmetric. Graph Distance Ctrl+G Calculates the length of the shortest path between two nodes... Graph Distance The graph distance (or geodesic distance) of two nodes is the length (number of edges) of the shortest path between them. Distance &Matrix Ctrl+M Displays the matrix of graph distances between all nodes Distance Matrix A distance matrix is a NxN matrix, where the (i,j) element is the graph distance from node i to node j. Diameter Ctrl+D Calculates and displays the diameter of the network. Diameter The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network. Average Graph Distance Ctrl+B Calculates and displays the average shortest path length. Average Graph Distance This the average length of all shortest paths between the connected pair of nodes of the network. Clustering Coefficient Ctrl+C Calculates and displays the average Clustering Coefficient of the network. Clustering Coefficient The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. OutDegree Calculates and displays OutDegree Centralities OutDegree Centrality For each node k, this is the number of arcs starting from it. This is oftenly a measure of activity. InDegree Calculates and displays InDegree Centralities InDegree Centrality For each node k, this the number of arcs ending at k. Most in-degree central node might be considered more prominent among others. Calculates and displays Closeness Centralities Closeness Centrality For each node k, this the invert sum of the shortest distances between k and every other node. It is interpreted as the ability to access information through the "grapevine" of network members. Calculates and displays Betweenness Centralities Betweenness Centrality For each node k, this is the ratio of all geodesics between pairs of nodes which run through k. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. Calculates and displays Graph Centralities Graph Centrality For each node k, this is the invert of the maximum of all geodesic distances from k to all other nodes in the network. Nodes with high GC have short distances to all other nodes in the graph. Calculate and display Stress Centrality Stress Centrality For each node k, this is the total number of geodesics between all other nodes which run through k. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality Calculate and display Eccentricity Centrality Stress Centrality For each node k, this is the largest geodesic distance (k,t) from every other vertex t. Therefore, EC(u) reflects how far, at most, is each node from every other node. Calculate and display Informational Centrality Informational Centrality Calculate and display Informational Centrality Display Num&bers Toggles displaying of node numbers Display Numbers Enables/disables node numbers Display Labels Toggles displaying of node labels Display Labels Enables/disables node labels Display Links Toggle to display or not links Display Links Click to enable or disable displaying of links Display Weight Numbers Toggles displaying of numbers of links weights Display Weight Numbers Click to enable or disable displaying numbers of links weight Display Arrows Toggles displaying of arrows in the end of links Display Arrows Click to enable or disable displaying of arrows in the end of links Thickness=Weight Draws links as thick as their weights (if specified) Draw As Thick As Weights Click to toggle having all links as thick as their weight (if specified) Bezier Curves Draws links as Bezier curves Links Bezier Enables/Disables drawing Links as Bezier curves. Anti-Aliasing F8 Enables/disables anti-aliasing Enable or disable Anti-Aliasing Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... Progress Bars F10 Enables/disables Progress Bars Enable or disable Progress Bars Progress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation. Debug Messages F9 Enables/disables printing debug messages to stdout Enables or disable Debug Messages Printing debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing. Toolbar Enables/disables the toolbar Enable or disable Toolbar The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like... Statusbar Enables/disables the statusbar Enable or disable Statusbar The statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it... Manual F1 Read the manual... Manual Displays the documentation of SocNetV Tip of the Day Read useful tips Quick Tips Displays some useful and quick tips About SocNetV About Basic information about SocNetV About Qt About About Qt &Network Create Random Network Export... &Edit Node... Link... Filter... Colors &Layout In circles by centrality... In levels by centrality... Physical... &Analysis Centralities &Options Nodes Links &View &Help 25% 50% 75% 100% 125% 150% 175% Rotation: &Add Node Add a new node to the network (Ctrl+A). Alternately, you can create a new node in a specific position by double-clicking on that spot of the canvas. &Remove Node Remove a node from the network (Ctrl+Shift+A). Alternately, you can remove a node by right-clicking on it. Add &Link Add a new link to the network (Ctrl+L). Alternately, you can create a new link between two nodes by middle-clicking on them consequetively. Remove Link Remove a link from the network Alternately, you can remove a link by right-clicking on it. Edit Network Edit Total Nodes Total Links Counts how many nodes (vertices) exist in the whole network. Counts how many links (in and out) exist in the whole network. Density The density of a network is the ratio of existing links to all possible links (n(n-1)) between nodes. OutLinked Nodes: This the number of nodes with outEdges to another node. They may also have inEdges or reciprocal links. Meaningful on directed graphs. InLinked Nodes: This the number of nodes with inEdges from another node. These may also have outEdges or reciprocal links. Meaningful on directed graphs. Reciprocal-Linked: This the number of nodes with reciprocal links, namely, both inEdges and outEdges to another node. Active Node Node Number: This is the number of the last selected node. Node In-Degree: This is the number of edges ending at the node you clicked on. Node Out-Degree: This is the number of edges starting from the node you clicked on. Clustering Coef. The Clustering Coefficient quantifies how close the vertex and its neighbors are to being a clique. The proportion of links between the vertices within the neighbourhood of the clicked vertex, divided by the number of links that could possibly exist between them. Network Analysis Embeds a spring-gravitational model on the network, where each node is regarded as physical object reppeling all other nodes, while springs between connected nodes attact them. The result is constant movement. This is a very SLOW process on networks with N > 100! In Fruchterman-Reingold model, the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces to each other. Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. Kamanda-Kwei ! If you enable this, all nodes will be resized so that their size reflect their out-degree (the amount of links from them). To put it simply, more out-linked nodes will be bigger... If you enable this, all nodes will be resized so that their size reflect their in-degree (the amount of links to them from other nodes). To put it simply, more in-linked nodes will be bigger... Layout Ready. Social Network Visualiser Ready Do you want to save the changes to the network file? Yes No Cancel Choose a network file... Select one file to open All (*);;GraphML (*.graphml *.gml);;GraphViz (*.dot);;Adjacency (*.txt *.csv *.net);;Pajek (*.net *.pajek);;DL (*.dl *.net) Loaded network: Error loading requested file. Aborted. Opening aborted Saving file... No network loaded. Do you want to save this network in Pajek-formatted or SocioMatrix - formatted file? Pajek Sociomatrix Network saved under filename: . Saving network under new filename... Saving aborted Closing file... Network has not been saved. Do you want to save before closing it? Erasing old network data.... Printing... Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Links. Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Links. Dot formatted network, named %1, loaded with %2 Nodes and %3 total Links. GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Links. DL-formatted network, named %1, loaded with %2 Nodes and %3 total Links. New node (numbered %1) added. The canvas is empty! Load a network file or create a new network first. Cannot export PNG. Save Image Files (*.png) Image Saved as: Exporting completed Nothing to export! Load a network file or create a new network first. Cannot export BMP. Save Image as Image Files (*.bmp) Export to BMP... Cannot export PDF. Export to PDF Portable Document Format files (*.pdf) Export to PDF... File saved as: Cannot export to Pajek. Could not write to %1 File %1 saved Nothing to export! Load a network file or create a new network first. Cannot export to Adjacency Matrix. Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1.) Adjacency matrix-formatted network saved into file %1 Cannot export to DL. Cannot export to GW. Viewing network file - Loaded network text file Network not saved yet. I will open a dialog for you to save it now. Network has been modified. Please save it now. Empty network! Load a network file first or create and save a new one... Nothing here. Not my fault, though! Empty network! Load a network file or create something by double-clicking on the canvas! Nothing to show! creating adjacency adjacency matrix of %1 nodes View Adjacency Matrix - This will create a new random symmetric network of G(n,p) model, where n is the nodes and p is the edge probability. Please enter the number n of nodes you want: Creating random network. Please wait... Random network created. Nodes: Edges: Average path length: Clustering coefficient: On the average, edges should be This graph is almost surely connected because: probability > ln(n)/n, that is: bigger than This graph is almost surely not connected because: probability < ln(n)/n, that is: Creating uniform random network. Please wait... This will create a same degree network. Please enter the number of nodes you want: Sorry. I cannot create such a network. Links must be even number This will create a small world network, that is an undirected graph with N nodes and N*d/2 edges, where d is the mean edge degree. Please enter the number N of nodes you want: Now, enter an even number d. This is the mean edge degree each new node will have: Now, enter a parameter beta. This is the edge rewiring probability: Erasing any existing network. Creating small world. Please wait... Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ). Small world random network created: Small world network created. This will create a ring lattice network, where each node has degree d: d/2 edges to the right and d/2 to the left. Please enter the number of nodes you want: Ring lattice network created. No nodes present! Load a network file first or create some nodes... OK Nothing to find! Enter node label or number: Node found! Sorry. There is no such node in this network. Try again. Options (%1, %2); Node %3, with label %4, has %5 in-Links and %6 out-Links. Link from Node %1 to Node %2, has weight %3 and color %4. Link between node %1 and node %2, weight %3 and color %4. Nothing to do! Load a network file or add some nodes first. Nothing to remove. Choose a node to remove between ( Node removed completely. Ready. Nothing to link to! Create some nodes first. There are no nodes yet... This will draw a new link between two nodes. Enter source node ( Aborting. Source node accepted. Now enter target node ( Source and target nodes accepted. Please, enter the weight of new link: Ready. No links present! Load a network file or create a new network first. No links to remove - sorry. Remove link Source node: ( Target node: ( There is no such link. This link is reciprocal. Select what Direction to delete or Both... Both There are no nodes! Load a network file or create a new network first. No nodes created. Please click on a node first... Enter new node label: Changed label to %1. Ready. No label text. Abort. No nodes... Select node: ( Error. There is no such link. Change node color aborted. Cannot change nothing. Change node size to: (1-16) No links here! Load a network file or create a new network first. No links present... Select link source node: ( Select link target node: ( Change link color cancelled. User abort. There are no links here! Load a network file or create a new network first. New link Weight: input error. Abort. Change link weight Select what Direction to change or Both... New link weight: Change link weight cancelled. Weight not changed. Sorry. Nothing to filter! Load a network file or create a new network. Then ask me to compute something! Nothing to filter! All links are reciprocal. Your network is symmetric... There are node nodes yet! Load a network file or create a new network first. Then we can talk about layouts! I am really sorry. You must really load a file first... Embedding a spring-gravitational model on the network.... Click on the checkbox "Spring-Embedder" to stop movement! Movement stopped! There are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Embedding a repelling-attracting forces model on the network.... Click on the checkbox "Fruchterman-Reingold" to stop movement! Wake up! Load a network file or create a new network first. Then we can talk about layouts! Embedding node size model on the network.... You must be dreaming! Load a network file or create a new network first. Then we can talk about layouts! Sorry, I can't follow! Load a network file or create a new network first. Then we can talk about layouts! Nothing to layout! Are you dreaming? Calculating new nodes positions. Please wait... Nodes in inner circles have greater In-Degree Centrality. Load a network file or create a new network first! Nodes in inner circles have greater Out-Degree Centrality. Sorry, there are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Closeness Centrality. No nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Betweenness Centrality. Nothing to do! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Stress Centrality. Nothing to do here! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Graph Centrality. Nodes in inner circles have greater Eccentricity Centrality. Nodes in upper levels have greater In-Degree Centrality. Load a network file or create a new network first. Then we can talk about layouts! Nodes in upper levels have greater Out-Degree Centrality. Nodes in upper levels have greater Closeness Centrality. Nodes in upper levels have greater Betweenness Centrality. There are no nodes! Load a network file or create a new network. Then ask me to compute something! There is no network! Adjacency matrix symmetry = YES Adjacency matrix symmetry = NO There are no nodes. Nothing to do... Distance between two nodes Select source node: ( Select target node: ( Distance calculation operation cancelled. Distance Network distance ( The nodes are connected. The nodes are not connected. There are no nodes nor links! Load a network file or create a new network. Then ask me to compute something! Nothing to do! Creating distance matrix. Please wait... Matrix of sigmas Matrix of distances Cannot find the diameter of nothing... Diameter calculated. Ready. Average distance calculated. Ready. Window resized to (%1, %2) pixels. Nothing to do! Load a network file or create a new network. Then ask me to compute something! No network here. Sorry. Nothing to do. CLUSTERING COEFFICIENT REPORT Created: CLUSTERING COEFFICIENT (CLC) OF EACH NODE CLC range: 0 < C < 1 Range: 0 < GCLC < 1 GCLC = 0, when there are no cliques (i.e. acyclic tree). GCLC = 1, when every node and its neighborhood are complete cliques. Take weights into account (Default: No)? OUT-DEGREE CENTRALITY REPORT OUT-DEGREE CENTRALITIES (ODC) OF EACH NODE ODC range: 0 < C < ODC' range: 0 < C'< 1 GODC range: 0 < GODC < 1 GODC = 0, when all in-degrees are equal (i.e. regular lattice). GODC = 1, when one node completely dominates or overshadows the other nodes. The degree of the node is a measure of the 'activity' of the node it represents Nothing to do! Load a network file or create a new network. Then ask me to compute something! Nothing to do... IN-DEGREE CENTRALITY REPORT IN-DEGREE CENTRALITIES (IDC) OF EACH NODE IDC range: 0 < C < IDC' range: 0 < C'< 1 GIDC range: 0 < GIDC < 1 GIDC = 0, when all in-degrees are equal (i.e. regular lattice). GIDC = 1, when one node is linked from every other node. The in-degree of the node is a measure of the 'activity' of the node it represents CLOSENESS - CENTRALITY REPORT CLOSENESS CENTRALITY (CC) OF EACH NODE CC(u) is the invert sum of the distances of node u from all other nodes. CC' is the standardized CC CC range: 0 < C < CC' range: 0 < C'< 1 All nodes have the same CC value. Node has the maximum ACC value (std): has the minimum ACC value (std): There are different Closeness Centrality classes. GROUP CLOSENESS CENTRALISATION (GCC) GCC = GCC range: 0 < GCC < 1 GCC = 0, when the lengths of the geodesics are all equal (i.e. a complete or a circle graph). GCC = 1, when one node has geodesics of length 1 to all the other nodes, and the other nodes have geodesics of length 2 to the remaining (N-2) nodes. This is exactly the situation realised by a star graph. This measure focuses on how close a node is to all the other nodes in the set of nodes. The idea is that a node is central if it can quickly interact with all others Nothing to do... Please wait... Finished with shortest-path distances... BETWEENESS - CENTRALITY REPORT BETWEENESS CENTRALITY (BC) OF EACH NODE BC of a node u is the sum of delta (s,t,u) for all s,t in V Delta(s,t,u) is the ratio of all geodesics between s and t which run through u. Therefore, BC(u) reflects how often the node u lies on the geodesics between the other nodes of the network BC' is the standardized BC BC range: 0 < BC < (Number of pairs of nodes excluding i) BC' range: 0 < BC'< 1 (C' is 1 when the node falls on all geodesics) All nodes have the same BC value. Node has the maximum BC value: has the minimum BC value: different Betweenness Centrality classes. GROUP BETWEENESS CENTRALISATION (GBC) GBC = GBC range: 0 < GBC < 1 GBC = 0, when all the nodes have exactly the same betweenness index. GBC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Nothing to do! Why dont you try creating something first? STRESS CENTRALITY REPORT STRESS CENTRALITY (SC) OF EACH NODE SC(u) is the sum of sigma(s,t,u): the number of geodesics from s to t through u. SC(u) reflects the total number of geodesics between all other nodes which run through u SC range: 0 < SC < SC' range: 0 < SC'< 1 (SC'=1 when the node falls on all geodesics) All nodes have the same SC value. has the maximum SC value: has the minimum SC value: different Stress Centrality classes. GROUP STRESS CENTRALISATION (GSC) GSC = GSC range: 0 < GSC < 1 GSC = 0, when all the nodes have exactly the same stress index. GSC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Try creating a network first. Then I compute whatever you want... GRAPH - CENTRALITY REPORT GRAPH CENTRALITY (GC) OF EACH NODE GC range: 0 < GC < GC' range: 0 < GC'< 1 (GC'=1 => directly linked with all nodes) All nodes have the same GC value. has the maximum GC value: has the minimum GC value: different Graph Centrality classes. GROUP GRAPH CENTRALISATION (GGC) GGC = GGC range: 0 < GGC < 1 GGC = 0, when all the nodes have exactly the same graph index. GGC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. ECCENTRICITY- CENTRALITY REPORT ECCENTRICITY CENTRALITY (EC) OF EACH NODE EC of a node u is the largest geodesic distance (u,t) for t in V Therefore, EC(u) reflects how far, at most, is each node from every other node. EC' is the standardized EC EC range: 0 < EC < (max geodesic distance) EC' range: 0 < EC'< 1 All nodes have the same EC value. has the maximum EC value: has the minimum EC value: different eccentricity Centrality classes. GROUP ECCENTRICITY CENTRALISATION (GEC) GEC = GEC range: 0 < GEC < 1 GEC = 0, when all the nodes have exactly the same betweenness index. GEC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Eccentricity Centralities saved as: There are no nodes! Load a network file or create a new network. Errr...no nodes here. Sorry! Toggle Nodes Numbers. Please wait... Node Numbers are invisible now. Click the same option again to display them. Node Labels are visible again... There are no nodes! Load a network file or create a new network first. No nodes found. Sorry... Toggle Nodes Labels. Please wait... Node Labels are invisible now. Click the same option again to display them. Select new size for all nodes: (1-16) All shapes have been changed. Ready Change node shapes aborted... Change all nodenumbers size to: (1-16) Change font size: Aborted. Changed numbers size. Ready. There are no links! Load a network file or create a new network first. No nodes or edges found. Sorry... Toggle Edges Weights. Please wait... Edge weights are invisible now. Click the same option again to display them. Edge weights are visible again... There are no nodes nor links! Load a network file or create a new network first! No links found... Toggle Edges Arrows. Please wait... Links are invisible now. Click again the same menu to display them. Links visible again... There are no links! Load a network file or create a new network first! There are no links! Load a network file or create a new network! There are NO links here! Toggle links bezier. Please wait... Change link color aborted. Numbers' colors changed. Ready. Label colors changed. Ready. Toggle anti-aliasing. This will take some time if the network is large (>500)... Anti-aliasing off. Anti-aliasing on. Toggle progressbar... Progress bars off. Progress bars on. Debug messages off. Debug messages on. Toggle toolbar... Toolbar off. Toolbar on. Toggle statusbar... Status bar off. Status bar on. Tip Of The Day You can add a new node by double-clicking on the scene. You can add a new node by clicking on Add button. You can remove a node by clicking on Remove button. You can rotate the network by selecting a new angle on the dock. You can add a new link between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node. You can remove a node by right-clicking on it and selecting Remove. You can change background color (from the menu Edit > Colors). Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette. The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering). You can move a node easily by dragging it with your mouse. SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format. You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes You can change the label of node by right-clicking on it, and selecting Options > Change Label. All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or a link. Node information is displayed on the Status bar, when you left-click on it. Link information is displayed on the Status bar, when you left-click on it. Manual TextEditor &New Ctrl+N Create a new file &Open... Ctrl+O Open an existing file &Save Ctrl+S Save the document to disk Save &As... Save the document under a new name E&xit Ctrl+Q Exit the application Cu&t Ctrl+X Cut the current selection's contents to the clipboard &Copy Ctrl+C Copy the current selection's contents to the clipboard &Paste Ctrl+V Paste the clipboard's contents into the current selection &About Show the application's About box About &Qt Show the Qt library's About box &File &Edit &Help File Edit Ready TextEditor The document has been modified. Do you want to save your changes? Application Cannot read file %1: %2. File loaded Cannot write file %1: %2. File saved %1[*] - %2 socnetv-1.9/translations/socnetv_de.ts0000664000175000017500000062323012534331160020526 0ustar dimitrisdimitris HTMLViewer &File Datei &Open Öffnen Ctrl+O Strg+O Opens another helpfile Öffnet andere Hilfequelle &Print Drucken Ctrl+P Strg+P Prints out the actual network Druckt das aktuelle Netzwerk E&xit Beenden Ctrl+X Strg+X Close Manual Schließe Handbuch &Back Zurück Ctrl+B Strg+B &Backward Rückwärts &Forward Vorwärts Ctrl+F Strg+F &Home Start Ctrl+H Strg+H &Go Los MainWindow Welcome to Social Networks Visualiser, Version Willkommen zu Social Networks Visualiser, Version &New Neu Ctrl+N Strg+N Creates a new network Erstellt neues Netzwerk New network (Ctrl+N) Neues Netzwerk (Strg+N) New Creates a new network Neu Erstellt neues Netzwerk &Open Öffnen Ctrl+O Strg+O Open network (Ctrl+O) Öffne Netzwerk (Strg+O) Opens a a file of an existing network Öffnet Datei mit existierendem Netzwerk Open Opens a file of an existing network Öffnen Öffnet Datei mit existierendem Netzwerk &Save Speichern Ctrl+S Strg+S Save network (Ctrl+S) Speichere Netzwerk (Srtg+S) Saves the actual network to the current file Speichert aktuelles Netzwerk aktueller Datei Save. Saves the actual network Speichern Speichert aktuelles Netzwerk Save &As... Speichern unter Ctrl+Shift+S Strg+Shift+S Saves the actual network under a new filename Speichere aktuelles Netzwerk unter neuer Datei Save As Saves the actual network under a new filename Speichern unter Speichert aktuelles Netzwerk unter neuer Datei &BMP... BMP Export network to a BMP image Exportiere Netzwerk als BMP Bild Export BMP Export network to a BMP image Export BMP Exportiere Netzwerk als BMP Bild &PNG... PNG Export network to a PNG image Exportiere Netzwerk als PNG Bild Export PNG Export network to a PNG image Export PNG Exportiert Netzwerk als PNG Bild &PDF... PDF Export network to a PDF file Exportiere Netzwerk als PDF Datei Export PDF Export network to a PDF document Export PDF Exportiere Netzwerk als PDF Dokument &Adjacency Matrix Adjacency Matrix Export network to an adjacency matrix file Exportiere Netzwerk als Adjacency Matrix Datei Export Sociomatrix Export network to a adjacency matrix-formatted file Export Sociomatrix Exportiere Netzwerk als Adjacency Matrix-formatierte Datei &Pajek Pajek Export network to a Pajek-formatted file Exportiere Netzwerk als Pajek-formatierte Datei Export Pajek Export network to a Pajek-formatted file Export Pajek Exportiere Netzwerk als Pajek-formatierte Datei &List List Export network to a List-formatted file. Exportiere Netzwerk als List-formatierte Datei. Export List Export network to a List-formatted file Export List Exportiere Netzwerk als List-formatierte Datei &DL... DL... Export network to a DL-formatted file Exportiere Netzwerk als DL-formatierte Datei Export DL Export network to a DL-formatted Export DL Exportiere Netzwerk als DL-formatierte Datei &GW... GW... Export network to a GW-formatted file Exportiere Netzwerk als GW-formatierte Datei Export Export network to a GW formatted file Exportiere Exportiere Netzwerk als GW-formatierte Datei &Close Schließen Closes the actual network Schließt das aktuelle Netzwerk Close Closes the actual network Schließen Schließt das aktuelle Netzwerk &Print Drucken Ctrl+P Strg+P Prints whatever is viewable on the canvas. Druckt sichtbaren Bereich Printing This function prints whatever is viewable on the canvas. To print the whole network, you might want to zoom-out. Drucken Diese Funktion druckt den sichtbaren Bereich. Um das gesamte Netzwerk zu drucken ggf. herauszoomen E&xit Beenden Ctrl+Q Strg+Q Quits the application Beendet die Anwendung Exit Quits the application Beenden Beendet die Anwendung View Loaded File Betrachte geladene Datei F5 F5 Displays the loaded network file Zeigt geladene Netzwerk-Datei an View Loaded File Displays the file of the loaded network Betrachte geladene Datei Zeigt die geladene Netzwerk-Datei an View Adjacency Matrix Zeige Adjacency Matrix F6 F6 Displays the adjacency matrix of the active network Zeigt Adjacency Matrix des aktiven Netzwerkes View Network file Displays the adjacency matrix of the active network Zeige Netzwerk-Datei an Zeigt die Adjacency Matrix des aktiven Netzwerkes Erdos-Renyi G(n,p) Erdos-Renyi G(n,p) Shift+U Shift+U Creates a random network where each edge is included with a given probability Erschafft ein zufälliges Netzwerk in dem jede Kante mit gegebener Wahrscheinlichkeit eingeschlossen ist Uniform Creates a random network of G(n, p) model by connecting nodes randomly. Each edge is included in the graph with equal probability p, independently of the other edges Uniform Erschafft ein zufälliges Netzwerk im G(n, p) Model durch zufälliges Verbinden der Knoten. Jede Kante ist mit gleicher Wahrscheinlichkeit p im Graphen eingschlossen, unabhängig von den anderen Kanten Connected Verbunden Creates a connected random network Erschafft ein verbundenes zufälliges Netzwerk Uniform Connected Creates a connected random network Uniform Connected Erschafft ein verbundenes zufälliges Netzwerk Ring Lattice Ring Lattice Shift+L Shift+L Creates a ring lattice random network Erschafft ein zufälliges Ring Lattice Netzwerk Ring Lattice A ring lattice or a physicist's lattice is a graph with N nodes each connected to K neighbors, K / 2 on each side. Ring Lattice Ein Ring Lattice oder Physicist' Lattice ist ein Graph mit N Knoten, jeder verbunden mit K Nachbarn, K / 2 auf jeder Seite. Same Degree Same Degree Creates a random network where all nodes have the same degree. Erschafft ein Netzwerk in dem alle Knoten den gleichen Grad besitzen. Same Degree Creates a random network where all nodes have the same degree Same Degree Erschafft ein zufälliges Netzwerk in dem alle Knoten den selben Grad besitzen Gaussian Gaussian Creates a Gaussian distributed random network Erschafft ein zufälliges normalverteiltes Netzwerk Gaussian Creates a random network of Gaussian distribution Gaussian Erschafft ein zufälliges Netzwerk mit Gauß'scher Verteilung Small World Small World Shift+W Shift+W Creates a random network with small world properties Erschafft ein zufälliges Netzwerk mit Small World Eigenschaften Small World A Small World, according to the Watts and Strogatz model, is a random network with short average path lengths and high clustering coefficient. Small World Eine Small World, nach Watts' und Strogatz' Model, ist ein zufälliges Netzwerk mit kurzen mittleren Pfadlängen und hohem Clustering Koeffizienten Find Node Finde Knoten Ctrl+F Strg+F Finds and highlights a node by number or label. Press Ctrl+F again to undo. Findet einen Knoten nach Nummer oder Label und hebt ihn hervor. Umschalten mit Strg+F Find Node Finds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node Finde Knoten Findet einen Knoten mit gegebener Nummer oder Label und verdoppelt dessen Größe. Strg+F setzt die Skalierung wieder zurück Add Node Füge Knoten hinzu Ctrl+A Strg+A Adds a node Fügt einen Knoten hinzu Add Node Adds a node to the network Füge Knoten hinzu Fügt dem Netzwerk einen Knoten hinzu Remove Node Entferne Knoten Ctrl+Shift+A Strg+Shift+A Removes a node Entfernt einen Knoten Remove Node Removes a node from the network Entferne Knoten Entfernt einen Knoten des Netzwerkes Change Label Ändere Label Changes the Label of a node Ändert das Label eines Knotens Change Label Changes the label of a node Ändere Label Ändert das Label eines Knotens Change Color Ändere Farbe Changes the color of a node Ändert die Farbe eines Knotens Change Color Changes the Color of a node Ändere Farbe Ändert die Farbe eines Knotens Change Size Ändere Größe Changes the actual size of a node Ändert die Größe eines Knotens Change Size Changes the actual size of a node Ändere Größe Ändert die Größe eines Knotens Change Value Ändere Wert Changes the value of a node Ändert den Wert eines Knotens Change Value Changes the value of a node Ändere Wert Ändert den Wert eines Knotens Change all Nodes Size Ändere Größe aller Knoten This option lets you change the size of all nodes Diese Option erlaubt die Größe aller Knoten zu ändern Nodes Size This option lets you change the size of all nodes Knoten Größe Dies Option erlaubt die Größe aller Knoten zu ändern Change all Nodes Shape Ändere Form aller Knoten This option lets you change the shape of all nodes Diese Option erlaubt die Form aller Knoten zu ändern Nodes Shape This option lets you change the shape of all nodes Knoten Form Diese Option erlaubt die Form aller Knoten zu ändern Change Node Shape to Box Ändere Knotenform zu Box This option lets you change the shape of a node to a box Diese Option erlaubt die Form eines Knotens zu Box zu ändern Node as a box This option lets you change the shape of a node to a box Knoten als Box Diese Option erlaubt die Form eines Knotens zu Box zu ändern Change Node Shape to Triangle Ändere Knotenform zu Dreieck Change Node Shape to Circle Ändere Knotenform zu Kreis Change Node Shape to Diamond Ändere Knotenform zu Diamant Change Node Shape to Ellipse Ändere Knotenform zu Ellipse Change all Numbers Size Ändere Größe aller Nummern It lets you change the font size of the numbers of all nodes Erlaubt die Schrifftgröße der Nummern aller Knoten zu ändern Numbers Size Changes the size of the numbers of all nodes Nummern Größe Ändert die Größe der Nummern aller Knoten Change all Labels Size Ändere Größe aller Labels You can change the font size of the labels of all nodes Erlaubt die Schrifftgröße der Labels aller Knoten zu ändern Labels Size Change the fontsize of the labels of all nodes Label Größe Ändere die Schrifftgröße der Label aller Knoten Add Link Füge Link hinzu Ctrl+L Strg+L Adds a Link to a Node Fügt einem Knoten einen Link hinzu Add Link Adds a Link to the network Fügt dem Netzwerk einen Link hinzu Remove Entfernen Ctrl+Shift+L Strg+Shift+L Removes a Link Entfernt einen Link Remove Link Removes a Link from the network Entferne Link Entfernt einen Link vom Netzwerk Changes the Label of a Link Ändere Label eines Links Change Label Changes the label of a Link Ändere Label Ändert das Label eines Links Changes the Color of a Link Ändert die Farbe eines Links Change Color Changes the Color of a Link Ändere Farbe Ändert die Farbe eines Links Change Weight Ändere Gewichtung Changes the Weight of a Link Ändert die Gewichtung eines Links Change Value Changes the Weight of a Link Ändere Wert Ändert die Gewichtung eines Links Filter Nodes Filtere Knoten Filters Nodes of some value out of the network Filtert Knoten eines bestimmten Wertes aus dem Netzwerk Filter Nodes Filters Nodes of some value out of the network. Filter Knoten Filtert Knoten eines bestimmten Wertes aus dem Netzwerk Filter Links Filtere Links Filters Links of some weight out of the network Filtert Links einer bestimmten Gewichtung aus dem Netzwerk Filter Links Filters Link of some specific weight out of the network. Filter Links Filtert Links einer bestimmten Gewichtung aus dem Netzwerk Change Background Color Ändere Hintergrundfarbe Click to change the background color Klicken um Hintergrundfarbe zu ändern Background Changes background color Hintergrund Ändert Hintergrundfarbe Change all Nodes Colors Ändere Farbe aller Knoten Click to choose a new color for all nodes. Klicken um neue Farbe für alle Knoten zu wählen All Nodes Changes all nodes color at once. Alle Knoten Ändert die Farbe aller Knoten. Change all Numbers Colors Ändere Farbe aller Nummern Click to change the color of all numbers. Klicken um Farbe aller Nummern zu ändern Numbers Changes the color of all numbers. Nummern Ändert die Farbe aller Nummern Change all Labels Colors Ändere Farbe aller Label Click to change the color of all node labels. Klicken um Farbe aller Labels zu ändern Numbers Changes the color of all node labels. Nummern Ändert die Farbe aller Label Change all Links Colors Ändere Farbe aller Links Click to change the color of all links. Klicken um Farbe aller Links zu ändern Background Changes all links color Hintergrund Ändert die Farbe aller Links Transform Nodes to Links Transformiere Knoten zu Link Transforms the network so that nodes become links and vice versa Transformiert das Netzwerk so dass Knoten zu Links werden und umgekehrt Transform Nodes LinksAct Transforms network so that nodes become links and vice versa Transformiere Knoten LinksAct Transformiert das Netzwerk so dass Knoten zu Links werden und umgekehrt Symmetrize Links Mache Links symmetrisch Shift+R Shift+R Makes all edges reciprocal (thus, a symmetric graph). Macht alle Kanten reziprok (d.h. einen symmetrischen Graphen) Symmetrize Edges Transforms all arcs to double links (edges). The result is a symmetric network Mache Kanten symmetrisch Transformiert alle Bögen zu doppelten Links (Kanten). Das Ergebnis ist ein symmetrisches Netzwerk Strong Structural Strong Structural Nodes are assigned the same color if they have identical in and out neighborhoods Knoten erhalten die selbe Farbe wenn sie identische In- und Out-Nachbarn besitzen Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods Klicken um Knoten zu färben; Knoten erhalten die selbe Farbe wenn sie identische In- und Out-Nachbarn besitzen Regular Regular Nodes are assigned the same color if they have neighborhoods of the same set of colors Knoten erhalten die selbe Farbe wenn sie Nachbarschaften gleicher Farbsets besitzen Click this to colorize nodes; Nodes are assigned the same color if they have neighborhoods of the same set of colors Klicken um Knoten zu färben; Knoten erhalten die selbe Farbe wenn sie Nachbarschaften gleicher Farbsets besitzen Random Random Repositions the nodes in random places Setzt die Knoten in zufälliger Verteilung zurück Random Layout Repositions the nodes in random places Zufälliges Layout Setzt die Knoten in zufälliger Verteilung zurück Random Circle Zufälliger Kreis Repositions the nodes randomly on a circle Verteilt die Knoten zufällig auf einen Kreis Random Circle Layout Repositions the nodes randomly on a circle Kreis Layout Verteilt die Knoten zufällig auf einen Kreis In-Degree In-Degree Ctrl+1 Strg+1 Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Circle In-Degree Centrality Layout Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Out-Degree Ctrl+2 Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Circle Out-Degree Centrality Layout Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Closeness Ctrl+3 Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Circle Closeness Centrality Layout Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Betweenness Ctrl+4 Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Circle Betweenness Centrality Layout Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Informational Ctrl+5 Repositions the nodes on circles of different radius. More Informational Central Nodes are situated towards the centre. Circle Informational Centrality Layout Repositions the nodes on circles of different radius. More Informational Central Nodes are positioned towards the centre. Stress Ctrl+6 Repositions the nodes on circles of different radius. More Stressed Central Nodes are positioned towards the centre. Circle Stress Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Stress Centrality are situated towards the centre. Graph Ctrl+7 Repositions the nodes on circles of different radius. More Graphed Central Nodes are positioned towards the centre. Circle Graph Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Graph Centrality are situated towards the centre. Eccentricity Ctrl+8 Repositions the nodes on circles of different radius. Nodes of large eccentricity are positioned towards the centre. Circle Eccentricity Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Eccentricity Centrality are situated towards the centre. Remove Layout GuideLines Removes Red GuideLines from the canvas. Remove GuideLines Removes any guidelines (circles or horizontal lines) created for the network layout. Ctrl+Shift+1 Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Level In-Degree Centrality Layout Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Ctrl+Shift+2 Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Level Out-Degree Centrality Layout Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Ctrl+Shift+3 Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. level Closeness Centrality Layout Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. Ctrl+Shift+4 Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. level Betweenness Centrality Layout Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. Ctrl+Shift+5 Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Level Informational Centrality Layout Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Spring Embedder Alt+1 All nodes repel each other while the connected ones are attracted as if connected by springs. Spring Embedder Layout In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. Fruchterman-Reingold Alt+2 Repelling forces between all nodes, and attracting forces between adjacent nodes. Fruchterman-Reingold Layout Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other. Zoom &in Ctrl++ Zoom in (Ctrl++) Zooms inside the actual network. Zoom In. Zooms in. What else did you expect? Zoom &out Ctrl+- Zoom out (Ctrl+-) Zooms out of the actual network. Zoom out. Zooms out. What else did you expect? NodeSize = F (OutDegree) Alt+3 Resizes all nodes according to their out edges. NodeSize = F (OutDegree) Adjusts the size of each node according to their out-edges (OutDegree). The more out-likned a node is, the bigger will appear... NodeSize = F (InDegree) Alt+4 Resizes all nodes according to their in edges. NodeSize = F (InDegree) This method adjusts the size of each node according to their in-edges (InDegree). The more in-linked a node is, the bigger will appear... Network Symmetry Shift+S Tests if the network is symmetric or not Network Symmetry A network is symmetric when all edges are reciprocal, or, in mathematical language, when the adjacency matrix is symmetric. Graph Distance Ctrl+G Calculates the length of the shortest path between two nodes... Graph Distance The graph distance (or geodesic distance) of two nodes is the length (number of edges) of the shortest path between them. Distance &Matrix Ctrl+M Displays the matrix of graph distances between all nodes Distance Matrix A distance matrix is a NxN matrix, where the (i,j) element is the graph distance from node i to node j. Diameter Ctrl+D Calculates and displays the diameter of the network. Diameter The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network. Average Graph Distance Ctrl+B Calculates and displays the average shortest path length. Average Graph Distance This the average length of all shortest paths between the connected pair of nodes of the network. Clustering Coefficient Ctrl+C Calculates and displays the average Clustering Coefficient of the network. Clustering Coefficient The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. OutDegree Calculates and displays OutDegree Centralities OutDegree Centrality For each node k, this is the number of arcs starting from it. This is oftenly a measure of activity. InDegree Calculates and displays InDegree Centralities InDegree Centrality For each node k, this the number of arcs ending at k. Most in-degree central node might be considered more prominent among others. Calculates and displays Closeness Centralities Closeness Centrality For each node k, this the invert sum of the shortest distances between k and every other node. It is interpreted as the ability to access information through the "grapevine" of network members. Calculates and displays Betweenness Centralities Betweenness Centrality For each node k, this is the ratio of all geodesics between pairs of nodes which run through k. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. Calculates and displays Graph Centralities Graph Centrality For each node k, this is the invert of the maximum of all geodesic distances from k to all other nodes in the network. Nodes with high GC have short distances to all other nodes in the graph. Calculate and display Stress Centrality Stress Centrality For each node k, this is the total number of geodesics between all other nodes which run through k. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality Calculate and display Eccentricity Centrality Stress Centrality For each node k, this is the largest geodesic distance (k,t) from every other vertex t. Therefore, EC(u) reflects how far, at most, is each node from every other node. Calculate and display Informational Centrality Informational Centrality Calculate and display Informational Centrality Display Num&bers Toggles displaying of node numbers Display Numbers Enables/disables node numbers Display Labels Toggles displaying of node labels Display Labels Enables/disables node labels Display Links Toggle to display or not links Display Links Click to enable or disable displaying of links Display Weight Numbers Toggles displaying of numbers of links weights Display Weight Numbers Click to enable or disable displaying numbers of links weight Display Arrows Toggles displaying of arrows in the end of links Display Arrows Click to enable or disable displaying of arrows in the end of links Thickness=Weight Draws links as thick as their weights (if specified) Draw As Thick As Weights Click to toggle having all links as thick as their weight (if specified) Bezier Curves Draws links as Bezier curves Links Bezier Enables/Disables drawing Links as Bezier curves. Anti-Aliasing F8 Enables/disables anti-aliasing Enable or disable Anti-Aliasing Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... Progress Bars F10 Enables/disables Progress Bars Enable or disable Progress Bars Progress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation. Debug Messages F9 Enables/disables printing debug messages to stdout Enables or disable Debug Messages Printing debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing. Toolbar Enables/disables the toolbar Enable or disable Toolbar The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like... Statusbar Enables/disables the statusbar Enable or disable Statusbar The statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it... Manual F1 Read the manual... Manual Displays the documentation of SocNetV Tip of the Day Read useful tips Quick Tips Displays some useful and quick tips About SocNetV About Basic information about SocNetV About Qt About About Qt &Network Create Random Network Export... &Edit Node... Link... Filter... Colors &Layout In circles by centrality... In levels by centrality... Physical... &Analysis Centralities &Options Nodes Links &View &Help 25% 50% 75% 100% 125% 150% 175% Rotation: &Add Node Add a new node to the network (Ctrl+A). Alternately, you can create a new node in a specific position by double-clicking on that spot of the canvas. &Remove Node Remove a node from the network (Ctrl+Shift+A). Alternately, you can remove a node by right-clicking on it. Add &Link Add a new link to the network (Ctrl+L). Alternately, you can create a new link between two nodes by middle-clicking on them consequetively. Remove Link Remove a link from the network Alternately, you can remove a link by right-clicking on it. Edit Network Edit Total Nodes Total Links Counts how many nodes (vertices) exist in the whole network. Counts how many links (in and out) exist in the whole network. Density The density of a network is the ratio of existing links to all possible links (n(n-1)) between nodes. OutLinked Nodes: This the number of nodes with outEdges to another node. They may also have inEdges or reciprocal links. Meaningful on directed graphs. InLinked Nodes: This the number of nodes with inEdges from another node. These may also have outEdges or reciprocal links. Meaningful on directed graphs. Reciprocal-Linked: This the number of nodes with reciprocal links, namely, both inEdges and outEdges to another node. Active Node Node Number: This is the number of the last selected node. Node In-Degree: This is the number of edges ending at the node you clicked on. Node Out-Degree: This is the number of edges starting from the node you clicked on. Clustering Coef. The Clustering Coefficient quantifies how close the vertex and its neighbors are to being a clique. The proportion of links between the vertices within the neighbourhood of the clicked vertex, divided by the number of links that could possibly exist between them. Network Analysis Embeds a spring-gravitational model on the network, where each node is regarded as physical object reppeling all other nodes, while springs between connected nodes attact them. The result is constant movement. This is a very SLOW process on networks with N > 100! In Fruchterman-Reingold model, the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces to each other. Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. Kamanda-Kwei ! If you enable this, all nodes will be resized so that their size reflect their out-degree (the amount of links from them). To put it simply, more out-linked nodes will be bigger... If you enable this, all nodes will be resized so that their size reflect their in-degree (the amount of links to them from other nodes). To put it simply, more in-linked nodes will be bigger... Layout Ready. Social Network Visualiser Ready Do you want to save the changes to the network file? Yes No Cancel Choose a network file... Select one file to open All (*);;GraphML (*.graphml *.gml);;GraphViz (*.dot);;Adjacency (*.txt *.csv *.net);;Pajek (*.net *.pajek);;DL (*.dl *.net) Loaded network: Error loading requested file. Aborted. Opening aborted Saving file... No network loaded. Do you want to save this network in Pajek-formatted or SocioMatrix - formatted file? Pajek Sociomatrix Network saved under filename: . Saving network under new filename... Saving aborted Closing file... Network has not been saved. Do you want to save before closing it? Erasing old network data.... Printing... Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Links. Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Links. Dot formatted network, named %1, loaded with %2 Nodes and %3 total Links. GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Links. DL-formatted network, named %1, loaded with %2 Nodes and %3 total Links. New node (numbered %1) added. The canvas is empty! Load a network file or create a new network first. Cannot export PNG. Save Image Files (*.png) Image Saved as: Exporting completed Nothing to export! Load a network file or create a new network first. Cannot export BMP. Save Image as Image Files (*.bmp) Export to BMP... Cannot export PDF. Export to PDF Portable Document Format files (*.pdf) Export to PDF... File saved as: Cannot export to Pajek. Could not write to %1 File %1 saved Nothing to export! Load a network file or create a new network first. Cannot export to Adjacency Matrix. Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1.) Adjacency matrix-formatted network saved into file %1 Cannot export to DL. Cannot export to GW. Viewing network file - Loaded network text file Network not saved yet. I will open a dialog for you to save it now. Network has been modified. Please save it now. Empty network! Load a network file first or create and save a new one... Nothing here. Not my fault, though! Empty network! Load a network file or create something by double-clicking on the canvas! Nothing to show! creating adjacency adjacency matrix of %1 nodes View Adjacency Matrix - This will create a new random symmetric network of G(n,p) model, where n is the nodes and p is the edge probability. Please enter the number n of nodes you want: Creating random network. Please wait... Random network created. Nodes: Edges: Average path length: Clustering coefficient: On the average, edges should be This graph is almost surely connected because: probability > ln(n)/n, that is: bigger than This graph is almost surely not connected because: probability < ln(n)/n, that is: Creating uniform random network. Please wait... This will create a same degree network. Please enter the number of nodes you want: Sorry. I cannot create such a network. Links must be even number This will create a small world network, that is an undirected graph with N nodes and N*d/2 edges, where d is the mean edge degree. Please enter the number N of nodes you want: Now, enter an even number d. This is the mean edge degree each new node will have: Now, enter a parameter beta. This is the edge rewiring probability: Erasing any existing network. Creating small world. Please wait... Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ). Small world random network created: Small world network created. This will create a ring lattice network, where each node has degree d: d/2 edges to the right and d/2 to the left. Please enter the number of nodes you want: Ring lattice network created. No nodes present! Load a network file first or create some nodes... OK Nothing to find! Enter node label or number: Node found! Sorry. There is no such node in this network. Try again. Options (%1, %2); Node %3, with label %4, has %5 in-Links and %6 out-Links. Link from Node %1 to Node %2, has weight %3 and color %4. Link between node %1 and node %2, weight %3 and color %4. Nothing to do! Load a network file or add some nodes first. Nothing to remove. Choose a node to remove between ( Node removed completely. Ready. Nothing to link to! Create some nodes first. There are no nodes yet... This will draw a new link between two nodes. Enter source node ( Aborting. Source node accepted. Now enter target node ( Source and target nodes accepted. Please, enter the weight of new link: Ready. No links present! Load a network file or create a new network first. No links to remove - sorry. Remove link Source node: ( Target node: ( There is no such link. This link is reciprocal. Select what Direction to delete or Both... Both There are no nodes! Load a network file or create a new network first. No nodes created. Please click on a node first... Enter new node label: Changed label to %1. Ready. No label text. Abort. No nodes... Select node: ( Error. There is no such link. Change node color aborted. Cannot change nothing. Change node size to: (1-16) No links here! Load a network file or create a new network first. No links present... Select link source node: ( Select link target node: ( Change link color cancelled. User abort. There are no links here! Load a network file or create a new network first. New link Weight: input error. Abort. Change link weight Select what Direction to change or Both... New link weight: Change link weight cancelled. Weight not changed. Sorry. Nothing to filter! Load a network file or create a new network. Then ask me to compute something! Nothing to filter! All links are reciprocal. Your network is symmetric... There are node nodes yet! Load a network file or create a new network first. Then we can talk about layouts! I am really sorry. You must really load a file first... Embedding a spring-gravitational model on the network.... Click on the checkbox "Spring-Embedder" to stop movement! Movement stopped! There are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Embedding a repelling-attracting forces model on the network.... Click on the checkbox "Fruchterman-Reingold" to stop movement! Wake up! Load a network file or create a new network first. Then we can talk about layouts! Embedding node size model on the network.... You must be dreaming! Load a network file or create a new network first. Then we can talk about layouts! Sorry, I can't follow! Load a network file or create a new network first. Then we can talk about layouts! Nothing to layout! Are you dreaming? Calculating new nodes positions. Please wait... Nodes in inner circles have greater In-Degree Centrality. Load a network file or create a new network first! Nodes in inner circles have greater Out-Degree Centrality. Sorry, there are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Closeness Centrality. No nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Betweenness Centrality. Nothing to do! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Stress Centrality. Nothing to do here! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Graph Centrality. Nodes in inner circles have greater Eccentricity Centrality. Nodes in upper levels have greater In-Degree Centrality. Load a network file or create a new network first. Then we can talk about layouts! Nodes in upper levels have greater Out-Degree Centrality. Nodes in upper levels have greater Closeness Centrality. Nodes in upper levels have greater Betweenness Centrality. There are no nodes! Load a network file or create a new network. Then ask me to compute something! There is no network! Adjacency matrix symmetry = YES Adjacency matrix symmetry = NO There are no nodes. Nothing to do... Distance between two nodes Select source node: ( Select target node: ( Distance calculation operation cancelled. Distance Network distance ( The nodes are connected. The nodes are not connected. There are no nodes nor links! Load a network file or create a new network. Then ask me to compute something! Nothing to do! Creating distance matrix. Please wait... Matrix of sigmas Matrix of distances Cannot find the diameter of nothing... Diameter calculated. Ready. Average distance calculated. Ready. Window resized to (%1, %2) pixels. Nothing to do! Load a network file or create a new network. Then ask me to compute something! No network here. Sorry. Nothing to do. CLUSTERING COEFFICIENT REPORT Created: CLUSTERING COEFFICIENT (CLC) OF EACH NODE CLC range: 0 < C < 1 Range: 0 < GCLC < 1 GCLC = 0, when there are no cliques (i.e. acyclic tree). GCLC = 1, when every node and its neighborhood are complete cliques. Take weights into account (Default: No)? OUT-DEGREE CENTRALITY REPORT OUT-DEGREE CENTRALITIES (ODC) OF EACH NODE ODC range: 0 < C < ODC' range: 0 < C'< 1 GODC range: 0 < GODC < 1 GODC = 0, when all in-degrees are equal (i.e. regular lattice). GODC = 1, when one node completely dominates or overshadows the other nodes. The degree of the node is a measure of the 'activity' of the node it represents Nothing to do! Load a network file or create a new network. Then ask me to compute something! Nothing to do... IN-DEGREE CENTRALITY REPORT IN-DEGREE CENTRALITIES (IDC) OF EACH NODE IDC range: 0 < C < IDC' range: 0 < C'< 1 GIDC range: 0 < GIDC < 1 GIDC = 0, when all in-degrees are equal (i.e. regular lattice). GIDC = 1, when one node is linked from every other node. The in-degree of the node is a measure of the 'activity' of the node it represents CLOSENESS - CENTRALITY REPORT CLOSENESS CENTRALITY (CC) OF EACH NODE CC(u) is the invert sum of the distances of node u from all other nodes. CC' is the standardized CC CC range: 0 < C < CC' range: 0 < C'< 1 All nodes have the same CC value. Node has the maximum ACC value (std): has the minimum ACC value (std): There are different Closeness Centrality classes. GROUP CLOSENESS CENTRALISATION (GCC) GCC = GCC range: 0 < GCC < 1 GCC = 0, when the lengths of the geodesics are all equal (i.e. a complete or a circle graph). GCC = 1, when one node has geodesics of length 1 to all the other nodes, and the other nodes have geodesics of length 2 to the remaining (N-2) nodes. This is exactly the situation realised by a star graph. This measure focuses on how close a node is to all the other nodes in the set of nodes. The idea is that a node is central if it can quickly interact with all others Nothing to do... Please wait... Finished with shortest-path distances... BETWEENESS - CENTRALITY REPORT BETWEENESS CENTRALITY (BC) OF EACH NODE BC of a node u is the sum of delta (s,t,u) for all s,t in V Delta(s,t,u) is the ratio of all geodesics between s and t which run through u. Therefore, BC(u) reflects how often the node u lies on the geodesics between the other nodes of the network BC' is the standardized BC BC range: 0 < BC < (Number of pairs of nodes excluding i) BC' range: 0 < BC'< 1 (C' is 1 when the node falls on all geodesics) All nodes have the same BC value. Node has the maximum BC value: has the minimum BC value: different Betweenness Centrality classes. GROUP BETWEENESS CENTRALISATION (GBC) GBC = GBC range: 0 < GBC < 1 GBC = 0, when all the nodes have exactly the same betweenness index. GBC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Nothing to do! Why dont you try creating something first? STRESS CENTRALITY REPORT STRESS CENTRALITY (SC) OF EACH NODE SC(u) is the sum of sigma(s,t,u): the number of geodesics from s to t through u. SC(u) reflects the total number of geodesics between all other nodes which run through u SC range: 0 < SC < SC' range: 0 < SC'< 1 (SC'=1 when the node falls on all geodesics) All nodes have the same SC value. has the maximum SC value: has the minimum SC value: different Stress Centrality classes. GROUP STRESS CENTRALISATION (GSC) GSC = GSC range: 0 < GSC < 1 GSC = 0, when all the nodes have exactly the same stress index. GSC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Try creating a network first. Then I compute whatever you want... GRAPH - CENTRALITY REPORT GRAPH CENTRALITY (GC) OF EACH NODE GC range: 0 < GC < GC' range: 0 < GC'< 1 (GC'=1 => directly linked with all nodes) All nodes have the same GC value. has the maximum GC value: has the minimum GC value: different Graph Centrality classes. GROUP GRAPH CENTRALISATION (GGC) GGC = GGC range: 0 < GGC < 1 GGC = 0, when all the nodes have exactly the same graph index. GGC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. ECCENTRICITY- CENTRALITY REPORT ECCENTRICITY CENTRALITY (EC) OF EACH NODE EC of a node u is the largest geodesic distance (u,t) for t in V Therefore, EC(u) reflects how far, at most, is each node from every other node. EC' is the standardized EC EC range: 0 < EC < (max geodesic distance) EC' range: 0 < EC'< 1 All nodes have the same EC value. has the maximum EC value: has the minimum EC value: different eccentricity Centrality classes. GROUP ECCENTRICITY CENTRALISATION (GEC) GEC = GEC range: 0 < GEC < 1 GEC = 0, when all the nodes have exactly the same betweenness index. GEC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Eccentricity Centralities saved as: There are no nodes! Load a network file or create a new network. Errr...no nodes here. Sorry! Toggle Nodes Numbers. Please wait... Node Numbers are invisible now. Click the same option again to display them. Node Labels are visible again... There are no nodes! Load a network file or create a new network first. No nodes found. Sorry... Toggle Nodes Labels. Please wait... Node Labels are invisible now. Click the same option again to display them. Select new size for all nodes: (1-16) All shapes have been changed. Ready Change node shapes aborted... Change all nodenumbers size to: (1-16) Change font size: Aborted. Changed numbers size. Ready. There are no links! Load a network file or create a new network first. No nodes or edges found. Sorry... Toggle Edges Weights. Please wait... Edge weights are invisible now. Click the same option again to display them. Edge weights are visible again... There are no nodes nor links! Load a network file or create a new network first! No links found... Toggle Edges Arrows. Please wait... Links are invisible now. Click again the same menu to display them. Links visible again... There are no links! Load a network file or create a new network first! There are no links! Load a network file or create a new network! There are NO links here! Toggle links bezier. Please wait... Change link color aborted. Numbers' colors changed. Ready. Label colors changed. Ready. Toggle anti-aliasing. This will take some time if the network is large (>500)... Anti-aliasing off. Anti-aliasing on. Toggle progressbar... Progress bars off. Progress bars on. Debug messages off. Debug messages on. Toggle toolbar... Toolbar off. Toolbar on. Toggle statusbar... Status bar off. Status bar on. Tip Of The Day You can add a new node by double-clicking on the scene. You can add a new node by clicking on Add button. You can remove a node by clicking on Remove button. You can rotate the network by selecting a new angle on the dock. You can add a new link between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node. You can remove a node by right-clicking on it and selecting Remove. You can change background color (from the menu Edit > Colors). Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette. The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering). You can move a node easily by dragging it with your mouse. SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format. You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes You can change the label of node by right-clicking on it, and selecting Options > Change Label. All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or a link. Node information is displayed on the Status bar, when you left-click on it. Link information is displayed on the Status bar, when you left-click on it. Manual TextEditor &New Ctrl+N Create a new file &Open... Ctrl+O Open an existing file &Save Ctrl+S Save the document to disk Save &As... Save the document under a new name E&xit Ctrl+Q Exit the application Cu&t Ctrl+X Cut the current selection's contents to the clipboard &Copy Ctrl+C Copy the current selection's contents to the clipboard &Paste Ctrl+V Paste the clipboard's contents into the current selection &About Show the application's About box About &Qt Show the Qt library's About box &File &Edit &Help File Edit Ready TextEditor The document has been modified. Do you want to save your changes? Application Cannot read file %1: %2. File loaded Cannot write file %1: %2. File saved %1[*] - %2 socnetv-1.9/changelog.gz0000775000175000017500000003166712542274070015614 0ustar dimitrisdimitris8xUchangelog[rFS(;*hWL-ۉ6qj/$"Iվ>>>C q<}iYYa t銺5W+gs)N0hM93ܫu_9{;wlqkW*K,*ئ.jg%aHOۑ0:ƛ/8dҖ񳷤Řwf|trxprnXH$4-gVܫm ÓSH;;Ie2)\}g Q@}XzQFglۓ`,1ڲyiY8?6.,QOZx|آ(kiM&خ(k߽rxPTTb/G:ʸ6ZUS[Ш.]H$}۵>sW\8Wpy T`UnQ;>k0HoԈ3K}>|p?Ӡ׿oz3xHSa̳4:M~Ȗ0D&+**6 h5vx}JH(&p7˨ XyLctmhA&)"[$G;$6w3ri殀E;- naAA!N b/le_>9=Ă.H-^dἅ@khL_*t($&,jyv[@ E%ԍ3aB!dE k ҍҁS;^UaE; MX3/1oIǿ՞yM4/AGzHJKlK'5:(BdSUshD,%(>XFtoW4-DAO?)k"X֗HS!tyg6tDzb=Ծ/Mӟzp#xVi/j],#ϊۯZ wF5w*| ?ߟƵO~%"a3ӴДa o~k7ȝNB4?;^y4{5H 즲h,Igd!MC rM! !ķY|ja=R%S!'`)hj1E9|oc~K&nⱾ B[WAt]uՂpH!e!MKo. Vu%n6˒L;'Mޖߌ'G94$!#x~x>9`[YUYâϸJC0db,bM\H  6$^Ћĺ/hFȳHzʕpҔǀwWZ>m)>U BxN7msss~q,`SGZ∉[D='m!qVKzՓt;N0瘛Ι%p"Lt.ufl2П3vHW=v` < 1~BVtvwlq A !r)Lե(ްǫBWdB&&\f UxeLuXs'6:6(h9t1dIV1"(siƯne0ZeeO*ABpӥPh1;q^xK>t`3"%u NQY vjry=C%`tƙtuC=I4 Q{^+t 5U\ i&ӣ ָS6)>>@zJ?@{(TWň\1bJLx xd';=s{\t J^hyv;0*jWB4=>&EZaݘ@ׇ/P߀x "!QnnyI384M Dietաe[yžg:.|2j$ZGCiU|wJךdy4K7`,ڏ!=haαQ6<̒D8bţHܔf;Y6RU=`8<>;9=3ˍ"e13Y_f~EzlZWeY/7L̵\SR5tX?0M>9~wBK&_,NJdrܛk,AdatMW[g.'wElt<98$J6{;6D'A2;wlw"ETCIwAa0]YǓ51m\"%;g':YOcGÍY3VRRbhiBkW;D.պhЧgG'RI!/Z3k)+1BDjkRPtiC/XAj /.v9:0ϯ^09f%{1fqciukrBB73 ӸC>"!neƏkt1nBkm'QYԉW,tyG*3&i1GgA]/ArOf>§6‡GGB*;C&&1=/Hp386-"ͺu[;Y\\C-b(ThaY-=GWAw&ehͩL$coMHkC ԥ$xa+޾ ,rZ>t2 ZpHF S4>aQNu12AIs޸Tp.X4ђY Ni  nڧ2Tnn΋I:77t.핲+3~.k||ͫW7]Sj #h6I޺eCNl"GgPz2"lΞ7s39{ gכHZ͘ǛutY" -X 72ƅz!ӲkWr& 4fbf;h(&$|:ƕ1نoSgؠCPRF&{\-mP"eOv88Vw"bڬ Zsdz)\0_ b:l$őQ,M4;.U 6%h7P&> <%륛 ؇]z}~Chn l>!IeF=~PjvyV闟PCk^ֻmf p6рv6eehWo8'{&DW߫"˵yvr+al>8\ӻvչiԥXf:]nwE01(o{THUrYA a3zRD@_5{fp%&!:.j HSނ+rW݇kk4d$pPo#p^/CɢH>v]c!_%G7L\![8%[+) =`7!3}`gӒ **A b'6w zDXnW&uja I!)R9 dÐ9aھ-ٷ=Od54p8Kg)C | Aq|a8eS&ZHQ>)\iKv9UTiL„2>D@N^v݊wwEYā9SL(Ibޝ׋XeyL^^`):w`?S;,,\n4#[:"M;]{P.5E2{7z =[I}zjT6FM0o_ʣwg+ç++A )joZsG OY!$*ɝrVzWYWH4 )~txlZIoEI, ʏzqH5p,ыRH8qN02Qa:o!^}0puhPvҲoٸx~-qvKz6J|$%S6|0Y,rmff~,S|+*SY%xm#{xoK_e뾽g3JiYrkXi]bdjʼ.GHo~( Rց[3xH"̌y2_o˂5.CSK5ӵ4ҹU?k& nص2hn],t=TS.z/çBɋ`zhOrBodF}Z0?PU/?A$E2-tanhzz TȮw&uCSXV+h nGrq5p;KtޮK-8v bOġgkQ)LE:p8Т43N?Mi- Ht$yI~ξ[DOҭog,z4ҴS0<% 1D*DQ#q9] mhQ!D$?4+ˊUNazB$?j UyNFYmLk,\(j3D{ʛ05}q},8YLK[ pfhSEkuGRܕZ|9NAj=I*+^^H92Y9pQAX^Txs,yJjJbf45v]euH$_im}_֋zqۿw\m,ʣrj}Kgֿdʿ%>p{ڴ=6{XޑW}epV˻Qs$wU\* Bqz8 n/{iU}uƫetXY>]:Zyۉyyi(V;aw%U`hB>}9x6u>ꔈ `ū/V⠵M;n>elϺb$ruĄeL_ J6oLKVWJfaXR.Uz3W `oWNG=em,_hZc,$ϫKH>lmv/l)Oae?lU(X)M*O1FJl*yB|X.2 {sܦy'pQ60.3;VQ*F>0glMsOBa5\؁dXo~(vZޭw3ѫb['tUXFl EΑs ޟ%4;Ma=$Z0bo(;ϔ8- _\-W9MHL mkPݪz;ª+k*;גo}@N2H< ƥ5LV2 FadZ@NS,f?|TϢ4%U5VD'λ7ק_9i4-؝NQϙVER\̬{|iZv>oVX~}!7 wIi*k $T|$]!4AF7Xk߱{SgQm˞x]BR&TaOlfr!&bNLwirfQG4ɿɗXE4bC}cE`}4bR=O,Gn-,l~4Hv_ȟٗ#uc$C'$9$4ҝ/u=9gIߢ:tGA s!HߦG$^cAY=)rhVK6>MHjrt}o\hcW[uÇڑWǏP(d;r恭%R0jNG0̒I>4R˻xx;J[X{޵B:Y3<  6) ^h#$ j5QVvBQcRÖ &c1"RAv(< EKzbR8sh0f^>-s!e$2flƸ(=-HsQꮗ̐1rM5"oI)3;٬0g#1H#v*8ar $HOhP=K n.d\Q8-}pU5RTr&ljV5/=G)0\KN_#;EIvVoʋ_qG,ק[aBJ$n]$Gȹ^sFH/l =SN67J!ɕ@{Tm;=GH:4g9MfEƝ5SӲx @Vʈw<rK}du54b:^`ALe0DudvM2t-OghP>p *ytg'mCx JXexP]c,~&Ô) (Xs.޹5Wߴ_zrP0 #u 5ު6o^[Ӝ}κ^~!-rOv.]pNb\pS[ߘB-QqE$S [ ~ wJLG}[)˵%9wyͦF-sT]woHuf9sr&R4ptDYo001LԌ_˵#OD5IM;A³B6v!NkI":X#Rmu$pڙ|SˬxUib_eCXGԀ%`53U>yrǴuY0#錽D_ib1"77i>]…%01e#oҼ,؆.ܺX9s#j3>(F|p~lբ? ] "lꭗU8˻Vexy'dHJcAǐ)ne[2ԚC lwV8Jʇ6 αtzCwW4ay#fhnp:6G~?9;t'p~r|WWq1si6̓ŀ\L\Xѻ<.Q_U'3C1^ +wx̾5iz|(qK9Sv^cU!plT"+mƱ/ 3 ]wzF[*YCK"|Q顇wC])"@2߄ B8E9L$L0KBp#=)<*\>OZ\k9r:ŋQS4kzk!C5V:x0|*M.+PB㱬G0~<4uJvAS.,+Tj4pSWP]yn9U8D~p%8nH6#D^G \)YQ#?}reC¾-8 ӡ4[qd&KP.*2d ׽tG'חu~w?k%Eu/gOK__^]ECQm8=":"9g|S&3xcz4-Hr{.|& F=:yoDY0&٪2]vp·vT6wPPXy4נkè%/tYpye<䖯anаpE+Rbu0 !Αα!}V4% #I8O{ HeC G$WY!O yAt+UbQόgE[-4b0A%+"$\p4בp.`oH=l{iYIr<(Ðc6$}'+6}?]1V&I<'T2;E]uc(o#QprC^C<[Rq掓 GRE>o*9HRm~wM>fBHOosV$-Cjg!MgHπ OT-JdjK?mHW or>v-#9[]?9FWS*AW3@<:6"GdP3,W5W8 vt8!"|RBpZ{g2dNq7`eh3yӯq"8ļK}n!X4Xrʤ2NV y6-Ժȕ@ ,yn$MI"Zz]MRsج`|e0Lݨ5eXj:IeSJ~ azCV <$(`: Sd#5\D# O tq=rgI(]z  7uSQdOɛh$b`<9yH9z,B RSFrš#D[9-OdMV) váhWBk4۰J%=ؤfBh?3.F[y1p4c|BY0 H `j%3P8>pcASG}(n@,#n80_|)4L!d?f:įr]aGwCdBĸׁ*C`ǁƞSB ׯVվgŒ 5SS$b*F㾔:ZJHxsjK6F)"G$iJ^2nmlV3Շsocnetv-1.9/socnetv.pro0000775000175000017500000000611012534331160015502 0ustar dimitrisdimitrislessThan(QT_VERSION, 5.0) { error("SocNetV requires at least Qt 5.0!") } # START added for ArchLinux / openSUSE compatibility INSTALLPATH = / target.path = $$[INSTALLPATH]usr/bin TARGET = socnetv pixmap.path = $$[INSTALLPATH]usr/share/pixmaps pixmap.files = src/images/socnetv.png documentation.path = $$[INSTALLPATH]usr/share/doc/socnetv documentation.files = manual manpage.path = $$[INSTALLPATH]usr/share/man/man1 manpage.files = man/socnetv.1.gz translations.path = $$[INSTALLPATH]usr/share/socnetv translations.files = translations doc.path = $$[INSTALLPATH]usr/share/doc/socnetv doc.files = license changelog.gz NEWS README TODO COPYING AUTHORS INSTALL INSTALLS += target pixmap documentation manpage translations doc # END TEMPLATE = app CONFIG += qt thread warn_on release #CONFIG += qt thread warn_on release debug LANGUAGE = C++ # support QT += xml QT += network QT += widgets QT += printsupport INCLUDEPATH += ./src FORMS += src/forms/filteredgesbyweightdialog.ui \ src/forms/webcrawlerdialog.ui \ src/forms/nodeeditdialog.ui \ src/forms/datasetselectdialog.ui \ src/forms/randsmallworlddialog.ui \ src/forms/randscalefreedialog.ui \ src/forms/randerdosrenyidialog.ui HEADERS += src/guide.h \ src/graphicswidget.h \ src/edge.h \ src/edgeweight.h \ src/graph.h \ src/mainwindow.h \ src/matrix.h \ src/node.h \ src/nodelabel.h \ src/nodenumber.h \ src/texteditor.h \ src/vertex.h \ src/parser.h \ src/filteredgesbyweightdialog.h \ src/webcrawlerdialog.h \ src/webcrawler.h \ src/datasetselectdialog.h \ src/previewform.h \ src/nodeeditdialog.h \ src/randerdosrenyidialog.h \ src/randsmallworlddialog.h \ src/randscalefreedialog.h SOURCES += src/guide.cpp \ src/graphicswidget.cpp \ src/edge.cpp \ src/edgeweight.cpp \ src/graph.cpp \ src/main.cpp \ src/mainwindow.cpp \ src/matrix.cpp \ src/node.cpp \ src/nodelabel.cpp \ src/nodenumber.cpp \ src/texteditor.cpp \ src/vertex.cpp \ src/parser.cpp \ src/filteredgesbyweightdialog.cpp \ src/webcrawlerdialog.cpp \ src/webcrawler.cpp \ src/datasetselectdialog.cpp \ src/previewform.cpp \ src/nodeeditdialog.cpp \ src/randerdosrenyidialog.cpp \ src/randsmallworlddialog.cpp \ src/randscalefreedialog.cpp # Extra optimization flags win32 { QMAKE_CXXFLAGS += -msse -mfpmath=sse -ffast-math } unix:!macx{ QMAKE_CXXFLAGS += -ffast-math } macx { QMAKE_CXXFLAGS += -msse -ffast-math } INCLUDEPATH += /usr/local/include /usr/include /usr/include/qt5 /usr/share/qt5/include INCLUDEPATH += /usr/local/include /usr/include /usr/include/qt /usr/include/qt5 /usr/share/qt5/include RESOURCES = src/src.qrc win32 { RC_FILE = src/icon.rc } macx:ICON = src/images/socnetv.icns TRANSLATIONS = translations/socnetv_es.ts \ translations/socnetv_el.ts socnetv-1.9/TODO0000664000175000017500000000136412525314416013777 0ustar dimitrisdimitrisKNOWN BUGS ========== 1: Negative weights break centralities 2: UCINET (DL) files in nodelist1 format (one-mode) are not supported. 3: isSymmetric() not 100% (check a-symmetry.net) 4: Some Graphviz (dot) files do not load. See test2.dot. Cant read when one line and nodes dont appear... 7: SocNetV cannot load GraphML files with node labels which contain the & character. 8: 2014-02-28: In Reciprocal links with diferrent weight, only the first weight is taken into account 9: closeness reports wrong indices in digraphs, where one actor has only one outLink 10: fix other related indices for digraphs when d(i,j) = inf TODO ==== To request a feature and/or see the TODO list of SocNetV, visit : https://blueprints.launchpad.net/socnetv socnetv-1.9/socnetv.spec0000664000175000017500000001661312542300240015634 0ustar dimitrisdimitris# spec file for package socnetv # # Copyright (c) 2014 Dimitris Kalamaras dimitris.kalamaras@gmail.com # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ %define name socnetv %define version 1.9 %define release 1 %define prefix /usr/local %define lastrev %(LANG=en_US.UTF-8 && date +"%a %b %e %Y") %define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) %define is_fedora %(test -e /etc/fedora-release && echo 1 || echo 0) %define qmake qmake %define lrelease lrelease #BEGIN BUILDSERVICE COMMANDS %if 0%{?fedora_version} %define is_suse 0 %define is_fedora 1 %endif %if 0%{?suse_version} %define is_suse 1 %define is_fedora 0 %endif #END BUILDSERVICE COMMANDS %if %{is_fedora} %define distr Fedora %define breqr qt5-qtbase,qt5-qtbase-devel, qt5-qttools, qt5-qtwebkit, qt5-qtwebkit-devel, fedora-release, desktop-file-utils %define qmake /usr/bin/qmake-qt5 %define lrelease /usr/bin/lrelease %endif %if %{is_suse} %define distr SUSE # %(head -1 /etc/SuSE-release) %define breqr libqt5-qtbase, libqt5-qtbase-devel, libqt5-qttools, libQt5WebKit5, libQt5WebKit5-devel, update-desktop-files %define qmake /usr/bin/qmake-qt5 %define lrelease /usr/bin/lrelease %endif Name: %{name} Version: %{version} Release: %{release} Summary: A Social Networks Analyser and Visualiser License: GPL-3.0 Group: Productivity/Scientific/Math URL: http://socnetv.sourceforge.net/ Vendor: Dimitris V. Kalamaras #Packager: Dimitris V. Kalamaras # Removed for OBS warnings... Source0: SocNetV-%{version}.tar.bz2 Distribution: %{distr} Prefix: %{prefix} BuildRequires: gcc-c++, %{breqr} BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Gui) BuildRequires: pkgconfig(Qt5PrintSupport) BuildRequires: pkgconfig(Qt5Widgets) BuildRequires: pkgconfig(Qt5Network) #BuildRequires: pkgconfig(Qt5WebKitWidgets) BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot # #DESCRIPTION SECTION # %description SocNetV (Social Network Visualiser) is a flexible and user-friendly tool for Social Networks Analysis and Visualisation. It lets you create new networks (graphs) with a few clicks on a virtual canvas or load networks of various formats (GraphViz, GraphML, Adjacency, Pajek, etc) and modify them to suit your needs. The application can compute network properties, such as density, diameter and distances, as well as node and network centralities. Various layout algorithms (i.e. Spring-embedder, circular and in levels according to centralities) are supported for meaningful visualisations of your networks. Furthermore, simple random networks (lattice, same degree, etc) can be created. Author: Dimitris V. Kalamaras # #PREPARATION SECTION # %prep %setup chmod -R a-x+X COPYING changelog.gz INSTALL NEWS README TODO manual man nets src chmod 644 nets/* find . -type f -name '*~' -delete find . -type f -name '*.bak' -delete rm -f config.log config.status Makefile socnetv.spec socnetv.mak sed -i -e 's/INSTALLPATH = \//INSTALLPATH = ./g' socnetv.pro # #MAKE SECTION # %build %{qmake} %__make # #INSTALL SECTION # %install %if %{is_fedora} desktop-file-validate %{name}.desktop #desktop-file-install --add-category="Math" --delete-original --dir=%{buildroot}%{_datadir}/applications %{buildroot}/%{_datadir}/applnk/Edutainment/%{name}.desktop %endif %makeinstall mkdir -p %{buildroot}%{_bindir} mkdir -p %{buildroot}%{_datadir}/pixmaps/ mkdir -p %{buildroot}%{_datadir}/applications/ mkdir -p %{buildroot}%{_mandir}/man1/ cp -r socnetv %{buildroot}%{_bindir}/%{name} cp -r src/images/socnetv.png %{buildroot}%{_datadir}/pixmaps/%{name}.png cp -r socnetv.desktop %{buildroot}%{_datadir}/applications/ cp -r man/socnetv.1.gz %{buildroot}%{_mandir}/man1 rm -rf %{buildroot}/%{_datadir}/doc/%{name} %clean [ -d %{buildroot} -a "%{buildroot}" != "" ] && %__rm -rf %{buildroot} # #FILES SECTION # %files %defattr(-,root,root) %{_bindir}/%{name} %{_datadir}/applications/%{name}.desktop %{_datadir}/pixmaps/%{name}.png %{_mandir}/man1/* %doc changelog.gz NEWS README TODO COPYING AUTHORS INSTALL manual # #CHANGELOG SECTION # %changelog * Tue Jun 23 2015 Dimitris Kalamaras - 1.9-1 - Synced with DEV version from upstream. * Fri Jun 05 2015 Dimitris Kalamaras - 1.8-1 - Synced with new stable version from upstream. * Wed May 20 2015 Dimitris Kalamaras - 1.7-1 - Synced with new stable version from upstream. * Mon May 11 2015 Dimitris Kalamaras - 1.6-1 - Synced with new stable version from upstream. * Fri Oct 10 2014 Dimitris Kalamaras - 1.5-1 - Synced with new stable version from upstream. * Mon Sep 01 2014 Dimitris Kalamaras - 1.4-1 - Synced with new stable version from upstream. * Wed Aug 27 2014 Dimitris Kalamaras - 1.3-1 - Synced with new stable version from upstream. * Mon Aug 18 2014 Dimitris Kalamaras - 1.2-1 - Synced with new stable version 1.2 from upstream. * Fri Aug 01 2014 Dimitris Kalamaras - 1.1-1 - Synced with new version from upstream. * Thu Feb 27 2014 Dimitris Kalamaras - 1.0-2 - Fixed spec for openSUSE * Thu Feb 27 2014 Dimitris Kalamaras - 1.0-1 - Synced with new version from upstream. * Thu Oct 14 2010 Dimitris Kalamaras - 0.90-1 - Synced with upstream. * Thu Jan 28 2010 Dimitris Kalamaras - 0.81-1 - Synced with upstream. - Bugfixes for Windows version * Sat Jan 09 2010 Dimitris Kalamaras - 0.80-1 - Synced with upstream, * Mon Jun 29 2009 Dimitris Kalamaras - 0.70-1 - Synced with upstream * Wed May 27 2009 Dimitris Kalamaras - 0.6.0-1 - Synced with upstream * Thu Feb 26 2009 Dimitris Kalamaras - 0.52-1 - Synced with upstream. - Bugfixes into .spec.in for RPMs (Fedora, openSUSE and Mandriva). * Tue Feb 17 2009 Dimitris Kalamaras - 0.51-3 - Bugfixes into .spec.in for Fedora and Mandriva. - RPM for Fedora * Mon Feb 16 2009 Dimitris Kalamaras - 0.51-2 - Minor changes to RPM * Mon Feb 16 2009 Dimitris Kalamaras - 0.51-1 - Updated to upstream version 0.51 * Fri Feb 13 2009 Dimitris Kalamaras - 0.50-1 - Updated to upstream version 0.50 * Wed Jan 14 2009 Dimitris Kalamaras - 0.49-2 - Package .spec fixes * Tue Jan 13 2009 Dimitris Kalamaras - 0.49-1 - Updated to 0.49 * Wed Sep 17 2008 Dimitris Kalamaras - 0.48-1 - First RPM release socnetv-1.9/NEWS0000664000175000017500000001374112542274070014010 0ustar dimitrisdimitrisSocial Networks Visualiser (SocNetV) SocNetV News -==========- June 2015 ========= - Version 1.9 released with lots of bugfixes and a faster matrix inverse routine using LU decomposition. Also Information Centrality is greatly improved in terms of computation speed. PageRank Prestige algorithm corrected to compute PR using the correct formula. The initial PR score of each node is now 1/N. Bugs closed: #1463069 wrong average distance when there are isolates #1365037 certain sparse matrices crash socnetv on invertMatrix method #1365582 centralityInformation() is slow when network N>100 #1463095 edge filter works but the user cannot undo #1464422 wrong pagerank results #1464430 socnetv refuses to read pajek files not starting with *Network #1465774 edges do not always follow relations #1463082 edge color change is not taking place #1464418 socnetv crashes on pagerank computation on isolated nodes - Version 1.8 released with the following new features: New clique census routine to compute maximal cliques with up to 4 vertices. New Scale-free random generation methods. Improved Erdos-Renyi generation to include G(n,M) model. Fixed bug in Clustering Coefficient - SocNetV now computes CluCof correctly in all cases. New improved dialogs for easy random network generation (Scale-free, Erdos-Renyi, and Small-World) Fixed bug in Node Properties dialog. It is now populated with current node settings. May 2015 ======== - Version 1.7 released. New node group select/edit functionality and file previewer supporting different codecs - Version 1.6 released. New and improved web crawler functionality. See Changelog for more. Oct 2014 ======== - Version 1.5 released. First version with dijkstra algorithm for the SSSP in weighted nets. See Changelog for more. Sep 2014 ======== - Version 1.4 released. Brought new layout type (nodal size by prominence index), edgelist1 UCINET format import method and many bugfixes. Aug 2014 ======== - Version 1.3 released. - First time SocNetV works with multigraphs Aug 2014 ======= - Version 1.2 released. It features a major GUI overhaul and brings in a new "prominence indices" conceptualization based on Wasserman & Faust. In general, Centrality indices focus on outLinks (choices given) while Prestige indices consider inLinks (choices received). Added 3 Prestige indices (Degree, Proximity and PageRank), new reachability measures (Walks, Connectedness, and Reachability Matrix) and fixed a slew of bugs in indices calculation. All algorithms are now tested to report 100% correct results. - Version 1.1 released with major bug fixes. See ChangeLog. - First time distribution of a disk image for installation in Mac OS X Feb 2014 ======== - Version 1.0 released, starting a new 1.x series based on Qt5. The 0.x series is no longer maintained. Please upgrade :) - PageRank calculation and layout - SRS Documentation by Vagelis Motesnitsalis July 2013 ======== - Moved project code to git/BB - Started development for Qt5 Oct 2010 ======== - Version 0.90 released - New Power & Information Centralities Jan 2010 ======== - Version 0.80 - New List import feature - New Triad Census feature - Various Bug Fixes June 2009 ========= - Version 0.70 - First web crawler implementation May 2009 ======== - Version 0.6 (release) - GraphML becomes native SocNetV load format Feb 2009 ======= - Version 0.51 (bugfix release) - Version 0.50 (released) - Small world creation - Clustering coefficient - Exporting to PDF - Printing works OK. Jan 2009 ======== - Version 0.49 (released) - Ubuntu repository created. Sep 2008 ======== - New logo - New openSUSE package repo. - Version 0.48 released - Version 0.47 released - Version 0.46 released. Lots of bugfixes. New features: - Node sizes may reflect degree. Aug 2008 ======== - New Debian Package - Version 0.45 released. New features: - GraphML initial support. - New man page and updated online documentation. - HtmlViewer renders online help with the help of QtWebKit (openSUSE: libQtWebKit-devel) - New widget for network rotation. - New widget for zooming replaces the old one. - Nodes may have 4 different shapes: circles, diamonds, triangles, boxes and ellipses are supported. - There was a bug in Qt 4.3 QGraphicsView causing redraw delays. Is fixed in Qt 4.4 :) - Cosmetic changes, i.e. new icons, new layout for the left dock. - Code clean-up in MainWindows Class and Matrix. - Deleted obsolete members and functions such as nodeExists(), mousePosGW(), Dijkstra, etc. - Bug-fixes on loading Pajek networks and layout algorithm. May 2008 ======== - Version 0.44 released one year after v.0.43. New features: Ported to Qt4: Code rewritten almost from scratch. Splitted MainWindow/GUI from algorithms via a new Graph Class. Improved GUI with docks. Network zooming via mouse wheel. Spring Embedder: Dynamic network reallocation Thread support. Much faster calculation of distances and centralities (BFS/dijkstra). Betweenness centrality now is much more efficiently calculated. Changed license to GPL3 Layout in circles and levels by centrality. Better graphics and antialiasing (disabled - enable by pressing F8). New centrality index: Eccentricity. Sep 2006 ======== - version 0.43 released with new layout features. June 2006 ========= - version 0.42 released with updated help files. May 2006 ======== - I did some work on the webpages at http://socnetv.sourceforge.net. Hope it is better now. April 2006 ========== March 2006 ========== - version 0.41 released. February 2006 ------------- - version 0.40 released. Efforts to be a pretty trustworthy release. - sourceforge project downloads are more than enough daily, but there is no feedback yet for versions 0.38 and 0.39. - version 0.39 released. Somewhat rushed release. - constant changes in the homepage. - updated links in www.insna.org January 2006 ------------ - version 0.38 released after one year of silence. - The project moved to sourceforge.net - The homepage is http://socnetv.sourceforge.net socnetv-1.9/man/0000775000175000017500000000000012542300363014051 5ustar dimitrisdimitrissocnetv-1.9/man/socnetv.1.gz0000664000175000017500000000432612542300240016232 0ustar dimitrisdimitrisUsocnetv.1Xko8_q`c+bfPyY;I7|%Zb{)N:>}6Z:I[?xDG 8>~I_FY@&ʚ [\`'iӤwHRN\< O4Ȧ2VgXa?ƏE`\ׇݡ|8Ov;d)ʽ aXWW#jqfFׅݒjkr S\vC$q+z9HV@[R}\t\)_PdY!mL\üPpEa͓u /B!E>o#__nF4)TԟyDۋųfe+Bk"{K! |`ܫ6jZ`*jkF+P`"FU:"00!:װ::Dj\F|w+aB7 J@H$(]Ҷ G6lLg@]kKo T %իɑ&!,\ςy"`ljXUM*~4Vx:־qz|6}gD[du=έN^*B!٫AbQh|p7J|"_'Jk%:LiSJ5aF0fZ,l)quVpFy@t֎hX?[]iPD+`PQ.Jg/\B#,FcU'^WETd~'ghf(y(j+,h$\#:J5񥃵ա/ eP .;0Џ]_ m:d: .K9(a=!f7{ 'ߋʗ:O,^FިTŁO&qTI'23#u.ƫlkT[qkLLHsv)椻Z%;7b }1>֪\1T%^H(JsL7QW֚&65&\*\}d: `3a^ݐ(ž5/؎zᎆ[&ke D  Fo^ƈ>SjmL,)Zds>uscV0dAgtZ0X⾰C%z~;6Þ`(2E]f, /ظu@xp/Js/ex3:%p-·:}S<@*cM`a}a%h7ad~nfZħ xv1q{ա_vB.&J!warE-}zZP}pv`4LC#~7[ |"Jw$,Zh5J.&GB)4Ljf;ɀ~6{%~;30]u:)ſ/D.&׳9KePׁ~WVU Ǣ[˞J3 pi~q7/sYPΎuM䄿3:_7cp=7Sϵ2TGm9HPBp|_ yh!PƢYia2G.Mr4RC)͖k`'MK*_"v/&c*s:E4[пE707ׯ:o#Y:UVHćD;z{~Aixȹ5\t۟V3: +IwwSӫ!:5DQ]qFF#4:bpF_{T}6MVm؜$*ֱBA{f;+tDqcj@ aDJsqALC `_Ҭusocnetv-1.9/nets/0000775000175000017500000000000012542300363014247 5ustar dimitrisdimitrissocnetv-1.9/nets/Freemans_EIES-1_n48.lst0000644000175000017500000001226012410526714020172 0ustar dimitrisdimitris1 2 4 1 3 2 1 6 2 1 8 2 1 10 2 1 11 2 1 13 2 1 14 2 1 18 2 1 19 2 1 20 2 1 21 2 1 22 2 1 23 2 1 24 2 1 25 2 1 26 3 1 27 2 1 31 2 1 32 2 1 33 2 1 35 2 1 36 2 1 37 2 1 38 2 1 39 3 1 40 2 1 41 2 1 42 2 1 43 2 1 44 4 1 45 2 1 46 2 2 1 4 2 3 2 2 8 1 2 11 3 2 13 3 2 14 4 2 18 1 2 19 3 2 21 2 2 22 2 2 23 2 2 24 3 2 25 2 2 27 1 2 32 2 2 33 3 2 35 2 2 37 2 2 40 2 2 41 1 2 42 2 2 43 3 2 44 4 2 45 4 2 46 2 3 1 3 3 2 1 3 6 4 3 8 1 3 13 2 3 18 2 3 19 4 3 20 4 3 22 4 3 23 1 3 24 2 3 25 2 3 26 2 3 27 1 3 31 1 3 32 2 3 33 2 3 35 2 3 36 4 3 37 2 3 39 2 3 41 1 3 42 1 3 43 1 6 1 2 6 3 2 6 8 2 6 13 2 6 14 2 6 18 2 6 19 2 6 20 2 6 21 2 6 22 2 6 23 2 6 24 1 6 27 4 6 31 1 6 32 2 6 33 2 6 35 2 6 36 2 6 37 2 6 38 2 6 40 2 6 41 2 6 42 2 6 44 2 8 1 3 8 6 2 8 13 2 8 14 3 8 18 2 8 19 2 8 20 1 8 22 2 8 23 1 8 24 2 8 25 2 8 27 1 8 32 2 8 33 2 8 35 2 8 37 2 8 38 1 8 40 1 8 41 2 8 42 2 8 44 2 8 45 2 10 1 3 10 13 2 10 22 2 10 24 1 10 27 2 10 33 1 10 40 2 10 42 2 10 44 2 11 1 3 11 2 2 11 3 1 11 13 2 11 14 2 11 19 1 11 21 3 11 41 2 13 1 2 13 2 2 13 3 2 13 6 2 13 8 2 13 14 1 13 19 2 13 21 2 13 22 2 13 23 2 13 24 2 13 25 2 13 27 1 13 32 2 13 33 2 13 35 1 13 36 1 13 37 2 13 38 2 13 40 2 13 42 2 13 43 2 14 1 3 14 2 4 14 8 2 14 13 2 14 19 1 14 21 2 14 22 1 14 33 1 14 35 3 14 40 3 14 45 4 18 1 2 18 2 1 18 3 3 18 6 3 18 8 2 18 11 1 18 13 2 18 14 2 18 19 2 18 20 3 18 22 1 18 23 2 18 24 2 18 25 2 18 27 2 18 31 2 18 32 3 18 33 2 18 35 2 18 36 4 18 37 2 18 38 2 18 41 2 18 42 2 18 43 2 19 1 1 19 2 3 19 3 2 19 6 1 19 8 1 19 13 3 19 14 1 19 18 1 19 22 2 19 23 1 19 24 2 19 25 2 19 27 1 19 31 2 19 32 2 19 33 2 19 35 2 19 36 1 19 37 2 19 38 2 19 40 2 19 41 1 19 42 1 19 44 1 20 1 1 20 3 1 20 6 2 20 13 1 20 18 3 20 22 2 20 24 1 20 27 2 20 32 2 20 33 2 20 35 2 20 38 2 20 42 2 20 43 2 21 1 3 21 2 3 21 3 1 21 6 2 21 8 1 21 11 3 21 13 3 21 14 2 21 18 1 21 19 1 21 22 1 21 23 1 21 24 1 21 27 2 21 31 1 21 32 1 21 33 1 21 35 1 21 36 1 21 39 2 21 40 4 21 41 2 21 42 2 21 43 2 21 44 3 21 45 3 22 1 3 22 2 2 22 3 4 22 6 2 22 8 3 22 13 3 22 14 2 22 18 1 22 19 2 22 20 3 22 21 1 22 23 3 22 24 4 22 25 3 22 26 2 22 27 3 22 31 2 22 32 3 22 33 3 22 35 4 22 36 3 22 37 3 22 38 3 22 39 2 22 40 1 22 41 2 22 42 4 22 43 3 22 44 2 22 46 1 23 1 3 23 2 2 23 3 2 23 6 3 23 8 1 23 11 1 23 13 2 23 14 2 23 18 2 23 19 2 23 20 1 23 22 3 23 24 2 23 25 2 23 27 2 23 31 4 23 32 1 23 33 2 23 35 1 23 36 2 23 37 2 23 38 2 23 42 3 23 44 2 23 46 1 24 1 2 24 2 2 24 3 2 24 6 1 24 8 3 24 13 3 24 14 1 24 19 2 24 22 3 24 23 2 24 25 3 24 27 1 24 31 2 24 32 2 24 33 4 24 35 3 24 37 3 24 38 2 24 42 2 25 1 3 25 2 2 25 3 3 25 8 2 25 13 3 25 14 2 25 18 1 25 19 2 25 22 3 25 23 2 25 24 2 25 27 1 25 32 3 25 33 3 25 35 3 25 37 2 25 41 1 25 42 1 25 44 2 25 46 1 26 1 4 26 2 1 26 3 2 26 19 2 26 22 2 26 23 1 26 27 1 26 37 1 26 39 2 26 40 2 26 41 1 26 42 2 26 43 2 26 44 4 26 46 2 27 1 2 27 3 2 27 6 4 27 8 1 27 13 2 27 18 2 27 20 2 27 22 2 27 23 2 27 24 1 27 32 1 27 33 2 27 35 3 27 36 2 27 37 2 27 38 2 27 39 2 27 41 2 27 42 2 27 43 1 27 44 2 27 46 2 31 1 1 31 3 2 31 6 1 31 8 1 31 18 2 31 19 2 31 20 2 31 22 2 31 23 2 31 24 2 31 32 1 31 35 3 31 36 1 31 37 3 31 38 2 31 42 1 32 1 2 32 2 2 32 3 2 32 6 2 32 8 2 32 13 2 32 18 3 32 19 2 32 20 2 32 22 3 32 23 1 32 24 2 32 25 2 32 27 2 32 31 1 32 33 3 32 35 4 32 36 2 32 37 3 32 38 3 32 41 2 32 42 3 32 43 1 33 1 3 33 2 3 33 3 2 33 6 2 33 8 2 33 13 3 33 14 1 33 18 2 33 19 3 33 20 2 33 22 2 33 23 3 33 24 4 33 25 3 33 27 2 33 31 2 33 32 2 33 35 3 33 36 2 33 37 2 33 38 3 33 40 1 33 41 2 33 42 2 33 43 1 33 45 1 35 1 2 35 2 2 35 3 2 35 6 3 35 13 2 35 14 3 35 18 2 35 19 2 35 22 3 35 24 3 35 25 2 35 27 3 35 32 3 35 33 3 35 37 4 35 38 2 35 41 2 35 42 4 36 1 2 36 3 4 36 6 3 36 18 4 36 20 1 36 22 2 36 23 1 36 24 1 36 27 2 36 31 1 36 32 2 36 33 2 36 35 1 36 37 1 36 38 2 36 41 1 36 42 2 37 1 2 37 2 2 37 3 2 37 6 2 37 8 2 37 13 3 37 14 2 37 18 2 37 19 2 37 20 2 37 22 3 37 23 2 37 24 3 37 25 2 37 27 3 37 31 4 37 32 3 37 33 3 37 35 4 37 36 2 37 38 3 37 40 2 37 41 2 37 42 4 38 1 2 38 2 2 38 3 2 38 6 2 38 8 1 38 13 2 38 18 3 38 19 2 38 20 2 38 22 3 38 23 2 38 24 3 38 27 2 38 31 2 38 32 4 38 33 3 38 35 3 38 36 3 38 37 4 38 41 1 39 1 4 39 2 1 39 3 2 39 6 1 39 8 1 39 11 1 39 13 1 39 18 1 39 19 1 39 20 1 39 21 2 39 22 2 39 23 1 39 24 1 39 26 3 39 27 2 39 32 1 39 33 1 39 35 2 39 36 1 39 37 2 39 38 1 39 41 2 39 42 2 39 44 3 39 46 1 40 1 2 40 2 2 40 3 1 40 6 2 40 8 1 40 13 2 40 14 2 40 18 1 40 19 1 40 21 4 40 22 1 40 23 1 40 24 1 40 25 1 40 27 1 40 32 1 40 33 1 40 43 2 41 1 3 41 2 2 41 6 3 41 18 1 41 19 1 41 21 1 41 22 2 41 23 2 41 24 2 41 27 3 41 32 2 41 33 2 41 35 3 41 37 2 41 38 1 41 39 2 41 40 1 41 42 2 41 44 2 42 1 2 42 2 2 42 3 2 42 6 2 42 8 2 42 13 2 42 18 2 42 20 2 42 22 3 42 23 2 42 24 2 42 26 2 42 27 2 42 32 2 42 33 2 42 35 4 42 36 2 42 37 3 42 39 2 42 40 2 42 41 2 42 43 2 42 44 2 42 46 3 43 1 3 43 2 4 43 3 1 43 13 4 43 18 2 43 21 2 43 22 2 43 24 2 43 25 2 43 26 2 43 27 2 43 32 2 43 33 2 43 35 2 43 40 2 43 41 1 43 42 2 43 45 2 43 46 1 44 1 4 44 2 4 44 3 2 44 6 2 44 8 2 44 10 2 44 11 1 44 13 2 44 14 2 44 19 2 44 21 2 44 22 2 44 23 2 44 24 1 44 25 2 44 26 3 44 27 2 44 33 1 44 35 2 44 36 2 44 37 2 44 39 2 44 40 2 44 41 2 44 42 2 44 43 2 44 46 1 45 1 3 45 2 3 45 6 1 45 8 2 45 13 3 45 14 4 45 19 1 45 21 2 45 22 1 45 24 1 45 27 1 45 32 1 45 33 1 45 40 2 45 43 3 45 44 3 45 46 1 46 1 2 46 2 2 46 42 3socnetv-1.9/nets/11actors-pajek-matrix.adj0000644000175000017500000000216512410526714020764 0ustar dimitrisdimitris*Vertices 11 1 "minister1" 0.2912 0.2004 ellipse 2 "pminister" 0.4875 0.0153 diamond 3 "minister2" 0.3537 0.3416 ellipse 3 "minister2" 0.3537 0.3416 ellipse 4 "minister3" 0.4225 0.5477 ellipse 5 "minister4" 0.4538 0.1603 ellipse 6 "minister5" 0.4900 0.3836 ellipse 7 "minister6" 0.6212 0.5038 ellipse 8 "minister7" 0.6450 0.2023 ellipse 9 "advisor1" 0.6488 0.6031 box 10 "advisor2" 0.3212 0.5515 box 11 "advisor3" 0.7188 0.4218 box *Matrix 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0socnetv-1.9/nets/4-actors-sm.csv0000644000175000017500000000003712410526714017037 0ustar dimitrisdimitris0,1,1,2 1,0,2,1 0,0,0,1 1,0,0,0socnetv-1.9/nets/Freemans_EIES-3-messages_n48.lst0000644000175000017500000000714212410526714022004 0ustar dimitrisdimitris1 1 24 1 2 488 1 3 28 1 4 65 1 5 20 1 6 65 1 7 45 1 8 346 1 9 82 1 10 52 1 11 177 1 12 28 1 13 24 1 14 49 1 15 81 1 16 77 1 17 77 1 18 73 1 19 33 1 20 31 1 21 22 1 22 46 1 23 31 1 24 128 1 25 38 1 26 89 1 27 95 1 28 25 1 29 388 1 30 71 1 31 212 1 32 185 2 1 364 2 2 6 2 3 17 2 4 17 2 5 15 2 7 30 2 8 20 2 9 35 2 10 20 2 11 22 2 12 15 2 13 15 2 14 15 2 15 15 2 16 50 2 17 25 2 18 8 2 20 15 2 21 15 2 22 15 2 23 15 2 25 15 2 26 15 2 27 10 2 28 24 2 29 89 2 30 23 2 31 163 2 32 39 3 1 4 3 2 5 3 8 5 4 1 52 4 2 30 4 4 4 4 6 2 4 8 32 4 9 21 4 10 34 4 11 9 4 16 5 4 17 4 4 18 2 4 19 35 4 24 12 4 27 12 4 28 5 4 29 20 4 30 4 4 31 19 4 32 33 5 1 26 5 2 4 5 3 4 5 4 4 5 6 4 5 7 8 5 8 4 5 9 4 5 10 4 5 11 4 5 12 4 5 13 4 5 14 4 5 15 4 5 16 4 5 17 4 5 18 4 5 19 4 5 21 4 5 22 8 5 23 4 5 24 14 5 25 4 5 27 4 5 29 4 5 30 7 5 31 4 5 32 4 6 1 72 6 2 23 6 4 2 6 6 34 6 8 16 6 10 7 6 11 15 6 15 8 6 16 7 6 17 6 6 24 14 6 27 7 6 28 3 6 29 34 6 30 3 6 31 22 7 1 14 7 31 6 8 1 239 8 2 82 8 3 5 8 4 37 8 5 3 8 6 34 8 7 5 8 8 10 8 9 12 8 10 18 8 11 164 8 12 18 8 16 30 8 17 53 8 18 27 8 19 20 8 20 4 8 22 5 8 23 4 8 24 55 8 26 9 8 27 34 8 29 146 8 30 216 8 31 88 8 32 288 9 1 24 9 2 25 9 4 2 9 8 8 9 9 16 9 11 15 9 13 10 9 17 5 9 27 15 9 29 10 9 31 30 9 32 44 10 1 43 10 2 15 10 4 32 10 6 12 10 8 14 10 10 5 10 11 25 10 12 2 10 16 10 10 17 10 10 19 20 10 20 15 10 22 5 10 23 20 10 24 29 10 26 4 10 27 10 10 29 47 10 30 6 10 31 22 10 32 19 11 1 178 11 2 36 11 4 11 11 6 19 11 7 10 11 8 172 11 9 39 11 10 28 11 11 29 11 13 4 11 16 23 11 17 15 11 18 24 11 21 8 11 24 29 11 25 10 11 26 11 11 27 22 11 29 46 11 31 119 11 32 34 12 2 5 12 8 5 12 12 3 12 19 5 12 29 53 12 31 5 12 32 9 13 1 5 13 11 5 13 31 5 14 1 12 14 3 9 14 14 2 14 16 12 14 19 5 14 29 35 14 31 8 15 1 120 15 6 4 15 12 5 15 15 78 15 27 8 15 29 58 15 31 32 16 1 58 16 2 25 16 4 10 16 8 20 16 10 5 16 11 10 16 14 5 16 16 15 16 17 10 16 21 5 16 24 5 16 29 35 16 31 10 17 1 63 17 2 18 17 3 9 17 4 7 17 6 6 17 8 36 17 10 5 17 11 9 17 12 5 17 14 5 17 16 5 17 20 5 17 21 2 17 27 15 17 29 10 17 30 9 17 31 15 17 32 9 18 1 58 18 2 8 18 3 5 18 4 4 18 8 4 18 10 5 18 11 18 18 18 4 18 27 20 18 29 8 18 30 10 18 31 48 19 1 5 19 2 5 19 4 25 19 8 10 19 14 5 19 19 5 19 23 5 19 31 10 20 21 4 20 29 4 21 1 9 21 11 3 21 16 5 21 29 5 22 1 10 22 24 40 22 29 15 22 32 5 23 1 5 23 2 5 23 3 5 23 10 19 23 19 5 23 29 14 23 31 5 24 1 89 24 2 17 24 3 4 24 4 14 24 5 14 24 6 18 24 7 8 24 8 41 24 9 4 24 10 19 24 11 31 24 12 4 24 13 4 24 14 9 24 15 4 24 16 14 24 17 4 24 18 9 24 19 4 24 20 4 24 21 4 24 22 58 24 23 4 24 24 5 24 25 18 24 26 14 24 27 9 24 28 4 24 29 156 24 30 4 24 31 56 24 32 10 25 1 32 25 2 5 25 14 15 25 22 10 25 24 23 25 25 10 25 30 9 25 31 15 26 1 35 26 2 5 26 10 5 26 29 10 26 31 13 27 1 50 27 2 28 27 4 13 27 8 19 27 9 29 27 10 5 27 11 8 27 13 33 27 15 4 27 17 10 27 18 15 27 24 10 27 28 3 27 29 32 27 31 13 27 32 33 28 1 9 28 2 6 28 6 3 28 28 3 28 32 6 29 1 559 29 2 132 29 3 5 29 4 24 29 5 21 29 6 29 29 8 155 29 9 15 29 10 98 29 11 69 29 12 89 29 13 37 29 14 76 29 15 80 29 16 63 29 17 15 29 18 4 29 19 9 29 20 18 29 21 43 29 22 108 29 23 29 29 24 218 29 26 15 29 27 66 29 29 6 29 30 14 29 31 91 29 32 126 30 1 39 30 2 21 30 4 6 30 5 3 30 6 3 30 8 140 30 10 7 30 12 2 30 17 9 30 18 5 30 27 2 30 29 18 30 30 2 30 31 20 30 32 8 31 1 82 31 2 125 31 3 10 31 4 22 31 5 10 31 6 15 31 7 18 31 8 70 31 9 35 31 10 23 31 11 114 31 12 20 31 13 16 31 14 15 31 15 24 31 16 30 31 17 28 31 18 49 31 19 30 31 20 5 31 21 5 31 22 15 31 23 8 31 24 53 31 25 25 31 26 8 31 27 21 31 28 8 31 29 65 31 30 28 31 32 67 32 1 239 32 2 99 32 4 27 32 5 3 32 8 268 32 9 101 32 10 18 32 11 35 32 12 4 32 17 7 32 22 14 32 24 5 32 27 50 32 28 6 32 29 71 32 30 7 32 31 107 32 32 219socnetv-1.9/nets/10actors-colors.dot0000644000175000017500000000022012410526714017706 0ustar dimitrisdimitrisdigraph mydot { node [color=red, shape=box]; a -> b -> c ->d node [color=pink, shape=circle]; d->e->a->f->j->k->l->o [weight=1, color=gray]; } socnetv-1.9/nets/jin3-sm.txt0000644000175000017500000000037512410526714016277 0ustar dimitrisdimitris0 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-1.9/nets/jin2-sm.txt0000644000175000017500000000040112410526714016264 0ustar dimitrisdimitris0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-1.9/nets/lion_share.dot0000644000175000017500000001332712410526714017112 0ustar dimitrisdimitris##"A few people in the field of genetics are using dot to draw "marriage node diagram" pedigree drawings. Here is one I have done of a test pedigree from the FTREE pedigree drawing package (Lion Share was a racehorse)." Contributed by David Duffy. ##Command to get the layout: "dot -Tpng thisfile > thisfile.png" digraph Ped_Lion_Share { # page = "8.2677165,11.692913" ; ratio = "auto" ; mincross = 2.0 ; label = "Pedigree Lion_Share" ; "001" [shape=box , regular=1,style=filled,fillcolor=white ] ; "002" [shape=box , regular=1,style=filled,fillcolor=white ] ; "003" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "004" [shape=box , regular=1,style=filled,fillcolor=white ] ; "005" [shape=box , regular=1,style=filled,fillcolor=white ] ; "006" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "007" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "009" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "014" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "015" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "016" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "ZZ01" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "ZZ02" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "017" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "012" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "008" [shape=box , regular=1,style=filled,fillcolor=white ] ; "011" [shape=box , regular=1,style=filled,fillcolor=white ] ; "013" [shape=box , regular=1,style=filled,fillcolor=white ] ; "010" [shape=box , regular=1,style=filled,fillcolor=white ] ; "023" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "020" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "021" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "018" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "025" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "019" [shape=box , regular=1,style=filled,fillcolor=white ] ; "022" [shape=box , regular=1,style=filled,fillcolor=white ] ; "024" [shape=box , regular=1,style=filled,fillcolor=white ] ; "027" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "026" [shape=box , regular=1,style=filled,fillcolor=white ] ; "028" [shape=box , regular=1,style=filled,fillcolor=grey ] ; "marr0001" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "001" -> "marr0001" [dir=none,weight=1] ; "007" -> "marr0001" [dir=none,weight=1] ; "marr0001" -> "017" [dir=none, weight=2] ; "marr0002" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "001" -> "marr0002" [dir=none,weight=1] ; "ZZ02" -> "marr0002" [dir=none,weight=1] ; "marr0002" -> "012" [dir=none, weight=2] ; "marr0003" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0003" [dir=none,weight=1] ; "003" -> "marr0003" [dir=none,weight=1] ; "marr0003" -> "008" [dir=none, weight=2] ; "marr0004" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0004" [dir=none,weight=1] ; "006" -> "marr0004" [dir=none,weight=1] ; "marr0004" -> "011" [dir=none, weight=2] ; "marr0005" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0005" [dir=none,weight=1] ; "ZZ01" -> "marr0005" [dir=none,weight=1] ; "marr0005" -> "013" [dir=none, weight=2] ; "marr0006" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "004" -> "marr0006" [dir=none,weight=1] ; "009" -> "marr0006" [dir=none,weight=1] ; "marr0006" -> "010" [dir=none, weight=2] ; "marr0007" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0007" [dir=none,weight=1] ; "015" -> "marr0007" [dir=none,weight=1] ; "marr0007" -> "023" [dir=none, weight=2] ; "marr0008" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0008" [dir=none,weight=1] ; "016" -> "marr0008" [dir=none,weight=1] ; "marr0008" -> "020" [dir=none, weight=2] ; "marr0009" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0009" [dir=none,weight=1] ; "012" -> "marr0009" [dir=none,weight=1] ; "marr0009" -> "021" [dir=none, weight=2] ; "marr0010" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "008" -> "marr0010" [dir=none,weight=1] ; "017" -> "marr0010" [dir=none,weight=1] ; "marr0010" -> "018" [dir=none, weight=2] ; "marr0011" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "011" -> "marr0011" [dir=none,weight=1] ; "023" -> "marr0011" [dir=none,weight=1] ; "marr0011" -> "025" [dir=none, weight=2] ; "marr0012" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "013" -> "marr0012" [dir=none,weight=1] ; "014" -> "marr0012" [dir=none,weight=1] ; "marr0012" -> "019" [dir=none, weight=2] ; "marr0013" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "010" -> "marr0013" [dir=none,weight=1] ; "021" -> "marr0013" [dir=none,weight=1] ; "marr0013" -> "022" [dir=none, weight=2] ; "marr0014" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "019" -> "marr0014" [dir=none,weight=1] ; "020" -> "marr0014" [dir=none,weight=1] ; "marr0014" -> "024" [dir=none, weight=2] ; "marr0015" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "022" -> "marr0015" [dir=none,weight=1] ; "025" -> "marr0015" [dir=none,weight=1] ; "marr0015" -> "027" [dir=none, weight=2] ; "marr0016" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "024" -> "marr0016" [dir=none,weight=1] ; "018" -> "marr0016" [dir=none,weight=1] ; "marr0016" -> "026" [dir=none, weight=2] ; "marr0017" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "026" -> "marr0017" [dir=none,weight=1] ; "027" -> "marr0017" [dir=none,weight=1] ; "marr0017" -> "028" [dir=none, weight=2] ; } socnetv-1.9/nets/network.gw0000644000175000017500000000253712410526714016310 0ustar dimitrisdimitrisLEDA.GRAPH unknown unknown 5 |{1 0 1}| |{2 0 1}| |{3 0 1}| |{4 0 1}| |{5 0 1}| 4 1 2 0 |{edge 3 1 3 1}| 2 3 0 |{edge 3 1 3 1}| 3 4 0 |{edge 3 1 3 1}| 4 5 0 |{edge 3 1 3 1}| # version string GraphWin 1.400000 # scaling wxmin wymin wxmax wymax 0.9821777 -274.8779 -281.2244 274.8779 283.8025 # node label font and size 0 15 # edge label font and size 0 13 # node index format %d # edge index format %d # multi-edge distance 4.793233 # # node infos # x y shape bclr(r,g,b) bwidth r1 r2 clr(r,g,b) ltype lclr(r,g,b) lpos lstr -146.6016 65.93628 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 1 59.04785 107.677 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 2 54.97559 -44.01489 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 3 -145.5835 -44.01489 6 0 0 0 1.018519 20.36133 10.18066 160 0 0 1 255 255 228 4 4 -73.30078 -148.8757 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 5 # # edge infos # width clr(r,g,b) shape style dir ltype lclr(r,g,b) lpos sanch tanch poly lstr 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (-136.6243,67.96136) (49.07063,105.6519) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (58.77464,97.5) (55.24879,-33.8379) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (44.79492,-44.01489) (-125.2222,-44.01489) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (-140.3645,-51.58607) (-79.07878,-140.4936) 1 socnetv-1.9/nets/jin4-sm.txt0000644000175000017500000000040112410526714016266 0ustar dimitrisdimitris0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-1.9/nets/sampson.dl0000644000175000017500000001631312410526714016256 0ustar dimitrisdimitrisDL N=18 NM=10 FORMAT = FULLMATRIX DIAGONAL PRESENT ROW LABELS: ROMUL_10 BONAVEN_5 AMBROSE_9 BERTH_6 PETER_4 LOUIS_11 VICTOR_8 WINF_12 JOHN_1 GREG_2 HUGH_14 BONI_15 MARK_7 ALBERT_16 AMAND_13 BASIL_3 ELIAS_17 SIMP_18 COLUMN LABELS: ROMUL_10 BONAVEN_5 AMBROSE_9 BERTH_6 PETER_4 LOUIS_11 VICTOR_8 WINF_12 JOHN_1 GREG_2 HUGH_14 BONI_15 MARK_7 ALBERT_16 AMAND_13 BASIL_3 ELIAS_17 SIMP_18 LEVEL LABELS: SAMPLK1 SAMPLK2 SAMPLK3 SAMPDLK SAMPES SAMPDES SAMPIN SAMPNIN SAMPPR SAMNPR DATA: 0 0 2 0 3 0 0 0 0 0 0 0 0 1 0 0 0 0 3 0 0 0 0 0 2 0 0 0 0 0 0 1 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 3 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 1 0 0 0 0 0 1 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 0 0 3 0 0 3 2 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 2 0 0 0 0 0 0 0 0 3 0 0 1 0 0 0 0 0 2 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 1 2 0 0 2 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 1 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 0 3 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 2 0 0 1 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 3 2 0 0 0 0 1 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 3 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 3 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 1 0 0 3 1 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 1 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 2 3 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 3 0 0 0 0 1 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 0 0 0 0 0 0 0 0 0 2 0 0 0 3 0 0 0 3 0 0 0 0 0 0 0 0 1 0 2 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 1 2 0 0 0 0 3 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 3 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 0 0 0 2 0 0 0 0 0 3 0 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 3 1 0 0 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 1 0 0 0 0 3 2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 0 3 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 1 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 3 2 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 2 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 2 0 0 0 1 0 0 0 0 0 0 0 3 1 0 2 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 1 0 0 0 0 0 0 0 0 1 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 2 2 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 2 2 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 1 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 3 0 1 1 0 0 0 0 3 1 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 3 2 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 2 2 1 0 0 0 3 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 2 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 2 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 2 1 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 1 2 0 0 0 0 0 0 0 1 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 3 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 0 1 1 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 2 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 1 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 3 0 0 0 0 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 2 1 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 3 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 2 2 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 2 3 0 0 0 0 3 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-1.9/nets/4actors-line-properties.dot0000644000175000017500000000051112410526714021454 0ustar dimitrisdimitris graph graphname { // The label attribute can be used to change the label of a node a [label="Foo"]; // Here, the node shape is changed. b [shape=box]; // These edges both have different line properties a -- b -- c [color=blue]; b -- d [style=dotted]; c -- d [color=#ddd, style=dotted]; }socnetv-1.9/nets/jin1-sm.txt0000644000175000017500000000157312410526714016276 0ustar dimitrisdimitris0 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-1.9/nets/Freemans_EIES-2_n48.lst0000644000175000017500000001432512410526714020177 0ustar dimitrisdimitris1 2 4 1 3 2 1 6 2 1 8 2 1 10 2 1 11 2 1 13 3 1 14 3 1 18 2 1 19 3 1 20 2 1 21 3 1 22 2 1 23 2 1 24 2 1 25 2 1 26 3 1 27 2 1 31 2 1 32 2 1 33 2 1 35 2 1 36 2 1 37 2 1 38 2 1 39 3 1 40 2 1 41 2 1 42 3 1 43 2 1 44 4 1 45 3 1 46 3 2 1 4 2 3 2 2 6 2 2 8 1 2 10 2 2 11 2 2 13 3 2 14 4 2 18 2 2 19 3 2 21 2 2 22 2 2 23 2 2 24 2 2 25 2 2 26 2 2 27 2 2 32 2 2 33 2 2 35 2 2 36 2 2 37 2 2 38 2 2 39 2 2 40 2 2 41 2 2 42 2 2 43 3 2 44 4 2 45 4 2 46 2 3 1 3 3 2 1 3 6 4 3 8 1 3 13 2 3 18 2 3 19 4 3 20 4 3 22 4 3 23 1 3 24 2 3 25 2 3 26 2 3 27 1 3 31 1 3 32 2 3 33 2 3 35 2 3 36 4 3 37 2 3 39 2 3 41 1 3 42 1 3 43 1 6 1 2 6 2 2 6 3 2 6 8 2 6 10 2 6 13 2 6 14 2 6 18 3 6 19 2 6 20 2 6 21 1 6 22 2 6 23 2 6 24 2 6 26 2 6 27 4 6 31 1 6 32 2 6 33 2 6 35 2 6 36 2 6 37 2 6 38 2 6 39 2 6 40 2 6 41 2 6 42 2 6 43 2 6 44 2 6 46 2 8 1 3 8 6 2 8 13 2 8 14 3 8 18 2 8 19 2 8 20 1 8 22 2 8 23 1 8 24 2 8 25 2 8 27 1 8 32 2 8 33 2 8 35 2 8 37 2 8 38 1 8 40 1 8 41 2 8 42 2 8 44 2 8 45 2 10 1 4 10 2 2 10 13 3 10 18 2 10 19 2 10 22 2 10 23 2 10 24 2 10 27 2 10 31 2 10 33 2 10 37 3 10 39 2 10 40 2 10 41 2 10 42 3 10 44 4 10 45 2 10 46 3 11 1 3 11 2 2 11 3 1 11 13 2 11 14 2 11 19 1 11 21 3 11 41 2 13 1 3 13 2 2 13 3 2 13 6 2 13 8 2 13 10 2 13 11 1 13 14 1 13 18 2 13 19 4 13 20 1 13 21 2 13 22 2 13 23 2 13 24 2 13 25 2 13 26 2 13 27 2 13 32 2 13 33 2 13 35 2 13 36 2 13 37 2 13 38 2 13 40 2 13 41 1 13 42 2 13 43 2 13 44 2 13 45 4 13 46 2 14 1 3 14 2 4 14 8 2 14 13 2 14 19 2 14 21 2 14 22 1 14 24 1 14 25 2 14 33 2 14 35 2 14 40 3 14 42 1 14 44 2 14 45 4 14 46 2 18 1 3 18 3 2 18 6 3 18 8 2 18 11 1 18 13 2 18 14 1 18 19 2 18 20 3 18 21 2 18 22 1 18 23 2 18 24 2 18 25 2 18 26 2 18 27 2 18 31 2 18 32 4 18 33 2 18 35 2 18 36 4 18 37 2 18 38 2 18 40 2 18 41 2 18 42 3 18 43 2 18 44 2 18 45 1 19 1 3 19 2 2 19 3 2 19 6 2 19 8 2 19 10 2 19 13 4 19 14 2 19 18 2 19 21 2 19 22 2 19 23 2 19 24 2 19 25 2 19 26 2 19 27 2 19 31 2 19 32 2 19 33 2 19 35 2 19 36 1 19 37 2 19 38 2 19 40 2 19 41 2 19 42 2 19 44 3 19 45 3 19 46 2 20 1 2 20 3 1 20 6 2 20 11 1 20 13 1 20 18 3 20 22 2 20 23 1 20 24 1 20 27 2 20 31 2 20 32 3 20 33 2 20 35 1 20 36 1 20 37 1 20 38 2 20 41 1 20 43 1 20 44 2 20 45 2 20 46 2 21 1 3 21 2 3 21 3 1 21 6 2 21 8 1 21 11 3 21 13 3 21 14 2 21 18 1 21 19 2 21 22 1 21 23 1 21 24 2 21 26 2 21 27 2 21 31 1 21 32 1 21 33 2 21 35 2 21 36 1 21 39 2 21 40 4 21 41 2 21 42 2 21 43 2 21 44 3 21 45 3 22 1 3 22 2 2 22 3 4 22 6 3 22 8 3 22 13 3 22 18 2 22 19 2 22 20 3 22 21 2 22 23 3 22 24 4 22 25 4 22 26 2 22 27 3 22 31 3 22 32 3 22 33 3 22 35 4 22 36 3 22 37 3 22 38 3 22 39 2 22 40 2 22 41 3 22 42 4 22 43 3 22 44 3 22 45 2 22 46 2 23 1 3 23 2 2 23 3 2 23 6 3 23 8 1 23 13 2 23 14 2 23 18 2 23 19 2 23 20 2 23 22 3 23 24 2 23 25 2 23 27 2 23 31 4 23 32 1 23 33 2 23 35 2 23 36 2 23 37 2 23 38 2 23 40 1 23 42 3 23 44 3 23 45 1 24 1 2 24 2 2 24 3 2 24 6 2 24 8 3 24 10 2 24 13 3 24 14 1 24 18 2 24 19 2 24 22 3 24 23 2 24 25 2 24 27 2 24 31 2 24 32 2 24 33 4 24 35 3 24 37 2 24 38 2 24 39 2 24 40 1 24 41 1 24 42 2 24 44 2 24 45 2 24 46 2 25 1 3 25 2 2 25 3 3 25 6 1 25 8 2 25 13 3 25 14 2 25 18 1 25 19 3 25 20 1 25 21 1 25 22 3 25 23 2 25 24 3 25 26 1 25 27 1 25 32 3 25 33 3 25 35 3 25 37 2 25 39 1 25 40 2 25 41 1 25 42 2 25 43 2 25 44 2 25 45 2 25 46 1 26 1 4 26 2 2 26 3 2 26 11 1 26 13 2 26 19 2 26 21 1 26 22 2 26 39 2 26 40 2 26 42 2 26 43 2 26 44 4 26 45 1 26 46 2 27 1 2 27 3 2 27 6 4 27 8 1 27 13 2 27 18 2 27 20 2 27 22 2 27 23 2 27 24 1 27 32 2 27 33 2 27 35 3 27 36 2 27 37 2 27 38 2 27 39 2 27 41 2 27 42 2 27 43 1 27 44 2 27 46 2 31 1 1 31 3 2 31 6 1 31 8 1 31 18 2 31 19 2 31 20 2 31 22 2 31 23 2 31 24 2 31 32 1 31 35 3 31 36 1 31 37 3 31 38 2 31 42 1 32 1 2 32 2 2 32 3 2 32 6 2 32 8 2 32 13 2 32 18 3 32 19 2 32 20 2 32 22 3 32 23 1 32 24 2 32 25 2 32 27 2 32 31 1 32 33 3 32 35 4 32 36 2 32 37 3 32 38 3 32 41 2 32 42 3 32 43 1 33 1 3 33 2 3 33 3 2 33 6 2 33 8 2 33 13 3 33 14 1 33 18 2 33 19 3 33 20 2 33 22 2 33 23 3 33 24 4 33 25 3 33 27 2 33 31 2 33 32 2 33 35 3 33 36 2 33 37 2 33 38 3 33 40 1 33 41 2 33 42 2 33 43 1 33 45 1 35 1 2 35 2 2 35 3 2 35 6 3 35 13 2 35 14 3 35 18 2 35 19 2 35 22 3 35 23 2 35 24 3 35 25 2 35 27 3 35 32 3 35 33 3 35 37 4 35 38 2 35 41 2 35 42 4 35 45 2 36 1 3 36 2 2 36 3 4 36 6 3 36 13 2 36 18 4 36 20 1 36 22 3 36 23 1 36 24 1 36 27 3 36 31 1 36 32 2 36 33 1 36 35 1 36 37 2 36 38 2 36 41 2 36 42 3 36 43 2 36 44 2 36 46 2 37 1 3 37 2 2 37 3 2 37 6 2 37 8 3 37 10 2 37 13 3 37 14 2 37 18 2 37 19 3 37 20 2 37 21 2 37 22 3 37 23 2 37 24 3 37 25 2 37 26 2 37 27 2 37 31 4 37 32 3 37 33 3 37 35 4 37 36 2 37 38 3 37 40 2 37 41 3 37 42 3 37 43 2 37 44 2 37 45 2 37 46 2 38 1 2 38 2 2 38 3 2 38 6 3 38 8 1 38 13 3 38 18 3 38 19 2 38 20 2 38 22 3 38 23 2 38 24 3 38 27 2 38 31 3 38 32 3 38 33 3 38 35 3 38 36 3 38 37 3 38 41 1 38 42 2 39 1 4 39 2 1 39 3 2 39 6 1 39 8 1 39 11 1 39 13 1 39 18 1 39 19 1 39 20 1 39 21 2 39 22 2 39 23 1 39 24 1 39 26 3 39 27 2 39 32 1 39 33 1 39 35 2 39 36 1 39 37 2 39 38 1 39 41 2 39 42 2 39 44 3 39 46 1 40 1 3 40 2 2 40 3 2 40 6 2 40 8 2 40 10 2 40 13 3 40 14 3 40 18 2 40 19 2 40 21 4 40 22 1 40 23 2 40 24 2 40 25 2 40 26 2 40 27 2 40 32 1 40 33 2 40 35 2 40 36 1 40 37 2 40 42 2 40 43 2 40 44 2 40 45 2 40 46 2 41 1 3 41 2 2 41 6 3 41 13 2 41 18 1 41 19 1 41 21 2 41 22 2 41 23 2 41 24 2 41 27 3 41 32 2 41 33 2 41 35 3 41 37 2 41 38 1 41 39 2 41 40 1 41 42 2 41 44 2 41 45 2 41 46 2 42 1 3 42 2 2 42 3 2 42 6 3 42 8 2 42 10 2 42 13 3 42 18 3 42 19 2 42 20 3 42 21 2 42 22 4 42 23 3 42 24 2 42 25 2 42 26 2 42 27 2 42 32 3 42 33 2 42 35 4 42 36 2 42 37 4 42 39 2 42 40 2 42 41 2 42 43 2 42 44 3 42 45 2 42 46 4 43 1 3 43 2 3 43 3 1 43 6 2 43 10 2 43 13 3 43 18 2 43 19 2 43 21 2 43 22 2 43 24 2 43 25 2 43 26 2 43 27 2 43 32 2 43 33 2 43 35 2 43 37 2 43 38 2 43 40 3 43 41 2 43 42 3 43 44 3 43 45 3 43 46 2 44 1 4 44 2 4 44 3 2 44 6 2 44 8 2 44 10 3 44 11 2 44 13 2 44 14 2 44 18 2 44 19 3 44 20 2 44 21 3 44 22 2 44 23 3 44 24 2 44 25 2 44 26 3 44 27 2 44 31 2 44 32 2 44 33 2 44 35 2 44 36 2 44 37 2 44 38 2 44 39 2 44 40 2 44 41 2 44 42 3 44 43 2 44 45 4 44 46 3 45 1 4 45 2 4 45 6 2 45 8 2 45 10 2 45 13 4 45 14 4 45 18 2 45 19 3 45 21 2 45 22 1 45 24 3 45 25 2 45 32 1 45 33 2 45 35 3 45 36 1 45 37 1 45 39 2 45 40 2 45 41 1 45 42 3 45 43 2 45 44 4 45 46 3 46 1 3 46 2 2 46 6 1 46 8 1 46 10 2 46 13 3 46 14 2 46 18 2 46 19 2 46 24 2 46 25 2 46 27 1 46 37 2 46 42 4 46 44 3 46 45 3socnetv-1.9/nets/11actors-pajek-matrix.paj0000644000175000017500000000216512410526714021000 0ustar dimitrisdimitris*Vertices 11 1 "minister1" 0.2912 0.2004 ellipse 2 "pminister" 0.4875 0.0153 diamond 3 "minister2" 0.3537 0.3416 ellipse 4 "minister2" 0.3537 0.3416 ellipse 5 "minister3" 0.4225 0.5477 ellipse 6 "minister4" 0.4538 0.1603 ellipse 7 "minister5" 0.4900 0.3836 ellipse 8 "minister6" 0.6212 0.5038 ellipse 9 "minister7" 0.6450 0.2023 ellipse 10 "advisor1" 0.6488 0.6031 box 11 "advisor2" 0.3212 0.5515 box 12 "advisor3" 0.7188 0.4218 box *Matrix 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0socnetv-1.9/nets/network.dl0000644000175000017500000000065212410526714016266 0ustar dimitrisdimitrisDL N=4 FORMAT = FULLMATRIX DIAGONAL PRESENT LABELS: On the normalization and visualization of author co-citation data:Salton's cosine versus the Jaccard index Caveats for the use of citation indicators in research and journalevaluations Should co-occurrence data be normalized? A rejoinder Home on the range - What and where is the middle in science andtechnology studies? DATA: 0 0 0.158114 0 0.201234 0 1 0 1 0 0 0 0.1 1 1 0socnetv-1.9/nets/6actors-plain.dot0000644000175000017500000000014412410526714017442 0ustar dimitrisdimitrisdigraph mydot { "test-1" -> "test-2" -> "test-3" -> "test-4" -> "test-5" -> "test-6" -> "test-1"; } socnetv-1.9/nets/5-actors-plain-list.lst0000644000175000017500000000002712410526714020503 0ustar dimitrisdimitris1 2 3 4 2 3 4 5 3 4 5 1socnetv-1.9/nets/mexico-power-network.lst0000644000175000017500000000050012410526714021075 0ustar dimitrisdimitris18 8 10 23 21 19 11 21 29 5 9 10 23 8 9 18 11 4 7 6 8 20 5 21 5 4 29 20 7 6 8 9 26 21 6 5 7 4 20 21 8 7 4 6 5 8 20 21 9 5 8 23 29 20 21 11 10 8 18 23 4 5 6 7 21 24 26 25 9 10 37 20 10 18 29 8 11 9 20 25 26 11 19 23 9 10 25 21 36 20 4 5 6 7 8 9 10 24 8 26 26 5 8 24 10 21 19 4 5 6 7 8 9 11 18 36 37 11 37 8 36 25 10 11 8 socnetv-1.9/nets/.directory0000600000175000017500000000012412531067601016242 0ustar dimitrisdimitris[Dolphin] SortOrder=1 SortRole=date Timestamp=2015,5,26,16,5,5 Version=3 ViewMode=1 socnetv-1.9/src/0000775000175000017500000000000012542300363014065 5ustar dimitrisdimitrissocnetv-1.9/src/nodeeditdialog.h0000664000175000017500000000477612542300240017221 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt nodeeditdialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef NODEEDITDIALOG_H #define NODEEDITDIALOG_H #include #include "ui_nodeeditdialog.h" class NodeEditDialog : public QDialog { Q_OBJECT public: explicit NodeEditDialog(QWidget *parent = 0, const QString &l = "", const int &s = 8, const QColor &c= QColor("red"), const QString &sh = "circle"); public slots: void checkErrors (); void gatherData (); void selectColor(); signals: void userChoices( const QString, const int, const QString, const QColor, const QString); void nodeEditDialogError(QString); private: QColor nodeColor; QString nodeShape; QString nodeValue; QString nodeLabel; int nodeSize; QPixmap pixmap; Ui::NodeEditDialog ui; }; #endif // NODEEDITDIALOG_H socnetv-1.9/src/nodelabel.h0000775000175000017500000000371312542300240016164 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt nodelabel.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef NODELABEL_H #define NODELABEL_H #include class Node; static const int TypeLabel = QGraphicsItem::UserType+4; class NodeLabel : public QGraphicsTextItem{ public: NodeLabel(Node * , int size, QString); void removeRefs(); enum { Type = UserType + 4 }; int type() const { return Type; } ~NodeLabel(); Node* node() { return source; } private: Node *source; }; #endif socnetv-1.9/src/nodenumber.h0000775000175000017500000000371012542300240016372 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt nodenumber.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef NODENUMBER_H #define NODENUMBER_H #include class Node; static const int TypeNumber=QGraphicsItem::UserType+3; class NodeNumber : public QGraphicsTextItem { public: NodeNumber(Node * , int, QString); void removeRefs(); enum { Type = UserType + 3 }; int type() const { return Type; } Node* node() { return source; } ~NodeNumber(); private: Node *source; }; #endif socnetv-1.9/src/graphicswidget.cpp0000775000175000017500000007320512542300240017601 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt graphicswidget.cpp description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "graphicswidget.h" #include #include #include #include #include #include #include "mainwindow.h" #include "node.h" #include "edge.h" #include "nodenumber.h" #include "nodelabel.h" #include "guide.h" #include "edgeweight.h" /** Constructor method. Called when a GraphicsWidget object is created in MW */ GraphicsWidget::GraphicsWidget( QGraphicsScene *sc, MainWindow* par) : QGraphicsView ( sc,par) { setScene(sc); secondDoubleClick=false; dynamicMovement=false; moving=0; timerId=0; layoutType=0; m_nodeLabel=""; zoomIndex=3; m_currentScaleFactor = 1; m_currentRotationAngle = 0; markedNodeExist=false; //used in findNode() markedEdgeExist = false; //used in selecting and edge edgesHash.reserve(1000); nodeHash.reserve(1000); } /** http://thesmithfam.org/blog/2007/02/03/qt-improving-qgraphicsview-performance/#comment-7215 */ void GraphicsWidget::paintEvent ( QPaintEvent * event ){ QPaintEvent *newEvent=new QPaintEvent(event->region().boundingRect()); QGraphicsView::paintEvent(newEvent); delete newEvent; } /** Clears the scene */ void GraphicsWidget::clear() { qDebug() << " clear GW"; nodeHash.clear(); edgesHash.clear(); scene()->clear(); m_curRelation=0; markedNodeExist=false; markedEdgeExist = false; } /** * @brief GraphicsWidget::changeRelation * Called from Graph::relationChanged(int) signal * @param relation */ void GraphicsWidget::changeRelation(int relation) { qDebug() << "GraphicsWidget::changeRelation() to " << relation; m_curRelation = relation; } /** Adds a new node onto the scene Called from Graph::createVertex method when: we load files or the user presses "Add Node" button or the user double clicks (mouseDoubleClickEvent() calls Graph::createVertex */ void GraphicsWidget::drawNode( int num, int size, QString nodeColor, QString numberColor, int numberSize, QString nodeLabel, QString labelColor, int labelSize, QPointF p, QString shape, bool showLabels, bool numberInsideNode, bool showNumbers ) { qDebug()<< "GW: drawNode(): drawing new node at: " << p.x() << ", "<< p.y() ; if (numberInsideNode) size = size +3; Node *jim= new Node ( this, num, size, nodeColor, shape, numberInsideNode, m_labelDistance, m_numberDistance, p ); //Drawing node label - label will be moved by the node movement (see last code line in this method) NodeLabel *labelJim = new NodeLabel (jim, labelSize, nodeLabel ); labelJim -> setDefaultTextColor (labelColor); labelJim -> setTextInteractionFlags(Qt::TextEditorInteraction); if (showLabels) { //qDebug()<< "GW: drawNode: display label " << nodeLabel.toUtf8() << " for node " << num; } else { //qDebug()<<"GW: drawNode: hiding label for node " << num; labelJim->hide(); } // drawing node number - label will be moved by the node movement (see last code line in this method) if (numberInsideNode) numberSize = size-2; NodeNumber *numberJim = new NodeNumber ( jim, numberSize, QString::number(num)); numberJim -> setDefaultTextColor (numberColor); if (!showNumbers){ numberJim->hide(); } nodeHash.insert(num, jim);//add new node to a container to ease finding, edge creation etc jim -> setPos( p.x(), p.y()); //finally, move the node where it belongs! } /** Draws an edge from source to target Node. This is used when we do not have references to nodes - only nodeNumbers: a) when we load a network file (check = FALSE) b) when the user clicks on the AddLink button on the MW. */ void GraphicsWidget::drawEdge(int i, int j, float weight, bool reciprocal, bool drawArrows, QString color, bool bezier){ QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number(i) + QString(">")+ QString::number(j); qDebug()<<"GW: drawEdge "<< edgeName << "weight "<nodeNumber()<< " to node " // << nodeHash.value(j)->nodeNumber() << " weight " // << weight << " nodesize " // << m_nodeSize << " edgecolor "<< color ; Edge *edge=new Edge (this, nodeHash.value(i), nodeHash.value(j), Qt::SolidLine, weight, m_nodeSize, color, reciprocal, drawArrows, bezier); edge -> setZValue(253); //Edges have lower z than nodes. Nodes always appear above edges. // Keep it here so that it doesnt interfere with dashed lines. edge->setBoundingRegionGranularity(0.05); // Slows down the universe...Keep it 0.05... //edge->setCacheMode (QGraphicsItem::DeviceCoordinateCache); //Also slows down the universe... // qDebug()<<"GW: drawEdge() - adding new edge between "<x() + (nodeHash.value(j))->x() ) / 2.0; double y = ( (nodeHash.value(i))->y() + (nodeHash.value(j))->y() ) / 2.0; // qDebug()<< "GW: drawEdge(): edge weight will be at " << x << ", " << y; EdgeWeight *edgeWeight = new EdgeWeight (edge, 7, QString::number(weight) ); edgeWeight-> setPos(x,y); edgeWeight-> setDefaultTextColor (color); edgeWeight-> hide(); // qDebug()<< "Scene items now: "<< scene()->items().size() << " - GW items now: "<< items().size(); } /** Called from Graph to make an existing arc symmetric (reciprocal) */ void GraphicsWidget::drawEdgeReciprocal(int source, int target){ qDebug("GW: drawEdgeReciprocal ()"); QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number(source) + QString(">")+ QString::number(target); // qDebug("GW: making existing edge between %i and %i reciprocal. Name: "+edgeName.toUtf8(), source, target ); edgesHash.value(edgeName)->makeReciprocal(); } /** Called from Graph to unmake an existing symmetric (reciprocal) edge to one-directed only. */ void GraphicsWidget::unmakeEdgeReciprocal(int source, int target){ qDebug("GW: unmakeEdgeReciprocal ()"); QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number(source) + QString(">")+ QString::number(target); // qDebug("GW: removing edge between %i and %i. Name: "+edgeName.toUtf8(), source, target ); edgesHash.value(edgeName)->unmakeReciprocal(); } /** Called when the user middle-clicks on two nodes consecutively. . It saves the source & target nodes that were clicked On the second middle-click, it emits the userMiddleClicked() signal to MW, which will notify activeGraph, which in turn will signal back to drawEdge()... */ void GraphicsWidget::startEdge(Node *node){ if (secondDoubleClick){ qDebug("GW: startEdge(): this is the second double click. Emitting userMiddleClicked() to create edge"); secondNode=node; emit userMiddleClicked(firstNode->nodeNumber(), secondNode->nodeNumber(), 1.0); ( (MainWindow*)parent() )->setCursor(Qt::ArrowCursor); secondDoubleClick=false; } else{ qDebug("GW: startEdge(): this is the first double click."); firstNode=node; secondDoubleClick=true; ( (MainWindow*)parent() )->setCursor( Qt::PointingHandCursor); } } /** This is called from each node when the user clicks on it. It emits the selectedNode signal to MW which is used to - display node info on the status bar - notify context menus for the clicked node. */ void GraphicsWidget::nodeClicked(Node *node){ qDebug ("GW: Emitting selectedNode()"); emit selectedNode(node); } /** This is called from each edge when the user clicks on it. It emits the selectedEdge signal to MW which is used to - display edge info on the status bar - notify context menus for the clicked edge. Also, it makes source and target nodes to stand out of other nodes. */ void GraphicsWidget::edgeClicked(Edge *edge){ qDebug ("GW: Emitting selectedEdge()"); if (markedEdgeExist) { //unselect them, restore their color markedEdgeSource->setSelected(false); markedEdgeTarget->setSelected(false); //restore their size markedEdgeSource->setSize(markedEdgeSourceOrigSize); markedEdgeTarget->setSize(markedEdgeTargetOrigSize); markedEdgeExist=false; return; } markedEdgeSource=edge->sourceNode(); markedEdgeTarget=edge->targetNode(); markedEdgeExist=true; // select nodes to change their color markedEdgeSource->setSelected(true); markedEdgeTarget->setSelected(true); // save their original size markedEdgeSourceOrigSize=markedEdgeSource->size(); markedEdgeTargetOrigSize=markedEdgeTarget->size(); //now, make them larger markedEdgeSource->setSize(2*markedEdgeSourceOrigSize-1); markedEdgeTarget->setSize(2*markedEdgeTargetOrigSize-1); emit selectedEdge(edge); } /** On the event of a right-click on a node, the node calls this function to emit a signal to MW to open a context menu at the mouse position. Node is already passed with selectedNode(Node *) signal The position of the menu is determined by QMouse:pos()... */ void GraphicsWidget::openNodeContextMenu(){ qDebug("GW: emitting openNodeMenu()"); emit openNodeMenu(); } /** On the event of a right-click on an edge, the edge calls this function to emit a signal to MW to open a context menu at the mouse position. Edge is already passed with selectedEdge(Node *) signal The position of the menu is determined by QMouse:pos()... */ void GraphicsWidget::openEdgeContextMenu(){ qDebug("GW: emitting openEdgeMenu()"); emit openEdgeMenu(); } /** Called from each node when they move. Updates - node coordinates in activeGraph (via updateNodeCoords() signal) */ void GraphicsWidget::nodeMoved(int number, int x, int y){ //qDebug ("GW: nodeMoved() for %i with %i, %i. Emitting updateNodeCoords() signal", number, x,y); emit updateNodeCoords(number, x, y); } /** Called from activeGraph to update node coordinates on the canvas */ void GraphicsWidget::moveNode(int number, qreal x, qreal y){ qDebug() << " GW: moveNode() " << number << ": " << x << y; nodeHash.value(number)->setPos(x,y); // qDebug() << "GW: moveNode() node reports x, y as " // << nodeHash.value(number)->x() << nodeHash.value(number)->x(); } /** Called from Graph signal eraseNode(int) */ void GraphicsWidget::eraseNode(long int doomedJim){ qDebug() << "GW: Deleting node "<< doomedJim; QList list=scene()->items(); qDebug("GW: Scene items= %i - View items : %i",scene()->items().size(), items().size()); for (QList::iterator it=list.begin(); it!=list.end(); it++) { if ( (*it)->type()==TypeNode) { Node *jim=(Node*) (*it); if ( jim->nodeNumber()==doomedJim) { qDebug() << "GW: found doomedJim " << jim->nodeNumber() << " Demanding node->die() :)" ; delete *it; break; } } } qDebug("GW: Scene items now= %i - View items now= %i ", scene()->items().size(), items().size() ); } /** Called from MainWindow when erasing edges using vertex numbers */ void GraphicsWidget::eraseEdge(int sourceNode, int targetNode){ qDebug("GW: Scene items= %i - View items : %i",scene()->items().size(), items().size()); QList list=scene()->items(); for (QList::iterator it=list.begin(); it!= list.end() ; it++){ if ( (*it)->type()==TypeEdge ) { Edge *edge=(Edge*) (*it); if ( edge->sourceNodeNumber()==sourceNode && edge->targetNodeNumber()==targetNode ) { removeItem(edge); break; } } } qDebug("GW: Scene items now= %i - View items now= %i ", scene()->items().size(), items().size() ); } /** Called from Node::die() to removeItem from nodeHash... FIXME : Do we use it ???? */ void GraphicsWidget::removeItem( Node *node){ long int i=node->nodeNumber(); foreach ( Node *candidate, nodeHash) { if ( candidate->nodeNumber() == i ) nodeHash.remove( i ); } node->deleteLater (); qDebug() << "GW items now: " << items().size(); } /** Called from Node::die() to remove Edge edge ... */ void GraphicsWidget::removeItem( Edge * edge){ //edge->remove(); delete (edge); } void GraphicsWidget::removeItem( NodeLabel *nodeLabel){ qDebug() << "GW items now: " << items().size(); delete (nodeLabel); qDebug() << "GW items now: " << items().size(); } void GraphicsWidget::removeItem( NodeNumber *nodeNumber){ delete (nodeNumber); } /** Accepts initial node color from MW. It is called from MW on startup and when user changes it. */ void GraphicsWidget::setInitNodeColor(QString color){ qDebug("GW setting initNodeColor"); m_nodeColor=color; } /** Sets initial edge color. Called from MW on startup and when user changes it. */ void GraphicsWidget::setInitLinkColor(QString color){ qDebug("GW setting initLinkColor"); m_linkColor=color; } /** Sets the color of an node. Called from MW when the user changes the color of a node (right-clicking). */ bool GraphicsWidget::setNodeColor(long int nodeNumber, QString color){ qDebug() << "GraphicsWidget::setNodeColor() : " << color; nodeHash.value(nodeNumber) -> setColor(color); return true; } /** Sets the label of an node. Called from MW when the user changes it */ bool GraphicsWidget::setNodeLabel(long int nodeNumber, QString label){ qDebug() << "GraphicsWidget::setNodeLabel() : " << label; nodeHash.value(nodeNumber) -> setLabelText (label); return true; } /** Makes node label appear inside node. Called from MW on user request. */ void GraphicsWidget::setNumbersInsideNodes(bool numIn){ qDebug("GW setting initNumberDistance"); foreach ( Node *m_node, nodeHash) { m_node->setNumberInside(numIn); if (numIn) this->setInitNodeSize(m_nodeSize+2); else this->setInitNodeSize(m_nodeSize-2); } } /** Changes/Sets the color of an edge. Called from MW when the user changes the color of an edge (right-clicking). */ void GraphicsWidget::setEdgeColor(long int source, long int target, QString color){ QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); qDebug()<<"GW::setEdgeColor() -" << edgeName << " to " << color;; // if ( edgesHash.contains (edgeName) ) { // VERY SLOW edgesHash.value(edgeName) -> setColor(color); } /** Changes/Sets the weight of an edge. Called from MW when the user changes the weight of an edge (right-clicking). */ bool GraphicsWidget::setEdgeWeight(int source, int target, float weight){ QList list=scene()->items(); for (QList::iterator it=list.begin(); it!= list.end() ; it++){ if ( (*it)->type()==TypeEdge) { Edge *edge=(Edge*) (*it); if ( edge->sourceNodeNumber()==source && edge->targetNodeNumber()==target ) { edge->setWeight(weight); edge->update(); return true; } } } return false; } /** Sets initial node size from MW. It is Called from MW on startup and when user changes it. */ void GraphicsWidget::setInitNodeSize(int size){ qDebug("GW setting initNodeSize"); m_nodeSize=size; } /** Sets initial number distance from node Called from MW on startup and when user changes it. */ void GraphicsWidget::setInitNumberDistance(int numberDistance){ qDebug("GW setting initNumberDistance"); m_numberDistance=numberDistance; } /** Passes initial label distance from node It is called from MW on startup and when user changes it. */ void GraphicsWidget::setInitLabelDistance(int labelDistance){ qDebug("GW setting initLabelDistance"); m_labelDistance=labelDistance; } /** * Changes the visibility of an GraphicsView edge (number, label, edge, etc) */ void GraphicsWidget::setEdgeVisibility(int relation, int source, int target, bool toggle){ QString edgeName = QString::number(relation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); if ( edgesHash.contains (edgeName) ) { qDebug()<<"GW: setEdgeVisibility(). relation " << relation << " : " << source << " -> "<< target << " to " << toggle; edgesHash.value(edgeName) -> setVisible(toggle); edgesHash.value(edgeName) -> setEnabled(toggle); return; } qDebug()<<"GW: setEdgeVisibility(). Cannot find edge " << relation << " : " << source << " -> "<< target ; } /** * Changes the visibility of a Node */ void GraphicsWidget::setNodeVisibility(long int number, bool toggle){ if ( nodeHash.contains (number) ) { qDebug() << "GW: setNodeVisibility(): for " << number << " to " << toggle; nodeHash.value(number) -> setVisible(toggle); nodeHash.value(number) -> setEnabled(toggle); return; } qDebug() << "GW: setNodeVisibility(): cannot find node " << number; } /** * @brief GraphicsWidget::setNodeSize * @param number * @param size * @return */ bool GraphicsWidget::setNodeSize(long int number, int size ){ qDebug () << " GraphicsWidget::setNodeSize() node: "<< number << " new size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ qDebug() << "GW: setNodeSize(): for "<< number << " to " << size ; nodeHash.value(number) -> setSize(size); return true; } else { qDebug() << "GW: setNodeSize(): for "<< number << " to initial size" << m_nodeSize; nodeHash.value(number) -> setSize(m_nodeSize); return true; } } qDebug() << "GW: setNodeSize(): cannot find node " << number; return false; } /** * @brief GraphicsWidget::setAllNodeSize * @param size * @return */ void GraphicsWidget::setAllNodeSize(int size ){ qDebug() << "GW: setAllNodeSize() "; foreach ( Node *m_node, nodeHash ) { if (size>0){ qDebug() << "GW: setAllNodeSize(): "<< m_node->nodeNumber() << " to new size " << size ; m_node -> setSize(size); } else { qDebug() << "GW: setAllNodeSize(): "<< m_node->nodeNumber() << " to initial size " << m_nodeSize; m_node -> setSize(m_nodeSize); } } } /* * Used by findNode. * Returns, if found, the node with label or number 'text' */ Node* GraphicsWidget::hasNode( QString text ){ bool ok = false; foreach ( Node *candidate, nodeHash) { if ( candidate->nodeNumber()==text.toInt(&ok, 10) || ( candidate->labelText() == text) ) { qDebug() << "GW: hasNode(): Node " << text << " found!"; markedNodeExist=true; return candidate; break; } } return markedNode1; //dummy return. We check markedNodeExist flag first... } /** Marks (by double-sizing and highlighting) or unmarks a node, given its number or label. Called by MW:slotFindNode() */ bool GraphicsWidget::setMarkedNode(QString nodeText){ qDebug ("GW: setMarkedNode()"); if (markedNodeExist) { markedNode1->setSelected(false); //unselect it, so that it restores its color markedNode1->setSize(markedNodeOrigSize); //restore its size markedNodeExist=false; return true; } markedNode1 = hasNode (nodeText); if (!markedNodeExist) return false; markedNode1->setSelected(true); //select it, so that it changes color markedNodeOrigSize=markedNode1->size(); // save its original size markedNode1->setSize(2*markedNodeOrigSize-1); //now, make it larger return true; } /** * Changes the visibility of all items of certain type (i.e. number, label, edge, etc) */ void GraphicsWidget::setAllItemsVisibility(int type, bool visible){ QList list = scene()->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { qDebug()<< "GW::setAllItemsVisibility. item type is " << (*item)->type(); if ( (*item)->type() == type){ if (visible) (*item)->show(); else (*item)->hide(); } } } void GraphicsWidget::addGuideCircle( int x0, int y0, int radius){ Guide *circ=new Guide (this, x0, y0, radius); circ->show(); } void GraphicsWidget::addGuideHLine( int y0){ Guide *line=new Guide (this, y0, this->width()); line->show(); } /** * Removes all items of certain type (i.e. number, label, edge, etc) */ void GraphicsWidget::removeAllItems(int type){ qDebug()<< "GW: removeAllItems"; QList list = scene()->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { if ( (*item)->type() == type){ Guide *guide = qgraphicsitem_cast (*item); qDebug()<< "GW: removeAllItems - located element"; guide->die(); guide->deleteLater (); delete *item; } } } void GraphicsWidget::clearGuides(){ qDebug()<< "GW: clearGuides"; this->removeAllItems(TypeGuide); } /* Called from MW */ void GraphicsWidget::selectAll() { qDebug() << "GraphicsWidget::selectAll()"; QPainterPath path; path.addRect(0,0, this->scene()->width() , this->scene()->height()); this->scene()->setSelectionArea(path); qDebug() << "selected items now: " << selectedItems().count(); } /* Called from MW */ void GraphicsWidget::selectNone() { qDebug() << "GraphicsWidget::selectNone()"; this->scene()->clearSelection(); } /* Called from MW */ QList GraphicsWidget::selectedItems(){ return this->scene()->selectedItems(); } /** Starts a new node when the user double-clicks somewhere Emits userDoubleClicked to MW slot addNodeWithMouse() which - displays node info on MW status bar and - calls Graph::createVertex(), which in turn calls this->drawNode()... Yes, we make a full circle! :) */ void GraphicsWidget::mouseDoubleClickEvent ( QMouseEvent * e ) { if ( QGraphicsItem *item= itemAt(e->pos() ) ) { if (Node *node = qgraphicsitem_cast(item)) { Q_UNUSED(node); qDebug() << "GW: mouseDoubleClickEvent() double click on a node detected!" << " Cant create new one!"; return; } } QPointF p = mapToScene(e->pos()); qDebug()<< "GW::mouseDoubleClickEvent()" << " double click on empty space. " << " Signaling MW to create a new vertex in graph. e->pos() " << e->pos().x() << ","<< e->pos().y() << ", "<< p.x() << "," <items().size() << " GW items: " << items().size(); } void GraphicsWidget::mousePressEvent( QMouseEvent * e ) { QPointF p = mapToScene(e->pos()); bool ctrlKey = (e->modifiers() == Qt::ControlModifier); qDebug() << "GW::mousePressEvent() click at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<< p.y() << " selectedItems " << selectedItems().count(); // emit selectedItems(m_selectedItems); if ( QGraphicsItem *item= itemAt(e->pos() ) ) { qDebug() << "GW::mousePressEvent() click on an item" ; if (Node *node = qgraphicsitem_cast(item)) { Q_UNUSED(node); qDebug() << "GW::mousePressEvent() single click on a node " ; QGraphicsView::mousePressEvent(e); return; } if (Edge *edge= qgraphicsitem_cast(item)) { Q_UNUSED(edge); qDebug() << "GW::mousePressEvent() single click on an edge "; QGraphicsView::mousePressEvent(e); return; } } else{ qDebug() << "GW::mousePressEvent() click on empty space. "; if ( selectedItems().count() > 0 && ctrlKey ) { qDebug() << " opening selection context menu "; emit openContextMenu(p); } else if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() Right-click on empty space "; emit openContextMenu(p); } QGraphicsView::mousePressEvent(e); } } void GraphicsWidget::mouseReleaseEvent( QMouseEvent * e ) { QPointF p = mapToScene(e->pos()); qDebug() << "GW::mouseReleaseEvent() at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<pos() ) ) { if (Node *node = qgraphicsitem_cast(item)) { qDebug() << "GW::mouseReleaseEvent() on a node "; Q_UNUSED(node); QGraphicsView::mouseReleaseEvent(e); } } else{ qDebug() << "GW::mouseReleaseEvent() on empty space. "; } qDebug() << "GW::mouseReleaseEvent() - selected items now: " << selectedItems().count(); } /** Calls the scaleView() when the user spins the mouse wheel It passes delta as new m_scale */ void GraphicsWidget::wheelEvent(QWheelEvent *e) { qDebug("GW: Mouse wheel event"); qDebug() << "GW: delta = " << e->delta(); float m_scale = e->delta() / qreal(600); qDebug("GW: m_scale = %f", m_scale); if ( m_scale > 0) zoomIn(); else if ( m_scale < 0) zoomOut(); else m_scale=1; } /** Called from MW magnifier widgets */ void GraphicsWidget::zoomOut (){ if (zoomIndex > 0) { zoomIndex--; changeZoom(zoomIndex); } qDebug("GW: ZoomOut() index %i", zoomIndex); emit zoomChanged(zoomIndex); } /** Called from MW magnifier widgets */ void GraphicsWidget::zoomIn(){ qDebug("GW: ZoomIn()"); if (zoomIndex < 6) { zoomIndex++; changeZoom(zoomIndex); } qDebug("GW: ZoomIn() index %i", zoomIndex); emit zoomChanged(zoomIndex); } /** Initiated from MW zoomCombo widget to zoom in or out. */ void GraphicsWidget::changeZoom(int value) { double scaleFactor = 0.25; scaleFactor *= (value + 1); m_currentScaleFactor = scaleFactor; resetMatrix(); this->scale(scaleFactor, scaleFactor); rotate(m_currentRotationAngle); } void GraphicsWidget::rot(int angle){ qDebug("rotating"); m_currentRotationAngle = angle; resetMatrix(); scale(m_currentScaleFactor, m_currentScaleFactor); rotate(angle); } /** Resizing the view causes a repositioning of the nodes maintaining the same pattern*/ void GraphicsWidget::resizeEvent( QResizeEvent *e ) { Q_UNUSED(e); // qDebug ("GraphicsWidget: resizeEvent"); // int w=e->size().width(); // int h=e->size().height(); // int w0=e->oldSize().width(); // int h0=e->oldSize().height(); // qreal fX= (double)(w)/(double)(w0); // qreal fY= (double)(h)/(double)(h0); // foreach (QGraphicsItem *item, scene()->items()) { // qDebug ("item will move by %f, %f", fX, fY); // if (Node *node = qgraphicsitem_cast(item) ) { // qDebug("Node original position %f, %f", item->x(), item->y()); // qDebug("Node will move to %f, %f",item->x()*fX, item->y()*fY); // node->setPos(mapToScene(item->x()*fX, item->y()*fY)); // } // else item->setPos(mapToScene(item->x()*fX, item->y()*fY)); // } // emit windowResized(w, h); } /** Destructor. */ GraphicsWidget::~GraphicsWidget(){ } socnetv-1.9/src/filteredgesbyweightdialog.cpp0000775000175000017500000000502012542300240022003 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt filteredgesbyweightdialog.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "filteredgesbyweightdialog.h" #include #include FilterEdgesByWeightDialog::FilterEdgesByWeightDialog (QWidget *parent) : QDialog (parent) { ui.setupUi(this); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.overThresholdBt)-> setChecked(true); } void FilterEdgesByWeightDialog::gatherData(){ qDebug()<< "Dialog: gathering Data!..."; bool overThreshold=false; float my_threshold = static_cast ( (ui.weightThreshold)->value() ); if ( ui.overThresholdBt -> isChecked() ) { qDebug()<< "Dialog: We will filter edges weighted more than threshold: " << my_threshold; overThreshold = true; } else { qDebug()<< "Dialog: We will filter edges weighted less than threshold: " << my_threshold; overThreshold = false; } qDebug()<< "Dialog: emitting userChoices" ; emit userChoices( my_threshold, overThreshold ); } socnetv-1.9/src/edgeweight.cpp0000775000175000017500000000416512542300240016710 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt edgeweight.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "edgeweight.h" #include "edge.h" #include #include EdgeWeight::EdgeWeight( Edge *link , int size, QString labelText) : QGraphicsTextItem( 0) { qDebug()<< "EdgeWeight:: creating new edgeweight and attaching it to link"; link -> addWeight(this); setPlainText( labelText ); setParentItem(link); //auto disables child items like this, when link is disabled. this->setFont( QFont ("Courier", size, QFont::Light, true) ); setZValue(253); } void EdgeWeight::move(double x, double y) { this -> move(x,y); } EdgeWeight::~EdgeWeight() { } socnetv-1.9/src/graphicswidget.h0000775000175000017500000001255312542300240017245 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt graphicswidget.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef GRAPHICSWIDGET_H #define GRAPHICSWIDGET_H #include class MainWindow; class Node; class Edge; class NodeNumber; class NodeLabel; class Guide; class EdgeWeight; typedef QHash H_StrToEdge; typedef QHash H_NumToNode; class GraphicsWidget : public QGraphicsView { Q_OBJECT public: GraphicsWidget(QGraphicsScene*, MainWindow* parent); ~GraphicsWidget(); void clear(); Node* hasNode(QString text); // Node* hasNode(int number); bool setMarkedNode(QString text); QList selectedItems(); void selectAll(); void selectNone(); void removeItem(Edge*); void removeItem(Node*); void removeItem(NodeNumber*); void removeItem(NodeLabel*); void nodeMoved(int, int, int); void setInitNodeColor(QString); void setInitLinkColor(QString); void setInitNodeSize(int); void setInitNumberDistance(int); void setInitLabelDistance(int); void setNumbersInsideNodes(bool); bool setEdgeWeight(int, int, float); void setAllItemsVisibility(int, bool); void removeAllItems(int); protected: void wheelEvent(QWheelEvent *event); void mouseDoubleClickEvent ( QMouseEvent * e ); void mousePressEvent ( QMouseEvent * e ); void mouseReleaseEvent(QMouseEvent * e ); void resizeEvent( QResizeEvent *e ); void paintEvent ( QPaintEvent * event ); public slots: void changeRelation(int relation); void drawNode( int i, int size, QString aColor, QString nColor, int nSize, QString label, QString lColor, int lSize, QPointF p, QString nodeShape, bool showLabels, bool labelIn, bool showNumbers ); void eraseNode(long int doomedJim); void setNodeVisibility(long int, bool ); //Called from Graph via MW bool setNodeColor(long int, QString); bool setNodeLabel(long int , QString ); void openNodeContextMenu(); void nodeClicked(Node *); void moveNode(int, qreal, qreal); //Called from Graph when creating random nets. bool setNodeSize(long int, int size=0); void setAllNodeSize(int size=0); void drawEdge(int, int, float, bool, bool, QString, bool); void eraseEdge(int, int); void setEdgeVisibility (int relation, int, int, bool); void setEdgeColor(long int, long int, QString); void edgeClicked(Edge *); void openEdgeContextMenu(); void changeZoom(const int value); void startEdge(Node *node); void drawEdgeReciprocal(int, int); void unmakeEdgeReciprocal(int, int); void clearGuides(); void addGuideCircle( int x0, int y0, int radius); void addGuideHLine(int y0); void zoomIn(); void zoomOut(); void rot(int angle); signals: void windowResized(int,int); void userDoubleClicked(int, QPointF); void userMiddleClicked(int, int, float); void openNodeMenu(); void openEdgeMenu(); void openContextMenu(const QPointF p); void updateNodeCoords(int, int, int); void selectedNode(Node *); void selectedEdge(Edge *); void zoomChanged(int); private: H_NumToNode nodeHash; //This is used in drawEdge() method H_StrToEdge edgesHash; // helper hash to easily find edges int m_curRelation; int timerId, layoutType, m_nodeSize, m_numberDistance, m_labelDistance; double m_currentScaleFactor; int m_currentRotationAngle; int zoomIndex, markedNodeOrigSize,markedEdgeSourceOrigSize, markedEdgeTargetOrigSize; QString m_nodeLabel, m_numberColor, m_nodeColor, m_labelColor, m_linkColor; bool secondDoubleClick, dynamicMovement, markedNodeExist, markedEdgeExist; QGraphicsItem *moving; QPointF startPoint, endPoint; Node *firstNode, *secondNode, *markedNode1, *markedEdgeSource; Node *markedEdgeTarget, *tempNode ; }; #endif socnetv-1.9/src/graph.cpp0000775000175000017500000151442212542300240015700 0ustar dimitrisdimitris/****************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt graph.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net *******************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "graph.h" #include #include #include #include //used for qDebug messages #include #include #include #include //allows the use of RAND_MAX macro #include #include //for BFS queue Q #include // for makeThingsLookRandom static qreal Pi = 3.14159265; Graph::Graph() { m_totalVertices=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; order=true; //returns true if the indexes of the list is ordered. graphModified=false; m_undirected=false; symmetricAdjacencyMatrix=true; adjacencyMatrixCreated=false; reachabilityMatrixCreated=false; distanceMatrixCreated=false; calculatedDP=false; calculatedDC=false; calculatedIC=false; calculatedCentralities=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; m_precision = 5; m_curRelation=0; dynamicMovement=false; timerId=0; layoutType=0; file_parser = 0; wc_parser = 0; wc_spider = 0; // edgesHash.reserve(40000); influenceDomains.reserve(1000); influenceRanges.reserve(1000); } /** * @brief Graph::changeRelation * Called from MW and Parser * @param relation */ void Graph::changeRelation(int relation){ qDebug() << "++ Graph::changeRelation(int) to relation " << relation << " current relation is " << m_curRelation ; if (m_curRelation == relation ) { qDebug() << "++ Graph::changeRelation(int) - same relation - END"; return; } if ( relation < 0) { qDebug() << "++ Graph::changeRelation(int) - negative relation - END "; return; } QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; (*it)->changeRelation(relation); } m_curRelation = relation; emit relationChanged(m_curRelation); graphModified=true; emit graphChanged(); } /** * @brief Graph::addRelationFromUser * Called from MW to add a relation and change to that new relation * @param newRelation */ void Graph::addRelationFromUser(QString newRelation){ m_relationsList << newRelation; qDebug() << "Graph::addRelationFromUser(string) " << newRelation << " total relations now " << relations() ; } /** * @brief Graph::addRelationFromGraph * Called when creating random networks * emits addRelationToMW * @param newRelation */ void Graph::addRelationFromGraph(QString newRelation) { qDebug() << "Graph::addRelationFromGraph(string) " << newRelation; m_relationsList << newRelation; emit addRelationToMW(newRelation); } /** * @brief Graph::addRelationFromParser * Called by file parser to add a new relation * emits addRelationToMW * @param newRelation */ void Graph::addRelationFromParser(QString newRelation) { qDebug() << "Graph::addRelationFromParser(string) " << newRelation; m_relationsList << newRelation; emit addRelationToMW(newRelation); } /** * @brief Graph::currentRelation * @return int current relation index */ int Graph::currentRelation(){ return m_curRelation; } int Graph::relations(){ //qDebug () << " relations count " << m_relationsList.count(); return m_relationsList.count(); } /** main node creation slot, associated with homonymous signal from Parser. Adds a Vertex to the Graph and calls addNode of GraphicsWidget p holds the desired position of the new node. The new Vertex is named i and stores its color, label, label color, shape and position p. */ void Graph::createVertex(int i, int size, QString nodeColor, QString numColor, int numSize, QString label, QString lColor, int lSize, QPointF p, QString nodeShape, bool signalMW){ int value = 1; addVertex(i, value, size, nodeColor, numColor, numSize, label, lColor, lSize, p, nodeShape); emit drawNode( i, size, nodeColor, numColor, numSize, label, lColor, lSize, p, nodeShape, initShowLabels, initNumbersInsideNodes, true); if (signalMW) emit graphChanged(); //draw new user-clicked nodes with the same color with that of the file loaded initVertexColor=nodeColor; initVertexShape=nodeShape; initVertexSize=size; } /** auxilliary node creation slot. Called from GW, with i and p as parameters. p holds the desired position of the new node. Calls the main creation slot with init node values. */ void Graph::createVertex(int i, QPointF p){ if ( i < 0 ) i = lastVertexNumber() +1; qDebug() << "Graph::createVertex() " << i << " fixed coords."; createVertex( i, initVertexSize, initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number(i), initVertexLabelColor, initVertexLabelSize, p, initVertexShape, true ); } /** second auxilliary node creation slot. Called from MW only with parameter i. Calculates a random position p from canvasWidth and Height. Then calls the main creation slot with init node values. */ void Graph::createVertex(int i, int cWidth, int cHeight){ if ( i < 0 ) i = lastVertexNumber() +1; qDebug() << "Graph::createVertex() " << i << " random coords."; QPointF p; p.setX(rand()%cWidth); p.setY(rand()%cHeight); createVertex( i, initVertexSize, initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number(i), initVertexLabelColor, initVertexLabelSize, p, initVertexShape, true ); } /** third auxilliary node creation slot. Called from WebCrawler with parameter i. Calculates a random position p from canvasWidth and Height. Then calls the main creation slot with init node values. */ void Graph::createVertexWebCrawler(QString label, int i) { if ( i < 0 ) i = lastVertexNumber() +1; qDebug() << "Graph::createVertexWebCrawler() " << i << " rand coords with label"; QPointF p; p.setX(rand()%canvasWidth); p.setY(rand()%canvasHeight); createVertex( i, initVertexSize, initVertexColor, initVertexNumberColor, initVertexNumberSize, label, initVertexLabelColor, initVertexLabelSize, p, initVertexShape, true ); } void Graph::setCanvasDimensions(int w, int h){ qDebug() << "Graph:: setCanvasDimensions() to " << w << " " << h ; canvasWidth = w; canvasHeight= h; } /** * @brief Graph::createEdge Called from homonymous signal of Parser class. Adds an Edge to the Graph, then emits drawEdge() which calls GraphicsWidget::addEdge() to draw the new edge. Also called from MW when user clicks on the "add link" button Alse called from GW (via createEdge() below) when user middle-clicks. * @param v1 * @param v2 * @param weight * @param color * @param reciprocal * @param drawArrows * @param bezier */ void Graph::createEdge(int v1, int v2, float weight, QString color, int reciprocal=0, bool drawArrows=true, bool bezier=false){ qDebug()<<"-- Graph::createEdge() - " << v1 << " -> " << v2 << " weight " << weight << " reciprocal " << reciprocal; // check whether there is already such an edge // (see #713617 - https://bugs.launchpad.net/socnetv/+bug/713617) if (!hasArc(v1,v2)){ if ( reciprocal == 2) { qDebug()<< "-- Graph::createEdge() - " << "Creating RECIPROCAL edge - emitting drawEdge signal to GW"; addEdge ( v1, v2, weight, color, reciprocal); emit drawEdge(v1, v2, weight, reciprocal, drawArrows, color, bezier); } else if (this->hasArc( v2, v1) ) { qDebug()<<"-- Graph::createEdge() - Opposite arc exists. " << " Emitting drawEdgeReciprocal to GW "; reciprocal = 1; addEdge ( v1, v2, weight, color, reciprocal); emit drawEdgeReciprocal(v2, v1); } else { qDebug()<< "-- Graph::createEdge() - " << "Opposite arc does not exist. Emitting drawEdge to GW..."; reciprocal = 0; addEdge ( v1, v2, weight, color, reciprocal); emit drawEdge(v1, v2, weight, reciprocal, drawArrows, color, bezier); } } else { qDebug() << "-- Graph::createEdge() - " << "Edge " << v1 << " -> " << v2 << " declared previously (exists) - nothing to do \n\n"; } //draw new edges the same color with those of the file loaded, // on user clicks on the canvas initEdgeColor=color; emit graphChanged(); } /** Called (via MW::addLink()) from GW when user middle-clicks on two nodes. Calls the above createEdge() method with initEdgeColor to set the default edge color. */ void Graph::createEdge(int v1, int v2, float weight, int reciprocal=0, bool drawArrows=true, bool bezier=false){ qDebug()<< "Graph::createEdge() - " << v1<< " -> " << v2 ; createEdge(v1, v2, (float) weight, initEdgeColor, reciprocal, drawArrows, bezier); } /** Called from WebCrawler when it finds an new link Calls the above createEdge() method with initEdgeColor */ void Graph::createEdgeWebCrawler (int source, int target){ qDebug()<< " Graph::createEdgeWebCrawler() - from " << source << " to " << target ; float weight = 1.0; bool reciprocal=false; bool drawArrows=true; bool bezier=false; createEdge(source, target, weight, initEdgeColor, reciprocal, drawArrows, bezier); } /** * @brief Graph::removeDummyNode * This is called from loadPajek method of Parser in order to delete any * redundant (dummy) nodes. * @param i */ void Graph::removeDummyNode(int i){ qDebug("**Graph: RemoveDummyNode %i", i); removeVertex(i); } /** * @brief Graph::addVertex * Adds a Vertex named v1, valued val, sized nszm colored nc, labeled nl, * labelColored lc, shaped nsp, at point p. * This method is called by createVertex() method * @param v1 * @param val * @param size * @param color * @param numColor * @param numSize * @param label * @param labelColor * @param labelSize * @param p * @param shape */ void Graph::addVertex ( int v1, int val, int size, QString color, QString numColor, int numSize, QString label, QString labelColor, int labelSize, QPointF p, QString shape ){ qDebug() << "Graph::addVertex() "; if (order) index[v1]=m_totalVertices; else index[v1]=m_graph.size(); m_graph.append( new Vertex (this, v1, val, m_curRelation , size, color, numColor, numSize, label, labelColor, labelSize, p, shape ) ); m_totalVertices++; // qDebug() << "Graph: addVertex(): Vertex named " << m_graph.back()->name() // << " appended with index= "<0) return m_graph.back()->name(); else return 0; } /** Returns the name of the first vertex. Used by slotRemoveNode of MW */ int Graph::firstVertexNumber() { if (m_totalVertices>0) return m_graph.front()->name(); else return 0; } /** Removes the vertex named Doomed from the graph First, it removes all edges to Doomed from other vertices Then it changes the index of all subsequent vertices inside m_graph Finally, it removes the vertex. */ void Graph::removeVertex(long int Doomed){ qDebug() << "Graph: removeVertex - Doomed: " << m_graph[ index[Doomed] ]->name() << " indexOfDoomed= " << index[Doomed] ; long int indexOfDoomed=index[Doomed]; //Remove links to Doomed from each other vertex QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it)->hasEdgeTo(Doomed) != 0) { qDebug()<< "Graph: Vertex " << (*it)->name() << " is linked to doomed "<< Doomed << " and has " << (*it)->outEdges() << " and " << (*it)->outDegree() ; if ( (*it)->outEdges() == 1 && (*it)->hasEdgeFrom(Doomed) != 0 ) { // qDebug() << "Graph: decreasing reciprocalEdgesVert"; (*it)->setReciprocalLinked(false); } (*it)->removeEdgeTo(Doomed) ; } if ( (*it)->hasEdgeFrom(Doomed) != 0 ) { (*it)->removeEdgeFrom(Doomed); } } qDebug()<< "Graph: Finished with vertices. Update the index which maps vertices inside m_graph " ; long int prevIndex=indexOfDoomed; qDebug () << " Updating index of all subsequent vertices "; H_Int::const_iterator it1=index.cbegin(); while (it1 != index.cend()){ if ( it1.value() > indexOfDoomed ) { prevIndex = it1.value(); qDebug() << "Graph::removeVertex - vertex " << it1.key() << " had prevIndex: " << prevIndex << " > indexOfDoomed " << indexOfDoomed << " Setting new index. Index size was: "<< index.size(); index.insert( it1.key(), --prevIndex) ; qDebug() << "Graph::removeVertex - vertex " << it1.key() << " new index: " << index.value( it1.key(), -666) << " Index size now: "<< index.size(); } else { qDebug() << "Graph::removeVertex " << it1.key() << " with index " << it1.value() << " < indexOfDoomed. CONTINUE"; } ++it1; } //Now remove vertex Doomed from m_graph qDebug() << "Graph: graph vertices=size="<< vertices() << "=" << m_graph.size() << " removing vertex at index " << indexOfDoomed ; m_graph.removeAt( indexOfDoomed ) ; m_totalVertices--; qDebug() << "Graph: Now graph vertices=size="<< vertices() << "=" << m_graph.size() << " total edges now " << enabledEdges(); order=false; graphModified=true; emit graphChanged(); emit eraseNode(Doomed); } /** * Creates an edge between v1 and v2 */ void Graph::addEdge (int v1, int v2, float weight, QString color, int reciprocal) { int source=index[v1]; int target=index[v2]; qDebug()<< "Graph: addEdge() from vertex "<< v1 << "["<< source << "] to vertex "<< v2 << "["<< target << "] of weight "<addEdgeTo(v2, weight ); m_graph [ target ]->addEdgeFrom(v1, weight); if (reciprocal == 1){ m_graph [ source ]->setReciprocalLinked(true); m_graph [ target ]->setReciprocalLinked(true); } else if (reciprocal == 2){ m_graph [ source ]->setReciprocalLinked(true); m_graph [ target ]->setReciprocalLinked(true); m_graph [ target ]->addEdgeTo(v1, weight ); m_graph [ source ]->addEdgeFrom(target, weight); } // qDebug()<<"Graph: addEdge() now a("<< v1 << ","<< v2<< ") = " << weight // << " with color "<< color // <<" . Storing edge color..." ; m_graph[ source]->setOutLinkColor(v2, color); graphModified=true; } /** Change edge (arc) weight between v1 and v2 */ void Graph::setArcWeight (const long &v1, const long &v2, const float &weight) { qDebug() << "Graph::setArcWeight between " << v1 << "[" << index[v1] << "] and " << v2 << "[" << index[v2] << "]" << " = " << weight; m_graph [ index[v1] ]->changeOutEdgeWeight(v2, weight); graphModified=true; emit graphChanged(); } /** Removes the edge (arc) between v1 and v2 */ void Graph::removeEdge (int v1, int v2) { qDebug ()<< "\n\n Graph::removeEdge() edge from " << v1 << " index " << index[v1] << " to " << v2 << " to be removed from graph"; m_graph [ index[v1] ]->removeEdgeTo(v2); m_graph [ index[v2] ]->removeEdgeFrom(v1); // qDebug()<< "Graph: removeEdge between " << v1 << " i " << index[v1] // << " and " << v2 << " i "<< index[v2] // << " NOW vertex v1 reports edge weight " // << m_graph [ index[v1] ]->hasEdgeTo(v2) ; if ( this->hasArc(v2,v1) !=0) symmetricAdjacencyMatrix=false; graphModified=true; emit eraseEdge(v1,v2); emit graphChanged(); } //Called by MW to start a web crawler... void Graph::webCrawl( QString seed, int maxNodes, int maxRecursion, bool extLinks, bool intLinks){ qDebug() << "Graph::webCrawl() - seed " << seed ; //WebCrawler *crawler = new WebCrawler; qDebug() << "Graph::webCrawl() Creating wc_spider & wc_parser objects"; WebCrawler_Parser *wc_parser = new WebCrawler_Parser(seed, maxNodes, maxRecursion, extLinks, intLinks); WebCrawler_Spider *wc_spider = new WebCrawler_Spider (seed, maxNodes, maxRecursion, extLinks, intLinks); qDebug() << "Graph::webCrawl() Moving parser & spider to new QThreads!"; qDebug () << " graph thread " << this->thread(); qDebug () << " wc_parser thread " << wc_parser->thread(); qDebug () << " wc_spider thread " << wc_spider->thread(); wc_parser->moveToThread(&wc_parserThread); wc_spider->moveToThread(&wc_spiderThread); qDebug () << " graph thread is " << this->thread(); qDebug () << " wc_parser thread now " << wc_parser->thread(); qDebug () << " wc_spider thread now " << wc_spider->thread(); qDebug() << "Graph::webCrawl() Connecting signals from/to parser & spider"; connect(&wc_parserThread, &QThread::finished, wc_parser, &QObject::deleteLater); connect(&wc_spiderThread, &QThread::finished, wc_spider, &QObject::deleteLater); connect(this, &Graph::operateSpider, wc_spider, &WebCrawler_Spider::get); connect(wc_parser, &WebCrawler_Parser::signalCreateNode, this, &Graph::createVertexWebCrawler); connect(wc_parser, &WebCrawler_Parser::signalCreateEdge, this, &Graph::createEdgeWebCrawler); connect (wc_spider, &WebCrawler_Spider::finished, this, &Graph::terminateCrawlerThreads); connect (wc_parser, &WebCrawler_Parser::finished, this, &Graph::terminateCrawlerThreads); connect (wc_spider, &WebCrawler_Spider::parse, wc_parser, &WebCrawler_Parser::parse ); connect (wc_parser, &WebCrawler_Parser::startSpider, wc_spider, &WebCrawler_Spider::get ); qDebug() << "Graph::webCrawl() Starting parser & spider QThreads!"; wc_parserThread.start(); wc_spiderThread.start(); qDebug() << "Graph::webCrawl() Creating initial node 1, url: " << seed; createVertexWebCrawler(seed, 1); qDebug() << "Graph::webCrawl() calling spider get() for that url!"; emit operateSpider(); qDebug("Graph::webCrawl() - reach the end - See the threads running? "); } //called from Graph, when closing network, to terminate all processes //also called indirectly when wc_spider finishes void Graph::terminateCrawlerThreads (QString reason){ qDebug() << "Graph::terminateCrawlerThreads() - reason " << reason; qDebug() << "Graph::terminateCrawlerThreads() check if wc_parserThread is running..."; if (wc_parserThread.isRunning() ) { qDebug() << "Graph::terminateCrawlerThreads() parser thread quit"; wc_parserThread.quit(); qDebug() << "Graph::terminateCrawlerThreads() - deleting wc_parser pointer"; delete wc_parser; wc_parser = 0; // see why here: https://goo.gl/tQxpGA } qDebug() << "Graph::terminateCrawlerThreads() check if wc_spiderThread is running..."; if (wc_spiderThread.isRunning() ) { qDebug() << "Graph::terminateCrawlerThreads() spider thread quit"; wc_spiderThread.quit(); qDebug() << "Graph::terminateCrawlerThreads() - deleting wc_spider pointer"; delete wc_spider; wc_spider= 0; // see why here: https://goo.gl/tQxpGA emit signalNodeSizesByInDegree(true); } } /** Called from filterOrphanNodes via MainWindow to filter nodes with no links For each orphan Vertex in the Graph, emits the filterVertex() */ void Graph::filterIsolateVertices(bool filterFlag){ qDebug() << "*** Graph::filterIsolateVertices() " << " setting all isolate nodes to " << filterFlag; QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( !(*it)->isIsolated() ){ continue; } else { qDebug() << "Graph::filterOrphanNodes() Vertex " << (*it)->name() << " isolate. Toggling it and emitting setVertexVisibility signal to GW..."; (*it)->setEnabled (filterFlag) ; graphModified=true; emit graphChanged(); emit setVertexVisibility( (*it)-> name(), filterFlag ); } } } /** Called from filterEdgesDialog via MainWindow to filter edges over or under a specified weight (m_threshold) For each Vertex in the Graph, calls the homonymous method of Vertex class. */ void Graph::filterEdgesByWeight(float m_threshold, bool overThreshold){ if (overThreshold) qDebug() << "Graph: filterEdgesByWeight() over " << m_threshold ; else qDebug() << "Graph: filterEdgesByWeight() below "<< m_threshold ; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ (*it)->filterEdgesByWeight ( m_threshold, overThreshold ); } graphModified=true; emit graphChanged(); emit statusMessage("Edges have been filtered."); } /** * @brief Graph::filterEdgesByRelation * Not called by Called from MW to filter out all edges of a given relation * calls the homonymous method of Vertex class. * @param relation */ void Graph::filterEdgesByRelation(int relation, bool status){ qDebug() << "Graph::filterEdgesByRelation() " ; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; (*it)->filterEdgesByRelation ( relation, status ); } } void Graph::slotSetEdgeVisibility ( int relation, int source, int target, bool visible) { //qDebug() << "Graph: slotSetEdgeVisibility - emitting signal to GW"; emit setEdgeVisibility ( relation, source, target, visible); } /** Checks if there is a specific vertex in the graph Returns the index or -1 Complexity: O(logN) for index retrieval */ int Graph::hasVertex(long int num){ qDebug () << "Graph: hasVertex() v: " << num << " with index " << index[num] << " named " << m_graph[ index[num]] ->name(); if ( m_graph[ index[num]] ->name() == num) return index[num]; else return -1; } /** Checks if there is a vertex with a specific label in the graph Returns the index or -1 Complexity: O(N) */ int Graph::hasVertex(QString label){ qDebug ()<<"Graph: hasVertex( "<< label.toUtf8() <<" ) ?" ; QList::const_iterator it; int i=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it) ->label() == label) { // qDebug()<< "Graph: hasVertex() at pos %i" << i; return i; } i++; } return -1; } void Graph::setInitVertexSize (const long int size) { initVertexSize=size; } //Changes the size.of vertex v void Graph::setVertexSize(const long int &v, const int &size) { m_graph[ index[v] ]->setSize(size); graphModified=true; emit graphChanged(); emit setNodeSize(v, size); } int Graph::vertexSize(const long &v ) { return m_graph[ index[v] ]-> size(); } //Changes the size.of all vertices void Graph::setAllVerticesSize(const int &size) { setInitVertexSize(size); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { (*it)->setSize(size) ; emit setNodeSize((*it)->name(), size); } } graphModified=true; emit graphChanged(); } void Graph::setInitVertexShape(const QString shape) { initVertexShape=shape; } //Changes the shape.of vertex v void Graph::setVertexShape(const int v1, const QString shape){ m_graph[ index[v1] ]->setShape(shape); emit setNodeShape( v1, shape); graphModified=true; emit graphChanged(); } //returns the shape of this vertex QString Graph::vertexShape(const int &v1){ return m_graph[ index[v1] ]->shape(); } void Graph::setAllVerticesShape(const QString shape) { setInitVertexShape(shape); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { (*it)->setShape(shape); emit setNodeShape((*it)->name(), shape); } } graphModified=true; emit graphChanged(); } //Changes the initial color of vertices numbers void Graph::setInitVertexNumberColor (QString color) { initVertexNumberColor = color; } //Changes the initial size of vertices numbers void Graph::setInitVertexNumberSize (int size) { initVertexNumberSize = size; } /**Changes the label.of vertex v */ void Graph::setVertexLabel(int v1, QString label){ qDebug()<< "Graph: setVertexLabel for "<< v1 << ", index " << index[v1]<< " with label"<< label; m_graph[ index[v1] ]->setLabel ( label); emit setNodeLabel ( m_graph[ index[v1] ]-> name(), label); graphModified=true; emit graphChanged(); } //Changes the init size of new vertices labels void Graph::setInitVertexLabelSize(int newSize) { initVertexLabelSize = newSize; } //Changes the size of a vertex label void Graph::setVertexLabelSize(int v1, int newSize) { qDebug()<< "Graph: setVertexLabelSize for "<< v1 << ", index " << index[v1]<< " with size "<< newSize; m_graph[ index[v1] ] -> setLabelSize ( newSize ); graphModified=true; emit graphChanged(); } //Changes the shape.of vertex v void Graph::setVertexLabelColor(int v1, QString color){ m_graph[ index[v1] ]->setLabelColor(color); graphModified=true; emit graphChanged(); } void Graph::setInitVertexLabelColor(QString color){ initVertexLabelColor=color; } QString Graph::vertexLabel(const long int &v1){ return m_graph[ index[v1] ]->label (); } /** Changes the color of vertex v1 */ void Graph::setVertexColor(const long int &v1, const QString &color){ qDebug()<< "Graph: setVertexColor for "<< v1 << ", index " << index[v1]<< " with color "<< color; m_graph[ index[v1] ]->setColor ( color ); emit setNodeColor ( m_graph[ index[v1] ]-> name(), color ); graphModified=true; emit graphChanged(); } QColor Graph::vertexColor(const long int &v1){ return QColor ( m_graph[ index[v1] ] -> color() ) ; } void Graph::setInitVertexColor(const QString &color){ initVertexColor=color; } void Graph::setAllVerticesColor(const QString &color) { qDebug() << "*** Graph::setAllVerticesColor() " << " to " << color; setInitVertexColor(color); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::setAllVerticesColor() Vertex " << (*it)->name() << " new color " << color; (*it)->setColor(color) ; emit setNodeColor ( (*it)-> name(), color ); } } graphModified=true; emit graphChanged(); } void Graph::setInitEdgeColor(const QString &color){ initEdgeColor=color; } //Returns the edgeColor QString Graph::edgeColor (const long &v1, const long &v2){ return m_graph[ index[v1] ]->outLinkColor(v2); } /** Changes the color of all edges. */ bool Graph::setAllEdgesColor(const QString &color){ qDebug()<< "\n\nGraph::setAllEdgesColor()" << color; int target=0, source=0; setInitEdgeColor(color); QHash *enabledOutEdges = new QHash; QHash::const_iterator it1; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ //updateProgressDialog(++count); source = (*it)->name(); if ( ! (*it)->isEnabled() ) continue; enabledOutEdges=(*it)->returnEnabledOutEdges(); it1=enabledOutEdges->cbegin(); while ( it1!=enabledOutEdges->cend() ){ target = it1.key(); // qDebug() << "=== Graph::setAllEdgesColor() : " // << source << "->" << target << " new color " << color; (*it)->setOutLinkColor(target, color); emit changeEdgeColor(source, target, color); ++it1; } } delete enabledOutEdges; graphModified=true; emit graphChanged(); return true; } /** Changes the color of edge (s,t). */ void Graph::setEdgeColor(const long &v1, const long &v2, const QString &color){ qDebug()<< "Graph::setEdgeColor() - "<< v1 << " -> "<< v2 <<" index ("<< index[v1]<< " -> "<setOutLinkColor(v2, color); emit changeEdgeColor(v1, v2, color); if (isSymmetric()) { m_graph[ index[v2] ]->setOutLinkColor(v1, color); emit changeEdgeColor(v2, v1, color); } graphModified=true; emit graphChanged(); } /** Checks if there is a directed edge (arc) from v1 to v2 Complexity: O(logN) for index retrieval + O(1) for QList index retrieval + O(logN) for checking edge(v2) */ float Graph::hasArc (const long int &v1, const long int &v2) { //qDebug() << "Graph::hasArc() " << v1 << " -> " << v2 << " ? " ; return m_graph[ index[v1] ]->hasEdgeTo(v2); } /** Checks if there is a edge between v1 and v2 (both arcs exist) */ bool Graph::hasEdge (const int &v1, const long &v2) { qDebug() << "Graph::hasEdge() " << v1 << " <-> " << v2 << " ? " ; return ( ( m_graph[ index[v1] ]->hasEdgeTo(v2) != 0 ) && ( m_graph[ index[v2] ]->hasEdgeTo(v1) != 0) ) ? true: false; } /** Returns |E| of graph */ int Graph::enabledEdges() { int recountedEdges=0; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ recountedEdges+=(*it)->outEdges(); } qDebug() << "Graph::enabledEdges() - edges recounted: " << recountedEdges; return recountedEdges; } void Graph::edges(){ H_edges::const_iterator it1; QList::const_iterator it; int relation=0,source=0, target=0, w=0; float weight=0; Q_UNUSED(weight); bool edgeStatus=false; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( ! (*it)->isEnabled() ) continue ; source = index[ (*it)->name() ]; it1=m_graph [ source ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ source ] -> m_outEdges.cend() ){ relation = it1.value().first; if ( relation != currentRelation() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } target = it1.key(); w=index[ target ]; weight = it1.value().second.first; qDebug("u=%i is connected with node %i of index w=%i. ", source, target, w); ++it1; } } } /** Called from MainWindow */ void Graph::updateVertCoords(int v1, int x, int y){ //qDebug("Graph: updateVertCoords() for %i with index %i with %i, %i", v1, index[v1], x,y); m_graph[ index[v1] ]->setX( x ); m_graph[ index[v1] ]->setY( y ); graphModified=true; } /** * @brief Graph::outboundEdges * *Returns the number of outbound edges (arcs) from vertex v1 * @param v1 * @return */ int Graph::outboundEdges(int v1) { qDebug("Graph: outboundEdges()"); return m_graph[ index[v1] ]->outEdges(); } /** * @brief Graph::inboundEdges * Returns the number of inbound edges (arcs) to vertex v1 * @param v1 * @return int */ int Graph::inboundEdges (int v1) { qDebug("Graph: inboundEdges()"); return m_graph[ index[v1] ]->inEdges(); } /** * @brief Graph::outDegree * Returns the outDegree (sum of outEdges weights) of vertex v1 * @param v1 * @return */ int Graph::outDegree (int v1) { qDebug("Graph: outDegree()"); return m_graph[ index[v1] ]->outDegree(); } /** Returns the inDegree (sum of inEdges weights) of vertex v1 */ int Graph::inDegree (int v1) { qDebug("Graph: inDegree()"); return m_graph[ index[v1] ]-> inDegree(); } /** Returns |V| of graph */ int Graph::vertices(const bool dropIsolates, const bool countAll) { qDebug("Graph: vertices()"); m_totalVertices=0; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (countAll) { ++m_totalVertices; } else { if (dropIsolates && (*it)->isIsolated()) continue; ++m_totalVertices; } } return m_totalVertices; } /** Returns a list of all isolated vertices inside the graph */ QList Graph::verticesIsolated(){ qDebug()<< "Graph::verticesIsolated()"; if (!graphModified){ return m_isolatedVerticesList; } QList::const_iterator it; m_isolatedVerticesList.clear(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ // if ( ! (*it)->isEnabled() ) // continue; if ((*it)->isIsolated()) { m_isolatedVerticesList << (*it)->name(); qDebug()<< "Graph::verticesIsolated() - node " << (*it)->name() << " is isolated. Marking it." ; } } return m_isolatedVerticesList ; } /** * Returns ratio of present edges to total possible edges. */ float Graph::density() { qDebug("Graph: density()"); int vert=vertices(); if (vert!=0 && vert!=1) return (float) enabledEdges() / (float)(vert*(vert-1.0)); else return 0; } /** * Checks if the graph is weighted, i.e. if any e in |E| has value > 1 * O(n^2) */ bool Graph::isWeighted(){ qDebug("Graph: isWeighted()"); QList::const_iterator it, it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ( this->hasArc ( (*it1)->name(), (*it)->name() ) ) > 1 ) { return true; } } } return false; } /** Returns the sum of vertices having outboundEdges */ int Graph::verticesWithOutboundEdges(){ return outboundEdgesVert; } /** Returns the sum of vertices having inboundEdges */ int Graph::verticesWithInboundEdges(){ return inboundEdgesVert; } /** Returns the sum of vertices having reciprocal edges */ int Graph:: verticesWithReciprocalEdges(){ return reciprocalEdgesVert; } /** Clears all vertices */ void Graph::clear() { qDebug("Graph::clear() m_graph reports size %i", m_graph.size()); qDeleteAll(m_graph.begin(), m_graph.end()); m_graph.clear(); index.clear(); //clear relations m_relationsList.clear(); m_curRelation=0; discreteDPs.clear(); discreteDCs.clear(); discreteCCs.clear(); discreteBCs.clear(); discreteSCs.clear(); discreteIRCCs.clear(); discreteECs.clear(); discreteEccentricities.clear(); discretePCs.clear(); discreteICs.clear(); discretePRPs.clear(); discretePPs.clear(); if ( DM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; DM.clear(); } if ( TM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; TM.clear(); } if ( sumM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; sumM.clear(); } if ( invAM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; invAM.clear(); } if ( AM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; AM.clear(); } if ( invM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; invM.clear(); } if ( XM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; XM.clear(); } if ( XSM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; XSM.clear(); } if ( XRM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; XRM.clear(); } m_isolatedVerticesList.clear(); disconnectedVertices.clear(); unilaterallyConnectedVertices.clear(); influenceDomains.clear(); influenceRanges.clear(); triadTypeFreqs.clear(); m_totalVertices=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; order=true; //returns true if the indexes of the list is ordered. m_undirected=false; calculatedDP=false; calculatedDC=false; calculatedIC=false; calculatedCentralities=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; adjacencyMatrixCreated=false; reachabilityMatrixCreated=false; graphModified=false; symmetricAdjacencyMatrix=true; qDebug ()<< "Graph::clear() -Do parser threads run ?"; terminateParserThreads("Graph::initNet()"); qDebug ()<< "Graph::clear() -Do web crawler threads run ?"; terminateCrawlerThreads("Graph::initNet"); qDebug("Graph: m_graph cleared. Now reports size %i", m_graph.size()); } /** * @brief Graph::isSymmetric * Returns TRUE if the adjacency matrix of the current relation is symmetric * @return bool */ bool Graph::isSymmetric(){ qDebug() << "Graph::isSymmetric() "; if (!graphModified){ return symmetricAdjacencyMatrix; } symmetricAdjacencyMatrix=true; int y=0, v2=0, v1=0; QHash *enabledOutEdges = new QHash; QHash::const_iterator hit; QList::const_iterator lit; for ( lit = m_graph.cbegin(); lit != m_graph.cend(); ++lit) { v1 = (*lit) -> name(); if ( ! (*lit)->isEnabled() ) continue; qDebug() << "Graph::isSymmetric() - Graph modified! " << " Iterate over all edges of " << v1 ; enabledOutEdges=(*lit)->returnEnabledOutEdges(); hit=enabledOutEdges->cbegin(); while ( hit!=enabledOutEdges->cend() ){ v2 = hit.key(); y=index[ v2 ]; float weight = hit.value(); if ( m_graph[y]->hasEdgeTo( v1) != weight) { symmetricAdjacencyMatrix=false; // qDebug() <<"Graph::isSymmetric() - " // << " graph not symmetric because " // << v1 << " -> " << v2 << " weight " << weight // << " differs from " << v2 << " -> " << v1 ; break; } ++hit; } } delete enabledOutEdges; qDebug() << "Graph: isSymmetric() -" << symmetricAdjacencyMatrix; return symmetricAdjacencyMatrix; } /** * Transform the digraph to undirected graph (all edges reciprocal) */ void Graph::symmetrize(){ qDebug("Graph: symmetrize"); QList::const_iterator it; int y=0, v2=0, v1=0, weight; QHash *enabledOutEdges = new QHash; QHash::const_iterator it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph:symmetrize() - iterate over edges of v1 " << v1; enabledOutEdges=(*it)->returnEnabledOutEdges(); it1=enabledOutEdges->cbegin(); while ( it1!=enabledOutEdges->cend() ){ v2 = it1.key(); weight = it1.value(); y=index[ v2 ]; qDebug() << "Graph:symmetrize() - " << " v1 " << v1 << " outLinked to " << v2 << " weight " << weight; if ( m_graph[y]->hasEdgeTo( v1 ) == 0 ) { qDebug() << "Graph:symmetrize(): s = " << v1 << " is NOT inLinked from y = " << v2 ; createEdge( v2, v1, weight, initEdgeColor, false, true, false); } else qDebug() << "Graph: symmetrize(): v1 = " << v1 << " is already inLinked from v2 = " << v2 ; ++it1; } } delete enabledOutEdges; graphModified=true; symmetricAdjacencyMatrix=true; emit graphChanged(); } //Returns TRUE if (v1, v2) is symmetric. bool Graph::symmetricEdge(int v1, int v2){ qDebug("***Graph: symmetricEdge()"); if ( (this->hasArc ( v1, v2 ) ) > 0 && (this->hasArc ( v2, v1 ) ) > 0 ) { // qDebug("Graph: symmetricEdge: YES"); return true; } else { // qDebug("Graph: symmetricEdge: NO"); return false; } } /** * Returns the distance between nodes numbered (i-1) and (j-1) */ int Graph::distance(const int i, const int j, const bool considerWeights, const bool inverseWeights){ if ( !distanceMatrixCreated || graphModified ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(false, considerWeights, inverseWeights, false); } return DM.item(index[i],index[j]); } /** * Returns the diameter of the graph, aka the largest geodesic distance between any two vertices */ int Graph::diameter(const bool considerWeights, const bool inverseWeights){ if ( !distanceMatrixCreated || graphModified ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(false, considerWeights, inverseWeights, false); } return graphDiameter; } /** * Returns the average distance of the graph */ float Graph::averageGraphDistance(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ if ( !distanceMatrixCreated || graphModified ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(false, considerWeights, inverseWeights,dropIsolates); } return averGraphDistance; } /** * @brief Graph::connectedness() * @return int: * 2: strongly connected digraph (exists path from i to j and vice versa for every i,j) * 1: connected undirected graph * 0: not connected undirected graph no isolates * -1: not connected undirected graph with isolates * -2: unilaterally connected digraph (exists path only from i to j or from j to i, not both) * -3 disconnected digraph (with isolates). * -4 disconnected digraph (there are pairs not connected at all). */ int Graph::connectedness() { qDebug() << "Graph::connectedness() "; if (!reachabilityMatrixCreated || graphModified) { reachabilityMatrix(false,false,false); } isolatedVertices=verticesIsolated().count(); if ( isSymmetric() ) { qDebug() << "Graph::connectedness() IS SYMMETRIC"; if ( disconnectedVertices.size() != 0 ) { if (isolatedVertices!=0 ) { qDebug() << "undirected graph is disconnected (has isolates)" ; return -1; } else { qDebug() << " undirected graph is disconnected (no isolates)"; return 0; } } qDebug() << " undirected graph is connected "; return 1; } else { qDebug() << "Graph::connectedness() NOT SYMMETRIC"; if ( disconnectedVertices.size() != 0 ) { if ( unilaterallyConnectedVertices.size() == 0 ) { if (isolatedVertices!=0) { qDebug() << " directed graph is disconnected (has isolates)"; return -3; // - can be connected directed if we remove isolate nodes } } qDebug () << " directed graph is disconnected (no isolates)"; return -4; } else { if ( unilaterallyConnectedVertices.size() != 0 ) { qDebug () << " directed graph is unilaterally connected"; return -2; // (exists path only from i to j or from j to i, not both) } else{ qDebug () << " directed graph is connected "; return 2; } } } return -666; // for sanity check :P } /** * Writes the matrix of distances to a file */ void Graph::writeDistanceMatrix (QString fn, const char* netName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug ("Graph::writeDistanceMatrix()"); if ( !distanceMatrixCreated || graphModified ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(false, considerWeights, inverseWeights, dropIsolates); } qDebug ("Graph::writeDistanceMatrix() writing to file"); QFile file (fn); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fn) ); return; } QTextStream outText(&file); outText.setCodec("UTF-8"); outText.setRealNumberPrecision(m_precision); outText << "-Social Network Visualizer- \n"; if (!netName) netName="Unnamed network"; outText << "Distance matrix of "<< netName<<": \n"; outText << DM ; file.close(); } /** * Saves the number of geodesic distances matrix TM to a file * */ void Graph::writeNumberOfGeodesicsMatrix(const QString fn, const char* netName, const bool considerWeights, const bool inverseWeights) { qDebug ("Graph::writeDistanceMatrix()"); if ( !distanceMatrixCreated || graphModified ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(false, considerWeights, inverseWeights, false); } qDebug ("Graph::writeDistanceMatrix() writing to file"); QFile file (fn); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fn) ); return; } QTextStream outText(&file); outText.setCodec("UTF-8"); outText << "-Social Network Visualizer- \n"; if (!netName) netName="Unnamed network"; outText << "Number of geodesics matrix of "<< netName<<": \n"; outText << TM ; file.close(); } void Graph::writeEccentricity( const QString fileName, const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false) { QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if ( !distanceMatrixCreated || graphModified ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(true, considerWeights, inverseWeights, dropIsolates); } emit statusMessage ( QString(tr("Writing eccentricity to file:")).arg(fileName) ); outText << tr("ECCENTRICITY (e)") << endl << endl; outText << tr("The eccentricity e of a node is the maximum geodesic distance " " from that node to all other nodes in the network.") ; outText << endl ; outText << tr("Therefore, e reflects farness: how far, at most, is each " " node from every other node.") ; outText << endl ; outText << tr("A node has maximum e when it has distance 1 " "to all other nodes (star node))\n"); outText << endl << endl ; outText << tr("Range: 0 < e < ") << vertices()-1 <<" (g-1, " << tr("where g is the number of nodes |V|)"); outText << endl << endl ; outText << "Node"<<"\te\t%e\n"; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ outText << (*it)->name()<<"\t"<<(*it)->eccentricity() << "\t" << (100* ((*it)->eccentricity()) / sumEccentricity)<::const_iterator it, it1; QList::iterator it2; int w=0, u=0,s=0, i=0; float d_sw=0, d_su=0; int progressCounter=0; graphDiameter=0; distanceMatrixCreated = false; averGraphDistance=0; nonZeroDistancesCounter=0; qDebug() << " graphDiameter "<< graphDiameter << " averGraphDistance " <setBC( 0.0 ); (*it)->setSC( 0.0 ); (*it)->setEccentricity( 0.0 ); (*it)->setEC( 0.0 ); (*it)->setCC( 0.0 ); (*it)->setPC( 0.0 ); } qDebug("MAIN LOOP: for every s in V solve the Single Source Shortest Path problem..."); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { progressCounter++; emit updateProgressDialog( progressCounter ); //isolates are dropped by default in the beginning // // if ( ! (*it)->isEnabled() ) // continue; s=index[(*it)->name()]; qDebug() << "Source vertex s = " << (*it)->name() << " of BFS algorithm has index " << s << ". Clearing Stack ..."; if (centralities){ qDebug()<< "Empty stack Stack which will return vertices in " "order of their (non increasing) distance from S ..."; //- Complexity linear O(n) while ( !Stack.empty() ) Stack.pop(); i=0; qDebug()<< "...and for each vertex: empty list Ps of predecessors"; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1) { (*it1)->clearPs(); //initialize all sizeOfNthOrderNeighborhood to zero sizeOfNthOrderNeighborhood.insert(i, 0); i++; } } qDebug() << "PHASE 1 (SSSP): Call BFS or dijkstra for source vertex " << (*it)->name() << " index " << s << " to determine distances and geodesics from s to every vertex t" ; if (!considerWeights) BFS(s,centralities, dropIsolates ); else dijkstra(s, centralities, inverseWeights, dropIsolates); qDebug("***** FINISHED PHASE 1 (SSSP) BFS ALGORITHM. Continuing to calculate centralities"); if (centralities){ qDebug() << "Set CC for source vertex " << (*it)->name() << " with index s = " << s ; if ( (*it)->CC() != 0 ) //Closeness centrality must be inverted CC=1.0/(*it)->CC(); else CC=0; (*it)->setCC( CC ); //Check eccentricity (max geodesic distance) eccentricity = (*it)->eccentricity(); if ( eccentricity != 0 ) { //Eccentricity Centrality is the inverted Eccentricity EC=1.0 / eccentricity; } else { EC=0; eccentricity=0; } (*it)->setEC( EC ); //Set Eccentricity Centrality (*it)->setSEC( EC ); //Set std EC = EC sumEC+=EC; //set sum EC //Find min/max Eccentricity minmax( eccentricity, (*it), maxEccentricity, minEccentricity, maxNodeEccentricity, minNodeEccentricity) ; resolveClasses(eccentricity, discreteEccentricities, classesEccentricity ,(*it)->name() ); sumEccentricity+=eccentricity; qDebug()<< "PHASE 2 (ACCUMULATION): Start back propagation of dependencies." << "Set dependency delta[u]=0 on each vertex"; i=1; //used in calculating power centrality sizeOfComponent = 1; PC=0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ (*it1)->setDelta(0.0); //Calculate Power Centrality: In = [ 1/(N-1) ] * ( Nd1 + Nd2 * 1/2 + ... + Ndi * 1/i ) // where Ndi (sizeOfNthOrderNeighborhood) is the number of nodes at distance i from this node. //FIXME do we need to check for disabled nodes somewhere? qDebug() << " sizeOfNthOrderNeighborhood.value("<< i<<")" << sizeOfNthOrderNeighborhood.value(i); PC += ( 1.0 / (float) i ) * sizeOfNthOrderNeighborhood.value(i); // where N is the sum Nd0 + Nd1 + Nd2 + ... + Ndi, that is the amount of nodes in the same component as the current node sizeOfComponent += sizeOfNthOrderNeighborhood.value(i); i++; } (*it)->setPC( PC ); //Power Centrality is stdized already t_sumPC += PC; //add to temp sumPC if ( sizeOfComponent != 1 ) SPC = ( 1.0/(sizeOfComponent-1.0) ) * PC; else SPC = 0; (*it)->setSPC( SPC ); //Set std PC sumPC += SPC; //add to sumPC -- used later to compute mean and variance qDebug() << "Visit all vertices in reverse order of their discovery (from s = " << s << " ) to sum dependencies. Initial Stack size has " << Stack.size(); while ( !Stack.empty() ) { w=Stack.top(); qDebug("Stack top is vertex w=%i. This is the furthest vertex from s. Popping it.", w); Stack.pop(); QList lst=m_graph[w]->Ps(); qDebug("preLOOP: Checking size of predecessors list Ps[w]... = %i ",lst.size()); qDebug("LOOP: for every other vertex u in the list of predecessors Ps[w] of w...."); if (lst.size() > 0) // just in case...do a sanity check for ( it2=lst.begin(); it2 != lst.end(); it2++ ){ u=(*it2); qDebug("Selecting Ps[w] element u=%i with delta_u=%f. sigma(u)=TM(s,u)=%f, sigma(w)=TM(s,w)=%f, delta_w=%f ", u, m_graph[u]->delta(),TM.item(s,u), TM.item(s,w), m_graph[w]->delta()); if ( TM.item(s,w) > 0) { //delta[u]=delta[u]+(1+delta[w])*(sigma[u]/sigma[w]) ; d_su=m_graph[u]->delta()+(1.0+m_graph[w]->delta() ) * ( (float)TM.item(s,u)/(float)TM.item(s,w) ); } else { d_su=m_graph[u]->delta(); qDebug("TM (s,w) zero, i.e. zero shortest path counts from s to w - using SAME DELTA for vertex u"); } qDebug("Assigning new delta d_su = %f to u = %i", d_su, u); m_graph[u]->setDelta( d_su); } qDebug()<<" Adding delta_w to BC of w"; if (w!=s) { qDebug("w!=s. For this furthest vertex we need to add its new delta %f to old BC index: %f", m_graph[w]->delta(), m_graph[w]->BC()); d_sw = m_graph[w]->BC() + m_graph[w]->delta(); qDebug("New BC = d_sw = %f", d_sw); m_graph[w]->setBC (d_sw); } } } } if (averGraphDistance!=0) { averGraphDistance = averGraphDistance / ( aVertices * ( aVertices-1.0 ) ); } if (centralities) { for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ){ qDebug() << "vertex " << (*it)->name() << " isolated, continue. "; continue; } // Compute classes and min/maxEC SEC=(*it)->SEC(); resolveClasses(SEC, discreteECs, classesEC,(*it)->name() ); minmax( SEC, (*it), maxEC, minEC, maxNodeEC, minNodeEC) ; // Compute classes and min/maxPC SPC = (*it)->SPC(); //same as PC resolveClasses(SPC, discretePCs, classesPC,(*it)->name() ); minmax( SPC, (*it), maxPC, minPC, maxNodePC, minNodePC) ; // Compute std BC, classes and min/maxBC if (symmetricAdjacencyMatrix) { qDebug()<< "Betweenness centrality must be divided by" <<" two if the graph is undirected"; (*it)->setBC ( (*it)->BC()/2.0); } BC=(*it)->BC(); SBC = BC/maxIndexBC; (*it)->setSBC( SBC ); resolveClasses(SBC, discreteBCs, classesBC); sumBC+=SBC; minmax( SBC, (*it), maxBC, minBC, maxNodeBC, minNodeBC) ; // Compute std CC, classes and min/maxCC CC = (*it)->CC(); SCC = maxIndexCC * CC; (*it)->setSCC ( SCC ); resolveClasses(SCC, discreteCCs, classesCC,(*it)->name() ); sumCC+=SCC; minmax( SCC, (*it), maxCC, minCC, maxNodeCC, minNodeCC) ; //prepare to compute stdSC SC=(*it)->SC(); if (symmetricAdjacencyMatrix){ (*it)->setSC(SC/2.0); SC=(*it)->SC(); qDebug() << "SC of " <<(*it)->name() << " divided by 2 (because the graph is symmetric) " << (*it)->SC(); } t_sumSC+=SC; qDebug() << "vertex " << (*it)->name() << " - " << " EC: "<< (*it)->EC() << " CC: "<< (*it)->CC() << " BC: "<< (*it)->BC() << " SC: "<< (*it)->SC() << " PC: "<< (*it)->PC(); } // calculate mean values and prepare to compute variances meanBC = sumBC /(float) aVertices ; varianceBC=0; tempVarianceBC=0; meanCC = sumCC /(float) aVertices ; varianceCC=0; tempVarianceCC=0; meanPC = sumPC /(float) aVertices ; variancePC=0; tempVariancePC=0; meanEC = sumEC /(float) aVertices ; varianceEC=0; tempVarianceEC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ) { continue; } // Compute std SC, classes and min/maxSC SC=(*it)->SC(); SSC=SC/t_sumSC; (*it)->setSSC(SSC); resolveClasses(SSC, discreteSCs, classesSC); sumSC+=SSC; minmax( SSC, (*it), maxSC, minSC, maxNodeSC, minNodeSC) ; //Compute numerator of groupBC SBC=(*it)->SBC(); nomBC +=(maxBC - SBC ); //calculate BC variance tempVarianceBC = ( SBC - meanBC ) ; tempVarianceBC *=tempVarianceBC; varianceBC += tempVarianceBC; //Compute numerator of groupCC nomCC += maxCC- (*it)->SCC(); //calculate CC variance tempVarianceCC = ( (*it)->SCC() - meanCC ) ; tempVarianceCC *=tempVarianceCC; varianceCC += tempVarianceCC; //Compute numerator of groupPC SPC=(*it)->SPC(); nomPC +=(maxPC - SPC ); //calculate PC variance tempVariancePC = ( (*it)->SPC() - meanPC ) ; tempVariancePC *=tempVariancePC; variancePC += tempVariancePC; //calculate EC variance tempVarianceEC = ( (*it)->EC() - meanEC ) ; tempVarianceEC *=tempVarianceEC; varianceEC += tempVarianceEC; } //compute final variances varianceBC /= (float) aVertices; varianceCC /= (float) aVertices; variancePC /= (float) aVertices; varianceEC /= (float) aVertices; // calculate SC mean value and prepare to compute variance meanSC = sumSC /(float) aVertices ; varianceSC=0; tempVarianceSC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ){ continue; } tempVarianceSC = ( (*it)->SSC() - meanSC ) ; tempVarianceSC *=tempVarianceSC; varianceSC += tempVarianceSC; } //calculate final SC variance varianceSC /= (float) aVertices; denomPC = ( (aVertices-2.0) ) / (2.0 ); //only for connected nets if (aVertices < 3 ) denomPC = aVertices-1.0; //what if the net is disconnected (isolates exist) ? groupPC = nomPC/denomPC; denomCC = ( ( aVertices-1.0) * (aVertices-2.0) ) / (2.0 * aVertices -3.0); if (aVertices < 3 ) denomCC = aVertices-1.0; groupCC = nomCC/denomCC; //Calculate group Closeness centrality //nomBC*=2.0; // denomBC = (aVertices-1.0) * (aVertices-1.0) * (aVertices-2.0); denomBC = (aVertices-1.0) ; // Wasserman&Faust - formula 5.14 groupBC=nomBC/denomBC; //Calculate group Betweenness centrality calculatedCentralities=true; } } distanceMatrixCreated=true; } /** * Breadth-First Search (BFS) method for unweighted graphs (directed or not) INPUT: a 'source' vertex with index s and a boolean computeCentralities. (Implicitly, BFS uses the m_graph structure) OUTPUT: For every vertex t: DM(s, t) is set to the distance of each t from s For every vertex t: TM(s, t) is set to the number of shortest paths between s and t Also, if computeCentralities is true then BFS does extra operations: a) For source vertex s: it calculates CC(s) as the sum of its distances from every other vertex. it calculates eccentricity(s) as the maximum distance from all other vertices. it increases sizeOfNthOrderNeighborhood [ N ] by one, to store the number of nodes at distance n from source s b) For every vertex u: it increases SC(u) by one, when it finds a new shor. path from s to t through u. appends each neighbor y of u to the list Ps, thus Ps stores all predecessors of y on all all shortest paths from s c) Each vertex u popped from Q is pushed to a stack Stack */ void Graph::BFS(int s, const bool computeCentralities=false, const bool dropIsolates=false){ Q_UNUSED(dropIsolates); int u,w, dist_u=0, temp=0, dist_w=0; int relation=0, target=0; //int weight=0; bool edgeStatus=false; H_edges::const_iterator it1; //set distance of s from s equal to 0 DM.setItem(s,s,0); //set sigma of s from s equal to 1 TM.setItem(s,s,1); // qDebug("BFS: Construct a queue Q of integers and push source vertex s=%i to Q as initial vertex", s); queue Q; // qDebug()<<"BFS: Q size "<< Q.size(); Q.push(s); qDebug("BFS: LOOP: While Q not empty "); while ( !Q.empty() ) { qDebug("BFS: Dequeue: first element of Q is u=%i", Q.front()); u=Q.front(); Q.pop(); if ( ! m_graph [ u ]->isEnabled() ) continue ; if (computeCentralities){ // qDebug("BFS: If we are to calculate centralities, we must push u=%i to global stack Stack ", u); Stack.push(u); } qDebug() << "BFS: LOOP over every edge (u,w) e E, that is all neighbors w of vertex u"; it1=m_graph [ u ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ u ] -> m_outEdges.cend() ){ relation = it1.value().first; if ( relation != currentRelation() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } target = it1.key(); // weight = it1.value().second.first; w=index[ target ]; qDebug("BFS: u=%i is connected with node %i of index w=%i. ", u, target, w); // qDebug("BFS: Start path discovery"); if ( DM.item(s, w) == RAND_MAX ) { //if distance (s,w) is infinite, w found for the first time. qDebug("BFS: first time visiting w=%i. Enqueuing w to the end of Q", w); Q.push(w); qDebug()<<"BFS: First check if distance(s,u) = -1 (aka infinite :)) and set it to zero"; dist_u=DM.item(s,u); dist_w = dist_u + 1; qDebug("BFS: Setting distance of w=%i from s=%i equal to distance(s,u) plus 1. New distance = %i",w,s, dist_w ); DM.setItem(s, w, dist_w); averGraphDistance += dist_w; nonZeroDistancesCounter++; qDebug()<< "Graph::BFS() - d(" << s <<"," << w <<")=" << DM.item(s,w) << " - inserting " << w << " to inflRange J of " << s << " - and " << s << " to inflDomain I of "<< w; XRM.setItem(s,w,1); influenceRanges.insert(s,w); influenceDomains.insert(w,s); // disconnectedVertices if (computeCentralities){ qDebug()<<"BFS: Calculate PC: store the number of nodes at distance " << dist_w << "from s"; sizeOfNthOrderNeighborhood.insert( dist_w, sizeOfNthOrderNeighborhood.value(dist_w)+1 ); qDebug()<<"BFS: Calculate CC: the sum of distances (will invert it l8r)"; m_graph [s]->setCC (m_graph [s]->CC() + dist_w); qDebug()<<"BFS: Calculate Eccentricity: the maximum distance "; if (m_graph [s]->eccentricity() < dist_w ) m_graph [s]->setEccentricity(dist_w); } // qDebug("BFS: Checking graphDiameter"); if ( dist_w > graphDiameter){ graphDiameter=dist_w; // qDebug() << "BFS: new graphDiameter = " << graphDiameter ; } } qDebug("BFS: Start path counting"); //Is edge (u,w) on a shortest path from s to w via u? if ( DM.item(s,w)==DM.item(s,u)+1) { temp= TM.item(s,w)+TM.item(s,u); qDebug("BFS: Found a NEW SHORTEST PATH from s=%i to w=%i via u=%i. Setting Sigma(%i, %i) = %i",s, w, u, s, w,temp); if (s!=w) TM.setItem(s,w, temp); if (computeCentralities){ qDebug("BFS/SC: If we are to calculate centralities, we must calculate SC as well"); if ( s!=w && s != u && u!=w ) { qDebug() << "BFS: setSC of u="<SC()+1; m_graph[u]->setSC(m_graph[u]->SC()+1); } else { // qDebug() << "BFS/SC: skipping setSC of u, because s=" // <SC(); qDebug() << "BFS: appending u="<< u << " to list Ps[w=" << w << "] with the predecessors of w on all shortest paths from s "; m_graph[w]->appendToPs(u); } } ++it1; } } } /** * Breadth-First Search (BFS) method for unweighted graphs (directed or not) INPUT: a 'source' vertex with index s and a boolean computeCentralities. (Implicitly, BFS uses the m_graph structure) OUTPUT: For every vertex t: DM(s, t) is set to the distance of each t from s For every vertex t: TM(s, t) is set to the number of shortest paths between s and t Also, if computeCentralities is true then BFS does extra operations: a) For source vertex s: it calculates CC(s) as the sum of its distances from every other vertex. it calculates eccentricity(s) as the maximum distance from all other vertices. it increases sizeOfNthOrderNeighborhood [ N ] by one, to store the number of nodes at distance n from source s b) For every vertex u: it increases SC(u) by one, when it finds a new shor. path from s to t through u. appends each neighbor y of u to the list Ps, thus Ps stores all predecessors of y on all all shortest paths from s c) Each vertex u popped from Q is pushed to a stack Stack */ void Graph::dijkstra(int s, const bool computeCentralities=false, const bool inverseWeights=false, const bool dropIsolates=false){ Q_UNUSED(dropIsolates); int u,w,v, temp=0; int relation=0, target=0; float weight=0, dist_u=0, dist_w=0; bool edgeStatus=false; H_edges::const_iterator it1; qDebug() << "dijkstra: Construct a priority queue Q of all vertices-distances"; priority_queue, CompareDistances> Q; //set distance of s from s equal to 0 DM.setItem(s,s,0); //set sigma of s from s equal to 1 TM.setItem(s,s,1); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { v=index[ (*it)->name() ]; if (v != s ){ // DM initialization to RAND_MAX already done in createDistanceMatrix //DM.setItem(s,v,RAND_MAX); qDebug() << " push " << v << " to Q with infinite distance from s"; Q.push(Distance(v,RAND_MAX)); //TODO // Previous node in optimal path from source // previous[v] := undefined } } qDebug() << " finally push source " << s << " to Q with 0 distance from s"; //crucial: without it the priority Q would pop arbitrary node at first loop Q.push(Distance(s,0)); qDebug()<<"dijkstra: Q size "<< Q.size(); qDebug() << "\n\n ### dijkstra: LOOP: While Q not empty "; while ( !Q.empty() ) { u=Q.top().target; qDebug()<< "\n\n *** dijkstra: take u = "<< u << " from Q which has minimum distance from s = " << s; Q.pop(); if ( ! m_graph [ u ]->isEnabled() ) continue ; if (computeCentralities){ qDebug()<< "dijkstra: We will calculate centralities, push u="<< u << " to global stack Stack "; Stack.push(u); } qDebug() << "*** dijkstra: LOOP over every edge ("<< u <<",w) e E, " << "that is for each neighbor w of u"; it1=m_graph [ u ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ u ] -> m_outEdges.cend() ){ relation = it1.value().first; if ( relation != currentRelation() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } target = it1.key(); weight = it1.value().second.first; w=index[ target ]; qDebug()<<"\ndijkstra: u="<< u << " --> w="<< w << " (node "<< target << ") of weight "<< weight; if (inverseWeights) { //only invert if user asked to do so weight = 1.0 / weight; qDebug () << " inverting weight to " << weight; } qDebug("dijkstra: Start path discovery"); dist_u=DM.item(s,u); if (dist_u == RAND_MAX || dist_u < 0) { dist_w = RAND_MAX; qDebug() << "dijkstra: dist_w = RAND_MAX " << RAND_MAX; } else { dist_w = dist_u + weight; qDebug() << "dijkstra: dist_w = dist_u + weight = " << dist_u << " + " << weight << " = " <SC()+1; m_graph[u]->setSC(m_graph[u]->SC()+1); } else { qDebug() << "dijkstra/SC: skipping setSC of u, because s=" <SC(); qDebug() << "dijkstra: appending u="<< u << " to list Ps[w=" << w << "] with the predecessors of w on all shortest paths from s "; m_graph[w]->appendToPs(u); } } else if (dist_w > 0 && dist_w < DM.item(s, w) ) { qDebug() << "dijkstra: Yeap. Set DM (s,w) = DM(" << s << ","<< w << ") = "<< dist_w ; DM.setItem(s, w, dist_w); averGraphDistance += dist_w; nonZeroDistancesCounter++; qDebug()<< "Graph::dijkstra() - d(" << s <<"," << w <<")=" << DM.item(s,w) << " - inserting " << w << " to inflRange J of " << s << " - and " << s << " to inflDomain I of "<< w; XRM.setItem(s,w,1); influenceRanges.insert(s,w); influenceDomains.insert(w,s); // disconnectedVertices if (s!=w) { qDebug()<<"dijkstra: Found NEW SP from s=" << s << " to w=" << w << " via u="<< u << " - Setting Sigma(s, w) = 1 "; TM.setItem(s,w, 1); } if (computeCentralities){ sizeOfNthOrderNeighborhood.insert( dist_w, sizeOfNthOrderNeighborhood.value(dist_w)+1 ); qDebug()<<"dijkstra/PC: number of nodes at distance " << dist_w << "from s is " << sizeOfNthOrderNeighborhood.value(dist_w); m_graph [s]->setCC (m_graph [s]->CC() + dist_w); qDebug()<<"dijkstra/CC:: sum of distances = " << m_graph [s]->CC() << " (will invert it l8r)"; if (m_graph [s]->eccentricity() < dist_w ) m_graph [s]->setEccentricity(dist_w); qDebug()<<"dijkstra/Eccentricity: max distance = " << m_graph [s]->eccentricity(); } qDebug("dijkstra/graphDiameter"); if ( dist_w > graphDiameter){ graphDiameter=dist_w; qDebug() << "dijkstra: new graphDiameter = " << graphDiameter ; } } else qDebug() << "dijkstra: NO"; // qDebug()<< "### dijkstra: Start path counting"; // // Is (u,w) on a shortest path from s to w via u? // if ( DM.item(s,w)==DM.item(s,u)+weight) { // temp= TM.item(s,w)+TM.item(s,u); // } ++it1; } } } /** minmax() facilitates the calculations of minimum and maximum centralities during createDistanceMatrix() */ void Graph::minmax(float C, Vertex *v, float &max, float &min, int &maxNode, int &minNode) { qDebug() << "MINMAX C = " << C << " max = " << max << " min = " << min << " name = " << v->name(); if (C > max ) { max=C; maxNode=v->name(); } if (C < min ) { min=C; minNode=v->name(); } } /** This method calculates the number of discrete centrality classes of all vertices It stores that number in a QHash type where the centrality value is the key. Called from createDistanceMatrix() */ void Graph::resolveClasses(float C, H_StrToInt &discreteClasses, int &classes){ H_StrToInt::iterator it2; it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), classes); } } /* * Overloaded method. It only adds displaying current vertex for debugging purposes. */ void Graph::resolveClasses(float C, H_StrToInt &discreteClasses, int &classes, int vertex){ H_StrToInt::iterator it2; Q_UNUSED(vertex); it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), classes); } } /* Calculates the Information centrality of each vertex - diagonal included * * Note that there is no known generalization of Stephenson&Zelen's theory * for information centrality to directional data */ void Graph::centralityInformation(const bool considerWeights, const bool inverseWeights){ qDebug()<< "Graph:: centralityInformation()"; if (calculatedIC && !graphModified) { return; } discreteICs.clear(); sumIC=0; maxIC=0; t_sumIC=0; minIC=RAND_MAX; classesIC=0; varianceIC=0; isolatedVertices=verticesIsolated().count(); int i=0, j=0, n=vertices(); float m_weight=0, weightSum=1, diagonalEntriesSum=0, rowSum=0; float IC=0, SIC=0; /* Note: isolated nodes must be dropped from the AM Otherwise, the TM might be singular, therefore non-invertible. */ bool dropIsolates=true; bool symmetrize=true; createAdjacencyMatrix(dropIsolates, considerWeights, inverseWeights, symmetrize); n-=isolatedVertices; //isolatedVertices updated in createAdjacencyMatrix TM.resize(n, n); invM.resize(n, n); for (i=0; i::const_iterator it; i=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it)->isIsolated() ) { (*it) -> setIC ( 0 ); continue; } IC= 1.0 / ( invM.item(i,i) + (diagonalEntriesSum - 2.0 * rowSum) / n ); (*it) -> setIC ( IC ); t_sumIC += IC; i++; } for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ IC = (*it)->IC(); SIC = IC / t_sumIC ; (*it)->setSIC( SIC ); sumIC+=SIC; resolveClasses(SIC, discreteICs, classesIC); minmax( SIC, (*it), maxIC, minIC, maxNodeIC, minNodeIC) ; } float x=0; meanIC = sumIC /(float) n ; varianceIC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ x = ( (*it)->SIC() - meanIC ) ; x *=x; varianceIC += x; } varianceIC /= (float) n; calculatedIC = true; } //Writes the information centralities to a file void Graph::writeCentralityInformation(const QString fileName, const bool considerWeights, const bool inverseWeights){ QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified || !calculatedIC ) { emit statusMessage ( (tr("Calculating IC scores...")) ); centralityInformation(considerWeights, inverseWeights); } emit statusMessage ( QString(tr("Writing information centralities to file: ")) .arg(fileName) ); outText.setRealNumberPrecision(m_precision); outText << tr("INFORMATION CENTRALITY (IC)")<<"\n"; outText << tr("The IC index measures the information flow through " "all paths between actors weighted by strength of tie and distance\n"); outText << tr("IC' is the standardized IC (IC divided by the sumIC).") <<"\n" << tr ("Warning: To compute this index, SocNetV drops all isolated " "nodes and symmetrizes (if needed) the adjacency matrix. " "Read the Manual for more.") << "\n\n"; outText << tr("IC range: 0 < IC < inf (this index has no max value)") << "\n"; outText << tr("IC' range: 0 < IC'< 1 (" )<<"\n\n"; outText << "Node"<<"\tIC\t\tIC'\t\t%IC'\n"; QList::const_iterator it; float IC=0, SIC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ IC = (*it)->IC(); SIC = (*it)->SIC(); outText << (*it)->name()<<"\t" << IC << "\t\t" << SIC << "\t\t" << ( 100* SIC )<name() << " SIC " << SIC; } qDebug ("min %f, max %f", minIC, maxIC); if ( minIC == maxIC ) outText << tr("\nAll nodes have the same IC value.\n"); else { outText << "\n"; outText << tr("Max IC' = ") << maxIC <<" (node "<< maxNodeIC << ") \n"; outText << tr("Min IC' = ") << minIC <<" (node "<< minNodeIC << ") \n"; outText << tr("IC classes = ") << classesIC<<" \n"; } outText << "\n"; outText << tr("IC' sum = ") << sumIC << " \n"; outText << tr("IC' Mean = ") << meanIC << " \n"; outText << tr("IC' Variance = ") << varianceIC << " \n\n"; outText << tr("Since there is no way to compute Group Information Centralization, " "you can use variance as a general centralization index.") <<" \n\n"; outText << tr("Variance = 0, when all nodes have the same IC value, i.e. a " "complete or a circle graph).\n"); outText << tr("Larger values of variance suggest larger variability between the " "IC' values.\n"); outText <<"(Wasserman & Faust, formula 5.20, p. 197)\n\n"; outText << tr("Information Centrality report, \n"); outText << tr("created by SocNetV on: ")<< actualDateTime.currentDateTime() .toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } //Calculates the outDegree centrality of each vertex - diagonal included void Graph::centralityDegree(const bool weights, const bool dropIsolates){ qDebug("Graph::centralityDegree()"); if (!graphModified && calculatedDC ) { qDebug() << "Graph::centralityDegree() - graph not changed - returning"; return; } float DC=0, nom=0, denom=0, SDC=0; float weight; classesDC=0; discreteDCs.clear(); sumDC=0; t_sumDC=0; maxDC=0; minDC=RAND_MAX; varianceDC=0; meanDC=0; int vert=vertices(dropIsolates); QList::const_iterator it, it1; H_StrToInt::iterator it2; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DC=0; if (!(*it)->isIsolated()) { for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( (weight=this->hasArc ( (*it)->name(), (*it1)->name() ) ) !=0 ) { // qDebug() << "Graph::centralityDegree() - vertex " // << (*it)->name() // << " hasEdgeTo = " << (*it1)->name(); if (weights) DC+=weight; else DC++; //check here if the matrix is symmetric - we need this below if ( ( this->hasArc ( (*it1)->name(), (*it)->name() ) ) != ( this->hasArc ( (*it)->name(), (*it1)->name() ) ) ) symmetricAdjacencyMatrix = false; } } } (*it) -> setDC ( DC ) ; //Set OutDegree t_sumDC += DC; // store temp sumDC (for std calc below) qDebug() << "Graph:centralityDegree() - vertex " << (*it)->name() << " has DC = " << DC ; } // Calculate std Out-Degree, min, max, classes and sumSDC for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DC= (*it)->DC(); if (!weights) { SDC = ( DC / (vert-1.0) ); } else { SDC= ( DC / (t_sumDC) ); } (*it) -> setSDC( SDC ); //Set Standard DC // qDebug() << "Graph::centralityDegree() - vertex " // << (*it)->name() << " SDC " << (*it)->SDC (); sumDC+=SDC; it2 = discreteDCs.find(QString::number(SDC)); if (it2 == discreteDCs.end() ) { classesDC++; // qDebug("This is a new DC class"); discreteDCs.insert ( QString::number(DC), classesDC ); } //qDebug() << "DC classes = " << classesDC; if (maxDC < SDC ) { maxDC = SDC ; maxNodeDC=(*it)->name(); } if (minDC > SDC ) { minDC = SDC ; minNodeDC=(*it)->name(); } } if (minDC == maxDC) maxNodeDC=-1; meanDC = sumDC / (float) vert; // qDebug() << "Graph::centralityDegree() - sumDC " << sumDC // << " vertices " << vert << " meanDC = sumDC / vert = " << meanDC; // Calculate Variance and the Degree Centralisation of the whole graph. for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } SDC= (*it)->SDC(); nom+= (maxDC-SDC); varianceDC += (SDC-meanDC) * (SDC-meanDC) ; // if ( dropIsolates ) { // if ( ! (*it)->isIsolated() ) { // varianceDC += (SDC-meanDC) * (SDC-meanDC) ; // nom+= (maxDC-SDC); // } // } // else { // } } varianceDC=varianceDC/(float) vert; // qDebug() << "Graph::centralityDegree() - variance = " << varianceDC; if (symmetricAdjacencyMatrix) denom=(vert-1.0)*(vert-2.0); else denom=(vert-1.0)*(vert-1.0); if (vert < 3 ) denom = vert-1.0; // qDebug () << "*** vert is " << vert << " nom " << nom << " denom is " << denom; if (!weights) { groupDC=nom/denom; } calculatedDC=true; } void Graph::writeCentralityDegree ( const QString fileName, const bool considerWeights, const bool dropIsolates) { QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); qDebug()<< "Graph:: writeCentralityDegree() considerWeights " << considerWeights << " dropIsolates " <::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t" <<(*it)->DC() << "\t"<< (*it)->SDC() << "\t" << ( 100* ((*it)->SDC() ) )<< "\n"; } } if ( minDC == maxDC ) { outText << "\n" << tr("All nodes have the same DC score.") << "\n"; } else { outText << "\n"; outText << tr("Max DC' = ") << maxDC <<" (node "<< maxNodeDC << ") \n"; outText << tr("Min DC' = ") << minDC <<" (node "<< minNodeDC << ") \n"; outText << tr("DC classes = ") << classesDC<<" \n"; } outText << "\n"; outText << tr("DC sum = ") << t_sumDC<<"\n" ; outText << tr("DC' sum = ") << sumDC<<"\n" ; outText << tr("DC' Mean = ") << meanDC<<"\n" ; outText << tr("DC' Variance = ") << varianceDC<<"\n"; if (!considerWeights) { outText << "\nGROUP DEGREE CENTRALISATION (GDC)\n\n"; outText << "GDC = " << qSetRealNumberPrecision(m_precision) << groupDC<<"\n\n"; outText << "GDC range: 0 < GDC < 1\n"; outText << "GDC = 0, when all out-degrees are equal (i.e. regular lattice).\n"; outText << "GDC = 1, when one node completely dominates or overshadows the other nodes.\n"; outText << "(Wasserman & Faust, formula 5.5, p. 177)\n\n"; outText << "(Wasserman & Faust, p. 101)\n"; } else { outText << "This graph is weighted. No GDC value can be computed. \n" << "You can use DC mean or variance as a group-level DC measure"; } outText << "\n\n"; outText << tr("Degree Centrality (Out-Degree) Report, \n"); outText << tr("created by SocNetV: ")<< actualDateTime.currentDateTime() .toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } /** * @brief Graph::centralityClosenessImproved * Improved node-level centrality closeness index which focuses on the * influence range of each node (the set of nodes that are reachable from it) * For each node v, this index calculates the fraction of nodes in its influence * range and divides it by the average distance of those nodes from v, * ignoring nodes that are not reachable from it. */ void Graph::centralityClosenessInfluenceRange(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug()<< "Graph::centralityClosenessImproved()"; if (!graphModified && calculatedIRCC ) { qDebug() << "Graph::centralityClosenessImproved() - " " graph not changed - returning"; return; } if (!reachabilityMatrixCreated || graphModified) { qDebug()<< "Graph::centralityClosenessImproved() - " "call reachabilityMatrix()"; reachabilityMatrix(considerWeights, inverseWeights, dropIsolates); } // calculate centralities QList::const_iterator it; float IRCC=0,SIRCC=0; float Ji=0; classesIRCC=0; discreteIRCCs.clear(); sumIRCC=0; maxIRCC=0; minIRCC=vertices(dropIsolates)-1; float V=vertices(dropIsolates); varianceIRCC=0; meanIRCC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ IRCC=0; if (!(*it)->isIsolated()) { // find connected nodes QList influencedVertices = influenceRanges.values((*it)->name()-1); Ji=influencedVertices.size(); for (int i = 0; i < Ji; ++i) { qDebug() << "Graph:: centralityClosenessImproved - vertex " << (*it)->name() << " is outbound connected to = " << influencedVertices.at(i) + 1 << " at distance " << DM.item ((*it)->name()-1, influencedVertices.at(i) ); IRCC += DM.item ((*it)->name()-1, influencedVertices.at(i) ) ; } qDebug()<< "Graph:: centralityClosenessImproved - size of influenceRange Ji = " << Ji << " IRCC=" << IRCC << " divided by Ji=" << Ji << " yields final IRCC =" << IRCC / Ji; // sanity check for IRCC=0 (=> node is disconnected) if (IRCC != 0) { IRCC /= Ji; IRCC = ( Ji / (V-1) ) / IRCC; } } sumIRCC += IRCC; (*it) -> setIRCC ( IRCC ) ; (*it) -> setSIRCC ( IRCC ) ; // IRCC is a ratio, already std resolveClasses(IRCC, discreteIRCCs, classesIRCC); minmax( IRCC, (*it), maxIRCC, minIRCC, maxNodeIRCC, minNodeIRCC) ; qDebug() << "Graph::centralityClosenessImproved - vertex " << (*it)->name() << " has IRCC = " << Ji / (V-1) << " / " << IRCC << " = " << (*it)->IRCC(); } meanIRCC = sumIRCC / (float) V; qDebug("Graph::centralityClosenessImproved - sumIRCC = %f, meanIRCC = %f", sumIRCC, meanIRCC); if (minIRCC == maxIRCC) maxNodeIRCC=-1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! dropIsolates || ! (*it)->isIsolated() ) { SIRCC= (*it) -> SIRCC(); varianceIRCC += (SIRCC-meanIRCC) * (SIRCC-meanIRCC) ; } } varianceIRCC=varianceIRCC/(float) V; calculatedIRCC=true; } //Writes the closeness centralities to a file void Graph::writeCentralityCloseness( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified || !calculatedCentralities ) { emit statusMessage ( (tr("Calculating shortest paths")) ); createDistanceMatrix(true, considerWeights, inverseWeights, dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } emit statusMessage ( QString(tr("Writing closeness indices to file:")) .arg(fileName) ); outText.setRealNumberPrecision(m_precision); outText << tr("CLOSENESS CENTRALITY (CC)")<<"\n"; outText << tr("The CC index is the inverted sum of geodesic distances " " from each node u to all other nodes.")<<"\n"; outText << tr("CC' is the standardized CC (multiplied by N-1 minus isolates).") <<"\n"; outText << tr("Note: The CC index considers outbound arcs only and " "isolate nodes are dropped by default. Read the Manual for more.") << "\n\n"; outText << tr("CC range: 0 < C < ")<::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->CC() << "\t\t" << (*it)->SCC() << "\t\t" << (100* ((*it)->SCC()) )<::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->IRCC() << "\t\t" << (100* ((*it)->SIRCC()) )<::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->BC() << "\t\t"<< (*it)->SBC() << "\t\t" << (100* ((*it)->SBC()))<::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->SC() << "\t\t" << (*it)->SSC() << "\t\t" << (100* ((*it)->SSC()) )< max distance to all other nodes is 1)") << "\n\n"; outText << "Node"<<"\tEC=EC'\t\t%EC\n"; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->EC() << "\t\t" << (100* ((*it)->SEC()) )<::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->PC() << "\t\t" << (*it)->SPC() << "\t\t" << (100* ((*it)->SPC()))<::const_iterator it, it1; H_StrToInt::iterator it2; int vert=vertices(dropIsolates); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DP=0; if (!(*it)->isIsolated()) { for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( (weight=this->hasArc ( (*it1)->name(), (*it)->name() ) ) !=0 ) { if (weights) DP+=weight; else DP++; } //check if the matrix is symmetric - we need this below if ( ( this->hasArc ( (*it1)->name(), (*it)->name() ) ) != ( this->hasArc ( (*it)->name(), (*it1)->name() ) ) ) symmetricAdjacencyMatrix = false; } } (*it) -> setDP ( DP ) ; //Set DP t_sumDP += DP; qDebug() << "Graph: prestigeDegree() vertex " << (*it)->name() << " DP " << DP; } // Calculate std DP, min,max, mean for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DP= (*it)->DP(); if (!weights) { SDP=( DP / (vert-1.0) ); //Set Standard InDegree } else { SDP =( DP / (t_sumDP) ); } (*it) -> setSDP( SDP ); sumDP += SDP; qDebug() << "Graph::prestigeDegree - vertex " << (*it)->name() << " DP " << DP << " SDP " << (*it)->SDP (); it2 = discreteDPs.find(QString::number(SDP)); if (it2 == discreteDPs.end() ) { classesDP++; qDebug("This is a new DP class"); discreteDPs.insert ( QString::number(SDP), classesDP ); } qDebug("DP classes = %i ", classesDP); if (maxDP < SDP ) { maxDP = SDP ; maxNodeDP=(*it)->name(); } if (minDP > SDP ) { minDP = SDP ; minNodeDP=(*it)->name(); } } if (minDP == maxDP) maxNodeDP=-1; meanDP = sumDP / (float) vert; qDebug("Graph: sumDP = %f, meanDP = %f", sumDP, meanDP); // Calculate Variance and the Degree Prestigation of the whole graph. :) for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } SDP= (*it)->SDP(); nom+= maxDP-SDP; varianceDP += (SDP-meanDP) * (SDP-meanDP) ; } varianceDP=varianceDP/(float) vert; if (symmetricAdjacencyMatrix) denom=(vert-1.0)*(vert-2.0); else denom=(vert-1.0)*(vert-1.0); if (vert < 3 ) denom = vert-1.0; //qDebug () << "*** vert is " << vert << " nom " << nom << " denom is " << denom; if (!weights) { groupDP=nom/denom; qDebug("Graph: varianceDP = %f, groupDP = %f", varianceDP, groupDP); } calculatedDP=true; } void Graph::writePrestigeDegree (const QString fileName, const bool considerWeights, const bool dropIsolates) { QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); prestigeDegree(considerWeights, dropIsolates); float maximumIndexValue=vertices()-1.0; outText.setRealNumberPrecision(m_precision); outText << tr("DEGREE PRESTIGE (DP)\n"); outText << tr("The DP index of a node u is the sum of inbound edges to " "that node from all adjacent nodes.\n"); outText << tr("If the network is weighted, DP is the sum of inbound arc " "weights (inDegree) to node u from all adjacent nodes.\n"); outText << tr("The DP index is also known as InDegree Centrality.") << "\n"; outText << tr("DP' is the standardized DP (divided by N-1)\n\n"); if (considerWeights){ maximumIndexValue=(vertices()-1.0)*maxDP; outText << tr("DP range: 0 < C < undefined (valued graph)")<<"\n"; } else outText << tr("DP range: 0 < C < ")<::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t"<<(*it)->DP() << "\t"<< (*it)->SDP() << "\t" << (100* ((*it)->SDP()) )<::const_iterator it; float PP=0; float Ii=0; int i=0; classesPP=0; discretePPs.clear(); sumPP=0; maxPP=0; minPP=vertices(dropIsolates)-1; float V=vertices(dropIsolates); variancePP=0; meanPP=0; H_StrToInt::iterator it2; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ PP=0; i=0; if (!(*it)->isIsolated()){ // find connected nodes QList influencerVertices = influenceDomains.values((*it)->name()-1); Ii=influencerVertices.size(); qDebug()<< "Graph::PP - vertex " << (*it)->name() << " Ii size: " << Ii; for ( i = 0; i < Ii; i++) { qDebug() << "Graph::PP - vertex " << (*it)->name() << " is inbound connected from " << influencerVertices.at(i) + 1 << " at distance " << DM.item ( influencerVertices.at(i), (*it)->name()-1); PP += DM.item ( influencerVertices.at(i), (*it)->name()-1); } qDebug()<< "Graph::PP - " "size of influenceDomain Ii = " << Ii << " PP=" << PP << " divided by Ii=" << Ii << " yields graph-dependant PP index =" << PP / Ii; qDebug() << "Graph::PP - vertex " << (*it)->name() << " has PP = " << Ii / (V-1) << " / " << PP / Ii << " = " << ( Ii / (V-1) ) / (PP/Ii); // sanity check for PP=0 (=> node is disconnected) if (PP != 0) { PP /= Ii; PP = ( Ii / (V-1) ) / PP; } sumPP += PP; } (*it) -> setPP ( PP ) ; (*it) -> setSPP ( PP ) ; // PP is already stdized it2 = discretePPs.find(QString::number(PP)); if (it2 == discretePPs.end() ) { classesPP++; qDebug() << "PP = " << (*it) -> PP() << " - this is a new PP class" ; discretePPs.insert ( QString::number(PP), classesPP ); } //qDebug("PP classes = %i ", classesPP); if (maxPP < PP ) { maxPP = PP ; maxNodePP=(*it)->name(); } if (minPP > PP ) { minPP = PP ; minNodePP=(*it)->name(); } } if (minPP == maxPP) maxNodePP=-1; meanPP = sumPP / (float) V; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } PP= (*it) -> PP(); variancePP += (PP-meanPP) * (PP-meanPP) ; } variancePP=variancePP/(float) V; qDebug() << "Graph::prestigeProximity - sumPP = " << sumPP << " meanPP = " << meanPP << " variancePP " << variancePP; calculatedPP=true; } //Writes the proximity prestige indeces to a file void Graph::writePrestigeProximity( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating prestige proximity indices")) ); prestigeProximity(considerWeights, inverseWeights, dropIsolates); emit statusMessage ( QString(tr("Writing proximity prestige indices to file:")) .arg(fileName) ); outText.setRealNumberPrecision(m_precision); outText << tr("PROXIMITY PRESTIGE (PP)\n" "The PP index of a node u is the ratio of the proportion of " "nodes who can reach u to the average distance these nodes are " "from u. Read the Manual for more.") <<"\n\n"; outText << tr("PP range: 0 < PP < 1 " " (PP is a ratio)")<<"\n\n"; outText << "Node"<<"\tPP=PP'\t\t%PP\n"; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { outText << (*it)->name()<<"\t -- \t\t --" <name()<<"\t" <<(*it)->PP() << "\t\t" << (100* (*it)->SPP() ) <::const_iterator it; int relation=0; bool edgeStatus=false; H_edges::const_iterator jt; qDebug()<< "Graph::prestigePageRank() " << "active vertices: " << aVert << " total vertices: " << vertices(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ // At first, PR scores have probability distribution // from 0 to 1, so each one is set to 1/N (*it)->setPRP( 1.0 / aVert ); // compute inEdges() to warm up inEdgesConst for everyone inLinks = (*it)->inEdges(); outLinks = (*it)->outEdges(); qDebug() << "Graph::prestigePageRank() - node " << (*it)->name() << " PR = " << (*it)->PRP() << " inLinks (set const): " << inLinks << " outLinks (set const): " << outLinks; } if ( enabledEdges() == 0 ) { qDebug()<< "Graph::prestigePageRank() " <<" - all vertices are isolated and of equal PR. Stop"; return; } // begin iteration - continue until we reach our desired delta while (maxDelta > delta) { qDebug()<< "Graph::prestigePageRank() - ITERATION : " << iterations; t_sumPRP=0; maxDelta = 0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { sumInLinksPR = 0; oldPRP = (*it)->PRP(); qDebug() << "Graph::prestigePageRank() - computing PR for node: " << (*it)->name() << " current PR " << oldPRP; if ( (*it)->isIsolated() ) { // isolates have constant PR = 1/N qDebug() << "Graph::prestigePageRank() - isolated - CONTINUE "; continue; } jt=(*it)->m_inEdges.cbegin(); qDebug() << "Graph::prestigePageRank() - Iterate over inEdges of " << (*it)->name() ; while ( jt != (*it) -> m_inEdges.cend() ) { relation = jt.value().first; if ( relation != currentRelation() ){ ++jt; continue; } edgeStatus=jt.value().second.second; if ( edgeStatus != true){ ++jt; continue; } referrer = jt.key(); qDebug() << "Graph::prestigePageRank() - Node " << (*it)->name() << " inLinked from neighbor " << referrer << " index " << index[referrer]; if ( this->hasArc( referrer , (*it)->name() ) ) { inLinks = m_graph[ index[referrer] ] ->inEdgesConst(); outLinks = m_graph[ index[referrer] ]-> outEdgesConst(); PRP = m_graph[ index[referrer] ]->PRP(); transferedPRP = (outLinks != 0 ) ? ( PRP / outLinks ) : PRP; qDebug()<< "Graph::prestigePageRank() - neighbor " << referrer << " has PR = " << PRP << " and outLinks = " << outLinks << " will transfer " << transferedPRP ; sumInLinksPR += transferedPRP; } ++jt; } PRP = (1-d_factor) / aVert + d_factor * sumInLinksPR; (*it) -> setPRP ( PRP ); t_sumPRP+=PRP; qDebug() << "Graph::prestigePageRank() - Node " << (*it)->name() << " new PR = " << PRP << " old PR was = " << oldPRP << " diff = " << fabs(PRP - oldPRP); // calculate diff from last PageRank value for this vertex // and set it to minDelta if the latter is bigger. if ( maxDelta < fabs(PRP - oldPRP) ) { maxDelta = fabs(PRP - oldPRP); qDebug()<< "Graph::prestigePageRank() - Setting new maxDelta = " << maxDelta; } } // normalize in every iteration qDebug() << "Graph::prestigePageRank() - sumPRP for this iteration " << t_sumPRP; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { PRP = (*it)->PRP(); SPRP = PRP / t_sumPRP ; // (*it)->setPRP( SPRP ); // ??? qDebug() << "Graph::prestigePageRank() - Node " << (*it)->name() << " normalized SPRP = " << SPRP; } iterations++; } // calculate std and min/max PRPs for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if (dropIsolates && (*it)->isIsolated()) { continue; } PRP = (*it)->PRP(); SPRP = PRP / t_sumPRP ; (*it)->setSPRP( SPRP ); sumPRP += SPRP; qDebug()<< "Graph::prestigePageRank() vertex: " << (*it)->name() << " PR = " << PRP << " standard PR = " << SPRP << " t_sumPRP " << t_sumPRP; } if (aVert != 0 ) meanPRP = sumPRP / aVert ; else meanPRP = SPRP; qDebug() << "sumPRP = " << sumPRP << " aVert = " << aVert << " meanPRP = " << meanPRP; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated()) { continue; } SPRP=(*it)->SPRP(); resolveClasses(SPRP,discretePRPs,classesPRP); if ( SPRP > maxPRP ) { maxPRP = SPRP; maxNodePRP=(*it)->name(); } if ( SPRP < minPRP ) { minPRP = SPRP; minNodePRP=(*it)->name(); } t_variance = ( SPRP - meanPRP ) ; t_variance *=t_variance; qDebug() << "SPRP " << (*it)->SPRP() << " t_variance " << SPRP - meanPRP << " t_variance^2" << t_variance ; variancePRP += t_variance; } qDebug() << "PRP' Variance " << variancePRP << " aVert " << aVert ; variancePRP = variancePRP / (aVert); qDebug() << "PRP' Variance: " << variancePRP ; calculatedPRP= true; return; } //Writes the PageRank indices to a file void Graph::writePrestigePageRank(const QString fileName, const bool dropIsolates){ QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating PageRank indices. Please wait...")) ); prestigePageRank(dropIsolates); emit statusMessage ( QString(tr("Writing PageRank indices to file: %1")) .arg(fileName) ); outText.setRealNumberPrecision(m_precision); outText << tr("PAGERANK PRESTIGE (PRP)")<<"\n"; outText << tr("")<<"\n"; outText << tr("PRP range: (1-d)/N = ") << ( 1- d_factor ) / vertices() << " < PRP" << endl; outText << " d =" << d_factor << endl; outText << tr("PRP' is the standardized PR (PR divided by sumPR)")<<"\n"; outText << tr("PRP' range: ") << " (1-d)/N < C'< 1" <<"\n\n"; outText << "Node"<<"\tPRP\t\tPRP'\t\t%PRP'\n"; QList::const_iterator it; float PRP=0, SPRP=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ PRP = (*it)->PRP(); SPRP = (*it)->SPRP(); outText << (*it)->name()<<"\t"<< PRP << "\t\t"<< SPRP << "\t\t" << ( 100* SPRP )<::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ outText << (*it)->name()<<"\t"<<(*it)->CLC() <SDC(); std= (*it)->SDC(); maxC=maxDC; break; } case 2 : { qDebug("Layout according to CC"); C=(*it)->CC(); std= (*it)->SCC(); maxC=maxCC; break; } case 3 : { qDebug("Layout according to IRCC"); qDebug() << "Layout according to IRCC C = " << (*it)->IRCC(); qDebug() << "Layout according to IRCC std = " << (*it)->SIRCC(); qDebug() << "Layout according to IRCC maxC= " << maxIRCC; C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { qDebug("Layout according to BC"); C=(*it)->BC(); std= (*it)->SBC(); maxC=maxBC; break; } case 5 : { qDebug("Layout according to SC"); C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSC; break; } case 6 : { qDebug("Layout according to EC"); C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { qDebug("Layout according to PC"); C=(*it)->PC(); std= (*it)->SPC(); maxC=maxPC; break; } case 8 : { qDebug("Layout according to IC"); C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { qDebug("Layout according to DP"); C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxDP; break; } case 10 : { qDebug("Layout according to PRP"); C=(*it)->PRP(); std= (*it)->SPRP(); maxC=maxPRP; break; } case 11 : { qDebug("Layout according to PP"); C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; qDebug () << "Vertice " << (*it)->name() << " at x=" << (*it)->x() << ", y= "<< (*it)->y() << ": C=" << C << ", stdC=" << std << ", maxradius " << maxRadius << ", maxC " << maxC << ", C/maxC " << (C/maxC) << ", newradius " << (std/maxC - offset)*maxRadius; switch (static_cast (ceil(maxC)) ){ case 0: { qDebug("maxC=0. Using maxHeight"); new_radius=maxRadius; break; } default: { new_radius=(maxRadius- (std/maxC - offset)*maxRadius); break; } }; qDebug ("new radius %f", new_radius); //Calculate new position rad= (2.0* Pi/ vert ); new_x=x0 + new_radius * cos(i * rad); new_y=y0 + new_radius * sin(i * rad); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug("Finished Calculation. Vertice will move to x=%f and y=%f ", new_x, new_y); //Move node to new position emit moveNode((*it)->name(), new_x, new_y); i++; emit addGuideCircle ( static_cast (x0), static_cast (y0), static_cast (new_radius) ); } graphModified=true; } /** * Repositions all nodes on different random positions * Emits moveNode(i, x,y) to tell GW that the node item should be moved. */ void Graph::layoutRandom(double maxWidth, double maxHeight){ qDebug("Graph: layoutRandom..."); double new_x=0, new_y=0; for (Vertices::iterator it=m_graph.begin(); it!=m_graph.end(); it++){ new_x= rand() % ( static_cast (maxWidth) ); new_y= rand() % ( static_cast (maxHeight) ); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug()<< "Graph: Emitting moveNode to move Vertice " << (*it)->name() //<< "indexed " << index((*it)->name()) << " to new position " << new_x << " , "<< new_y; emit moveNode((*it)->name(), new_x, new_y); } graphModified=true; } /** * @brief Graph::layoutCircularRandom * Repositions all nodes on the periphery of different circles with random radius * @param x0 * @param y0 * @param maxRadius */ void Graph::layoutCircularRandom(double x0, double y0, double maxRadius){ qDebug() << "Graph::layoutCircularRandom - "; double rad=0, new_radius=0, new_x=0, new_y=0; double i=0; //offset controls how far from the centre the central nodes be positioned float offset=0.06, randomDecimal=0; int vert=vertices(); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ randomDecimal = (float ) ( rand()%100 ) / 100.0; new_radius=(maxRadius- (randomDecimal - offset)*maxRadius); qDebug () << "Vertice " << (*it)->name() << " at x=" << (*it)->x() << ", y= "<< (*it)->y() << ", maxradius " << maxRadius << " randomDecimal " << randomDecimal << " new radius " << new_radius; //Calculate new position rad= (2.0* Pi/ vert ); new_x=x0 + new_radius * cos(i * rad); new_y=y0 + new_radius * sin(i * rad); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug("Vertice will move to x=%f and y=%f ", new_x, new_y); //Move node to new position emit moveNode((*it)->name(), new_x, new_y); i++; emit addGuideCircle ( static_cast (x0), static_cast (y0), static_cast (new_radius) ); } graphModified=true; } /** * Repositions all nodes on different top-down levels according to their centrality * Emits moveNode(i, x,y) to tell GW that the node item should be moved. */ void Graph::layoutLevelByProminenceIndex(double maxWidth, double maxHeight, int prominenceIndex, const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug("Graph: layoutLevelCentrality..."); if ( prominenceIndex == 1) { if (graphModified || !calculatedDC ) centralityDegree(true, dropIsolates); } else if ( prominenceIndex == 3 ){ if (graphModified || !calculatedIRCC ) centralityClosenessInfluenceRange(); } else if ( prominenceIndex == 8 ) { if (graphModified || !calculatedIC ) centralityInformation(); } else if ( prominenceIndex == 9){ if (graphModified || !calculatedDP ) prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == 10 ) { if (graphModified || !calculatedPRP ) prestigePageRank(); } else if ( prominenceIndex == 11 ){ if (graphModified || !calculatedPP ) prestigeProximity(considerWeights, inverseWeights); } else{ if (graphModified || !calculatedCentralities ) createDistanceMatrix(true, considerWeights, inverseWeights, dropIsolates); } double i=0, std=0; //offset controls how far from the top the central nodes will be float C=0, maxC=0, offset=50; double new_x=0, new_y=0; // int vert=vertices(); maxHeight-=offset; maxWidth-=offset; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ switch (prominenceIndex) { case 1 : { qDebug("Layout according to DC"); C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxDC; break; } case 2 : { qDebug("Layout according to CC"); C=(*it)->CC(); std= (*it)->SCC(); maxC=maxCC; break; } case 3 : { qDebug("Layout according to IRCC"); C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { qDebug("Layout according to BC"); C=(*it)->BC(); std= (*it)->SBC(); maxC=maxBC; break; } case 5 : { qDebug("Layout according to SC"); C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSC; break; } case 6 : { qDebug("Layout according to EC"); C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { qDebug("Layout according to PC"); C=(*it)->PC(); std= (*it)->SPC(); maxC=maxPC; break; } case 8 : { qDebug("Layout according to IC"); C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { qDebug("Layout according to DP"); C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxDP; break; } case 10 : { qDebug("Layout according to PRP"); C=(*it)->PRP(); std= (*it)->SPRP(); maxC=maxPRP; break; } case 11 : { qDebug("Layout according to PP"); C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; qDebug()<< "Vertice " << (*it)->name() << " at x="<< (*it)->x() << ", y="<< (*it)->y() << ": C=" << C << ", stdC=" << std << ", maxC "<< maxC << ", maxWidth " << maxWidth <<" , maxHeight "< (ceil(maxC)) ){ case 0: { qDebug("maxC=0. Using maxHeight"); new_y=maxHeight; break; } default: { new_y=offset/2.0+maxHeight-(std/maxC)*maxHeight; break; } }; new_x=offset/2.0 + rand() % ( static_cast (maxWidth) ); qDebug ("new_x %f, new_y %f", new_x, new_y); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug() << "Finished Calculation. " "Vertice will move to x="<< new_x << " and y= " << new_y; //Move node to new position emit moveNode((*it)->name(), new_x, new_y); i++; emit addGuideHLine(static_cast ( new_y ) ); } // graphModified=true; // emit graphChanged(); } /** * @brief Graph::layoutVerticesSizeByProminenceIndex * changes the node size to be proportinal to given prominence index * @param prominenceIndex */ void Graph::layoutVerticesSizeByProminenceIndex (int prominenceIndex, const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug() << "Graph::layoutVerticesSizeByProminenceIndex - " << "prominenceIndex index = " << prominenceIndex; double std=0; float C=0, maxC=0; int new_size=0; //first calculate centrality indices if needed if ( prominenceIndex == 0) { // do nothing } else if ( prominenceIndex == 1) { if (graphModified || !calculatedDC ) centralityDegree(true, dropIsolates); } else if ( prominenceIndex == 3 ){ if (graphModified || !calculatedIRCC ) centralityClosenessInfluenceRange(); } else if ( prominenceIndex == 8 ) { if (graphModified || !calculatedIC ) centralityInformation(); } else if ( prominenceIndex == 9){ if (graphModified || !calculatedDP ) prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == 10 ) { if (graphModified || !calculatedPRP ) prestigePageRank(); } else if ( prominenceIndex == 11 ){ if (graphModified || !calculatedPP ) prestigeProximity(considerWeights, inverseWeights); } else{ if (graphModified || !calculatedCentralities ) createDistanceMatrix(true, considerWeights, inverseWeights, dropIsolates); } QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ switch (prominenceIndex) { case 0: { C=0;maxC=0; break; } case 1 : { qDebug("VerticesSize according to DC"); C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxDC; break; } case 2 : { qDebug("VerticesSize according to CC"); C=(*it)->CC(); std= (*it)->SCC(); maxC=maxCC; break; } case 3 : { qDebug("VerticesSize according to IRCC"); C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { qDebug("VerticesSize according to BC"); C=(*it)->BC(); std= (*it)->SBC(); maxC=maxBC; break; } case 5 : { qDebug("VerticesSize according to SC"); C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSC; break; } case 6 : { qDebug("VerticesSize according to EC"); C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { qDebug("VerticesSize according to PC"); C=(*it)->PC(); std= (*it)->SPC(); maxC=maxPC; break; } case 8 : { qDebug("VerticesSize according to IC"); C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { qDebug("VerticesSize according to DP"); C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxDP; break; } case 10 : { qDebug("VerticesSize according to PRP"); C=(*it)->PRP(); std= (*it)->SPRP(); maxC=maxPRP; break; } case 11 : { qDebug("VerticesSize according to PP"); C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; qDebug () << "Vertex " << (*it)->name() << ": C=" << C << ", stdC=" << std << ", maxC " << maxC << "initVertexSize " << initVertexSize << " stdC/maxC " << std/maxC << ", (std/maxC) * initVertexSize " << (std/maxC *initVertexSize); switch (static_cast (ceil(maxC) )){ case 0: { qDebug()<<"maxC=0. Using initVertexSize"; new_size=initVertexSize; //emit signal to change node size emit setNodeSize((*it)->name(), new_size); break; } default: { //Calculate new size new_size=ceil ( initVertexSize/2.0 + (float) initVertexSize * (std/maxC)); qDebug ()<< "new vertex size "<< new_size << " call setSize()"; (*it)->setSize(new_size); //emit signal to change node size emit setNodeSize((*it)->name(), new_size); break; } }; } // graphModified=true; // emit graphChanged(); } /** Adds a little universal randomness :) */ void Graph::makeThingsLookRandom() { time_t now; /* define 'now'. time_t is probably a typedef */ now = time((time_t *)NULL); /* Get the system time and put it * into 'now' as 'calender time' the number of seconds since 1/1/1970 */ srand( (unsigned int ) now); } /** layman's attempt to create a random network */ void Graph::createRandomNetErdos( const int &vert, const QString &model, const int &edges, const float &eprob, const QString &mode, const bool &diag) { qDebug() << "Graph::createRandomNetErdos() - vertices " << vert << " model " << model << " edges " << edges << " edge probability " << eprob << " graph mode " << mode << " diag " << diag; index.reserve(vert); makeThingsLookRandom(); int progressCounter=0; int edgeCount = 0; qDebug() << "Graph::createRandomNetErdos() - Creating nodes..."; for (register int i=0; i< vert ; i++) { int x=10+rand() %640; int y=10+rand() %480; qDebug("Graph: createRandomNetErdos, new node i=%i, at x=%i, y=%i", i+1, x,y); createVertex ( i+1, initVertexSize, initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape, false ); progressCounter++; emit updateProgressDialog( progressCounter ); } qDebug() << "Graph::createRandomNetErdos() - Creating edges..."; if ( model == "G(n,p)") { qDebug() << "Graph::createRandomNetErdos() - G(n,p) model..."; for (register int i=0;i " << j+1; if (!diag && i==j) { qDebug()<< " Graph::createRandomNetErdos() - skip because " << i+1 << " = " << j+1 << " and diag " << diag; continue; } if ( ( rand() % 100 + 1 ) / 100.0 < eprob ) { edgeCount ++ ; if (mode == "graph") { qDebug() << "Graph::createRandomNetErdos() - " <<" create undirected Edge no " << edgeCount; createEdge(i+1, j+1, 1, "black", 2, true, false); } else { qDebug() << "Graph::createRandomNetErdos() - " <<" create directed Edge no " << edgeCount; createEdge(i+1, j+1, 1, "black", 0, true, false); } } else qDebug() << "Graph::createRandomNetErdos() - do not create Edge"; } progressCounter++; emit updateProgressDialog(progressCounter ); qDebug("Emitting UPDATE PROGRESS %i", progressCounter); } } else { qDebug() << "Graph::createRandomNetErdos() - G(n,M) model..."; int source = 0, target = 0 ; do { source = rand() % vert + 1; target = rand() % vert + 1; qDebug() << "Graph::createRandomNetErdos() - random pair " << " " << source << " , " << target ; if (!diag && source == target ) { qDebug() << "Graph::createRandomNetErdos() - skip self loop pair "; continue; } if ( hasArc(source, target) ) { qDebug() << "Graph::createRandomNetErdos() - skip pair - exists"; continue; } edgeCount ++; if (mode == "graph") { qDebug() << "Graph::createRandomNetErdos() - create " << " undirected Edge no " << edgeCount; createEdge(source, target, 1, "black", 2, true, false); } else { qDebug() << "Graph::createRandomNetErdos() - create " << " directed Edge no " << edgeCount; createEdge(source, target, 1, "black", 0, true, false); } } while ( edgeCount != edges ); } addRelationFromGraph(tr("erdos-renyi")); //FIXME emit graphChanged(); } /** layman's attempt to create a random ring lattice network. */ void Graph::createRandomNetRingLattice( int vert, int degree, double x0, double y0, double radius) { qDebug("Graph: createRingLatticeNetwork"); int x=0; int y=0; int progressCounter=0; double rad= (2.0* Pi/ vert ); makeThingsLookRandom(); index.reserve(vert); for (register int i=0; i< vert ; i++) { x=x0 + radius * cos(i * rad); y=y0 + radius * sin(i * rad); createVertex( i+1,initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape, false); qDebug("Graph: createPhysicistLatticeNetwork, new node i=%i, at x=%i, y=%i", i+1, x,y); progressCounter++; emit updateProgressDialog( progressCounter ); } int target = 0; for (register int i=0;i " << j+1; createEdge (i+1, j+1, 1, "black", 2, true, false); } progressCounter++; emit updateProgressDialog(progressCounter ); } qDebug()<< endl << "Graph::createRandomNetScaleFree() - " << " start network growth to " << n << " nodes with preferential attachment" << endl; for (register int i= m0 ; i < n ; ++i) { x=x0 + radius * cos(i * rad); y=y0 + radius * sin(i * rad); qDebug() << "Graph::createRandomNetScaleFree() - " << " adding new node i " << i+1 << " pos " << x << "," << y << endl; createVertex( i+1, initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape,false ); progressCounter++; emit updateProgressDialog( progressCounter ); // no need to multiply by 2, since enabledEdges already reports // twice the current number of edges in the network sumDegrees = enabledEdges(); newEdges = 0; for (;;) { //do until we create m new edges for (register int j=0; j < i ; ++j) { qDebug() << "Graph::createRandomNetScaleFree() - " << " preferential attachment test of new node i " << i+1 << " with node j " << j+1 << " - newEdges " << newEdges ; if (newEdges == m) break; k_j = inDegree(j+1); k_j = pow ( k_j , power ); if (sumDegrees < 1 ) prob_j = 1; // always create edge if no other edge exist else prob_j = ( alpha + k_j ) / sumDegrees ; prob = ( rand() % 100 + 1 ) / 100.0; qDebug() << "Graph::createRandomNetScaleFree() - " << " Edge probability with old node " << j+1 << " is: alpha + k_j ^ power " << alpha + k_j << " / sumDegrees " << sumDegrees << " = prob_j " << prob_j << " prob " << prob ; if ( prob <= prob_j ) { if ( mode == "graph") { qDebug() << " --- Creating pref.att. reciprocal edge " << i+1 << " <-> " << j+1; createEdge (i+1, j+1, 1, "black", 2, true, false); newEdges ++; } else { qDebug() << " --- Creating pref.att. directed edge " << i+1 << " <-> " << j+1; createEdge (i+1, j+1, 1, "black", 1, true, false); newEdges ++; } } } if ( newEdges == m ) break; } } addRelationFromGraph(tr("scale-free")); emit signalNodeSizesByInDegree(true); emit graphChanged(); } void Graph::createRandomNetSmallWorld ( int vert, int degree, double beta, double x0, double y0, double radius) { qDebug("Graph: createRandomNetSmallWorld. First creating a ring lattice"); createRandomNetRingLattice(vert, degree, x0, y0, radius); qDebug("******** Graph: REWIRING starts..."); int candidate; for (register int i=1;i>>>> REWIRING: Check if "<< i << " is linked to " << j; if ( this-> hasArc(i, j) ) { qDebug()<<">>>>> REWIRING: They're linked. Do a random REWIRING " "Experiment between "<< i<< " and " << j << " Beta parameter is " << beta; if (rand() % 100 < (beta * 100)) { qDebug(">>>>> REWIRING: We'l break this edge!"); removeEdge(i, j); removeEdge(j, i); qDebug()<<">>>>> REWIRING: OK. Let's create a new edge!"; for (;;) { //do until we create a new edge candidate=rand() % (vert+1) ; //pick another vertex. if (candidate == 0 || candidate == i) continue; qDebug()<<">>>>> REWIRING: Candidate: "<< candidate; //Only if differs from i and hasnot edge with it if (! this->hasArc(i, candidate) ) qDebug("<----> Random New Edge Experiment between %i and %i:", i, candidate); if (rand() % 100 > 0.5) { qDebug("Creating new link!"); createEdge(i, candidate, 1, "black", true, true, false); break; } } } else qDebug("Will not break link!"); } } } emit signalNodeSizesByInDegree(true); } /** layman's attempt to create a random network where nodes have the same degree. */ void Graph::createSameDegreeRandomNetwork(int vert, int degree){ qDebug("Graph: createSameDegreeRandomNetwork"); int progressCounter=0; makeThingsLookRandom(); index.reserve(vert); for (register int i=0; i< vert ; i++) { int x=10+rand() %640; int y=10+rand() %480; qDebug("Graph: createUniformRandomNetwork, new node i=%i, at x=%i, y=%i", i+1, x,y); createVertex( i+1, initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape,false ); progressCounter++; emit updateProgressDialog( progressCounter ); } int target = 0; for (register int i=0;i50), since it will calculate all powers of the sociomatrix up to n-1 in order to find out all possible walks. If you need to make a simple reachability test, we advise to use the reachabilityMatrix() function instead. */ void Graph::createNumberOfWalksMatrix(int length) { qDebug()<<"Graph::numberOfWalks() - first create the Adjacency Matrix AM"; bool dropIsolates=false; bool considerWeights=true; bool inverseWeights=false; bool symmetrize=false; createAdjacencyMatrix(dropIsolates, considerWeights, inverseWeights, symmetrize); int size = vertices(); int maxPower = length; XM = AM; // XM will be the product matrix XSM = AM; // XSM is the sum of product matrices Matrix PM; // temp matrix PM.zeroMatrix(size, size); qDebug()<< "Graph::writeNumberOfWalksMatrix() XM is " ; for (register int i=0; i < size ; i++) { for (register int j=0; j < size ; j++) { qDebug() << XM.item(i,j) << " "; } qDebug()<< endl; } qDebug()<< "Graph::writeNumberOfWalksMatrix() calculating sociomatrix powers up to " << maxPower; for (register int i=2; i <= maxPower ; i++) { PM.product(XM,AM, false); XM=PM; XSM = XSM+XM; } } void Graph::writeTotalNumberOfWalksMatrix(QString fn, QString netName, int length){ qDebug("Graph::writeTotalNumberOfWalksMatrix() "); QFile file (fn); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fn) ); return; } QTextStream outText(&file); outText.setCodec("UTF-8"); outText << "-Social Network Visualizer- \n"; outText << "Network name "<< netName<<": \n"; outText << "Total number of walks of any length less than or equal to "<< length <<" between each pair of nodes \n\n"; outText << "Warning: Walk counts consider unordered pairs of nodes\n\n"; createNumberOfWalksMatrix(length); outText << XSM ; file.close(); } void Graph::writeNumberOfWalksMatrix(QString fn, QString netName, int length){ qDebug("Graph::writeNumberOfWalksMatrix() "); QFile file (fn); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage (QString(tr("Could not write to %1")).arg(fn) ); return; } QTextStream outText(&file); outText.setCodec("UTF-8"); outText << "-Social Network Visualizer- \n"; outText << "Network name "<< netName<<": \n"; outText << "Number of walks of length "<< length <<" between each pair of nodes \n\n"; createNumberOfWalksMatrix(length); outText << XM ; file.close(); } /** Calculates and returns non-zero if vertices v1 and v2 are reachable. If v1, v2 are reachable it returns the geodesic distance. This method is actually a reachability test (if it returns non-zero) */ int Graph::reachable(int v1, int v2) { qDebug()<< "Graph::reachable()"; if (!distanceMatrixCreated || graphModified ) createDistanceMatrix(false); return DM.item(v1-1,v2-1); } /** * Returns the influence range of vertex v1, namely the set of nodes who are * reachable by v1 (See Wasserman and Faust, pp.200-201, based on Lin, 1976). * This function is for digraphs only */ QList Graph::influenceRange(int v1){ qDebug() << "Graph::influenceRange() "; if (!reachabilityMatrixCreated || graphModified) { // call reachabilityMatrix to construct a list of influence ranges // for each node reachabilityMatrix(); } return influenceRanges.values(v1); } /** * Returns the influence domain of vertex v1, namely the set of nodes who can * reach v1 * This function is for digraphs only */ QList Graph::influenceDomain(int v1){ qDebug() << "Graph::influenceDomain() "; if (!reachabilityMatrixCreated || graphModified) { // call reachabilityMatrix to construct a list of influence domains // for each node reachabilityMatrix(); } return influenceDomains.values(v1); } /** Calculates the reachability matrix X^R of the graph where the {i,j} element is 1 if the vertices i and j are reachable Actually, this just checks the corresponding element of Distance Matrix, If d(i,j) is non-zero, then the two nodes are reachable In the process, this function creates the InfluenceRange and InfluenceDomain of each node. */ void Graph::reachabilityMatrix( const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug()<< "Graph::reachabilityMatrix()"; if (reachabilityMatrixCreated && !graphModified) { qDebug()<< "Graph::reachabilityMatrix() - " "XRM calculated and graph unmodified. Returning..." ; return; } else { createDistanceMatrix(false, considerWeights,inverseWeights,dropIsolates); int size = vertices(false,false), i=0, j=0; qDebug()<< "Graph::reachabilityMatrix() - calculating XRM..." ; influenceRanges.clear(); influenceDomains.clear(); disconnectedVertices.clear(); bool isolateVertex=true; Q_UNUSED(isolateVertex); for (i=0; i < size ; i++) { for (j=i+1; j < size ; j++) { if ( XRM.item(i,j) ==1 ) { qDebug()<< "Graph::reachabilityMatrix() - d("<::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { (*it)->clearCliques(); } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( QString(tr("Writing clique census to file: ")).arg(fileName) ); outText << tr("CLIQUE CENSUS (CLQs)") << endl<name()); outText << (*it)->name()<<"\t"<< (*it)->cliques(2) << "\t" << (*it)->cliques(3) << "\t"<< (*it)->cliques(4) <::const_iterator i; outText << endl << tr("2-Vertex cliques") << endl ; for (i = cliques_2_Vertex.constBegin(); i != cliques_2_Vertex.constEnd(); ++i) { outText << i.key() << endl; } outText << endl << tr("3-Vertex cliques") << endl ; for (i = cliques_3_Vertex.constBegin(); i != cliques_3_Vertex.constEnd(); ++i) { outText << i.key() << endl; } outText << endl << tr("4-Vertex cliques") << endl ; for (i = cliques_4_Vertex.constBegin(); i != cliques_4_Vertex.constEnd(); ++i) { outText << i.key() << endl; } outText <<"\n\n" ; outText << tr("Clique Census Report,\n"); outText << tr("created by SocNetV: ")<< actualDateTime.currentDateTime(). toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } bool Graph:: addClique(const QList &list){ qDebug() << "*** Graph::addClique()" << list.count(); for (int i = 0; i < list.size(); ++i) { qDebug() << "*** Graph::addClique() - Found vertex " << list.at(i) << " at position " << i << endl; } QString dyad, dyad_alt; QString triad, triad_alt1, triad_alt2, triad_alt3,triad_alt4,triad_alt5; QString quart, quart_alt1, quart_alt2, quart_alt3,quart_alt4,quart_alt5; bool knownClique=false; if (list.size() == 2 ) { dyad = QString::number(list.at(0)) + ", " + QString::number(list.at(1)); dyad_alt = QString::number(list.at(1)) + ", " + QString::number(list.at(0)); if ( ! cliques_2_Vertex.contains(dyad) && ! cliques_2_Vertex.contains(dyad_alt) ) { cliques_2_Vertex.insert(dyad, true); qDebug() << "*** Graph::addClique() - new 2-vertex clique " << " adding it to global list "; } if ( m_graph[ index[list.at(0)] ]->addClique(dyad,list.size()) ) { qDebug() << "*** Graph::addClique() - new 2-vertex clique: " << list.at(0) << "," << list.at(1) ; return true; } return false; } else if (list.size() == 3 ) { triad = QString::number(list.at(0)) + ", " + QString::number(list.at(1)) + ", " + QString::number(list.at(2)); triad_alt1 = QString::number(list.at(0)) + ", " + QString::number(list.at(2)) + ", " + QString::number(list.at(1)); triad_alt2 = QString::number(list.at(1)) + ", " + QString::number(list.at(2)) + ", " + QString::number(list.at(0)); triad_alt3 = QString::number(list.at(1)) + ", " + QString::number(list.at(0)) + ", " + QString::number(list.at(2)); triad_alt4 = QString::number(list.at(2)) + ", " + QString::number(list.at(0)) + ", " + QString::number(list.at(1)); triad_alt5 = QString::number(list.at(2)) + ", " + QString::number(list.at(1)) + ", " + QString::number(list.at(0)); if ( ! cliques_3_Vertex.contains(triad) && ! cliques_3_Vertex.contains(triad_alt1) && ! cliques_3_Vertex.contains(triad_alt2) && ! cliques_3_Vertex.contains(triad_alt3) && ! cliques_3_Vertex.contains(triad_alt4) && ! cliques_3_Vertex.contains(triad_alt5) ) { cliques_3_Vertex.insert(triad, true); qDebug() << "*** Graph::addClique() - new 3-vertex clique " << " adding it to global list "; } if ( m_graph[ index[list.at(0)] ]->addClique(triad,list.size()) ) { qDebug() << "*** Graph::addClique() - new 3-vertex clique: " << list.at(0) << "," << list.at(1) << "," << list.at(2) ; return true; } return false; } else if (list.size() == 4 ) { for (int i = 0; i < list.size(); ++i) { quart = QString::number(list.at( (i)%4 )) + ", " + QString::number(list.at( (i+1) % 4 ) ) + ", " + QString::number(list.at( (i+2) % 4 ) ) + ", " + QString::number(list.at( (i+3) % 4 ) ); quart_alt1 = QString::number(list.at( (i)%4 )) + ", " + QString::number(list.at( (i+1) % 4 ) ) + ", " + QString::number(list.at( (i+3) % 4 ) ) + ", " + QString::number(list.at( (i+2) % 4 ) ); quart_alt2 = QString::number(list.at( (i)%4 )) + ", " + QString::number(list.at( (i+2) % 4 ) ) + ", " + QString::number(list.at( (i+3) % 4 ) ) + ", " + QString::number(list.at( (i+1) % 4 ) ); quart_alt3 = QString::number(list.at( (i)%4 )) + ", " + QString::number(list.at( (i+2) % 4 ) ) + ", " + QString::number(list.at( (i+1) % 4 ) ) + ", " + QString::number(list.at( (i+3) % 4 ) ); quart_alt4 = QString::number(list.at( (i)%4 )) + ", " + QString::number(list.at( (i+3) % 4 ) ) + ", " + QString::number(list.at( (i+1) % 4 ) ) + ", " + QString::number(list.at( (i+2) % 4 ) ); quart_alt5 = QString::number(list.at( (i)%4 )) + ", " + QString::number(list.at( (i+3) % 4 ) ) + ", " + QString::number(list.at( (i+2) % 4 ) ) + ", " + QString::number(list.at( (i+1) % 4 ) ); qDebug() << " checking other possible combinations: "; qDebug() << quart; qDebug() << quart_alt1; qDebug() << quart_alt2; qDebug() << quart_alt3; qDebug() << quart_alt4; qDebug() << quart_alt5; if ( cliques_4_Vertex.contains(quart) || cliques_4_Vertex.contains(quart_alt1) || cliques_4_Vertex.contains(quart_alt2) || cliques_4_Vertex.contains(quart_alt3) || cliques_4_Vertex.contains(quart_alt4) || cliques_4_Vertex.contains(quart_alt5) ) { knownClique = true; } } quart = QString::number(list.at( (0) )) + ", " + QString::number(list.at( (1) ) ) + ", " + QString::number(list.at( (2) ) ) + ", " + QString::number(list.at( (3) ) ); if (! knownClique) { cliques_4_Vertex.insert(quart, true); qDebug() << "*** Graph::addClique() - new 4-vertex clique " << quart << " adding it to global list "; } if ( m_graph[ index[list.at(0)] ]->addClique(quart,list.size()) ) { qDebug() << "*** Graph::addClique() - new 4-vertex clique: " << list.at(0) << "," << list.at(1) << "," << list.at(2) << "," << list.at(3) ; return true; } return false; } return false; } /** Calculates and returns the number of cliques which include given vertex 'source' A clique is a complete subgraph of N vertices. All N vertices must be mutually adjacenct. Due to computational complexity, SocNetV computes 2-vertex, 3-vertex and 4-vertex cliques only. */ float Graph:: countCliquesWith(int source, int size){ qDebug() << "*** Graph::countCliquesWith(" << source << ")"; int vert1=0, vert2=0, vert3=0; int relation=0; bool edgeStatus=false; H_edges::const_iterator it1, it2, it3; QList dyad, triad, quad; qDebug() << "Graph::countCliquesWith() Source vertex " << source << "[" << index[source] << "] has inEdges " << inboundEdges(source) << " and outEdges "<< outboundEdges(source); qDebug () << "Graph::countCliquesWith() - Checking inEdges to " << source; it1=m_graph [ index[source] ] ->m_inEdges.cbegin(); while ( it1!=m_graph [ index[source] ] -> m_inEdges.cend() ){ relation = it1.value().first; if ( relation != currentRelation() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } vert1 = it1.key(); // weight = it1.value().second.first; qDebug() << "Graph::countCliquesWith() - inLink from 1st neighbor " << vert1 << "[" << index[vert1] << "] "; if (source == vert1) { qDebug() << "Graph::countCliquesWith() - It's the source - CONTINUE"; ++it1; continue; } if ( this->hasArc( source, vert1 ) == 0 ) { qDebug() << "Graph::countCliquesWith() - incomplete 2v-subgraph - CONTINUE"; ++it1; continue; } qDebug() << "Graph::countCliquesWith() - complete 2v-subgraph "; dyad.clear(); dyad << source << vert1; if ( addClique( dyad ) ) { qDebug() << "Graph::countCliquesWith() - 2v cliques " << cliques_2_Vertex.count(); } qDebug() << "Graph::countCliquesWith() - " << " Iterate over all inEdges of " << vert1; it2=m_graph [ index[vert1] ] ->m_inEdges.cbegin(); while ( it2!=m_graph [ index[vert1] ] -> m_inEdges.cend() ){ relation = it2.value().first; if ( relation != currentRelation() ){ ++it2; continue; } edgeStatus=it2.value().second.second; if ( edgeStatus != true){ ++it2; continue; } vert2 = it2.key(); qDebug() << "Graph::countCliquesWith() - Possible other neighbor (for 3v clique)" << vert2 << "[" << index[vert2] << "]"; if (source == vert2) { qDebug() << "Graph::countCliquesWith() - It's the source - CONTINUE"; ++it2; continue; } if (vert1 == vert2) { qDebug() << "Graph::countCliquesWith() - It's the vert1 - CONTINUE"; ++it2; continue; } if ( this->hasArc( vert1, vert2 ) == 0 ) { qDebug() << "Graph::countCliquesWith() - " << vert1 << " not outLinked to " << vert2 << " - incomplete 3vertex-subgraph - CONTINUE"; ++it2; continue; } else { qDebug() << "Graph::countCliquesWith() - complete 3vertex-subgraph ? " << vert2 << " <-> " << vert1 << ". Checking if " << vert2 << " <-> " << source << " ... "; if ( this->hasEdge( source, vert2 ) ) { qDebug() << "Graph::countCliquesWith() - complete 3v-subgraph " << source << " <-> " << vert2 << " possible (new?) 3-vertex clique: "; triad.clear(); triad << source << vert1 << vert2; if ( addClique( triad ) ) { qDebug() << "Graph::countCliquesWith() - 3-vertex cliques " << cliques_3_Vertex.count(); } qDebug() << "Graph::countCliquesWith() - " << " Iterate over all inEdges of " << vert2; it3=m_graph [ index[vert2] ] ->m_inEdges.cbegin(); while ( it3!=m_graph [ index[vert2] ] -> m_inEdges.cend() ){ relation = it3.value().first; if ( relation != currentRelation() ){ ++it3; continue; } edgeStatus=it3.value().second.second; if ( edgeStatus != true){ ++it3; continue; } vert3 = it3.key(); qDebug() << "Graph::countCliquesWith() - Possible other neighbor (for 4v clique)" << vert3 << "[" << index[vert3] << "]"; if (source == vert3 || vert1 == vert3 || vert2 == vert3 ) { qDebug() << "Graph::countCliquesWith() - same as source, vert1 or vert2- CONTINUE"; ++it3; continue; } if ( this->hasEdge( source, vert3 ) == 0 || this->hasEdge( vert1, vert3 ) == 0 || this->hasArc ( vert2, vert3 ) == 0 ) { qDebug() << "Graph::countCliquesWith() - incomplete 4v-subgraph - CONTINUE"; ++it3; continue; } quad.clear(); quad << source << vert1 << vert2<< vert3; qDebug() << "Graph::countCliquesWith() - complete 4v-subgraph " << source << "," << vert1 << "," << vert2 << "," << vert3 << " possible (new?) 3-vertex clique: "; if ( addClique( quad ) ) { qDebug() << "Graph::countCliquesWith() - 4-vertex cliques " << cliques_4_Vertex.count(); } ++it3; } } else { qDebug() << "Graph::countCliquesWith() - Not mutual - CONTINUE"; } } ++it2; } ++it1; } // end 1st while switch (size) { case 2: { return m_graph [ index[source] ] -> cliques(2); break; } case 3: { return m_graph [ index[source] ] -> cliques(3); break; } case 4: { return m_graph [ index[source] ] -> cliques(4); break; } default: return 0; break; }; return 0; } /** Calculates and returns the total number of cliques in the graph. Calls countCliquesWith(v1) to calculate the number of cliques of each vertex v1, sums the total number, then divides it by 3 because each vertex has been counted three times. */ float Graph::countCliquesOfSize(int size){ qDebug("Graph::countCliquesOfSize()"); float cliques=0; QList::const_iterator v1; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { cliques += countCliquesWith( (*v1) -> name(), size ); } cliques = cliques / size; //actually we can just return cliques_*_Vertex.count(); qDebug() << "Graph::countCliquesOfSize - Dividing by size we get "<< cliques ; return cliques ; } /** Returns the number of triples of vertex v1 A triple Υ at a vertex v is a path of length two for which v is the center vertex. */ float Graph::numberOfTriples(int v1){ float totalDegree=0; if (isSymmetric()){ totalDegree=outboundEdges(v1); return totalDegree * (totalDegree -1.0) / 2.0; } totalDegree=outboundEdges(v1) + inboundEdges(v1); //FIXEM return totalDegree * (totalDegree -1.0); } /** Returns the local clustering coefficient (CLUCOF) of a vertex v1 CLUCOF in a graph quantifies how close the vertex and its neighbors are to being a clique, a connected subgraph. This is used to determine whether a graph is a small-world network. */ float Graph:: localClusteringCoefficient(const long int &v1){ if ( !graphModified && (m_graph[ index [v1] ] -> hasCLC() ) ) { float clucof=m_graph[ index [v1] ] ->CLC(); qDebug() << "Graph::localClusteringCoefficient("<< v1 << ") - " << " Not modified. Returning previous clucof = " << clucof; return clucof; } qDebug() << "Graph::localClusteringCoefficient("<< v1 << ") - " << " Graph changed or clucof not calculated."; bool graphSymmetric = false; if ( isSymmetric() ) { graphSymmetric = true; } else { graphSymmetric = false; } float clucof=0, denom = 0 , nom = 0; int u1 = 0 , u2 = 0, k = 0; H_StrToBool neighborhoodEdges; neighborhoodEdges.clear(); qDebug() << "Graph::localClusteringCoefficient() - vertex " << v1 << "[" << index[v1] << "] "; qDebug () << "Graph::localClusteringCoefficient() - " << " Checking edges adjacent to " << v1; QHash *reciprocalEdges = new QHash; reciprocalEdges = m_graph [ index[v1] ] -> returnReciprocalEdges(); QHash::const_iterator it1; QHash::const_iterator it2; it1=reciprocalEdges->cbegin(); while ( it1 != reciprocalEdges->cend() ) { u1 = it1.key(); qDebug() << "Graph::localClusteringCoefficient() - " << " edge with neighbor " << u1 << " [" << index[u1] << "] " << " weight " << it1.value(); if ( v1 == u1 ) { qDebug() << "Graph::localClusteringCoefficient() - " << " v1 == u1 - CONTINUE"; ++it1; continue; } it2=reciprocalEdges->cbegin(); while ( it2 != reciprocalEdges->cend() ){ u2 = it2.key(); qDebug() << "Graph::localClusteringCoefficient() - " << " cross-checking edge with neighbor " << u2 << " [" << index[u2] << "] " << " weight " << it2.value(); if ( u1 == u2 ) { qDebug() << "Graph::localClusteringCoefficient() - " << " u1 == u2 - CONTINUE"; ++it2; continue; } if ( hasArc( u1, u2 ) != 0 ) { qDebug() << "Graph::localClusteringCoefficient() - " << " connected neighbors: " << u1 << " -> " << u2; QString edge = QString::number(u1) + "->" + QString::number(u2); QString revedge = QString::number(u2) + "->" + QString::number(u1); if ( ! neighborhoodEdges.contains(edge) && ( graphSymmetric && ! neighborhoodEdges.contains(revedge) ) ) { neighborhoodEdges.insert(edge, true); qDebug() << "Graph::localClusteringCoefficient() - " << " adding edge to neighborhoodEdges "; } else { qDebug() << "Graph::localClusteringCoefficient() - " << " edge discovered previously... "; } } if ( ! graphSymmetric ) { if ( hasArc( u2, u1 ) != 0 ) { qDebug() << "Graph::localClusteringCoefficient() - " << " graph not symmetric " << " connected neighbors: " << u2 << " -> " << u1; QString edge = QString::number(u2) + "->" + QString::number(u1); if ( ! neighborhoodEdges.contains(edge) ) { neighborhoodEdges.insert(edge, true); qDebug() << "Graph::localClusteringCoefficient() - " << " adding edge to neighborhoodEdges "; } else { qDebug() << "Graph::localClusteringCoefficient() - " << " edge discovered previously... "; } } } ++it2; } ++it1; } nom=neighborhoodEdges.count(); qDebug() << "Graph::localClusteringCoefficient("<< v1 << ") - " << " actual edges in neighborhood " << nom; if ( nom == 0) return 0; //stop if we're at a leaf. if ( graphSymmetric ){ k=reciprocalEdges->count(); denom = k * (k -1.0) / 2.0; qDebug() << "Graph::localClusteringCoefficient("<< v1 << ") - " << " symmetric graph. " << " max edges in neighborhood" << denom ; } else { // fixme : normally we should have a special method // to compute the number of vertices k_i = |N_i|, in the neighborhood N_i k=reciprocalEdges->count(); denom = k * (k -1.0); qDebug() << "Graph::localClusteringCoefficient("<< v1 << ") - " << " not symmetric graph. " << " max edges in neighborhood" << denom ; } clucof = nom / denom; qDebug() << "=== Graph::localClusteringCoefficient("<< v1 << ") - " << " CLUCOF = "<< clucof; m_graph[ index [v1] ] -> setCLC(clucof); neighborhoodEdges.clear(); return clucof; } /** Calculates local clustering coefficients and returns the network average Clustering Coefficient */ float Graph::clusteringCoefficient (){ qDebug("=== Graph::clusteringCoefficient() "); averageCLC=0; maxCLC=0; minCLC=1; float temp=0; QList::const_iterator vertex; for ( vertex = m_graph.cbegin(); vertex != m_graph.cend(); ++vertex) { temp = localClusteringCoefficient( (*vertex)->name() ); if (temp > maxCLC) { maxCLC = temp; maxNodeCLC = (*vertex)->name(); } if ( temp < minCLC ) { minNodeCLC = (*vertex)->name(); minCLC= temp; } averageCLC += temp; } averageCLC = averageCLC / vertices(); qDebug() << "Graph::clusteringCoefficient() network average " << averageCLC; return averageCLC; } /* * Conducts a triad census and updates QList::triadTypeFreqs, * which is the list carrying all triad type frequencies * Complexity:O(n!) */ bool Graph::triadCensus(){ int mut=0, asy=0, nul =0; int temp_mut=0, temp_asy=0, temp_nul =0, counter_021=0; int ver1, ver2, ver3; int progressCounter = 0; qDebug() << "Graph::triadCensus()"; /* * QList::triadTypeFreqs stores triad type frequencies with the following order: * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 * 003 012 102 021D 021U 021C 111D 111U 030T 030C 201 120D 120U 120C 210 300 */ for (int i = 0; i <= 15; ++i) { triadTypeFreqs.append(0); qDebug() << " initializing triadTypeFreqs[" << i << "] = "<< triadTypeFreqs[i]; } QList::const_iterator v1; QList::const_iterator v2; QList::const_iterator v3; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); v1++) { for (v2=(v1+1); v2!=m_graph.cend(); v2++) { ver1=(*v1)->name(); ver2=(*v2)->name(); temp_mut=0, temp_asy=0, temp_nul =0; if ( (*v1)->hasEdgeTo( ver2 ) ) { if ( (*v2)->hasEdgeTo( ver1 ) ) temp_mut++; else temp_asy++; } else if ( (*v2)->hasEdgeTo( ver1 ) ) temp_asy++; else temp_nul++; for (v3=(v2+1); v3!=m_graph.cend(); v3++){ mut = temp_mut ; asy = temp_asy ; nul = temp_nul ; ver3=(*v3)->name(); if ( (*v1)->hasEdgeTo( ver3 ) ) { if ( (*v3)->hasEdgeTo( ver1 ) ) mut++; else asy++; } else if ( (*v3)->hasEdgeTo( ver1 ) ) asy++; else nul++; if ( (*v2)->hasEdgeTo( ver3 ) ) { if ( (*v3)->hasEdgeTo( ver2 ) ) mut++; else asy++; } else if ( (*v3)->hasEdgeTo( ver2 ) ) asy++; else nul++; qDebug()<< "triad of ("<< ver1 << ","<< ver2 << ","<< ver3 << ") = (" < m_triad; bool isDown=false, isUp=false, isCycle=false, isTrans=false; bool isOutLinked=false, isInLinked=false; qDebug () << "Graph::examine_MAN_label() " << " adding ("<< vert1->name() << ","<< vert2->name() << ","<< vert3->name() << ") to m_triad "; m_triad<name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; isInLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( source->hasEdgeTo(target->name()) ){ if ( isOutLinked ){ triadTypeFreqs[3] ++;//"021D" break; } else if (isInLinked){ triadTypeFreqs[5] ++;//"021C" break; } else{ isOutLinked=true; } } else if( target->hasEdgeTo(source->name()) ){ // qDebug() << " Vertex " << source->name() << " is IN linked from " <name(); if ( isInLinked ){ triadTypeFreqs[4] ++;//"021U" break; } else if (isOutLinked){ triadTypeFreqs[5] ++;//"021C" break; } else{ isInLinked=true; } } } } break; case 3: qDebug() << "triad vertices: ( "<< vert1->name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( source->hasEdgeTo(target->name()) ){ if ( isOutLinked ){ triadTypeFreqs[8] ++;//"030T" isTrans=true; break; } else{ isOutLinked=true; } } } } if ( ! isTrans ) {//"030C" triadTypeFreqs[9] ++; } break; } break; case 1: switch (asy){ case 0: //"102"; triadTypeFreqs[2] ++; break; case 1: isDown=false; isUp=false; //qDebug() << "triad vertices: ( "<< vert1->name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isInLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( target->hasEdgeTo(source->name()) ){ if ( isInLinked ){ triadTypeFreqs[6] ++;//"030T" isUp=true; break; } else{ isInLinked=true; } } } } if ( ! isUp ) {//"111U" triadTypeFreqs[7] ++; } break; case 2: isDown=false; isUp=false; isCycle=true; qDebug() << "triad vertices: ( "<< vert1->name() << ", " << vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; isInLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( source->hasEdgeTo(target->name()) ){ if (target->hasEdgeTo(source->name() ) ){ isInLinked=true; isOutLinked=true; continue; } else if ( isOutLinked && !isInLinked ){ triadTypeFreqs[11] ++;//"120D" isDown=true; isCycle=false; break; } else{ isOutLinked=true; } } else if( target->hasEdgeTo(source->name()) ){ // qDebug() << " Vertex " << source->name() << " is IN linked from " <name(); if (source->hasEdgeTo(target->name())){ isOutLinked=true; isInLinked=true; continue; } else if ( isInLinked && !isOutLinked ){ triadTypeFreqs[12] ++;//"120U" isUp=true; isCycle=false; break; } else{ isInLinked=true; } } } if (isUp || isDown) break; } if ( isCycle ) { //"120C" triadTypeFreqs[13] ++; } break; case 3: // nothing here! break; } break; case 2: switch (asy){ case 0: // "201" triadTypeFreqs[10] ++; break; case 1: // "210" triadTypeFreqs[14] ++; break; } break; case 3: // "300" if (asy==0 && nul==0) triadTypeFreqs[15] ++; break; } } /** Calculates and returns x! factorial... used in (n 2)p edges calculation */ int Graph:: factorial(int x) { int tmp; if(x <= 1) return 1; tmp = x * factorial(x - 1); return tmp; } /** Our almost universal network loader. :) Actually it calls the load() method of parser/qthread class. */ bool Graph::loadGraph ( const QString m_fileName, const QString m_codecName, const bool m_showLabels, const int maxWidth, const int maxHeight, const int fileFormat, const int two_sm_mode){ initShowLabels = m_showLabels; qDebug() << "Graph::loadGraph() : "<< m_fileName << " calling parser.load() from thread " << this->thread(); Parser *file_parser = new Parser( m_fileName, m_codecName, initVertexSize, initVertexColor, initVertexShape, initVertexNumberColor, initVertexNumberSize, initVertexLabelColor, initVertexLabelSize, initEdgeColor, maxWidth, maxHeight, fileFormat, two_sm_mode ); qDebug () << "Graph::loadGraph() file_parser thread " << file_parser->thread() << " moving it to new thread "; file_parser->moveToThread(&file_parserThread); qDebug () << "Graph::loadGraph() file_parser thread now " << file_parser->thread(); qDebug () << "Graph::loadGraph() connecting file_parser signals "; connect(&file_parserThread, &QThread::finished, file_parser, &QObject::deleteLater); connect ( file_parser, SIGNAL( addRelation (QString) ), this, SLOT(addRelationFromParser(QString) ) ) ; connect ( file_parser, SIGNAL( changeRelation (int) ), this, SLOT( changeRelation (int) ) ) ; connect ( file_parser, SIGNAL( createNode (int,int,QString, QString, int, QString, QString, int, QPointF, QString, bool) ), this, SLOT(createVertex(int,int,QString, QString, int, QString, QString, int, QPointF, QString, bool) ) ) ; connect ( file_parser, SIGNAL(createEdge (int, int, float, QString, int, bool, bool)), this, SLOT(createEdge (int, int, float, QString, int, bool, bool) ) ); connect ( file_parser, SIGNAL(fileType(int, QString, int, int, bool)), this, SLOT(setFileType(int, QString, int, int, bool)) ); connect ( file_parser, SIGNAL(removeDummyNode(int)), this, SLOT (removeDummyNode(int)) ); connect ( file_parser, &Parser::finished, this, &Graph::terminateParserThreads ); qDebug() << "Graph::loadGraph() Starting file_parserThread "; file_parserThread.start(); bool loadGraphStatus = file_parser->run(); qDebug() << "Graph::loadGraph() : loadGraphStatus "<< loadGraphStatus; return loadGraphStatus; } void Graph::terminateParserThreads(QString reason) { qDebug() << "Graph::terminateParserThreads() - reason " << reason <<" is file_parserThread running? "; if (file_parserThread.isRunning() ) { qDebug() << "Graph::terminateParserThreads() file_parserThread quit"; file_parserThread.quit(); qDebug() << "Graph::terminateCrawlerThreads() - deleting file_parser pointer"; delete file_parser; file_parser = 0; // see why here: https://goo.gl/tQxpGA } } /** Our almost universal graph saver. :) Actually it just checks the requested file type and calls the right saveGraphTo...() method */ bool Graph::saveGraph ( QString fileName, int fileType, QString networkName, int maxWidth, int maxHeight ) { qDebug() << "Graph::saveGraph to ..."; switch (fileType) { case 1 : { //Pajek qDebug() << " ... Pajek formatted file"; return saveGraphToPajekFormat(fileName, networkName, maxWidth, maxHeight); break; } case 2: { // Adjacency qDebug() << " ... Adjacency formatted file"; return saveGraphToAdjacencyFormat(fileName); break; } case 3: { // Dot qDebug() << " ... Dot formatted file"; return saveGraphToDotFormat(fileName, networkName, maxWidth, maxHeight); break; } case 4: { // GraphML qDebug() << " ... GraphML formatted file"; return saveGraphToGraphMLFormat(fileName, networkName, maxWidth, maxHeight); break; } default: { qDebug() << " ... Error! What format number is this anyway?"; break; } }; graphModified=false; return true; } /** Saves the active graph to a Pajek-formatted file Preserves node properties (positions, colours, etc) */ bool Graph::saveGraphToPajekFormat ( QString fileName, QString networkName, int maxWidth, int maxHeight) { qDebug () << " Graph::saveGraphToPajekFormat to file: " << fileName.toUtf8(); int weight=0; QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly ) ) { emit statusMessage (QString(tr("Could not write to %1")).arg(fileName)); return false; } QTextStream t( &f ); t.setCodec("UTF-8"); t<<"*Network "<::const_iterator it; QList::const_iterator jt; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ qDebug()<<" Name x "<< (*it)->name() ; t<<(*it)->name() <<" "<<"\""<<(*it)->label()<<"\"" ; t << " ic "; t<< (*it)->colorToPajek(); qDebug()<<" Coordinates x " << (*it)->x()<< " "<y()<< " "<x()/(maxWidth)<<" \t"<<(*it)->y()/(maxHeight); t << "\t"<<(*it)->shape(); t<<"\n"; } t<<"*Arcs \n"; qDebug()<< "Graph::saveGraphToPajekFormat: Arcs"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (jt=m_graph.begin(); jt!=m_graph.end(); jt++){ qDebug() << "Graph::saveGraphToPajekFormat: it=" << (*it)->name() << ", jt=" << (*jt)->name() ; if ( (weight=this->hasArc( (*it)->name(), (*jt)->name())) !=0 && ( this->hasArc((*jt)->name(), (*it)->name())) == 0 ) { qDebug()<<"Graph::saveGraphToPajekFormat weight "<< weight << " color "<< (*it)->outLinkColor( (*jt)->name() ) ; t << (*it)->name() <<" "<<(*jt)->name()<< " "<outLinkColor( (*jt)->name() ); t <<"\n"; } } } t<<"*Edges \n"; qDebug() << "Graph::saveGraphToPajekFormat: Edges"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (jt=m_graph.begin(); jt!=m_graph.end(); jt++){ qDebug() << "Graph::saveGraphToPajekFormat: it=" << (*it)->name() << ", jt=" <<(*jt)->name() ; if ( (weight=this->hasArc((*it)->name(), (*jt)->name()))!=0 && (this->hasArc((*jt)->name(), (*it)->name()))!=0 ) { if ( (*it)->name() > (*jt)->name() ) continue; t << (*it)->name() <<" "<<(*jt)->name()<< " "<outLinkColor( (*jt)->name() ); t <<"\n"; } } } f.close(); QString fileNameNoPath=fileName.split("/").last(); emit statusMessage (QString(tr( "File %1 saved" ) ).arg( fileNameNoPath )); return true; } /** * @brief Graph::saveGraphToAdjacencyFormat * @param fileName * @param maxWidth * @param maxHeight * @return */ bool Graph::saveGraphToAdjacencyFormat (QString fileName){ QFile file( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { emit statusMessage(QString(tr("Could not write to %1")).arg(fileName)); return false; } QTextStream outText( &file ); outText.setCodec("UTF-8"); qDebug("Graph: saveGraphToAdjacencyFormat() for %i vertices", vertices()); writeAdjacencyMatrixTo(outText); file.close(); QString fileNameNoPath=fileName.split("/").last(); emit statusMessage (QString( tr("Adjacency matrix-formatted network saved into file %1") ).arg( fileNameNoPath )); return true; } /** * @brief Graph::writeDataSetToFile * Writes a known dataset to the given file * @param fileName */ void Graph::writeDataSetToFile (const QString dir, const QString fileName) { qDebug() << "Graph::writeDataSetToFile() to " << dir+fileName; QFile file( dir+fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { emit statusMessage( QString(tr("Could not write to %1")).arg(fileName) ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); QString datasetDescription=QString::null; qDebug()<< " ... writing dataset "; if ( fileName == "Krackhardt_High-tech_managers_Advice_relation.sm" ) { qDebug()<< " ... to " << fileName; outText << "0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 1" << endl << "0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 0 1 1 0 1 1" << endl << "1 1 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 1 0 1 1" << endl << "1 1 0 0 0 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 1 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 1" << endl << "0 1 0 1 0 1 1 0 0 1 1 0 0 0 0 0 0 1 0 0 1" << endl << "1 1 0 0 0 1 1 1 0 1 1 1 0 1 0 1 1 1 0 0 1" << endl << "1 1 1 1 1 0 0 1 0 0 1 0 1 0 1 1 1 1 1 1 0" << endl << "1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0" << endl << "0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1" << endl << "1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1" << endl << "1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0" << endl << "1 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1" << endl << "1 1 1 0 1 0 1 0 0 1 1 0 0 1 1 0 0 1 0 1 0" << endl << "1 1 0 0 0 1 0 1 0 0 1 1 0 1 1 1 1 1 0 0 1" << endl << "0 1 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 1 0 1 0"; } else if (fileName == "Krackhardt_High-tech_managers_Friendship_relation.sm"){ outText<< "0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0" << endl << "1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0" << endl << "1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 0" << endl << "0 1 0 0 0 0 0 0 1 0 1 0 0 1 0 0 1 0 1 0 1" << endl << "0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 1 0 1 0 0 1 1 0 0 1 0 0 0 1 0 0 0 1 0" << endl << "1 1 1 1 1 0 0 1 1 0 0 1 1 0 1 0 1 1 1 0 0" << endl << "1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1" << endl << "0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0" << endl << "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0" << endl << "1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 0 1 1 1" << endl << "0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 1 1 0 1 0 0 0 0 0 1 1 0 1 1 0 0 0 0 1 0" << endl << "0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0" << endl << "0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0" ; } else if (fileName == "Krackhardt_High-tech_managers_ReportsTo_relation.sm"){ outText<< "0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0"; } else if (fileName == "Padgett_Florentine_Families_Marital_relation.net"){ outText<< "*Network Padgett's Florentine Families Marital Relation" << endl << "*Vertices 16" << endl << "1 \"Acciaiuoli\" 0.2024 0.1006" << endl << "2 \"Albizzi\" 0.3882 0.4754" << endl << "3 \"Barbadori\" 0.1633 0.7413" << endl << "4 \"Bischeri\" 0.6521 0.5605" << endl << "5 \"Castellani\" 0.6178 0.9114" << endl << "6 \"Ginori\" 0.3018 0.5976" << endl << "7 \"Guadagni\" 0.5219 0.5006" << endl << "8 \"Lamberteschi\" 0.4533 0.6299" << endl << "9 \"Medici\" 0.2876 0.3521" << endl << "10 \"Pazzi\" 0.0793 0.2587" << endl << "11 \"Peruzzi\" 0.6509 0.7365" << endl << "12 \"Pucci\" 0.4083 0.1186" << endl << "13 \"Ridolfi\" 0.6308 0.2060" << endl << "14 \"Salviati\" 0.0734 0.4455" << endl << "15 \"Strozzi\" 0.8639 0.5832" << endl << "16 \"Tornabuoni\" 0.5633 0.3713" << endl << "*Arcs \"Marital\""<< endl << "1 9 1" << endl << "2 6 1" << endl << "2 7 1" << endl << "2 9 1" << endl << "3 5 1" << endl << "3 9 1" << endl << "4 7 1" << endl << "4 11 1" << endl << "4 15 1" << endl << "5 3 1" << endl << "5 11 1" << endl << "5 15 1" << endl << "6 2 1" << endl << "7 2 1" << endl << "7 4 1" << endl << "7 8 1" << endl << "7 16 1" << endl << "8 7 1" << endl << "9 1 1" << endl << "9 2 1" << endl << "9 3 1" << endl << "9 13 1" << endl << "9 14 1" << endl << "9 16 1" << endl << "10 14 1" << endl << "11 4 1" << endl << "11 5 1" << endl << "11 15 1" << endl << "13 9 1" << endl << "13 15 1" << endl << "13 16 1" << endl << "14 9 1" << endl << "14 10 1" << endl << "15 4 1" << endl << "15 5 1" << endl << "15 11 1" << endl << "15 13 1" << endl << "16 7 1" << endl << "16 9 1" << endl << "16 13 1" ; } else if (fileName == "Padgett_Florentine_Families_Business_relation.paj"){ outText<< "*Network Padgett's Florentine Families Business Relation" << endl << "*Vertices 16" << endl << "1 \"Acciaiuoli\" 0.2024 0.1006" << endl << "2 \"Albizzi\" 0.3882 0.4754" << endl << "3 \"Barbadori\" 0.1633 0.7413" << endl << "4 \"Bischeri\" 0.6521 0.5605" << endl << "5 \"Castellani\" 0.6178 0.9114" << endl << "6 \"Ginori\" 0.3018 0.5976" << endl << "7 \"Guadagni\" 0.5219 0.5006" << endl << "8 \"Lamberteschi\" 0.4533 0.6299" << endl << "9 \"Medici\" 0.2876 0.3521" << endl << "10 \"Pazzi\" 0.0793 0.2587" << endl << "11 \"Peruzzi\" 0.6509 0.7365" << endl << "12 \"Pucci\" 0.4083 0.1186" << endl << "13 \"Ridolfi\" 0.6308 0.2060" << endl << "14 \"Salviati\" 0.0734 0.4455" << endl << "15 \"Strozzi\" 0.8639 0.5832" << endl << "16 \"Tornabuoni\" 0.5633 0.3713" << endl << "*Arcs \"Business\""<< endl << "3 5 1" << endl << "3 6 1" << endl << "3 9 1" << endl << "3 11 1" << endl << "4 7 1" << endl << "4 8 1" << endl << "4 11 1" << endl << "5 3 1" << endl << "5 8 1" << endl << "5 11 1" << endl << "6 3 1" << endl << "6 9 1" << endl << "7 4 1" << endl << "7 8 1" << endl << "8 4 1" << endl << "8 5 1" << endl << "8 7 1" << endl << "8 11 1" << endl << "9 3 1" << endl << "9 6 1" << endl << "9 10 1" << endl << "9 14 1" << endl << "9 16 1" << endl << "10 9 1" << endl << "11 3 1" << endl << "11 4 1" << endl << "11 5 1" << endl << "11 8 1" << endl << "14 9 1" << endl << "16 9 1"; } else if (fileName == "Zachary_Karate_Club_Simple_Ties.sm"){ outText<< "0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0" << endl << "1 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0" << endl << "1 1 0 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0" << endl << "1 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1" << endl << "0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1" << endl << "0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1" << endl << "1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1" << endl << "1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1" << endl << "0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1" << endl << "0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1" << endl << "0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1" << endl << "1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 1" << endl << "0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 1" << endl << "0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 0 1 1 1 0 1 1 0 0 1 1 1 1 1 1 1 0" ; } else if (fileName == "Zachary_Karate_Club_Weighted_Ties.sm"){ outText<< "0 4 5 3 3 3 3 2 2 0 2 3 1 3 0 0 0 2 0 2 0 2 0 0 0 0 0 0 0 0 0 2 0 0" << endl << "4 0 6 3 0 0 0 4 0 0 0 0 0 5 0 0 0 1 0 2 0 2 0 0 0 0 0 0 0 0 2 0 0 0" << endl << "5 6 0 3 0 0 0 4 5 1 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 0 0 2 0" << endl << "3 3 3 0 0 0 0 3 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "3 0 0 0 0 0 2 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "3 0 0 0 0 0 5 0 0 0 3 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "3 0 0 0 2 5 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "2 4 4 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "2 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 3 4" << endl << "0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2" << endl << "2 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "1 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "3 5 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 4" << endl << "0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2" << endl << "2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 1" << endl << "2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 4 0 3 0 0 5 4" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 0 2 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 2 0 0 0 0 0 0 7 0 0" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 2" << endl << "0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 3 0 0 0 0 0 0 0 0 4" << endl << "0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 2" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 4 0 0 0 0 0 4 2" << endl << "0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3" << endl << "2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 7 0 0 2 0 0 0 4 4" << endl << "0 0 2 0 0 0 0 0 3 0 0 0 0 0 3 3 0 0 1 0 3 0 2 5 0 0 0 0 0 4 3 4 0 5" << endl << "0 0 0 0 0 0 0 0 4 2 0 0 0 3 2 4 0 0 2 1 1 0 3 4 0 0 2 4 2 2 3 4 5 0"; } else if (fileName == "Galaskiewicz_CEOs_and_clubs_affiliation_network_data.2sm"){ outText<< "0 0 1 1 0 0 0 0 1 0 0 0 0 0 0" << endl << "0 0 1 0 1 0 1 0 0 0 0 0 0 0 0" << endl << "0 0 1 0 0 0 0 0 0 0 0 1 0 0 0" << endl << "0 1 1 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 0 1 0 0 0 0 0 0 0 0 0 1 1 0" << endl << "0 1 1 0 0 0 0 0 0 0 0 0 0 1 0" << endl << "0 0 1 1 0 0 0 0 0 1 1 0 0 0 0" << endl << "0 0 0 1 0 0 1 0 0 1 0 0 0 0 0" << endl << "1 0 0 1 0 0 0 1 0 1 0 0 0 0 0" << endl << "0 0 1 0 0 0 0 0 1 0 0 0 0 0 0" << endl << "0 1 1 0 0 0 0 0 1 0 0 0 0 0 0" << endl << "0 0 0 1 0 0 1 0 0 0 0 0 0 0 0" << endl << "0 0 1 1 1 0 0 0 1 0 0 0 0 0 0" << endl << "0 1 1 1 0 0 0 0 0 0 1 1 1 0 1" << endl << "0 1 1 0 0 1 0 0 0 0 0 0 1 0 1" << endl << "0 1 1 0 0 1 0 1 0 0 0 0 0 1 0" << endl << "0 1 1 0 1 0 0 0 0 0 1 1 0 0 1" << endl << "0 0 0 1 0 0 0 0 1 0 0 1 1 0 1" << endl << "1 0 1 1 0 0 1 0 1 0 0 0 0 0 0" << endl << "0 1 1 1 0 0 0 0 0 0 1 0 0 0 1" << endl << "0 0 1 1 0 0 0 1 0 0 0 0 0 0 0" << endl << "0 0 1 0 0 0 0 1 0 0 0 0 0 0 1" << endl << "0 1 1 0 0 1 0 0 0 0 0 0 0 0 1" << endl << "1 0 1 1 0 1 0 0 0 0 0 0 0 0 1" << endl << "0 1 1 0 0 0 0 0 0 0 0 0 1 0 0" << endl << "0 1 1 0 0 0 0 0 0 0 0 1 0 0 0"; } else if (fileName == "Bernard_Killworth_Fraternity.dl"){ datasetDescription = tr("Bernard & Killworth recorded the interactions among students living in a fraternity at " "a West Virginia college. Subjects had been residents in the fraternity from 3 months to 3 years. " "This network dataset contains two relations: \n" "The BKFRAB relation is symmetric and valued. It counts the number of times a pair of subjects were " "seen in conversation by an unobtrusive observer (observation time: 21 hours a day, for five days). \n" "The BKFRAC relation is non-symmetric and valued. Contains rankings made by the subjects themselves of " "how frequently they interacted with other subjects in the observation week."); outText << "DL"<::const_iterator it, it1; float weight=-1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = this->hasArc ( (*it)->name(), (*it1)->name() ) ) !=0 ) { os << static_cast (weight) << " "; } else os << "0 "; } os << endl; } } /** Outputs adjacency matrix to a text stream * Used in slotExportSM() of MainWindow class. */ //QTextStream& operator << (QTextStream& os, Graph& m){ // QList::const_iterator it, it1; // float weight=-1; // for (it=m.m_graph.begin(); it!=m.m_graph.end(); it++){ // for (it1=m.m_graph.begin(); it1!=m.m_graph.end(); it1++){ // if ( (weight = m.hasArc ( (*it)->name(), (*it1)->name() ) ) !=0 ) { // os << static_cast (weight) << " "; // } // else // os << "0 "; // } // os << endl; // } // return os; //} /** Writes the adjacency matrix of G to a specified file fn This is called by MainWindow::slotViewAdjacencyMatrix() The resulting matrix HAS NO spaces between elements. */ void Graph::writeAdjacencyMatrix (const QString fn, const char* netName) { qDebug()<<"Graph::writeAdjacencyMatrix() to : " << fn; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly ) ) { emit statusMessage( QString(tr("Could not write to %1")).arg(fn) ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); int sum=0; float weight=0; outText << "-Social Network Visualizer- \n"; outText << "Adjacency matrix of "<< netName<<": \n\n"; QList::const_iterator it, it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = hasArc ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText << (weight) << " "; // TODO make the matrix look symmetric } else outText << "0 "; } outText << endl; } qDebug("Graph: Found a total of %i edge",sum); if ( sum != enabledEdges() ) qDebug ("Error in edge count found!!!"); else qDebug("Edge count OK!"); file.close(); } /* * Creates an adjacency matrix AM * where AM(i,j)=1 if i is connected to j * and AM(i,j)=0 if i not connected to j * Used in Graph::centralityInformation() and Graph::invertAdjacencyMatrix() */ void Graph::createAdjacencyMatrix(const bool dropIsolates, const bool considerWeights, const bool inverseWeights, const bool symmetrize ){ qDebug() << "Graph::createAdjacencyMatrix()"; float m_weight=-1; int i=0, j=0; if (dropIsolates){ qDebug() << "Graph::createAdjacencyMatrix() - Find and drop possible isolates"; isolatedVertices = verticesIsolated().count(); int m = m_totalVertices-isolatedVertices; AM.resize( m , m); } else AM.resize(m_totalVertices, m_totalVertices); QList::const_iterator it, it1; //qDebug() << "Graph::createAdjacencyMatrix() - creating new adjacency matrix "; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || ( (*it)->isIsolated() && dropIsolates) ) { continue; } j=i; for (it1=it; it1!=m_graph.end(); it1++){ if ( ! (*it1)->isEnabled() || ( (*it1)->isIsolated() && dropIsolates) ) { continue; } if ( (m_weight = this->hasArc ( (*it)->name(), (*it1)->name() ) ) !=0 ) { if (!considerWeights) { AM.setItem(i,j, 1 ); } else { if (inverseWeights) AM.setItem(i,j, 1.0 / m_weight ); else AM.setItem(i,j, m_weight ); } } else{ AM.setItem(i,j, 0); } qDebug()<<" AM("<< i+1 << ","<< j+1 << ") = " << AM.item(i,j); if (i != j ) { if ( (m_weight = this->hasArc ( (*it1)->name(), (*it)->name() ) ) !=0 ) { if (!considerWeights) AM.setItem(j,i, 1 ); else { if (inverseWeights) AM.setItem(j,i, 1.0 / m_weight ); else AM.setItem(j,i, m_weight ); } if (symmetrize && (AM.item(i,j) != AM.item(j,i)) ) { AM.setItem(i,j, AM.item(j,i) ); } } else { AM.setItem(j,i, 0); if (symmetrize && (AM.item(i,j) != AM.item(j,i)) ) AM.setItem(j,i, AM.item(i,j) ); } qDebug()<<" AM("<< j+1 << ","<< i+1 << ") = " << AM.item(j,i); } j++; } i++; } adjacencyMatrixCreated=true; } bool Graph::invertAdjacencyMatrix(const QString &method){ qDebug()<<"Graph::invertAdjacencyMatrix() "; bool considerWeights=false; long int i=0, j=0; bool isSingular=true; bool dropIsolates=true; // always drop isolates else AM will be singular createAdjacencyMatrix(dropIsolates, considerWeights); int m = m_totalVertices-isolatedVertices; invAM.resize(m,m); if ( method == "gauss") { invAM.inverseByGaussJordanElimination(AM); } else { invAM.inverse(AM); } QList::const_iterator it, it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || (*it)->isIsolated()) continue; j=0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() || (*it)->isIsolated() ) continue; if ( invAM.item(i,j) != 0) isSingular = false; j++; } i++; } return !isSingular; } void Graph::writeInvertAdjacencyMatrix(const QString &fn, const QString &netName, const QString &method) { qDebug("Graph::writeInvertAdjacencyMatrix() "); int i=0, j=0; QList::const_iterator it, it1; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly ) ) { emit statusMessage( QString(tr("Could not write to %1")).arg(fn) ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << "-Social Network Visualizer- \n"; outText << "Invert Matrix of network named: "<< netName<< endl; if (!invertAdjacencyMatrix(method)) { outText << endl<< " The adjacency matrix is singular."; file.close(); return; } if ( verticesIsolated().count() > 0 ) outText << endl<< "Dropped "<< isolatedVertices << " isolated vertices" << endl<< endl; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || (*it)->isIsolated() ) continue; j=0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() || (*it)->isIsolated() ) continue; outText << invAM.item(i,j)<< " "; qDebug() << i << "," << j << " = " << invAM.item(i,j); j++; } i++; outText << endl; qDebug() << endl; } file.close(); } bool Graph::saveGraphToDotFormat ( QString fileName, QString networkName, int maxWidth, int maxHeight) { Q_UNUSED(fileName); Q_UNUSED(networkName); Q_UNUSED(maxWidth); Q_UNUSED(maxHeight); return true; } bool Graph::saveGraphToGraphMLFormat ( QString fileName, QString networkName, int maxWidth, int maxHeight) { qDebug () << " Graph::saveGraphToGraphMLFormat to file: " << fileName.toUtf8(); int weight=0, source=0, target=0, edgeCount=0, m_size=1, m_labelSize; QString m_color, m_labelColor, m_label; bool openToken; QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly ) ) { emit statusMessage( QString(tr("Could not write to %1")).arg(fileName) ); return false; } QTextStream outText( &f ); outText.setCodec("UTF-8"); qDebug () << " codec used for saving stream: " << outText.codec()->name(); qDebug()<< " ... writing xml version"; outText << "name() << "\"?> \n"; outText << " \n" ; outText << "" "\n"; qDebug()<< " ... writing keys "; outText << " \n" " " " \n" " \n"; outText << " \n" " " << "0.0" << " \n" " \n"; outText << " \n" " " << "0.0" << " \n" " \n"; outText << " \n" " "<< initVertexSize << " \n" " \n"; outText << " \n" " " << initVertexColor << " \n" " \n"; outText << " \n" " " << initVertexShape << " \n" " \n"; outText << " \n" " " << initVertexLabelColor << " \n" " \n"; outText << " \n" " " << initVertexLabelSize << " \n" " \n"; outText << " \n" " 1.0 \n" " \n"; outText << " \n" " " << initEdgeColor << " \n" " \n"; qDebug()<< " ... writing graph tag"; if (networkName == "") networkName = "G"; if (m_undirected) outText << " \n"; else outText << " \n"; QList::const_iterator it; QList::const_iterator jt; qDebug()<< " writing nodes data"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled () ) continue; qDebug() << " Node id: "<< (*it)->name() ; outText << " name() << "\"> \n"; m_color = (*it)->color(); m_size = (*it)->size() ; m_labelSize=(*it)->labelSize() ; m_labelColor=(*it)->labelColor() ; m_label=(*it)->label(); if (m_label.contains('&') ){ m_label=m_label.replace('&',"&"); } if (m_label.contains('<') ){ m_label=m_label.replace('<',"<"); } if (m_label.contains('>') ){ m_label=m_label.replace('>',">"); } if (m_label.contains('\"') ){ m_label=m_label.replace('\"',"""); } if (m_label.contains('\'') ){ m_label=m_label.replace('\'',"'"); } outText << " " << m_label <<"\n"; qDebug()<<" ... Coordinates x " << (*it)->x()<< " "<y()<< " "<" << (*it)->x()/(maxWidth) <<"\n"; outText << " " << (*it)->y()/(maxHeight) <<"\n"; if ( initVertexSize != m_size ) { outText << " " << m_size <<"\n"; } if ( QString::compare ( initVertexColor, m_color, Qt::CaseInsensitive) != 0) { outText << " " << m_color <<"\n"; } outText << " " << (*it)->shape() <<"\n"; if ( QString::compare ( initVertexLabelColor, m_labelColor, Qt::CaseInsensitive) != 0) { outText << " " << m_labelColor <<"\n"; } if ( initVertexLabelSize != m_labelSize ) { outText << " " << m_labelSize <<"\n"; } outText << " \n"; } qDebug() << " ... writing edges data"; edgeCount=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { for (jt=m_graph.begin(); jt!=m_graph.end(); jt++) { source=(*it)->name(); target=(*jt)->name(); if ( (weight= this->hasArc( source,target ) ) !=0 ) { ++edgeCount; m_color = (*it)->outLinkColor( target ); qDebug()<< " edge no "<< edgeCount << " from n1=" << source << " to n2=" << target << " with weight " << weight << " and color " << m_color.toUtf8() ; outText << " 1) { outText << "> \n"; outText << " " << weight<<"" <<" \n"; openToken=false; } if ( QString::compare ( initEdgeColor, m_color, Qt::CaseInsensitive) != 0) { if (openToken) outText << "> \n"; outText << " " << m_color <<"" <<" \n"; openToken=false; } if (openToken) outText << "/> \n"; else outText << " \n"; } } } outText << " \n"; outText << "\n"; f.close(); QString fileNameNoPath=fileName.split("/").last(); emit statusMessage( QString(tr( "File %1 saved" ) ).arg( fileNameNoPath ) ); return true; } void Graph::setShowLabels(bool toggle){ initShowLabels=toggle; } void Graph::setShowNumbersInsideNodes(bool toggle){ initNumbersInsideNodes=toggle; } /** This slot is activated when the user clicks on the relevant MainWindow checkbox (SpringEmbedder, Fruchterman) to start or stop the movement of nodes, according to the requested model. PARAMETERS: state: movement on/off toggle type: controls the type of layout model requested. Available options 1: Spring Embedder 2: FruchtermanReingold cW, cH: control the current canvasWidth and canvasHeight */ void Graph::nodeMovement(bool state, int type, int cW, int cH){ qDebug()<< "Graph: startNodeMovement() - state " << state; canvasWidth = cW; canvasHeight = cH; //factor controls speed. Decrease it to increase speed... // the smaller the factor is, the less responsive is the application // when there are many nodes. int factor=50; if (state == true){ qDebug()<< "Graph: startNodeMovement() - STARTING dynamicMovement" ; dynamicMovement = true; layoutType=type; if (!timerId) { qDebug("Graph: startTimer()"); timerId = startTimer(factor); } } else { qDebug()<< "Graph: startNodeMovement() - STOPPING dynamicMovement" ; dynamicMovement = false; killTimer(timerId); timerId = 0; } } /** This method is automatically invoked when a QTimerEvent occurs It checks layoutType to call the appropriate method with the Force Directed Placement algorithm. */ void Graph::timerEvent(QTimerEvent *event) { qDebug("Graph: timerEvent()"); Q_UNUSED(event); switch (layoutType){ case 1: { layoutForceDirectedSpringEmbedder(dynamicMovement); break; } case 2: { layoutForceDirectedFruchtermanReingold(dynamicMovement); break; } } if (!graphModified) { qDebug("Timer will be KILLED since no vertex is movin any more..."); killTimer(timerId); timerId = 0; } } /** The Spring Embedder model (Eades, 1984), part of the Force Directed Placement (FDP) family, assigns forces to all vertices and edges, as if nodes were electrically charged particles (Coulomb's law) and all edges were springs (i.e. Hooke's law). These forces are applied to the nodes iteratively, pulling them closer together or pushing them further apart, until the system comes to an equilibrium state (node positions do not change anymore). Note that, following Eades, we do not need to have a faithful simulation; we can -and we do- apply unrealistic forces in an unrealistic manner. For instance, instead of the forces described by Hooke's law, we will assume weaker logarithmic forces between far apart vertices... */ void Graph::layoutForceDirectedSpringEmbedder(bool &dynamicMovement){ qreal dist = 0; qreal f_rep=0, f_att=0; QPointF DV; qreal c4=0.1; //normalization factor for final displacement QList::const_iterator v1; QList::const_iterator v2; /** * compute max spring length as function of canvas area divided by the * total vertices area */ qreal V = (qreal) vertices() ; qreal naturalLength= computeOptimalDistance(V); qDebug() << "\n\n layoutForceDirectedSpringEmbedder() " << " vertices " << V << " naturalLength " << naturalLength; if (dynamicMovement){ //setup init disp for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { (*v1) -> disp().rx() = 0; (*v1) -> disp().ry() = 0; qDebug() << " 0000 s " << (*v1)->name() << " zeroing rx/ry"; } for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { qDebug() << "********* Calculate forces for source s " << (*v1) -> name() <<" pos "<< (*v1) -> x()<< ", "<< (*v1) -> y(); if ( ! (*v1)->isEnabled() ) { qDebug() << " vertex s disabled. Continue"; continue; } for (v2=m_graph.cbegin(); v2!=m_graph.cend(); ++v2) { if ( ! (*v2)->isEnabled() ) { qDebug() << " t " << (*v1)->name() << " disabled. Continue"; continue; } if (v2 == v1) { qDebug() << " s==t, continuing"; continue; } DV.setX( (*v2) -> x() - (*v1)->x()); DV.setY( (*v2) -> y() - (*v1)->y()); dist = euclideian_distance(DV); /** * calculate electric (repulsive) forces between * all vertices. */ f_rep = layoutForceDirected_F_rep (dist, naturalLength) ; (*v1)->disp().rx() += sign( DV.x() ) * f_rep ; (*v1)->disp().ry() += sign( DV.y() ) * f_rep ; qDebug() <<" s = "<< (*v1)->name() <<" pushed away from t = " << (*v2) -> name() << " dist " < naturalLength) * or push them apart (if d < naturalLength) */ if ( this->hasArc ( (*v1) ->name(), (*v2) -> name()) ) { f_att = layoutForceDirected_F_att (dist, naturalLength) ; (*v1)->disp().rx() += sign( DV.x() ) * f_att ; (*v1)->disp().ry() += sign( DV.y() ) * f_att ; (*v2)->disp().rx() -= sign( DV.x() ) * f_att ; (*v2)->disp().ry() -= sign( DV.y() ) * f_att ; qDebug() << " s= "<<(*v1)->name() << " attracted by t= "<< (*v2)->name() << " dist " <>> final s = "<< (*v1)->name() << " disp_s.x="<< (*v1)->disp().rx() << " disp_s.y="<< (*v1)->disp().ry(); } // end for v1 layoutForceDirected_Eades_moveNodes(c4) ; } //end dynamicMovement } /** Fruchterman and Reingold (1991) refined the Spring Embedder model by replacing the forces. In this model, "the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces on one another." (ibid). Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. These forces induce movement. The algorithm might resemble molecular or planetary simulations, sometimes called n-body problems. */ void Graph::layoutForceDirectedFruchtermanReingold(bool dynamicMovement){ qreal dist = 0; qreal f_att, f_rep; QPointF DV; //difference vector //qreal temperature=canvasWidth / 10.0; //limits the displacement of the vertex qreal temperature=2.0; //limits the displacement of the vertex qreal V = (qreal) vertices() ; qreal C=0.9; //this is found experimentally // optimalDistance (or k) is the radius of the empty area around a vertex - // we add vertexWidth to it qreal optimalDistance= C * computeOptimalDistance(V); QList::const_iterator v1; QList::const_iterator v2; if (dynamicMovement){ qDebug() << "Graph: layoutForceDirectedFruchtermanReingold() "; qDebug () << "Graph: Setting optimalDistance = "<< optimalDistance << "...following Fruchterman-Reingold (1991) formula "; //setup init disp for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { (*v1)->disp().rx() = 0; (*v1)->disp().ry() = 0; qDebug() << " 0000 s " << (*v1)->name() << " zeroing rx/ry"; } for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { qDebug() << "***** Calculate forces for s " << (*v1)->name() << " index " << index[(*v1)->name()] << " pos "<< (*v1)->x() << ", "<< (*v1)->y(); if ( ! (*v1)->isEnabled() ) { qDebug() << " vertex s " << (*v1)->name() << " disabled. Continue"; continue; } for (v2=m_graph.cbegin(); v2!=m_graph.cend(); ++v2) { qDebug () << " t = "<< (*v2)->name() << " pos (" << (*v2)->x() << "," << (*v2)->y() << ")"; if ( ! (*v2)->isEnabled() ) { qDebug()<< " t "<< (*v2)->name()<< " disabled. Continue"; continue; } if (v2 == v1) { qDebug() << " s==t, continuing"; continue; } DV.setX( (*v2)->x() - (*v1)->x() ); DV.setY( (*v2)->y() - (*v1)->y() ); dist = euclideian_distance( DV ); //calculate repulsive force from _near_ vertices f_rep = layoutForceDirected_F_rep(dist, optimalDistance); (*v1)->disp().rx() += sign( DV.x() ) * f_rep; (*v1)->disp().ry() += sign( DV.y() ) * f_rep ; qDebug()<< " dist( " << (*v1)->name() << "," << (*v2)->name() << " = " << dist << " f_rep " << f_rep << " disp_s.x="<< (*v1)->disp().rx() << " disp_s.y="<< (*v1)->disp().ry(); if ( this->hasArc ((*v1)->name(), (*v2)->name()) ) { //calculate attracting force f_att = layoutForceDirected_F_att (dist, optimalDistance); (*v1)->disp().rx() += sign( DV.x() ) * f_att; (*v1)->disp().ry() += sign( DV.y() ) * f_att; (*v2)->disp().rx() -= sign( DV.x() ) * f_att ; (*v2)->disp().ry() -= sign( DV.y() ) * f_att ; qDebug() << " s= "<<(*v1)->name() << " attracted by t= "<< (*v2)->name() <<" optimalDistance =" << optimalDistance << " f_att " << f_att << " disp_s.x="<< (*v1)->disp().rx() << " disp_s.y="<< (*v1)->disp().ry() << " disp_t.x="<< (*v2)->disp().rx() << " disp_t.y="<< (*v2)->disp().ry(); } //endif }//end for v2 //recompute optimalDistance (in case the user resized the window) optimalDistance= C * computeOptimalDistance(V); } //end for v1 layoutForceDirected_FR_moveNodes(temperature); // reduce the temperature as the layout approaches a better configuration //cool(temperature); } } /** * @brief Graph::computeOptimalDistance * @return qreal optimalDistance */ qreal Graph::computeOptimalDistance(const int &Vertices){ qreal vertexWidth = (qreal) 2.0 * initVertexSize ; qreal screenArea = canvasHeight*canvasWidth; qreal vertexArea = qCeil ( qSqrt( screenArea / Vertices ) ) ; // optimalDistance (or k) is the radius of the empty area around a vertex - // we add vertexWidth to it return (vertexWidth + vertexArea); } qreal Graph::layoutForceDirected_F_att( const qreal &dist, const qreal &optimalDistance) { qreal f_att; if (layoutType == 1) { //layoutType -> Eades qreal c_spring=2; f_att = c_spring * log10 ( dist / optimalDistance ); } else { // layoutType -> FR f_att= ( dist * dist ) / optimalDistance; } return f_att; } qreal Graph::layoutForceDirected_F_rep(const qreal &dist, const qreal &optimalDistance) { qreal f_rep; if (layoutType == 1) { //layoutType -> Eades if (dist !=0){ qreal c_rep= 1.0; f_rep = c_rep / (dist * dist); if ( dist > (2.0 * optimalDistance) ) { //neglect vertices outside circular area of radius 2 * optimalDistance f_rep=0; } } else { f_rep = optimalDistance ; //move away } } else { // layoutType -> FR if ( (2.0 * optimalDistance) < dist ) { //neglect vertices outside circular area of radius 2*optimalDistance f_rep=0; } else { // repelsive forces are computed only for vertices within a circular area // of radius 2*optimalDistance f_rep = (optimalDistance * optimalDistance / dist) ; } } return -f_rep; } /** * @brief Graph::sign * returns the sign of number D as integer (1 or -1) * @param D * @return */ int Graph::sign(const qreal &D) { if (D != 0 ) { return ( D / qAbs(D) ); } else { return 0; } } /** * @brief Graph::compute_angles * Computes the two angles of the orthogonal triangle shaped by two points * of difference vector DV and distance dist * A = 90 * B = angle1 * C = angle2 * * @param DV * @param dist * @param angle1 * @param angle2 * @param degrees1 * @param degrees2 */ void Graph::compute_angles(const QPointF &DV, const qreal &dist, qreal &angle1, qreal &angle2, qreal °rees1, qreal °rees2 ) { if ( dist >0 ) { angle1 = qAcos( qAbs(DV.x()) / dist ); // radians angle2 = (M_PI / 2.0) -angle1; // radians (pi/2 -a1) } else { angle1 =0; angle2 =0; } degrees1 = angle1 * 180.0 / M_PI; // convert to degrees degrees2 = angle2 * 180.0 / M_PI; // convert to degrees qDebug () << "angle1 " <::const_iterator v1; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { // calculate new overall velocity vector xvel = c4 * (*v1)->disp().rx(); yvel = c4 * (*v1)->disp().ry(); qDebug() << " ##### source vertex " << (*v1)->name() << " xvel,yvel = ("<< xvel << ", "<< yvel << ")"; //fix Qt error a positive QPoint to the floor // when we ask for moveNode to happen. xvel < 1 && xvel > 0 ? xvel = 1 : xvel = xvel; yvel < 1 && yvel > 0 ? yvel = 1 : yvel = yvel; //Move source node to new position according to overall velocity newPos = QPointF( (qreal) (*v1)->x() + xvel, (qreal) (*v1)->y() + yvel); qDebug() << " source vertex v1 " << (*v1)->name() << " current pos: (" << (*v1)->x() << " , " << (*v1)->y() << " Possible new pos (" << newPos.x() << " , " << newPos.y(); // check if new pos is out of screen and adjust newPos.rx() = qMin ( canvasWidth - 42.0 , qMax (42.0 , newPos.x() ) ); newPos.ry() = qMin ( canvasHeight -42.0 , qMax (42.0 , newPos.y() ) ); qDebug() << " Final new pos (" << newPos.x() << "," << newPos.y()<< ")"; emit moveNode((*v1)->name(), newPos.x(), newPos.y()); } } /** * @brief Graph::layoutForceDirected_FR_moveNodes * @param temperature */ void Graph::layoutForceDirected_FR_moveNodes(const qreal &temperature) { qDebug() << "\n\n ***** layoutForceDirected_FR_moveNodes() \n\n " ; QPointF newPos; qreal xvel = 0, yvel = 0; QList::const_iterator v1; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { // compute the new position // limit the maximum displacement to a maximum temperature xvel = sign((*v1)->disp().rx()) * qMin( qAbs((*v1)->disp().rx()), temperature) ; yvel = sign((*v1)->disp().ry()) * qMin( qAbs((*v1)->disp().ry()), temperature) ; newPos = QPointF((*v1)->x()+ xvel, (*v1)->y()+yvel); qDebug()<< " source vertex v1 " << (*v1)->name() << " current pos: (" << (*v1)->x() << "," << (*v1)->y() << ")" << "Possible new pos (" << newPos.x() << "," << newPos.y()<< ")"; newPos.rx() = qMin ( canvasWidth - 42.0 , qMax (42.0 , newPos.x() ) ); newPos.ry() = qMin ( canvasHeight -42.0 , qMax (42.0 , newPos.y() ) ); //Move node to new position if ( newPos.x() < 5.0 ||newPos.y() < 5.0 || newPos.x() >= (canvasWidth -5)|| newPos.y() >= (canvasHeight-5)|| ((*v1)->x() == newPos.x() && (*v1)->y() == newPos.y() ) ) continue; qDebug()<< " final new pos " << newPos.x() << "," << newPos.y()<< ")"; emit moveNode((*v1)->name(), newPos.x(), newPos.y()); } } Graph::~Graph() { clear(); } socnetv-1.9/src/nodenumber.cpp0000775000175000017500000000400512542300240016723 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt nodenumber.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "nodenumber.h" #include "node.h" #include NodeNumber::NodeNumber( Node *jim , int size, QString labelText) :QGraphicsTextItem(0) { source=jim; jim -> addNumber(this); setParentItem(jim); //auto disables child items like this, when node is disabled. setPlainText( labelText ); setFont( QFont ("Times", size, QFont::Black, false) ); setZValue(254); } void NodeNumber::removeRefs(){ source->deleteNumber(); } NodeNumber::~NodeNumber(){ } socnetv-1.9/src/datasetselectdialog.h0000775000175000017500000000407512542300240020246 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt datasetselectdialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef DATASETSELECTDIALOG_H #define DATASETSELECTDIALOG_H #include #include "ui_datasetselectdialog.h" class DataSetSelectDialog: public QDialog { Q_OBJECT public: DataSetSelectDialog (QWidget *parent = 0); ~DataSetSelectDialog(); public slots: void gatherData(); signals: void userChoices(QString); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DataSetSelectDialog ui; QStringList datasets_list, datasets_filenames; }; #endif socnetv-1.9/src/webcrawler.cpp0000775000175000017500000004321612542300240016731 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt webcrawler.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "webcrawler.h" #include #include #include #include /* * frontier is our Url buffer (global) */ QQueue frontier; /* * constructor called from parent thread * - Inits variables * - Connects http NetworkManager signal to httpfinished() slot * which in turn emits http reply to WebCrawler_Parser */ WebCrawler_Spider::WebCrawler_Spider(QString url, int maxN, int maxLinksPerPage, bool extLinks, bool intLinks) { qDebug() << " wc_spider::WebCrawler_Spider() " << " this->thread() " << this->thread() << " Initializing vars ..."; m_seed=url; //the initial url/domain we will crawl m_maxPages=maxN; //maxPages we'll check m_maxLinksPerPage = maxLinksPerPage; m_extLinks = extLinks; m_intLinks = intLinks; m_visitedNodes = 0; qDebug() << " wc_spider::WebCrawler_Spider() Creating http"; http = new QNetworkAccessManager(this); qDebug() << " wc_spider::WebCrawler_Spider() Connecting http signals"; connect ( http, &QNetworkAccessManager::finished, this, &WebCrawler_Spider::httpFinished ); qDebug () << " wc_spider::WebCrawler_Spider() http->thread() " << http->thread() ; } WebCrawler_Spider::~WebCrawler_Spider() { m_visitedNodes = 0; delete http; } /* * WebCrawer_Spider main functionality * Parses urls from frontier and downloads their data. * When http signals finished() the response data are passed to * wc_parser thread to parse them */ void WebCrawler_Spider::get(){ qDebug() << " wc_spider::get():"; do { //repeat forever.... // or until we crawl all urls in frontier. if (frontier.size() ==0 ) { qDebug () <<" wc_spider::get() #### Frontier is empty: " <0) { if (m_visitedNodes == m_maxPages ) { qDebug () <<" wc_spider::get(): #### Reached m_maxPages!" << " - STOP" ; emit finished("maxpages from spider"); break; } } qDebug() << " wc_spider::get():" <<" frontier size " << frontier.size() << " - Take the first url from frontier "; currentUrl = frontier.dequeue(); qDebug() << " wc_spider::get(): currentUrl: " << currentUrl; qDebug() << " wc_spider::get(): currentUrl not visited." << " Increasing visitedNodes to: " << m_visitedNodes + 1 << " Let us visit it."; qDebug() << " wc_spider::get(): currentUrl: " << currentUrl << " downloading html "; request = new QNetworkRequest; request->setUrl(currentUrl); request->setRawHeader( "User-Agent", "SocNetV innocent spider 1 - see http://socnetv.sf.net"); qDebug() << " wc_spider::get(): http->get() "; qDebug() << " wc_spider::get() http->thread() " << http->thread() ; QNetworkReply *reply = http->get(*request) ; Q_UNUSED(reply); m_visitedNodes++; } while ( 1 ); qDebug() << " wc_spider::get() Finished!"; } void WebCrawler_Spider::httpFinished(QNetworkReply *reply){ qDebug() << " wc_spider::httpFinished()"; emit parse (reply); } //constructor called from parent thread - does nothing WebCrawler_Parser::WebCrawler_Parser(QString url, int maxN, int maxLinksPerPage, bool extLinks, bool intLinks) { qDebug () << " wc_parser::WebCrawler_Parser() this->thread() " << this->thread() << " Initializing variables "; m_seed=QUrl (url); //the initial url/domain we will crawl m_maxPages=maxN; //maxPages we'll check m_maxLinksPerPage = maxLinksPerPage; m_extLinks = extLinks; m_intLinks = intLinks; //clear global variables frontier.clear(); ba.clear(); knownUrls.clear(); m_discoveredNodes=0; m_discoveredNodes++; frontier.enqueue(m_seed); knownUrls[m_seed]=m_discoveredNodes; qDebug() << " wc_parser::WebCrawler_Parser() seed: " << m_seed << " seed_domain: " << m_seed.host() << " Added seed to frontier queue and knownUrls map. " << " Node " << m_discoveredNodes << " should be already created. " << " m_maxPages " << m_maxPages << " m_maxLinksPerPage " << m_maxLinksPerPage << " m_extLinks " << m_extLinks << " m_intLinks " << m_intLinks; } WebCrawler_Parser::~WebCrawler_Parser() { ba.clear(); frontier.clear(); knownUrls.clear(); m_discoveredNodes = 0; } /* * This method is called when http has finished all pending requests. * First, we start by reading all from http to the QString page. * Then we parse the page string, searching for url substrings. */ void WebCrawler_Parser::parse(QNetworkReply *reply){ qDebug () << " wc_parser::parse() thread " << this->thread(); // find to which node the response HTML belongs to // Get this from the reply object request method QUrl requestUrl = reply->request().url(); QString requestUrlStr = requestUrl.toString(); QString locationHeader = reply->header(QNetworkRequest::LocationHeader).toString(); int sourceNode = knownUrls [ requestUrl ]; QString host = requestUrl.host(); QString path = requestUrl.path(); qDebug() << " wc_parser::parse() response HTML of url " << requestUrlStr << " sourceNode " << sourceNode << " host " << host << " path " << path; qDebug () << " wc_parser::parse(): original locationHeader" << reply->header(QNetworkRequest::LocationHeader) ; qDebug () << " wc_parser::parse(): decoded locationHeader" << locationHeader ; qDebug () << " wc_parser::parse(): encoded requestUrl " << requestUrl; qDebug () << " wc_parser::parse(): decoded requestUrl " << requestUrlStr; if ( locationHeader != "" && locationHeader != requestUrlStr ) { qDebug () << "&& wc_parser::parse() Location response header " << locationHeader << " differs from requestUrl " << requestUrlStr << " Creating node redirect - Creating edge - RETURN "; newLink( sourceNode, locationHeader , true ); return; } QUrl newUrl; QString newUrlStr; int start=-1, end=-1, equal=-1 , invalidUrlsInPage =0; // index=-1; int validUrlsInPage = 0; ba=reply->readAll(); QString page(ba); if (!page.contains ("href")) { //if a href doesnt exist, return //FIXME: Frameset pages are not parsed! See docs/manual.html for example. qDebug() << " wc_parser::parse(): ### Empty or not useful html from " << requestUrl << " page size " << page.size() << " \npage contents: " << page << " RETURN"; return; } qDebug() << " wc_parser::parse(): erasing "; start=page.indexOf (""); //Find head pos if ( start != -1 && end != -1 ) { page = page.remove(start, end); //erase head } else if ( start == -1 ) { qDebug() << " wc_parser::parse(): ERROR IN locating tag start"; } else if ( end == -1 ) { qDebug() << " wc_parser::parse(): ERROR IN locating tag end"; } while (page.contains(""); //Find pos if ( start != -1 && end != -1 ) { page = page.remove(start, end); //erase head } } while (page.contains("href")) { //as long there is a href in the page... if (m_maxPages>0) { if (m_discoveredNodes >= m_maxPages ) { qDebug () <<"!! wc_parser::parse(): Reached maxPages! STOP "; emit finished("maxpages from parse()"); return; } } // remove whitespace from the start and the end // all whitespace sequence becomes single space page=page.simplified(); start=page.indexOf ("href"); //Find its pos page = page.remove(0, start); //erase everything up to href equal=page.indexOf ("="); // Find next equal sign (=) page = page.remove(0, equal+1); //Erase everything up to = if (page.startsWith("\"") ) { page.remove(0,1); end=page.indexOf ("\""); } else if (page.startsWith("\'") ){ page.remove(0,1); end=page.indexOf ("\'"); } else { //end=page.indexOf ("\'"); } newUrlStr=page.left(end); //Save new url to newUrl :) newUrlStr=newUrlStr.simplified(); newUrl = QUrl(newUrlStr); if (!newUrl.isValid()) { invalidUrlsInPage ++; qDebug() << " wc_parser::parse(): found INVALID newUrl " << newUrl.toString() << " in page " << requestUrlStr << " Will CONTINUE only if invalidUrlsInPage < 200"; if (invalidUrlsInPage > 200) { qDebug() << " wc_parser::parse(): INVALID newUrls > 200"; emit finished("invalidUrlsInPage > 200"); return; } continue; } qDebug() << "@@ wc_parser::parse(): found VALID newUrl: " << newUrlStr << " in page " << requestUrlStr << " decoded newUrl " << newUrl.toString(); newUrlStr = newUrl.toString(); // ...skip css, favicon, rss, ping, etc if ( newUrlStr.startsWith("#", Qt::CaseInsensitive) || newUrlStr.endsWith("feed/", Qt::CaseInsensitive) || newUrlStr.endsWith("rss/", Qt::CaseInsensitive) || newUrlStr.endsWith("atom/", Qt::CaseInsensitive) || newUrl.fileName().endsWith("xmlrpc.php", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".xml", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".ico", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".gif", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".png", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".jpg", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".js", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".css", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".rsd", Qt::CaseInsensitive) ) { qDebug()<< "!! wc_parser::parse(): # newUrl " << " seems a page resource or anchor (rss, favicon, etc) " << "Skipping..."; continue; } if ( newUrl.isRelative() ) { newUrl = requestUrl.resolved(newUrl); newUrlStr = newUrl.toString(); qDebug() << " wc_parser::parse(): isRelative TRUE" << " host: " << host << " resolved url " << newUrl.toString(); if (!m_intLinks ){ qDebug()<< " wc_parser::parse(): m_IntLinks = FALSE" << " SKIPPING node creation"; continue; } if (requestUrl.path() == newUrl.path()) { qDebug()<< " wc_parser::parse(): m_IntLinks = TRUE" << " requestUrl.path() = newUrl.path()" << " Creating self link only"; this->newLink(sourceNode, newUrl, false); } else { qDebug()<< " wc_parser::parse(): m_IntLinks = TRUE" << " Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } else { qDebug() << " wc_parser::parse(): isRelative FALSE"; if ( newUrl.scheme() != "http" && newUrl.scheme() != "https" && newUrl.scheme() != "ftp" && newUrl.scheme() != "ftps") { qDebug() << " wc_parser::parse(): found INVALID newUrl SCHEME" << newUrl.toString(); continue; } if ( newUrl.host() != host ) { qDebug()<< " wc_parser::parse(): absolute newUrl " << " is EXTERNAL "; if ( !m_extLinks ) { qDebug()<< " wc_parser::parse(): m_extLinks = false . " <<" Creating new node but NOT ADDING it to frontier..."; this->newLink(sourceNode, newUrl, false); } else { qDebug()<< " wc_parser::parse(): m_extLinks = true " <<" Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } else { qDebug()<< " wc_parser::parse(): absolute newUrl" << " is INTERNAL "; if (!m_intLinks){ qDebug()<< " wc_parser::parse(): m_IntLinks = FALSE" << " SKIPPING node creation"; continue; } qDebug()<< " wc_parser::parse(): m_IntLinks = TRUE" <<" Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } validUrlsInPage ++; qDebug() << " wc_parser::parse(): validUrlsInPage " << validUrlsInPage; // or until we reach maxRecursion if ( m_maxLinksPerPage != 0 ) { if ( validUrlsInPage > m_maxLinksPerPage ) { qDebug () <<"!! wc_spider::parse() Reached m_maxLinksPerPage " <0) { if (m_discoveredNodes >= m_maxPages ) { qDebug () <<" wc_parser::newLink(): #### Seems we have reached maxPages!" << " - STOP!" ; emit finished("maxpages from newLink"); return; } } // check if the new url has been discovered previously QMap::const_iterator index = knownUrls.find(target); if ( index!= knownUrls.end() ) { qDebug()<< "-- wc_parser::newLink(): target already discovered " << " in knownUrls. Creating edge from " << s << " to " << index.value(); emit signalCreateEdge (s, index.value() ); return; } m_discoveredNodes++; knownUrls[target]=m_discoveredNodes; emit signalCreateNode( target.toString(), m_discoveredNodes); qDebug()<< "** wc_parser::newLink(): Creating node " << m_discoveredNodes << " url "<< target.toString(); if (enqueue_to_frontier) { frontier.enqueue(target); qDebug()<< "** wc_parser::newLink(): Enqueuing new node to frontier " << " frontier size: "<< frontier.size(); emit startSpider(); } else { qDebug()<< "## wc_parser::newLink(): NOT enqueuing to frontier"; } qDebug()<< "-- wc_parser::newLink(): Creating edge from " << s << " to " << m_discoveredNodes; emit signalCreateEdge (s, m_discoveredNodes); } socnetv-1.9/src/forms/0000775000175000017500000000000012542300363015213 5ustar dimitrisdimitrissocnetv-1.9/src/forms/nodeeditdialog.ui0000664000175000017500000002374212534331160020535 0ustar dimitrisdimitris NodeEditDialog 0 0 464 280 450 280 500 300 DejaVu Sans 11 Node Properties DejaVu Sans 11 Node label DejaVu Sans 11 false DejaVu Sans 11 Node value (disabled) DejaVu Sans 11 Qt::Horizontal 40 20 false DejaVu Sans 11 DejaVu Sans 11 Node shape DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 :/images/box.png:/images/box.png DejaVu Sans 11 :/images/circle.png:/images/circle.png DejaVu Sans 11 :/images/diamond.png:/images/diamond.png DejaVu Sans 11 :/images/ellipse.png:/images/ellipse.png DejaVu Sans 11 :/images/triangle.png:/images/triangle.png DejaVu Sans 11 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok DejaVu Sans 11 Node color (click the button to select) DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 ... 60 20 DejaVu Sans 11 Node size (default 8) DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 8 buttonBox accepted() NodeEditDialog accept() 233 316 157 274 buttonBox rejected() NodeEditDialog reject() 301 322 286 274 socnetv-1.9/src/forms/filteredgesbyweightdialog.ui0000664000175000017500000001044112534331160022772 0ustar dimitrisdimitris FilterEdgesByWeightDialog 0 0 400 210 400 210 DejaVu Sans Filter edges DejaVu Sans 11 With this temporary action, you will make invisible some links according to their weight. Select a threshold then click on the desired radiobox below: Qt::RichText true 6 QLayout::SetDefaultConstraint 0 0 DejaVu Sans 11 Weight Threshold DejaVu Sans 11 1 -100.000000000000000 DejaVu Sans 11 Filter edges with weight over threshold DejaVu Sans 11 Filter edges with weight below threshold DejaVu Sans 11 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() FilterEdgesByWeightDialog accept() 248 254 157 274 buttonBox rejected() FilterEdgesByWeightDialog reject() 316 260 286 274 socnetv-1.9/src/forms/randsmallworlddialog.ui0000664000175000017500000003213012534331160021756 0ustar dimitrisdimitris RandSmallWorldDialog 0 0 528 330 0 0 500 330 550 350 DejaVu Sans Small-World network generator 0 0 450 50 11 50 false false false QFrame::NoFrame QFrame::Sunken Generate random network according to the Watts & Strogatz model. This model produces graphs with small-world properties, including short average path lengths and high clustering. Qt::RichText true 0 0 380 0 11 Nodes 0 0 100 0 11 <html><head/><body><p>The resulting graph will have N nodes and N*d/2 edges</p></body></html> 4 9999 100 QLayout::SetMinimumSize 0 0 380 0 11 <html><head/><body><p>Mean Degree <span style=" font-style:italic;">d</span></p></body></html> 100 0 11 <html><head/><body><p>This is the mean edge degree each new node will have</p></body></html> 2 9999 2 10 0 0 400 0 11 <html><head/><body><p>Rewiring Probability <span style=" font-style:italic;">β</span></p></body></html> 0 0 100 0 11 0.010000000000000 1.000000000000000 0.010000000000000 0.500000000000000 0 0 400 0 11 50 false Graph Mode 0 0 100 0 11 false false false Undirected true false 0 0 100 0 11 Directed false 0 0 400 0 11 Allow diagonals (loops) or set to zero? 0 0 100 0 11 Check to allow loops (nodes linking to themselves) in the new network No loops false 11 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RandSmallWorldDialog accept() 257 373 157 274 buttonBox rejected() RandSmallWorldDialog reject() 325 373 286 274 socnetv-1.9/src/forms/datasetselectdialog.ui0000664000175000017500000001036112534331160021560 0ustar dimitrisdimitris DataSetSelectDialog true 0 0 422 160 400 160 600 195 12 Select Data Set 40 40 50 50 11 <html><head/><body><p><img width="40" height="40" src=":/images/socnetv-32px.png"/></p></body></html> Qt::RichText true 0 0 350 40 0 110 DejaVu Sans 11 SocNetV is able to automatically create known data sets, such as Padgett's florentine families etc. Select the data set you want to re-create from the list: Qt::RichText true DejaVu Sans 11 Select true 300 20 DejaVu Sans 10 PointingHandCursor <html><head/><body><p>Click to select a data set</p></body></html> DejaVu Sans 11 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-1.9/src/forms/webcrawlerdialog.ui0000664000175000017500000003224712534331160021077 0ustar dimitrisdimitris WebCrawlerDialog 0 0 500 310 0 0 500 310 627 350 DejaVu Sans Mono 12 Generate network from web links 0 0 450 80 DejaVu Sans 11 PreferAntialias <html><head/><body><p>SocNetV includes a simple web crawler. It scans the HTML code of a given initial URL (i.e. a website) and maps all internal or external links to other pages found there. As new urls are discovered, the crawler follows them to scan their HTML code for links as well. For more details, see the Manual.</p></body></html> Qt::RichText true 11 PreferAntialias Qt::Horizontal 200 0 DejaVu Sans 11 50 false PreferAntialias Initial URL 250 32 400 32 DejaVu Sans 11 PreferAntialias <html><head/><body><p>Enter the initial url/domain to start crawling from, i.e. http://www.iefimerida.gr </p><p>You may omit http:// if you want. </p></body></html> 11 PreferAntialias Qt::Horizontal 40 20 201 0 DejaVu Sans 11 50 false PreferAntialias Set the max links inside a page to be followed and crawled by SocNetV. Set this to zero if you don't want to have this limit. In this case SocNetV will follow and crawl each one link found in a page. Max links in each page to follow 11 PreferAntialias Qt::Horizontal 40 20 46 0 11 PreferAntialias Set the max links inside a page to be followed and crawled by SocNetV. Set this to zero if you don't want to have this limit. In this case SocNetV will follow and crawl each one link found in a page. 0 500 0 230 0 DejaVu Sans 11 50 false PreferAntialias Set the total urls to be crawled - this is the total nodes the result network will have. Set value to 0, if you don't want any limits... Total urls to crawl 46 0 11 PreferAntialias Set the total urls to be crawled - this is the total nodes the result network will have. Set value to 0, if you don't want any limits... <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Maximum Nodes to display</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">In this spinbox, you define the maximum nodes to be displayed by SocNetV when running in Web Crawler mode. Set value to 0, if you don't want any limits...</p></body></html> 0 9999 600 13 0 DejaVu Sans 11 PreferAntialias Qt::TabFocus <html><head/><body><p>If enabled, the crawler will map <span style=" font-weight:600;">external links</span>. For instance, if you start crawling from www.iefimerida.gr and we find there a link to another domain, i.e. www.linuxinsider.gr, then we will go outside of iefimerida.gr and crawl linuxinsider.gr too. </p><p>If you don't want to crawl external links, disable this option. </p></body></html> Qt::LeftToRight Crawl external links true false DejaVu Sans 11 PreferAntialias Qt::TabFocus <html><head/><body><p>If enabled the crawler will scan and map <span style=" font-weight:600;">internal links </span>as well (i.e. pages within the initial website).</p><p> If you do not want to crawl internal links disable this option. Default is not to crawl internal links.</p></body></html> Qt::LeftToRight Crawl internal links true true DejaVu Sans 11 PreferAntialias Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() WebCrawlerDialog accept() 248 254 157 274 buttonBox rejected() WebCrawlerDialog reject() 316 260 286 274 socnetv-1.9/src/forms/randerdosrenyidialog.ui0000664000175000017500000004617512534331160021777 0ustar dimitrisdimitris RandErdosRenyiDialog 0 0 610 371 0 0 610 365 650 400 DejaVu Sans Erdős–Rényi network generator 0 0 560 50 DejaVu Sans 11 50 false QFrame::NoFrame QFrame::Sunken Generate random network according to Erdős–Rényi (ER) model. In fact, there are two models: in G(n,p) edges are created with Bernoulli trials, while in G(n,M) a graph is randomly selected from all graphs with n nodes and M edges Qt::RichText true 0 0 380 0 DejaVu Sans 11 50 false Nodes 0 0 100 0 DejaVu Sans 11 50 false 0 9999 100 0 0 380 0 DejaVu Sans 11 50 false Model to use 100 0 DejaVu Sans 11 50 false <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This will create a new random network using <span style=" font-weight:600;">G(n,p)</span> model, where</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">n</span> is the number of nodes in the final graph</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">p</span> is the probability with which an edge is included in the graph</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you select this model, you must enter the number of nodes n and the edge probability p. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You may also select if the final network will be undirected or directed and if you want to allow nodes to link to themselves (diagonal non zero).</p></body></html> G(n, p) true false 100 0 DejaVu Sans 11 50 false <html><head/><body><p>This will create a new random network using <span style=" font-weight:600;">G(n,M)</span> model, where</p><p><span style=" font-weight:600;"> n</span> is the number of nodes in the final graph</p><p><span style=" font-weight:600;"> M</span> is the number of edges in the final graph</p><p>If you select this model, you must enter both the number of nodes n and the number of edges M</p><p>You may also select if the final network will be undirected or directed and if you want to allow nodes to link to themselves (diagonal non zero).</p></body></html> G(n, M) false QLayout::SetMinimumSize 0 0 380 0 DejaVu Sans 11 50 false <html><head/><body><p>Edges <span style=" color:#7c7c7c;">for G(n,M) model only</span></p></body></html> 100 0 DejaVu Sans 11 50 false 10 9999 0 0 380 0 DejaVu Sans 11 50 false <html><head/><body><p>Edge Probability <span style=" color:#7c7c7c;">applicable only in G(n,p) model</span></p></body></html> 0 0 100 0 DejaVu Sans 11 50 false 0.010000000000000 1.000000000000000 0.010000000000000 0.100000000000000 0 0 380 0 DejaVu Sans 11 50 false Graph Mode 0 0 100 0 DejaVu Sans 11 50 false false false false Undirected true false 0 0 100 0 DejaVu Sans 11 50 false Directed false 0 0 380 0 DejaVu Sans 11 50 false Allow diagonals (loops) or set to zero? 0 0 100 0 DejaVu Sans 11 50 false Yes, allow true DejaVu Sans 11 50 false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RandErdosRenyiDialog accept() 257 373 157 274 buttonBox rejected() RandErdosRenyiDialog reject() 325 373 286 274 socnetv-1.9/src/forms/randscalefreedialog.ui0000664000175000017500000004733112534331160021540 0ustar dimitrisdimitris RandScaleFreeDialog 0 0 528 427 0 0 528 350 550 450 DejaVu Sans Scale-free random network generator 0 0 450 50 11 50 false false false QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate a random scale-free network of <span style=" font-style:italic;">n</span> nodes according to the Barabási–Albert (BA) model which uses a preferential attachment mechanism. The model starts with <span style=" font-style:italic;">m</span><span style=" font-style:italic; vertical-align:sub;">0</span> connected nodes. In each step a new node is added, along with <span style=" font-style:italic;">m</span> edges to existing nodes. Read more in the manual.</p></body></html> Qt::RichText true 0 0 380 0 11 <html><head/><body><p>Nodes <span style=" font-style:italic;">n</span></p></body></html> 0 0 100 0 11 <html><head/><body><p>The amount of nodes in the resulting scale-free graph</p></body></html> 4 9999 100 QLayout::SetMinimumSize 0 0 380 0 11 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Power of preferential attachment <span style=" font-style:italic;">p </span></p></body></html> 100 0 11 <html><head/><body><p>The power p of preferential attachment </p><p>Leave 1 for linear preferential attachment (BA model).</p></body></html> 1 9999 1 1 QLayout::SetMinimumSize 0 0 380 0 11 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Initial connected nodes <span style=" font-style:italic;">m</span><span style=" font-style:italic; vertical-align:sub;">0</span></p></body></html> 100 0 11 <html><head/><body><p>The number m<span style=" vertical-align:sub;">0</span> of nodes in the initial connected network.</p><p>Leave 1 to start with just one node.</p></body></html> 1 9999 1 1 QLayout::SetMinimumSize 0 0 380 0 11 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Edges to add in each step <span style=" font-style:italic;">m</span></p></body></html> 100 0 11 <html><head/><body><p>Tthe number of edges to add in each step</p></body></html> 1 9999 1 2 QLayout::SetMinimumSize 0 0 380 0 11 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Zero appeal <span style=" font-style:italic;">α</span></p></body></html> 100 0 11 <html><head/><body><p>The initial attractiveness of a node - useful for isolate nodes with d =0</p></body></html> 0 9999 1 1 0 0 400 0 11 50 false Graph Mode 0 0 100 0 11 false false false Undirected true false 0 0 100 0 11 Directed false 0 0 400 0 11 Allow diagonals (loops) or set to zero? 0 0 100 0 11 Check to allow loops (nodes linking to themselves) in the new network No loops false 11 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RandScaleFreeDialog accept() 257 373 157 274 buttonBox rejected() RandScaleFreeDialog reject() 325 373 286 274 socnetv-1.9/src/forms/edgeeditdialog.ui0000664000175000017500000002210112534331160020500 0ustar dimitrisdimitris EdgeEditDialog 0 0 450 280 450 280 Node Properties 12 Enter node label 12 false 12 Enter node value (disabled) 12 Qt::Horizontal 40 20 false 12 12 Select line shape 12 Qt::Horizontal 40 20 12 :/images/box.png:/images/box.png 12 :/images/circle.png:/images/circle.png 12 :/images/diamond.png:/images/diamond.png 12 :/images/ellipse.png:/images/ellipse.png 12 :/images/triangle.png:/images/triangle.png 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 12 Select link color (click the button) 12 Qt::Horizontal 40 20 60 25 12 ... 60 20 12 Select the link weight (default 1) 12 Qt::Horizontal 40 20 12 8 buttonBox accepted() EdgeEditDialog accept() 233 316 157 274 buttonBox rejected() EdgeEditDialog reject() 301 322 286 274 socnetv-1.9/src/guide.cpp0000775000175000017500000000536012542300240015667 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt Guide.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "guide.h" #include "graphicswidget.h" Guide::Guide ( GraphicsWidget *gw, int x0, int y0, int radius ) : graphicsWidget ( gw ){ graphicsWidget->scene()->addItem ( this ); m_x0=x0; m_y0=y0; m_radius=radius; setZValue ( 250 ); circle=true; } Guide::Guide ( GraphicsWidget *gw, int y0, int w) : graphicsWidget ( gw ){ graphicsWidget->scene()->addItem ( this ); m_y0=y0; width= w; setZValue ( 250 ); circle=false; } /** Returns the bounding rectangle of the background circle*/ QRectF Guide::boundingRect() const { if (circle) { return QRectF ( m_x0 - m_radius-1, m_y0 - m_radius-1, m_x0 + 2 * m_radius + 1, m_y0 + 2* m_radius +1 ); } else { return QRectF ( 1, m_y0 -1, width, m_y0 + 1 ); } } void Guide::paint ( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * ){ Q_UNUSED(option); painter->setPen ( QPen ( QColor ( "red" ), 1, Qt::DotLine ) ); if (circle) { painter->drawEllipse ( QPoint(m_x0, m_y0), m_radius, m_radius ); } else { painter->drawLine ( 10 , m_y0, width-10 , m_y0); } } void Guide::die (){ this->prepareGeometryChange(); this->hide(); this->update(); graphicsWidget->scene()->removeItem(this); this->update(); } socnetv-1.9/src/texteditor.cpp0000775000175000017500000002454512542300240016773 0ustar dimitrisdimitris/**************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt texteditor.cpp ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com *****************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include #include "texteditor.h" TextEditor::TextEditor(const QString &fileName, QWidget *parent) : QMainWindow(parent) { qDebug("TextEditor()"); textEdit = new QTextEdit; setCentralWidget(textEdit); createActions(); createMenus(); createToolBars(); createStatusBar(); readSettings(); connect(textEdit->document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); resize( 640,400 ); if (!fileName.isEmpty()) loadFile(fileName); else setCurrentFile(""); } void TextEditor::closeEvent(QCloseEvent *event) { if (maybeSave()) { writeSettings(); event->accept(); } else { event->ignore(); } } void TextEditor::newFile() { if (maybeSave()) { textEdit->clear(); setCurrentFile(""); } } void TextEditor::open() { if (maybeSave()) { QString fileName = QFileDialog::getOpenFileName(this); if (!fileName.isEmpty()) loadFile(fileName); } } bool TextEditor::save() { if (curFile.isEmpty()) { return saveAs(); } else { return saveFile(curFile); } } bool TextEditor::saveAs() { QString fileName = QFileDialog::getSaveFileName(this); if (fileName.isEmpty()) return false; return saveFile(fileName); } void TextEditor::documentWasModified() { setWindowModified(textEdit->document()->isModified()); } void TextEditor::createActions() { newAct = new QAction(QIcon(":/images/new.png"), tr("&New"), this); newAct->setShortcut(tr("Ctrl+N")); newAct->setStatusTip(tr("Create a new file")); connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this); openAct->setShortcut(tr("Ctrl+O")); openAct->setStatusTip(tr("Open an existing file")); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction(QIcon(":/images/save.png"), tr("&Save"), this); saveAct->setShortcut(tr("Ctrl+S")); saveAct->setStatusTip(tr("Save the document to disk")); connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); saveAsAct = new QAction(tr("Save &As..."), this); saveAsAct->setStatusTip(tr("Save the document under a new name")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q")); exitAct->setStatusTip(tr("Exit the application")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this); cutAct->setShortcut(tr("Ctrl+X")); cutAct->setStatusTip(tr("Cut the current selection's contents to the " "clipboard")); connect(cutAct, SIGNAL(triggered()), textEdit, SLOT(cut())); copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Copy"), this); copyAct->setShortcut(tr("Ctrl+C")); copyAct->setStatusTip(tr("Copy the current selection's contents to the " "clipboard")); connect(copyAct, SIGNAL(triggered()), textEdit, SLOT(copy())); pasteAct = new QAction(QIcon(":/images/paste.png"), tr("&Paste"), this); pasteAct->setShortcut(tr("Ctrl+V")); pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " "selection")); connect(pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste())); aboutAct = new QAction(tr("&About"), this); aboutAct->setStatusTip(tr("Show the application's About box")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); aboutQtAct = new QAction(tr("About &Qt"), this); aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); cutAct->setEnabled(false); copyAct->setEnabled(false); connect(textEdit, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool))); connect(textEdit, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool))); } void TextEditor::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAct); fileMenu->addAction(openAct); fileMenu->addAction(saveAct); fileMenu->addAction(saveAsAct); fileMenu->addSeparator(); fileMenu->addAction(exitAct); editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(cutAct); editMenu->addAction(copyAct); editMenu->addAction(pasteAct); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); } void TextEditor::createToolBars() { fileToolBar = addToolBar(tr("File")); fileToolBar->addAction(newAct); fileToolBar->addAction(openAct); fileToolBar->addAction(saveAct); editToolBar = addToolBar(tr("Edit")); editToolBar->addAction(cutAct); editToolBar->addAction(copyAct); editToolBar->addAction(pasteAct); } void TextEditor::createStatusBar() { statusBar()->showMessage(tr("Ready")); } void TextEditor::readSettings() { QSettings settings("Trolltech", "Application Example"); QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); QSize size = settings.value("size", QSize(400, 400)).toSize(); resize(size); move(pos); } void TextEditor::writeSettings() { QSettings settings("Trolltech", "Application Example"); settings.setValue("pos", pos()); settings.setValue("size", size()); } bool TextEditor::maybeSave() { if (textEdit->document()->isModified()) { QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, tr("TextEditor"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (ret == QMessageBox::Save) return save(); else if (ret == QMessageBox::Cancel) return false; } return true; } void TextEditor::loadFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(this, tr("Application"), tr("Cannot read file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } QTextStream in(&file); QApplication::setOverrideCursor(Qt::WaitCursor); textEdit->setPlainText(in.readAll()); QApplication::restoreOverrideCursor(); setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); file.close(); } bool TextEditor::saveFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("TextEditor"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } QTextStream outText(&file); outText.setCodec("UTF-8"); QApplication::setOverrideCursor(Qt::WaitCursor); outText << textEdit->toPlainText(); QApplication::restoreOverrideCursor(); setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); file.close(); return true; } void TextEditor::setCurrentFile(const QString &fileName) { curFile = fileName; textEdit->document()->setModified(false); setWindowModified(false); QString shownName; if (curFile.isEmpty()) shownName = "untitled.txt"; else shownName = strippedName(curFile); setWindowTitle(tr("%1[*] - %2").arg(shownName).arg(tr("TextEditor"))); } QString TextEditor::strippedName(const QString &fullFileName) { return QFileInfo(fullFileName).fileName(); } void TextEditor::about() { QMessageBox::about( this, "SocNetV Editor", " Part of Social Network Visualizer" "

Developer:
Dimitris V. Kalamaras
" "
email: dimitris.kalamaras@gmail.com" "

Note: This text editor was adapted from Trolltech's application example." "

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 3 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

"); } socnetv-1.9/src/parser.cpp0000775000175000017500000032051512542300240016070 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt parser.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "parser.h" #include #include #include #include #include #include #include //used for qDebug messages #include #include #include #include #include "graph.h" //needed for setParent Parser::Parser( const QString fn, const QString m_codec, const int iNS, const QString iNC, const QString iNSh, const QString iNNC, const int iNNS, const QString iNLC, const int iNLS , const QString iEC, const int width, const int height, const int fFormat, const int sm_mode ) { qDebug() << "Parser::load() fn: " << fn << "running on thread " << this->thread() ; initNodeSize=iNS; initNodeColor=iNC; initNodeShape=iNSh; initNodeNumberColor=iNNC; initNodeNumberSize=iNNS; initNodeLabelColor=iNLC; initNodeLabelSize=iNLS; initEdgeColor=iEC; undirected=0; arrows=false; bezier=false; fileName=fn; userSelectedCodecName = m_codec; networkName=(fileName.split ("/")).last(); gwWidth=width; gwHeight=height; randX=0; randY=0; fileFormat= fFormat; two_sm_mode = sm_mode; xml=0; } Parser::~Parser () { qDebug()<< "**** Parser::~Parser() destructor " << this->thread() <<" clearing hashes... "; nodeNumber.clear(); keyFor.clear(); keyName.clear(); keyType.clear(); keyDefaultValue.clear(); edgesMissingNodesHash.clear(); edgeMissingNodesList.clear(); edgeMissingNodesListData.clear(); firstModeMultiMap.clear(); secondModeMultiMap.clear(); if (xml!=0) { qDebug()<< "**** Parser::~Parser() clearing xml reader object " ; xml->clear(); delete xml; xml=0; } } /** starts the new thread calling the load* methods */ bool Parser::run() { qDebug()<< "**** Parser:: run(). on a new thread " << this->thread() << " networkName "<< networkName << " fileFormat "<< fileFormat ; switch (fileFormat){ case 1: //GraphML if (loadGraphML()){ qDebug("* Parser: that was a GraphML network"); } else fileFormat=-1; break; case 2: //Pajek if ( loadPajek() ) { qDebug("* Parser: that was a Pajek network"); } else fileFormat=-1; break; case 3: //Adjacency if (loadAdjacency() ) { qDebug("* Parser: that was an adjacency-matrix network"); } else fileFormat=-1; break; case 4: //Dot if (loadDot() ) { qDebug("* Parser: that was a GraphViz (dot) network"); } else fileFormat=-1; break; case 5: //GML if (loadGML() ){ qDebug("* Parser: that was a GML (gml) network"); } else fileFormat=-1; break; case 6: //DL if (loadDL() ){ qDebug("Parser: this is a DL formatted (.dl) network"); } else fileFormat=-1; break; case 7: // Weighted List if (loadWeighedList() ){ qDebug("Parser: this is a weighted list formatted (.list) network"); } else fileFormat=-1; break; case 8: // List if (loadSimpleList() ){ qDebug("Parser: this is a simple list formatted (.list) network"); } else fileFormat=-1; break; case 9: // twomode sociomatrix, affiliation network matrix if (loadTwoModeSociomatrix() ){ qDebug("Parser: OK, this is a two-mode sociomatrix (.tsm) network"); } else fileFormat=-1; break; default: //GraphML if (loadGraphML() ){ qDebug("Parser: this is a GraphML network"); } else fileFormat=-1; break; } qDebug()<< "**** Parser::run() - we return back to Graph and MW! " << " fileFormat now "<< fileFormat ; emit finished ("Parser::run() - reach end"); return (fileFormat==-1) ? false: true; } void Parser::createRandomNodes(int nodeNum=1,QString label=NULL, int totalNodes=1){ if (totalNodes != 1 ) { int i=0; for (i=0; iaNodes) { source=1; relationCounter++; relation = relationsList[ relationCounter ]; qDebug() << " LOOKS LIKE WE ENTERED A NEW DATASET/MATRIX " << " init source node counter to " << source << " and relation to " << relation << ": " << relationCounter; emit changeRelation (relationCounter); } else { qDebug() << "source node counter is " << source; } for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { //qDebug()<< (*it1).toLatin1() ; if ( (*it1)!="0"){ edgeWeight=(*it1).toFloat(&floatOK); qDebug()<< "Parser-loadDL(): relation " << relationCounter << " found edge from " << source << " to " << target << " weight " << edgeWeight << " emitting createEdge() to parent" ; undirected=0; arrows=true; bezier=false; emit createEdge( source, target, edgeWeight, initEdgeColor, undirected, arrows, bezier); totalLinks++; qDebug() << "TotalLinks= " << totalLinks; } target++; } source++; } if (data_flag && edgelist1Format) { //read edges in edgelist1 format // check if we haven't created any nodes... if ( nodeSum < aNodes ){ qDebug() << " nodes have not been created yet. " << " calling createRandomNodes()" ; createRandomNodes(1, QString::null, aNodes); nodeSum = aNodes; } lineElement=str.split(QRegExp("\\s+"), QString::SkipEmptyParts); if ( lineElement.count() != 3 ) { qDebug()<< "*** Parser:loadDL(): Not an edgelist1 UCINET formatted file. Aborting!!"; file.close(); //emit something... return false; } source = (lineElement[0]).toInt(&intOK); target = (lineElement[1]).toInt(&intOK); qDebug() << " source node " << source << " target node " << target; edgeWeight=(lineElement[2]).toDouble(&intOK); if (intOK) { qDebug () << " list file declares edge weight: " << edgeWeight; } else { edgeWeight=1.0; qDebug () << " list file NOT declaring edge weight. Setting default: " << edgeWeight; } qDebug()<< " Parser::loadDL() - Creating link " << source << " -> "<< target << " weight= "<< edgeWeight << " TotalLinks= " << totalLinks+1; emit createEdge(source, target, edgeWeight, initEdgeColor, undirected, arrows, bezier); totalLinks++; } } //sanity check if (nodeSum != aNodes) { qDebug()<< "Error: aborting"; //emit something return false; } //The network has been loaded. Tell MW the statistics and network type // 0: no format, 1: GraphML, 2:Pajek, 3:Adjacency, 4: Dot, 5:DL, 6:GML, 7: List emit changeRelation (0); emit fileType(5, networkName, aNodes, totalLinks, undirected); qDebug() << "Parser-loadDL() clearing"; lineElement.clear(); labelsList.clear(); relationsList.clear(); return true; } /** Tries to load the file as Pajek-formatted network. If not it returns -1 */ bool Parser::loadPajek(){ qDebug ("\n\nParser: loadPajek"); QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QString str, label, temp; nodeColor=""; edgeColor=""; nodeShape=""; QStringList lineElement; bool ok=false, intOk=false, check1=false, check2=false; bool nodes_flag=false, edges_flag=false, arcs_flag=false, arcslist_flag=false, matrix_flag=false; fileContainsNodeColors=false; fileContainsNodeCoords=false; fileContainsLinkColors=false; bool zero_flag=false; int i=0, j=0, miss=0, source= -1, target=-1, nodeNum, colorIndex=-1, coordIndex=-1; unsigned long int lineCounter=0; int pos=-1, relationCounter=0; float weight=1; QString relation; list listDummiesPajek; totalLinks=0; aNodes=0; j=0; //counts how many real nodes exist in the file miss=0; //counts missing nodeNumbers. //if j + miss < nodeNum, it creates (nodeNum-miss) dummy nodes which are deleted in the end. QList toBeDeleted; //FIXME ? QStringList relationsList; while ( !ts.atEnd() ) { str= ts.readLine(); str = str.simplified(); if ( isComment(str) ) continue; lineCounter++; qDebug()<< "*** Parser:loadPajek(): " << str; if (lineCounter==1) { if ( str.startsWith("graph",Qt::CaseInsensitive) || str.startsWith("digraph",Qt::CaseInsensitive) || str.startsWith("DL",Qt::CaseInsensitive) || str.startsWith("list",Qt::CaseInsensitive) || str.startsWith("graphml",Qt::CaseInsensitive) || str.startsWith(" 0) { qDebug () << "Parser::loadPajek() relationCounter " << relationCounter << "emitting changeRelation"; emit changeRelation(relationCounter); i=0; // reset the source node index } relationCounter++; } continue; } /** READING NODES */ if (!edges_flag && !arcs_flag && !arcslist_flag && !matrix_flag) { //qDebug("=== Reading nodes ==="); nodes_flag=true; nodeNum=lineElement[0].toInt(&intOk, 10); //qDebug()<<"node number: "< nodeNum ) { qDebug ("Error: This Pajek net declares this node with nodeNumber smaller than previous nodes. Aborting"); return false; } emit createNode( nodeNum,initNodeSize, nodeColor, initNodeNumberColor, initNodeNumberSize, label, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), nodeShape, false ); initNodeColor=nodeColor; } // NODES CREATED. CREATE EDGES/ARCS NOW. // first check that all nodes are already created else { if (j && j!=aNodes) { //if there were more or less nodes than the file declared qDebug()<<"*** WARNING ***: The Pajek file declares " << aNodes <<" nodes, but I found " << j << " nodes...." ; aNodes=j; } else if (j==0) { //if there were no nodes at all, we need to create them now. qDebug()<< "The Pajek file declares "<< aNodes<< " but I didnt found any nodes. I will create them...."; for (int num=j+1; num<= aNodes; num++) { qDebug() << "Parser-loadPajek(): Creating node number i = "<< num; randX=rand()%gwWidth; randY=rand()%gwHeight; emit createNode( num,initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, QString::number(i), initNodeLabelColor,initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } j=aNodes; } if (edges_flag && !arcs_flag) { /**EDGES */ //qDebug("Parser-loadPajek(): ==== Reading edges ===="); qDebug()< (i-1) internally else if (source < 0 && target >0 ) { //weights come first... edgeWeight = lineElement[0].toFloat(&ok); source= lineElement[1].toInt(&ok, 10); if (lineElement.count()>2) { target = lineElement[2].toInt(&ok,10); } else { target = lineElement[1].toInt(&ok,10); //self link } } else if (lineElement.count()>2) edgeWeight =lineElement[2].toFloat(&ok); else edgeWeight =1.0; //qDebug()<<"Parser-loadPajek(): weight "<< weight; if (lineElement.contains("c", Qt::CaseSensitive ) ) { //qDebug("Parser-loadPajek(): file with link colours"); fileContainsLinkColors=true; colorIndex=lineElement.indexOf( QRegExp("[c]"), 0 ) +1; if (colorIndex >= lineElement.count()) edgeColor=initEdgeColor; else edgeColor=lineElement [ colorIndex ]; if (edgeColor.contains (".") ) edgeColor=initEdgeColor; //qDebug()<< " current color "<< edgeColor; } else { //qDebug("Parser-loadPajek(): file with no link colours"); edgeColor=initEdgeColor; } undirected=2; arrows=true; bezier=false; qDebug()<< "Parser-loadPajek(): EDGES: Create edge between " << source << " - "<< target; emit createEdge(source, target, edgeWeight, edgeColor, undirected, arrows, bezier); totalLinks=totalLinks+2; } //end if EDGES else if (!edges_flag && arcs_flag) { /** ARCS */ //qDebug("Parser-loadPajek(): === Reading arcs ==="); source= lineElement[0].toInt(&ok, 10); target = lineElement[1].toInt(&ok,10); if (source == 0 || target == 0 ) return false; // i --> (i-1) internally else if (source < 0 && target >0 ) { //weights come first... edgeWeight = lineElement[0].toFloat(&ok); source= lineElement[1].toInt(&ok, 10); if (lineElement.count()>2) { target = lineElement[2].toInt(&ok,10); } else { target = lineElement[1].toInt(&ok,10); //self link } } else if (lineElement.count()>2) edgeWeight =lineElement[2].toFloat(&ok); else edgeWeight =1.0; if (lineElement.contains("c", Qt::CaseSensitive ) ) { //qDebug("Parser-loadPajek(): file with link colours"); edgeColor=lineElement.at ( lineElement.indexOf( QRegExp("[c]"), 0 ) + 1 ); fileContainsLinkColors=true; } else { //qDebug("Parser-loadPajek(): file with no link colours"); edgeColor=initEdgeColor; } undirected=0; arrows=true; bezier=false; qDebug()<<"Parser-loadPajek(): ARCS: Creating arc from node "<< source << " to node "<< target << " with weight "<< weight; emit createEdge(source, target, edgeWeight , edgeColor, undirected, arrows, bezier); totalLinks++; } //else if ARCS else if (arcslist_flag) { /** ARCSlist */ //qDebug("Parser-loadPajek(): === Reading arcs list==="); if (lineElement[0].startsWith("-") ) lineElement[0].remove(0,1); source= lineElement[0].toInt(&ok, 10); fileContainsLinkColors=false; edgeColor=initEdgeColor; undirected=0; arrows=true; bezier=false; for (register int index = 1; index < lineElement.size(); index++) { target = lineElement.at(index).toInt(&ok,10); qDebug()<<"Parser-loadPajek(): ARCS LIST: Creating ARC source "<< source << " target "<< target << " with weight "<< weight; emit createEdge(source, target, edgeWeight, edgeColor, undirected, arrows, bezier); totalLinks++; } } //else if ARCSLIST else if (matrix_flag) { /** matrix */ //qDebug("Parser-loadPajek(): === Reading matrix of edges==="); i++; source= i; fileContainsLinkColors=false; edgeColor=initEdgeColor; undirected=0; arrows=true; bezier=false; for (target = 0; target < lineElement.size(); target ++) { if ( lineElement.at(target) != "0" ) { edgeWeight = lineElement.at(target).toFloat(&ok); qDebug()<<"Parser-loadPajek(): MATRIX: Creating arc source " << source << " target "<< target +1 << " with weight "<< weight; emit createEdge(source, target+1, edgeWeight, edgeColor, undirected, arrows, bezier); totalLinks++; } } } //else if matrix } //end if BOTH ARCS AND EDGES } //end WHILE file.close(); if (j==0) return false; //The network has been loaded. Tell MW the statistics and network type // 0: no format, 1: GraphML, 2:Pajek, 3:Adjacency, 4: Dot, 5:DL, 6:GML, 7: List emit fileType(2, networkName, aNodes, totalLinks, undirected); qDebug("Parser-loadPajek(): Removing all dummy aNodes, if any"); if (listDummiesPajek.size() > 0 ) { qDebug("Trying to delete the dummies now"); for ( list::iterator it=listDummiesPajek.begin(); it!=listDummiesPajek.end(); it++ ) { emit removeDummyNode(*it); } } qDebug("Parser-loadPajek(): Clearing DumiesList from Pajek"); listDummiesPajek.clear(); relationsList.clear(); emit changeRelation (0); return true; } /** Tries to load the file as adjacency sociomatrix-formatted. If not it returns -1 */ bool Parser::loadAdjacency(){ qDebug("\n\nParser: loadAdjacency()"); QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QString str; QStringList lineElement; int i=0, j=0, aNodes=0, newCount=0, lastCount=0; edgeWeight=1.0; bool intOK=false; totalLinks=0; i=1; while ( i < 11 && !ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "*** Parser:loadAdjacency(): Not an Adjacency-formatted file. Aborting!!"; file.close(); return false; } if ( str.contains (",")) newCount = (str.split(",")).count(); else newCount = (str.split(" ")).count(); qDebug() << str; qDebug() << "newCount "<1 ) || (newCount < i) ) { // line element count differ, therefore this can't be an adjacency matrix qDebug()<< "*** Parser:loadAdjacency(): Not an Adjacency-formatted file. Aborting!!"; file.close(); return false; } lastCount=newCount; i++; } ts.reset(); ts.seek(0); i=0; while ( !ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); // transforms "/t", " ", etc to plain " ". if ( isComment(str) ) continue; if ( str.contains (",")) lineElement=str.split(","); else lineElement=str.split(" "); if (i == 0 ) { aNodes=lineElement.count(); qDebug("Parser-loadAdjacency(): There are %i nodes in this file", aNodes); for (j=0; j values; for (int k = 1; k < i ; ++k) { qDebug() << "Checking earlier discovered actor k = " << k; if ( firstModeMultiMap.contains(k, j) ) { undirected=2; arrows=true; bezier=false; edgeWeight = 1; qDebug() << " Actor " << i << " on the same event as actor " << k << ". Creating edge "; emit createEdge(i, k, edgeWeight, initEdgeColor, undirected, arrows, bezier); totalLinks++; } } } j++; } } file.close(); // 0: no format, 1: GraphML, 2:Pajek, 3:Adjacency, 4: Dot, 5:DL, 6:GML, 7: List, 8 List, 9, TwoModeSociomatrix qDebug() << "Parser: Two-mode SM network has been loaded. Tell MW the statistics and network type"; emit fileType(9, networkName, aNodes, totalLinks, undirected); return true; } /** Tries to load a file as GraphML (not GML) formatted network. If not it returns -1 */ bool Parser::loadGraphML(){ qDebug("\n\nParser: loadGraphML()"); aNodes=0; totalLinks=0; nodeNumber.clear(); bool_key=false; bool_node=false; bool_edge=false; key_id = ""; key_name = ""; key_type = ""; key_value = ""; initEdgeWeight = 1; edgeWeight=1; edgeColor="black"; arrows=true; undirected=0; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QXmlStreamReader *xml = new QXmlStreamReader(); qDebug() << " loadGraphML(): read file to a byte array"; QByteArray encodedData = file.readAll(); QByteArray userSelectedCodec =userSelectedCodecName.toLatin1(); xml->addData(encodedData); qDebug() << " loadGraphML(): test if XML document encoding == userCodec"; xml->readNext(); if (xml->isStartDocument()) { qDebug()<< " loadGraphML(): Testing XML document " << " version " << xml->documentVersion() << " encoding " << xml->documentEncoding() << " userSelectedCodecName.toUtf8() " << userSelectedCodecName.toUtf8(); if ( xml->documentEncoding().toString() != userSelectedCodecName) { qDebug() << " loadGraphML(): Conflicting encodings. " << " Re-reading data with userCodec"; xml->clear(); QTextStream in(&encodedData); in.setAutoDetectUnicode(false); QTextCodec *codec = QTextCodec::codecForName( userSelectedCodec ); in.setCodec(codec); QString decodedData = in.readAll(); xml->addData(decodedData); } else { qDebug() << " loadGraphML(): Testing XML: OK"; xml->clear(); xml->addData(encodedData); } } while (!xml->atEnd()) { xml->readNext(); qDebug()<< " loadGraphML(): xml->token "<< xml->tokenString(); if (xml->isStartDocument()) { qDebug()<< " loadGraphML(): xml startDocument" << " version " << xml->documentVersion() << " encoding " << xml->documentEncoding(); } if (xml->isStartElement()) { qDebug()<< " loadGraphML(): element name "<< xml->name().toString(); if (xml->name() == "graphml") { qDebug()<< " loadGraphML(): OK. NamespaceUri is " << xml->namespaceUri().toString(); readGraphML(*xml); } else { //not a GraphML doc, return false. xml->raiseError( QObject::tr(" loadGraphML(): not a GraphML file.")); qDebug()<< "*** loadGraphML(): Error in startElement " << " The file is not an GraphML version 1.0 file "; file.close(); return false; } } else if ( xml->tokenString() == "Invalid" ){ xml->raiseError( QObject::tr(" loadGraphML(): invalid GraphML or encoding.")); qDebug()<< "*** loadGraphML(): Cannot find startElement" << " The file is not valid GraphML or has invalid encoding"; file.close(); return false; } } //The network has been loaded. Tell MW the statistics and network type // 0: no format, 1: GraphML, 2:Pajek, 3:Adjacency, 4: Dot, 5:DL, 6:GML, 7: List emit fileType(1, networkName, aNodes, totalLinks, undirected); //clear our mess - remove every hash element... keyFor.clear(); keyName.clear(); keyType.clear(); keyDefaultValue.clear(); nodeNumber.clear(); return true; } /* * Called from loadGraphML * This method checks the xml token name and calls the appropriate function. */ void Parser::readGraphML(QXmlStreamReader &xml){ qDebug()<< " Parser: readGraphML()"; bool_node=false; bool_edge=false; bool_key=false; //Q_ASSERT(xml.isStartElement() && xml.name() == "graph"); while (!xml.atEnd()) { //start reading until QXmlStreamReader end(). xml.readNext(); //read next token if (xml.isStartElement()) { //new token (graph, node, or edge) here qDebug()<< "\n readGraphML(): start of element: "<< xml.name().toString() ; if (xml.name() == "graph") //graph definition token readGraphMLElementGraph(xml); else if (xml.name() == "key") {//key definition token QXmlStreamAttributes xmlStreamAttr = xml.attributes(); readGraphMLElementKey( xmlStreamAttr ); } else if (xml.name() == "default") //default key value token readGraphMLElementDefaultValue(xml); else if (xml.name() == "node") //graph definition token readGraphMLElementNode(xml); else if (xml.name() == "data") //data definition token readGraphMLElementData(xml); else if ( xml.name() == "ShapeNode") { bool_node = true; } else if ( ( xml.name() == "Geometry" || xml.name() == "Fill" || xml.name() == "BorderStyle" || xml.name() == "NodeLabel" || xml.name() == "Shape" ) && bool_node ) { readGraphMLElementNodeGraphics(xml); } else if (xml.name() == "edge") {//edge definition token QXmlStreamAttributes xmlStreamAttr = xml.attributes(); readGraphMLElementEdge( xmlStreamAttr ); } else if ( xml.name() == "BezierEdge") { bool_edge = true; } else if ( ( xml.name() == "Path" || xml.name() == "LineStyle" || xml.name() == "Arrows" || xml.name() == "EdgeLabel" ) && bool_edge ) { readGraphMLElementEdgeGraphics(xml); } else readGraphMLElementUnknown(xml); } if (xml.isEndElement()) { //token ends here qDebug()<< " readGraphML(): element ends here: "<< xml.name().toString() ; if (xml.name() == "node") //node definition end endGraphMLElementNode(xml); else if (xml.name() == "edge") //edge definition end endGraphMLElementEdge(xml); } } // call createEdgesMissingNodes() to create any edges with missing nodes createEdgesMissingNodes(); } // this method reads a graph definition // called at Graph element void Parser::readGraphMLElementGraph(QXmlStreamReader &xml){ qDebug()<< " Parser: readGraphMLElementGraph()"; QXmlStreamAttributes xmlStreamAttr = xml.attributes(); QString defaultDirection = xmlStreamAttr.value("edgedefault").toString(); qDebug()<< " edgedefault "<< defaultDirection; if (defaultDirection=="undirected"){ undirected = 2; arrows=false; } else { undirected = 0; arrows=true; } networkName = xmlStreamAttr.value("id").toString(); qDebug()<< " graph id " << networkName; //store graph id to return it afterwards } // this method is needed because the QXmlStreamReader::hasAttribute // has been implemented in Qt 4.5. Therefore we need this ugly hack to // be able to compile SocNetV in all previous Qt4 version. :( //FIXME: This will be obsolete soon bool Parser::xmlStreamHasAttribute( QXmlStreamAttributes &xmlStreamAttr, QString str) const { int size = xmlStreamAttr.size(); for (register int i = 0 ; i < size ; i++) { qDebug() << " xmlStreamHasAttribute(): " << xmlStreamAttr.at(i).name().toString() << endl; if ( xmlStreamAttr.at(i).name() == str) return true; } return false; } // this method reads a key definition // called at key element void Parser::readGraphMLElementKey ( QXmlStreamAttributes &xmlStreamAttr ) { qDebug()<< " Parser: readGraphMLElementKey()"; key_id = xmlStreamAttr.value("id").toString(); qDebug()<< " key id "<< key_id; key_what = xmlStreamAttr.value("for").toString(); keyFor [key_id] = key_what; qDebug()<< " key for "<< key_what; // if (xmlStreamAttr.hasAttribute("attr.name") ) { // to be enabled in later versions.. if ( xmlStreamHasAttribute( xmlStreamAttr , QString ("attr.name") ) ) { key_name =xmlStreamAttr.value("attr.name").toString(); keyName [key_id] = key_name; qDebug()<< " key attr.name "<< key_name; } //if (xmlStreamAttr.hasAttribute("attr.type") ) { if ( xmlStreamHasAttribute( xmlStreamAttr , QString ("attr.type") ) ) { key_type=xmlStreamAttr.value("attr.type").toString(); keyType [key_id] = key_type; qDebug()<< " key attr.type "<< key_type; } //else if (xmlStreamAttr.hasAttribute("yfiles.type") ) { else if ( xmlStreamHasAttribute( xmlStreamAttr , QString ("yfiles.type") ) ) { key_type=xmlStreamAttr.value("yfiles.type").toString(); keyType [key_id] = key_type; qDebug()<< " key yfiles.type "<< key_type; } } // this method reads default key values // called at a default element (usually nested inside key element) void Parser::readGraphMLElementDefaultValue(QXmlStreamReader &xml) { qDebug()<< " Parser: readGraphMLElementDefaultValue()"; key_value=xml.readElementText(); keyDefaultValue [key_id] = key_value; //key_id is already stored qDebug()<< " key default value is "<< key_value; if (keyName.value(key_id) == "size" && keyFor.value(key_id) == "node" ) { qDebug()<< " this key default value "<< key_value << " is for node size"; conv_OK=false; initNodeSize= key_value.toInt(&conv_OK); if (!conv_OK) initNodeSize = 8; } if (keyName.value(key_id) == "shape" && keyFor.value(key_id) == "node" ) { qDebug()<< " this key default value "<< key_value << " is for nodes shape"; initNodeShape= key_value; } if (keyName.value(key_id) == "color" && keyFor.value(key_id) == "node" ) { qDebug()<< " this key default value "<< key_value << " is for nodes color"; initNodeColor= key_value; } if (keyName.value(key_id) == "label.color" && keyFor.value(key_id) == "node" ) { qDebug()<< " this key default value "<< key_value << " is for node labels color"; initNodeLabelColor= key_value; } if (keyName.value(key_id) == "label.size" && keyFor.value(key_id) == "node" ) { qDebug()<< " this key default value "<< key_value << " is for node labels size"; conv_OK=false; initNodeLabelSize= key_value.toInt(&conv_OK); if (!conv_OK) initNodeLabelSize = 8; } if (keyName.value(key_id) == "weight" && keyFor.value(key_id) == "edge" ) { qDebug()<< " this key default value "<< key_value << " is for edges weight"; conv_OK=false; initEdgeWeight= key_value.toFloat(&conv_OK); if (!conv_OK) initEdgeWeight = 1; } if (keyName.value(key_id) == "color" && keyFor.value(key_id) == "edge" ) { qDebug()<< " this key default value "<< key_value << " is for edges color"; initEdgeColor= key_value; } } // this method reads basic node attributes and sets the nodeNumber. // called at the start of a node element void Parser::readGraphMLElementNode(QXmlStreamReader &xml){ QXmlStreamAttributes xmlStreamAttr = xml.attributes(); node_id = (xmlStreamAttr.value("id")).toString(); aNodes++; qDebug()<<" Parser: readGraphMLElementNode() node id "<< node_id << " index " << aNodes << " added to nodeNumber map"; nodeNumber[node_id]=aNodes; //copy default node attribute values. //Some might change when reading element data, some will stay the same... nodeColor = initNodeColor; nodeShape = initNodeShape; nodeSize = initNodeSize; nodeNumberSize=initNodeNumberSize; nodeNumberColor=initNodeNumberColor; nodeLabel = node_id; nodeLabelSize=initNodeLabelSize; nodeLabelColor=initNodeLabelColor; bool_node = true; randX=rand()%gwWidth; randY=rand()%gwHeight; } // this method emits the node creation signal. // called at the end of a node element void Parser::endGraphMLElementNode(QXmlStreamReader &xml){ Q_UNUSED(xml); qDebug()<<" Parser: endGraphMLElementNode() *** signal to create node " << " nodenumber "<< aNodes << " id " << node_id << " label " << nodeLabel << " coords " <"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(undirected)); missingNode=true; } if (!nodeNumber.contains(edge_target)) { qDebug() << "\n\n\nParser::readGraphMLElementEdge() target node id " << edge_target << "for edge from " << edge_source << " to " << edge_target << " DOES NOT EXIST!"; edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(undirected)); missingNode=true; } if (missingNode) { return; } source = nodeNumber [edge_source]; target = nodeNumber [edge_target]; qDebug()<< " edge source "<< edge_source << " num "<< source; qDebug()<< " edge target "<< edge_target << " num "<< target; } // this method emits the edge creation signal. // called at the end of edge element void Parser::endGraphMLElementEdge(QXmlStreamReader &xml){ Q_UNUSED(xml); if (missingNode) { qDebug()<<" Parser: endGraphMLElementEdge() *** missingNode true " << " postponing edge creation signal"; return; } qDebug()<<" Parser: endGraphMLElementEdge() *** signal createEdge " << source << " -> " << target << " undirected value " << undirected; //FIXME need to return edge label as well! emit createEdge(source, target, edgeWeight, edgeColor, undirected, arrows, bezier); totalLinks++; bool_edge= false; } /* * this method reads data for edges and nodes * called at a data element (usually nested inside a node an edge element) */ void Parser::readGraphMLElementData (QXmlStreamReader &xml){ QXmlStreamAttributes xmlStreamAttr = xml.attributes(); key_id = xmlStreamAttr.value("key").toString(); key_value=xml.text().toString(); qDebug()<< " Parser: readGraphMLElementData(): key_id: " << key_id << " key_value "<< key_value; if (key_value.trimmed() == "") { qDebug()<< " Parser: readGraphMLElementData(): text: " << key_value; xml.readNext(); key_value=xml.text().toString(); qDebug()<< " Parser: readGraphMLElementData(): text: " << key_value; if ( key_value.trimmed() != "" ) { //if there's simple text after the StartElement, qDebug()<< " Parser: readGraphMLElementData(): key_id " << key_id << " value is simple text " <"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(undirected)); } } else if ( ( keyName.value(key_id) == "value" || keyName.value(key_id) == "weight" ) && keyFor.value(key_id) == "edge" ) { conv_OK=false; edgeWeight= key_value.toFloat( &conv_OK ); if (!conv_OK) edgeWeight = 1.0; if (missingNode){ edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(undirected)); } qDebug()<< " Data found. Edge value: "<< key_value << " Using "<< edgeWeight << " for this edge"; } else if ( keyName.value(key_id) == "size of arrow" && keyFor.value(key_id) == "edge" ) { conv_OK=false; float temp = key_value.toFloat( &conv_OK ); if (!conv_OK) arrowSize = 1; else arrowSize = temp; qDebug()<< " Data found. Edge arrow size: "<< key_value << " Using "<< arrowSize << " for this edge"; } else if (keyName.value(key_id) == "label" && keyFor.value(key_id) == "edge" ){ edgeLabel = key_value; if (missingNode){ edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(undirected)); } qDebug()<< " Data found. Edge label: "<< edgeLabel << " for this edge"; } } /** * Reads node graphics data and properties: label, color, shape, size, coordinates, etc. */ void Parser::readGraphMLElementNodeGraphics(QXmlStreamReader &xml) { qDebug()<< " Parser: readGraphMLElementNodeGraphics(): element name "<< xml.name().toString(); float tempX =-1, tempY=-1, temp=-1; QString color; QXmlStreamAttributes xmlStreamAttr = xml.attributes(); if ( xml.name() == "Geometry" ) { if ( xmlStreamHasAttribute ( xmlStreamAttr, "x") ) { conv_OK=false; tempX = xml.attributes().value("x").toString().toFloat (&conv_OK) ; if (conv_OK) randX = tempX; } if ( xmlStreamHasAttribute ( xmlStreamAttr, "y") ) { conv_OK=false; tempY = xml.attributes().value("y").toString().toFloat (&conv_OK) ; if (conv_OK) randY = tempY; } qDebug()<< " Node Coordinates: " << tempX << " " << tempY << " Using coordinates" << randX<< " "< 0 ) { qDebug()<<"Parser::createEdgesMissingNodes() - edges to create " << count; QHash::const_iterator it = edgesMissingNodesHash.constBegin(); while (it != edgesMissingNodesHash.constEnd()) { qDebug() << "creating missing edge " << it.key() << " data " << it.value() ; edgeMissingNodesList = (it.key()).split("===>"); if (! ((edgeMissingNodesList[0]).isEmpty() ) && !((edgeMissingNodesList[1]).isEmpty()) ) { source = nodeNumber.value(edgeMissingNodesList[0], -666); target = nodeNumber.value(edgeMissingNodesList[1], -666); if (source == -666 || target == -666 ) { //emit something that this node has not been declared continue; } edgeMissingNodesListData = (it.value()).split("|"); if (!edgeMissingNodesListData[0].isEmpty() ){ edgeWeight = edgeMissingNodesListData[0].toInt(&ok, 10); } if (!edgeMissingNodesListData[1].isEmpty() ){ edgeColor = edgeMissingNodesListData[1]; } if (!edgeMissingNodesListData[2].isEmpty() ){ if ( (edgeMissingNodesListData[2]).contains("2") ) undirected=2; } qDebug()<<" Parser: createEdgesMissingNodesHash() *** signal createEdge " << source << " -> " << target << " undirected value " << undirected; //FIXME need to return edge label as well! emit createEdge(source, target, edgeWeight, edgeColor, undirected, arrows, bezier); } ++it; } } } /** Tries to load a file as GML formatted network. If not it returns -1 */ bool Parser::loadGML(){ qDebug("\n\nParser: loadGML()"); QFile file ( fileName ); QString str, temp; int fileLine=0, start=0, end=0; Q_UNUSED(start); Q_UNUSED(end); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); while (!ts.atEnd() ) { str= ts.readLine() ; fileLine++; qDebug ()<<"Reading fileLine "<< fileLine; if ( fileLine == 1 ) { qDebug ()<<"Reading fileLine = "<< fileLine; if ( !str.startsWith("graph", Qt::CaseInsensitive) ) { qDebug() << "*** Parser:loadGML(): Not an GML-formatted file. Aborting"; file.close(); return false; } } if ( str.startsWith("directed",Qt::CaseInsensitive) ) { //key declarations } else if ( str.startsWith("id",Qt::CaseInsensitive) ) { } else if ( str.startsWith("label",Qt::CaseInsensitive) ) { } else if ( str.startsWith("node",Qt::CaseInsensitive) ) { //node declarations } else if ( str.startsWith("edge",Qt::CaseInsensitive) ) { //edge declarations } } //The network has been loaded. Tell MW the statistics and network type // 0: no format, 1: GraphML, 2:Pajek, 3:Adjacency, 4: Dot, 5:DL, 6:GML, 7: List emit fileType(6, networkName, aNodes, totalLinks, undirected); qDebug() << "Parser-loadGML()"; return true; } /** Tries to load the file as Dot (Graphviz) formatted network. If not it returns -1 */ bool Parser::loadDot(){ qDebug("\n\nParser: loadDotNetwork"); int fileLine=0, aNum=-1; int start=0, end=0, next=0; QString label, node, nodeLabel, fontName, fontColor, edgeShape, edgeColor, edgeLabel, networkLabel; QString str, temp, prop, value ; QStringList lineElement; nodeColor="red"; edgeColor="black"; nodeShape=""; edgeWeight=1.0; float nodeValue=1.0; bool netProperties = false; QStringList labels; QList nodeSequence; //holds edges QList nodesDiscovered; //holds nodes; undirected=0; arrows=true; bezier=false; source=0, target=0; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); aNodes=0; while (!ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); str=str.trimmed(); if ( isComment (str) ) continue; fileLine++; qDebug ()<<"Reading fileLine "<< fileLine; if ( fileLine == 1 ) { if ( str.contains("vertices",Qt::CaseInsensitive) //Pajek || str.contains("network",Qt::CaseInsensitive) //Pajek? || str.contains("[",Qt::CaseInsensitive) // GML || str.contains("DL",Qt::CaseInsensitive) //DL format || str.contains("list",Qt::CaseInsensitive) //list || str.startsWith("",Qt::CaseInsensitive) && str.contains ("=",Qt::CaseInsensitive) && !netProperties ) { qDebug()<< "* A node definition must be here ..." << str; end=str.indexOf('['); if (end!=-1) { temp=str.right(str.size()-end-1); //keep the properties temp=temp.remove(']'); temp=temp.remove(';'); node=str.left(end-1); node=node.remove('\"'); qDebug()<<"node named "<",Qt::CaseInsensitive) && !str.contains ("=",Qt::CaseInsensitive) && !netProperties ) { qDebug()<< "* A node definition must be here ..." << str; end=str.indexOf(';'); if (end!=-1) { temp=str.right(str.size()-end-1); //keep the properties temp=temp.remove(']'); temp=temp.remove(';'); node=str.left(end-1); node=node.remove('\"'); qDebug()<<"node named "<",Qt::CaseInsensitive) ){ //non directed = symmetric links if ( str.contains("--",Qt::CaseInsensitive) ) nodeSequence=str.split("--"); else nodeSequence=str.split("-"); } else { //is directed nodeSequence=str.split("->"); } //Create all nodes defined in nodeSequence for ( QList::iterator it=nodeSequence.begin(); it!=nodeSequence.end(); it++ ) { node=(*it).simplified(); qDebug () << " nodeSequence node "<< node; if ( (aNum=nodesDiscovered.indexOf( node ) ) == -1) { aNodes++; randX=rand()%gwWidth; randY=rand()%gwHeight; qDebug()<<" *** Creating node "<< aNodes << " at "<< randX <<","<< randY <<" label "< 2 ) {//there is a node definition here node=str.left(start).remove('\"').simplified(); qDebug()<<"node label: "<. * ********************************************************************************/ #ifndef EDGE_H #define EDGE_H using namespace std; #include #include #include //declares pair construct class GraphicsWidget; class QGraphicsSceneMouseEvent; class Node; class EdgeWeight; static const int TypeEdge= QGraphicsItem::UserType+2; class Edge : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: Edge(GraphicsWidget *, Node*, Node*, const Qt::PenStyle &style, const float &, const int &, const QString &, const bool&, const bool&, const bool &); ~Edge(); enum { Type = UserType + 2 }; int type() const { return Type; } Node *sourceNode() const; void setSourceNode(Node *node); Node *targetNode() const; void setTargetNode(Node *node); void setStartOffset(int ); void setEndOffset(int ); void removeRefs(); int sourceNodeNumber(); int targetNodeNumber(); void setWeight( const float &w) ; float weight() const; void addWeight (EdgeWeight* canvasWeight ) ; void clearWeightList(); void showArrows(bool); void toggleAntialiasing(bool); void makeReciprocal(); void unmakeReciprocal(); bool isReciprocal(); float width() const; QPen pen() const; void setStyle( const Qt::PenStyle &style); Qt::PenStyle style() const; void setColor( const QString &str) ; QString color() const ; QString colorToPajek(); QPainterPath shape() const; public slots: void adjust (); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void mousePressEvent(QGraphicsSceneMouseEvent *event); private: GraphicsWidget *graphicsWidget; Node *source, *target; QPointF sourcePoint, targetPoint; qreal m_arrowSize, m_startOffset, m_endOffset; Qt::PenStyle m_style; list weightList; QString m_color; int eFrom, eTo; float m_weight; int tox1, tox2, toy1, toy2, size; double rad, theta, theta1, theta2; qreal angle, line_length, line_dx, line_dy; bool m_Bezier, m_drawArrows, m_reciprocal; }; #endif socnetv-1.9/src/randerdosrenyidialog.cpp0000664000175000017500000001325712542300240021003 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt randerdosrenyidialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include #include #include #include #include #include #include "randerdosrenyidialog.h" RandErdosRenyiDialog::RandErdosRenyiDialog(QWidget *parent ) : QDialog(parent) { qDebug() << "::RandErdosRenyiDialog() " ; ui.setupUi(this); nodes = 0; model = ""; edges = 0; eprob = 0; mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &RandErdosRenyiDialog::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); connect (ui.gnpRadioButton, &QRadioButton::clicked, this, &RandErdosRenyiDialog::checkErrors); connect (ui.gnmRadioButton, &QRadioButton::clicked, this, &RandErdosRenyiDialog::checkErrors); //ui.gnpRadioButton->setChecked(true); ui.probDoubleSpinBox->setEnabled(true); ui.edgesSpinBox-> setDisabled(true); ui.undirectedRadioButton->setChecked(true); ui.diagCheckBox ->setChecked(true); connect (ui.gnpRadioButton, &QRadioButton::clicked, this, &RandErdosRenyiDialog::gnpModel ); connect (ui.gnmRadioButton, &QRadioButton::clicked, this, &RandErdosRenyiDialog::gnmModel ); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &RandErdosRenyiDialog::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &RandErdosRenyiDialog::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &RandErdosRenyiDialog::setDiag); } void RandErdosRenyiDialog::gnpModel (){ ui.gnmRadioButton -> setChecked(false); ui.probDoubleSpinBox -> setEnabled(true); ui.edgesSpinBox-> setDisabled(true); } void RandErdosRenyiDialog::gnmModel (){ ui.gnpRadioButton -> setChecked(false); ui.probDoubleSpinBox -> setDisabled(true); ui.edgesSpinBox-> setEnabled(true); } void RandErdosRenyiDialog::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void RandErdosRenyiDialog::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void RandErdosRenyiDialog::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void RandErdosRenyiDialog::checkErrors() { qDebug()<< " RandErdosRenyiDialog::checkErrors()" ; if ( !ui.gnpRadioButton->isChecked() && !ui.gnmRadioButton->isChecked()) { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); QGraphicsColorizeEffect *effect2 = new QGraphicsColorizeEffect; effect2->setColor(QColor("red")); ui.gnpRadioButton->setGraphicsEffect(effect); ui.gnmRadioButton->setGraphicsEffect(effect2); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } else { ui.gnpRadioButton->setGraphicsEffect(0); ui.gnmRadioButton->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } //gatherData(); } void RandErdosRenyiDialog::gatherData() { qDebug() << "RandErdosRenyiDialog::gatherData() " ; nodes = ui.nodesSpinBox->value(); model = ( ui.gnpRadioButton->isChecked() ) ? "G(n,p)" : "G(n,M)"; if ( ui.gnpRadioButton->isChecked() ) { eprob = ui.probDoubleSpinBox->value(); } else { edges = ui.edgesSpinBox->value(); } mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "model " << model; qDebug() << "eprob " << eprob; qDebug() << "edges " << edges; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, model, edges, eprob, mode, diag); } socnetv-1.9/src/nodeeditdialog.cpp0000664000175000017500000001207612542300240017544 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt nodeeditdialog.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include "nodeeditdialog.h" NodeEditDialog::NodeEditDialog(QWidget *parent, const QString &l, const int &s, const QColor &col, const QString &sh) : QDialog(parent) { ui.setupUi(this); nodeSize = s; nodeColor = col; nodeShape = sh; nodeLabel = l; ui.labelEdit->setText(nodeLabel); ui.sizeSpin->setValue(nodeSize); if ( nodeShape == "box" ){ ui.boxRadio->setChecked (true); } else if ( nodeShape == "circle" ){ ui.circleRadio->setChecked (true); } else if ( nodeShape == "diamond" ){ ui.diamondRadio->setChecked (true); } else if ( nodeShape == "ellipse" ){ ui.ellipseRadio->setChecked (true); } else if ( nodeShape == "triangle" ){ ui.triangleRadio->setChecked (true); } pixmap = QPixmap(60,20) ; pixmap.fill(nodeColor); ui.colorButton->setIcon(QIcon(pixmap)); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.labelEdit)->setFocus(); connect (ui.labelEdit, &QLineEdit::editingFinished, this, &NodeEditDialog::checkErrors); connect (ui.colorButton, &QToolButton::clicked, this, &NodeEditDialog::selectColor); } void NodeEditDialog::gatherData(){ qDebug()<< " NodeEditDialog::gatherData()" ; nodeLabel = ui.labelEdit->text(); nodeSize = ui.sizeSpin->value(); nodeValue = ui.valueEdit->text(); nodeShape = "circle"; if ( ui.boxRadio->isChecked () ){ nodeShape = "box"; } else if ( ui.circleRadio->isChecked() ){ nodeShape = "circle"; } else if ( ui.diamondRadio->isChecked() ){ nodeShape = "diamond"; } else if ( ui.ellipseRadio->isChecked() ){ nodeShape = "ellipse"; } else if ( ui.triangleRadio->isChecked() ){ nodeShape = "triangle"; } emit userChoices(nodeLabel,nodeSize,nodeValue,nodeColor,nodeShape); } void NodeEditDialog::checkErrors() { qDebug()<< " NodeEditDialog::checkErrors()" ; QString userLabel = ui.labelEdit->text(); userLabel = userLabel.simplified(); ui.labelEdit->setText(userLabel); if ( ui.labelEdit->text().isEmpty() ) { qDebug() << "empty label!"; QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui.labelEdit->setGraphicsEffect(effect); //(ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } else { ui.labelEdit->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } //gatherData(); } void NodeEditDialog::selectColor() { qDebug()<< " NodeEditDialog::selectColor()" ; nodeColor = QColorDialog::getColor( Qt::red, this, tr("Select node color") ); if ( nodeColor.isValid()) { qDebug() << " color selected " << nodeColor.name(); pixmap.fill(nodeColor); ui.colorButton->setIcon(QIcon(pixmap)); } else { // user pressed Cancel qDebug() << " Aborted node color"; } } socnetv-1.9/src/randscalefreedialog.cpp0000664000175000017500000000616412542300240020550 0ustar dimitrisdimitris#include "randscalefreedialog.h" #include #include #include #include #include RandScaleFreeDialog::RandScaleFreeDialog(QWidget *parent) : QDialog(parent) { qDebug() << "::RandScaleFreeDialog() " ; ui.setupUi(this); nodes = 0; initialNodes = 0; mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &RandScaleFreeDialog::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); ui.initialNodesSpinBox-> setEnabled(true); ui.undirectedRadioButton->setChecked(false); ui.directedRadioButton->setEnabled(true); ui.directedRadioButton->setChecked(true); ui.diagCheckBox->setText("No, set zero"); ui.diagCheckBox ->setChecked(false); ui.diagCheckBox -> setEnabled(false); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &RandScaleFreeDialog::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &RandScaleFreeDialog::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &RandScaleFreeDialog::setDiag); } void RandScaleFreeDialog::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void RandScaleFreeDialog::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void RandScaleFreeDialog::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void RandScaleFreeDialog::checkErrors() { qDebug()<< " RandSmallWorldDialog::checkErrors()" ; // if ( !ui.gnpRadioButton->isChecked() && !ui.gnmRadioButton->isChecked()) // { // QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; // effect->setColor(QColor("red")); // ui.gnpRadioButton->setGraphicsEffect(effect); // (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); // } // else { // ui.gnpRadioButton->setGraphicsEffect(0); // ui.gnmRadioButton->setGraphicsEffect(0); // (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); // } //gatherData(); } void RandScaleFreeDialog::gatherData() { qDebug() << "RandScaleFreeDialog::gatherData() " ; nodes = ui.nodesSpinBox->value(); power = ui.powerSpinBox->value(); initialNodes = ui.initialNodesSpinBox->value(); edgesPerStep = ui.edgesPerStepSpinBox ->value(); zeroAppeal = ui.zeroAppealSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); // diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "initialNodes " << initialNodes; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, power, initialNodes, edgesPerStep,zeroAppeal, mode); } socnetv-1.9/src/randsmallworlddialog.h0000664000175000017500000000462512542300240020444 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt randsmallworlddialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef RANDSMALLWORLDDIALOG_H #define RANDSMALLWORLDDIALOG_H #include #include "ui_randsmallworlddialog.h" class RandSmallWorldDialog : public QDialog { Q_OBJECT public: explicit RandSmallWorldDialog(QWidget *parent = 0); public slots: void checkErrors(); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int nodes, const int degree, const float prob, const QString mode, const bool diag); private: QString mode; int nodes, degree; float bprob; bool diag; Ui::RandSmallWorldDialog ui; }; #endif // RANDSMALLWORLDDIALOG_H socnetv-1.9/src/webcrawler.h0000775000175000017500000000575612542300240016405 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt webcrawler.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef WEBCRAWLER_H #define WEBCRAWLER_H using namespace std; #include #include class QNetworkAccessManager; class QNetworkRequest; class WebCrawler_Parser : public QObject { Q_OBJECT public: WebCrawler_Parser(QString seed, int maxNodes, int maxLinksPerPage, bool extLinks, bool intLinks); ~WebCrawler_Parser(); public slots: void parse(QNetworkReply *reply); void newLink(int s, QUrl target, bool enqueue_to_frontier); signals: void signalCreateNode(QString url, int no); void signalCreateEdge (int source, int target); void startSpider(); void finished (QString); private: QByteArray ba; QMap knownUrls; QUrl m_seed; int m_maxPages; int m_discoveredNodes; int m_maxLinksPerPage; bool m_extLinks, m_intLinks; }; class WebCrawler_Spider : public QObject { Q_OBJECT public: WebCrawler_Spider(QString seed, int maxNodes, int maxLinksPerPage ,bool extLinks, bool intLinks); ~WebCrawler_Spider(); public slots: void get(); void httpFinished(QNetworkReply *reply); signals: void parse(QNetworkReply *reply); void finished (QString); private: QNetworkAccessManager *http; QNetworkRequest *request; QNetworkReply *reply; QUrl currentUrl ; QString m_seed; int m_maxPages; int m_visitedNodes; int m_maxLinksPerPage; bool m_extLinks, m_intLinks; }; #endif socnetv-1.9/src/vertex.cpp0000775000175000017500000006233012542300240016107 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt vertex.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "vertex.h" #include //used for qDebug messages #include "graph.h" Vertex::Vertex(Graph* parent, const long &name, const int &val, const int &relation, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape): parentGraph (parent) { qDebug() << "Vertex::Vertex() - "<< name << " setting values"; m_name=name; m_value=val; m_size=size; m_color=color; m_numberColor=numColor; m_numberSize=numSize; m_label=label; m_labelColor=labelColor; m_labelSize=labelSize; m_shape=shape; m_x=p.x(); m_y=p.y(); //FIXME outLinkColors list need update when we remove vertices/edges // outLinkColors=new QString[1500]; //Q_CHECK_PTR(outLinkColors); //outLinkColors.reserve(2000); m_outEdges.reserve(100); m_inEdges.reserve(100); m_outEdgesCounter=0; m_inEdgesCounter=0; m_outDegree=0; m_inDegree=0; m_localDegree=0; m_Eccentricity=0; m_DC=0; m_SDC=0; m_DP=0; m_SDP=0; m_CC=0; m_SCC=0; m_BC=0; m_SBC=0; m_SC=0; m_SSC=0; m_IRCC=0; m_SIRCC=0; m_CLC=0; m_hasCLC=false; m_curRelation=relation; m_reciprocalLinked=false; m_enabled = true; connect (this, SIGNAL (setEdgeVisibility ( int, int, int, bool) ), parent, SLOT (slotSetEdgeVisibility (int, int, int, bool)) ); } // constructor with default values Vertex::Vertex(const long int &name) { qDebug() << "Vertex::Vertex() - "<< name << " using default values"; m_name=name; m_value=1; m_size=9; m_color="black"; m_label=""; m_labelColor="black"; m_shape="circle"; m_outEdgesCounter=0; m_inEdgesCounter=0; m_Eccentricity=0; m_DC=0; m_SDC=0; m_DP=0; m_SDP=0; m_CC=0; m_SCC=0; m_BC=0; m_SBC=0; m_IRCC=0; m_SIRCC=0; m_SC=0; m_SSC=0; m_curRelation=0; m_reciprocalLinked=false; } /** * @brief Vertex::changeRelation * @param relation */ void Vertex::changeRelation(int relation) { qDebug() << "Vertex::changeRelation() - Current: " << m_curRelation << " new: " << relation; // first make false all edges of current relation filterEdgesByRelation(m_curRelation, false); // then make true all edges of new relation filterEdgesByRelation(relation, true); // update current relation m_curRelation=relation; } QString Vertex::colorToPajek(){ if (m_color.startsWith("#")) { return ("RGB"+m_color.right( m_color.size()-1 )).toUpper() ; } return m_color; } /** * @brief Vertex::addEdgeTo * Adds an outLink to target with weight w * @param target * @param weight */ void Vertex::addEdgeTo (const long &v2, const float &weight) { qDebug() <<"Vertex::addEdgeTo() - new link " << name() << " -> "<< v2 << " weight "<< weight << " relation " << m_curRelation; // do not use [] operator - silently creates an item if key do not exist m_outEdges.insertMulti( v2, rel_w_bool(m_curRelation, pair_f_b(weight, true) ) ); } /** * @brief Vertex::setOutEdgeEnabled * @param target * @param status */ void Vertex::setOutEdgeEnabled (long int target, bool status){ qDebug () << "Vertex::setOutEdgeEnabled - set outLink to " << target << " as " << status << ". Finding outLink..."; QMutableHashIterator < int, rel_w_bool > it1 (m_outEdges); int linkTarget=0; float weight =0; int relation = 0; while ( it1.hasNext()) { it1.next(); relation = it1.value().first; if ( relation == m_curRelation ) { linkTarget=it1.key(); if ( linkTarget == target ) { weight = it1.value().second.first; qDebug() << " *** vertex " << m_name << " connected to " << linkTarget << " relation " << relation << " weight " << weight << " status " << it1.value().second.second; it1.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, status) )); emit setEdgeVisibility (m_curRelation, m_name, target, status ); } } else { } } } /** * @brief Vertex::addEdgeFrom * @param source * @param weight */ void Vertex::addEdgeFrom (const long int &v1, const float &weight) { // qDebug() <<"Vertex: "<< name() << " addEdgeFrom() "<< source; m_inEdges.insertMulti( v1, rel_w_bool (m_curRelation, pair_f_b(weight, true) ) ); } void Vertex::changeOutEdgeWeight(long int target, float weight){ qDebug() << "Vertex::changeEdgeWeightTo " << target << " weight " << weight ; qDebug() << " *** m_outEdges.count " << m_outEdges.count(); qDebug() << "first find and remove old relation-weight pair" ; H_edges::iterator it1=m_outEdges.find(target); while (it1 != m_outEdges.end() ) { if ( it1.key() == target && it1.value().first == m_curRelation ) { it1=m_outEdges.erase(it1); } else { ++it1; } } qDebug() << " *** m_outEdges.count " << m_outEdges.count(); qDebug() << " create new relation-weight pair "; m_outEdges.insertMulti( target, rel_w_bool(m_curRelation, pair_f_b(weight, true) ) ); qDebug() << " *** m_outEdges.count " << m_outEdges.count(); } /** * @brief Vertex::removeEdgeTo * finds and removes a link to vertex v2 * @param v2 */ void Vertex::removeEdgeTo (long int v2) { qDebug() << "Vertex: removeEdgeTo() - vertex " << m_name << " has " <0) { qDebug () << "checking all_outEdges"; H_edges::iterator it1=m_outEdges.find(v2); while (it1 != m_outEdges.end() && it1.key() == v2 ) { if ( it1.value().first == m_curRelation ) { qDebug() << " *** vertex " << m_name << " connected to " << it1.key() << " relation " << it1.value().first << " weight " << it1.value().second.first << " enabled ? " << it1.value().second.second; qDebug() << " *** erasing outEdge from m_outEdges "; it1=m_outEdges.erase(it1); } else { ++it1; } } qDebug() << "Vertex: vertex " << m_name << " now has " << outEdges() << " out-edges"; } else { qDebug() << "Vertex: vertex " << m_name << " has no edges" ; } } /** * @brief Vertex::removeEdgeFrom * @param v2 */ void Vertex::removeEdgeFrom(long int v2){ qDebug() << "Vertex: removeEdgeFrom() vertex " << m_name << " has " << inEdges() << " in-edges. RemovingEdgeFrom " << v2 ; if (inEdges()>0) { qDebug () << "checking all_inEdges"; H_edges::iterator it=m_inEdges.find(v2); while (it != m_inEdges.end() ) { if ( it.key() == v2 && it.value().first == m_curRelation ) { qDebug() << " *** vertex " << m_name << " connected from " << it.key() << " relation " << it.value().first << " weight " << it.value().second.first << " enabled ? " << it.value().second.second; qDebug() << " *** erasing inEdge from m_inEdges "; it=m_inEdges.erase(it); } else { ++it; } } qDebug() << "Vertex: vertex " << m_name << " now has " << inEdges() << " in-links" ; } else { qDebug() << "Vertex: vertex " << m_name << " has no edges"; } } /** * @brief Vertex::filterEdgesByWeight Called from Graph parent to filter edges over or under a specified weight (m_threshold) * @param m_threshold * @param overThreshold */ void Vertex::filterEdgesByWeight(float m_threshold, bool overThreshold){ qDebug() << "Vertex::filterEdgesByWeight of vertex " << this->m_name; int target=0; float weight=0; QMutableHashIterator < int, rel_w_bool > it (m_outEdges); while ( it.hasNext()) { it.next(); if ( it.value().first == m_curRelation ) { target=it.key(); weight = it.value().second.first; if (overThreshold) { if ( weight >= m_threshold ) { qDebug() << "Vertex::filterEdgesByWeight(). Edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "Vertex::filterEdgesByWeight(). Edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } else { if ( weight <= m_threshold ) { qDebug() << "Vertex::filterEdgesByWeight(). Edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "Vertex::filterEdgesByWeight(). Edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } } } } /** * @brief Vertex::filterEdgesByRelation * Called from Graph to filter out all edges of a given relation * @param relation */ void Vertex::filterEdgesByRelation(int relation, bool status ){ qDebug() << "Vertex::filterEdgesByRelation() - Vertex " << this->m_name << " filtering edges of relation " << relation << " to " << status; int target=0; float weight =0; int edgeRelation=0; QMutableHashIterator < int, rel_w_bool > it1 (m_outEdges); while ( it1.hasNext()) { it1.next(); edgeRelation = it1.value().first; if ( edgeRelation == relation ) { target=it1.key(); weight = it1.value().second.first; qDebug() << "*** outLink " << m_name << " -> " << target << " - emitting to GW to be " << status ; it1.setValue(rel_w_bool(relation, pair_f_b(weight, status) )); emit setEdgeVisibility ( relation, m_name, target, status ); } else { } } } /** * @brief Vertex::outEdges * Returns the number of active outbound arcs, aka the number of * outEdges, from this vertex for the current relation * @return long int */ long int Vertex::outEdges() { m_outEdgesCounter = 0; int relation=0; bool edgeStatus = false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_outEdgesCounter++; } } ++it1; } return m_outEdgesCounter; } /** * @brief Vertex::outEdgesConst * Returns the number of active outbound arcs * Needs to have outEdges called before the call to this method * @return long int */ long int Vertex::outEdgesConst() const { return m_outEdgesCounter; } /** * @brief Vertex::returnEnabledOutEdges * Returns a qhash of all enabled outEdges in the active relation * @return QHash* */ QHash* Vertex::returnEnabledOutEdges(){ //qDebug() << " Vertex::returnEnabledOutEdges() vertex " << this->name(); QHash *enabledOutEdges = new QHash; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; enabledOutEdges->insert(it1.key(), m_weight); // qDebug() << " Vertex::returnEnabledOutEdges() count:" // << enabledOutEdges->count(); } } ++it1; } // qDebug() << " Vertex::returnEnabledOutEdges() vertex " << this->name() // << " outEdges count:" // << enabledOutEdges->count(); return enabledOutEdges; } /** * @brief Vertex::allReciprocalEdges * Returns a qhash of all reciprocal edges to neighbors in the active relation * @return QHash* */ QHash* Vertex::returnReciprocalEdges(){ // qDebug() << "Vertex::returnReciprocalEdges() - vertex " << this->name(); QHash *reciprocalEdges = new QHash; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; if (this->hasEdgeFrom (it1.key()) ) reciprocalEdges->insertMulti(it1.key(), m_weight); } } ++it1; } qDebug() << "Vertex::returnReciprocalEdges() - vertex " << this->name() << " = " << reciprocalEdges->count(); return reciprocalEdges; } /** * @brief Vertex::neighborhood * Returns a qhash of all neighbors in the active relation * @return QHash* */ //QHash* Vertex::neighborhood(){ // qDebug() << " Vertex::neighborhood() vertex " << this->name(); // QHash *neighbors = new QHash; // float m_weight=0; // int relation = 0; // bool edgeStatus=false; // H_edges::const_iterator it1=m_outEdges.constBegin(); // while (it1 != m_outEdges.constEnd() ) { // relation = it1.value().first; // if ( relation == m_curRelation ) { // edgeStatus=it1.value().second.second; // if ( edgeStatus == true) { // m_weight=it1.value().second.first; // if (this->hasEdgeFrom (it1.key()) ) // neighbors->insertMulti(it1.key(), m_weight); // qDebug() << " Vertex::returnReciprocalEdges() count:" // << reciprocalEdges->count(); // } // } // ++it1; // } // qDebug() << " Vertex::returnReciprocalEdges() total " // << reciprocalEdges->count(); // qDebug() << " Vertex::returnReciprocalEdges() localDegree " // << this->localDegree(); // return reciprocalEdges; //} /** * @brief Vertex::inEdges * Returns the number of active inbound arcs, aka the number of * inEdges, to this vertex for the current relation * @return long int */ long int Vertex::inEdges() { m_inEdgesCounter = 0; int relation=0; bool edgeStatus = false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_inEdgesCounter++; } } ++it1; } return m_inEdgesCounter; } /** * @brief Vertex::inEdgesConst * Returns the number of active inbound arcs * Needs to have inEdges called before the call to this method * @return long int */ long int Vertex::inEdgesConst() const { return m_inEdgesCounter; } /** * @brief Vertex::outDegree * Returns the outDegree (the sum of all enabled outEdges weights) of this vertex * @return long int */ long int Vertex::outDegree() { qDebug() << "Vertex::outDegree()"; m_outDegree=0; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; m_outDegree += m_weight; } } ++it1; } return m_outDegree; } long int Vertex::outDegreeConst() { return m_outDegree; } /** * @brief Vertex::inDegree * Returns the inDegree (the sum of all enabled inEdges weights) of this vertex * @return long int */ long int Vertex::inDegree() { qDebug() << "Vertex::inDegree()"; m_inDegree=0; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; m_inDegree += m_weight; } } ++it1; } return m_inDegree; } long int Vertex::inDegreeConst() { return m_inDegree; } /** localDegree is the outDegree + inDegree minus the edges counted twice. */ long int Vertex::localDegree(){ long int v2=0; int relation = 0; bool edgeStatus=false; m_localDegree = (outDegree() + inDegree() ); H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { v2=it1.key(); if (this->hasEdgeFrom (v2) ) m_localDegree--; } } ++it1; } qDebug() << "Vertex:: localDegree() for " << this->name() << "is " << m_localDegree; return m_localDegree; } /** * @brief Vertex::hasEdgeTo * Checks if this vertex is outlinked to v2 and returns the weight of the link * only if the outLink is enabled. * @param v2 * @return */ float Vertex::hasEdgeTo(long int v2){ // qDebug()<< "Vertex::hasEdgeTo() " << name() << " -> " << v2 ; float m_weight=0; bool edgeStatus=false; H_edges::iterator it1=m_outEdges.find(v2); while (it1 != m_outEdges.end() && it1.key() == v2 ) { if ( it1.value().first == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; // qDebug()<< "***** Vertex::hasEdgeTo() - relation " // << it1.value().first // <<" link " << this->name() // << " -> " << v2 << "exists, weight "<< m_weight; return m_weight; } else qDebug()<< "Vertex::hasEdgeTo() - relation " << it1.value().first <<" link " << this->name() << " -> " << v2 << "exists, weight "<< m_weight << " but edgeStatus " << edgeStatus; return 0; } ++it1; } // qDebug()<< "Vertex::hasEdgeTo() - INEXISTENT LINK IN RELATION " << m_curRelation; return 0; } /** * @brief Vertex::hasEdgeFrom * Checks if this vertex is inLinked from v2 and returns the weight of the link * only if the inLink is enabled. * @param v2 * @return */ float Vertex::hasEdgeFrom(long int v2){ qDebug()<< "Vertex::hasEdgeFrom()" ; float m_weight=0; bool edgeStatus=false; H_edges::iterator it1=m_inEdges.find(v2); while (it1 != m_inEdges.end() && it1.key() == v2) { if ( it1.value().first == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; qDebug()<< "Vertex::hasEdgeFrom() - a (" << this->name() << ", " << v2 << ") = "<< m_weight; return m_weight; } else qDebug()<< "Vertex::hasEdgeFrom() - a (" << this->name() << ", " << v2 << ") = "<< m_weight << " but edgeStatus " << edgeStatus; return 0; } ++it1; } qDebug()<< "Vertex::hasEdgeFrom() - a (" << this->name() << ", " << v2 << ") = 0 "; return 0; } int Vertex::cliques (const int &size) { int count = 0; foreach (int value, m_cliques) { if ( value == size ) { count ++; } } return count ; } bool Vertex::addClique (const QString &clique, const int &size) { QStringList members = clique.split(","); switch (size) { case 2: { m_cliques.insert( clique, size); break; } case 3: { if (! m_cliques.contains( clique) && ! m_cliques.contains( QString::number (this->name()) + "," + members[2] + "," + members[1] ) ) { m_cliques.insert( clique, size); return true ; } else return false; break; } case 4: { if (! m_cliques.contains( clique) && ! m_cliques.contains( QString::number (this->name()) + "," + members[1] + "," + members[3] + "," + members[2] ) && ! m_cliques.contains( QString::number (this->name()) + "," + members[2] + "," + members[1] + "," + members[3] ) && ! m_cliques.contains( QString::number (this->name()) + "," + members[2] + "," + members[3] + "," + members[1] ) && ! m_cliques.contains( QString::number (this->name()) + "," + members[3] + "," + members[1] + "," + members[2] ) && ! m_cliques.contains( QString::number (this->name()) + "," + members[3] + "," + members[2] + "," + members[1] ) ) { m_cliques.insert( clique, size); return true ; } else { return false; } break; } }; return false; } void Vertex::clearPs() { myPs.clear(); } void Vertex::appendToPs(long int vertex ) { qDebug() << "adding " << vertex << " to myPs"; myPs.append(vertex); } ilist Vertex::Ps(void) { return myPs; } Vertex::~Vertex() { qDebug() << " Vertex:: destroying my data"; m_outEdges.clear(); outLinkColors.clear(); clearPs(); m_outEdges.clear(); m_inEdges.clear(); } socnetv-1.9/src/mainwindow.cpp0000775000175000017500000123200512542300240016745 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt - mainwindow.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "mainwindow.h" #include "graphicswidget.h" #include "node.h" #include "edge.h" #include "nodenumber.h" #include "nodelabel.h" #include "edgeweight.h" #include "texteditor.h" #include "filteredgesbyweightdialog.h" #include "guide.h" #include "vertex.h" #include "previewform.h" #include "randerdosrenyidialog.h" #include "randsmallworlddialog.h" #include "randscalefreedialog.h" bool printDebug=false; void myMessageOutput ( QtMsgType type, const QMessageLogContext &context, const QString &msg ) { QByteArray localMsg = msg.toLocal8Bit(); Q_UNUSED(context); if (printDebug) switch ( type ) { case QtDebugMsg: fprintf( stderr, "Debug: %s\n", localMsg.constData() ); break; case QtWarningMsg: fprintf( stderr, "Warning: %s\n", localMsg.constData() ); break; case QtFatalMsg: fprintf( stderr, "Fatal: %s\n", localMsg.constData() ); abort(); // deliberately core dump case QtCriticalMsg: fprintf( stderr, "Critical: %s\n", localMsg.constData() ); abort(); // deliberately core dump } } /** MainWindow contruction method **/ MainWindow::MainWindow(const QString & m_fileName) { qInstallMessageHandler( myMessageOutput ); setWindowIcon (QIcon(":/images/socnetv.png")); /** inits that invoke all other construction parts **/ initActions(); //register and construct menu Actions initMenuBar(); //construct menu initToolBar(); //build the toolbar initStatusBar(); //and now add the status bar. initToolBox(); //finally, build the toolbox //and fill a stringList with all X-supported color names colorList = QColor::colorNames(); //set MW minimum size, before creating scene and canvas this->setMinimumSize(900,600); initView(); //create the canvas //Connect some signals to/from the canvas and the Graph connect( graphicsWidget, SIGNAL( selectedNode(Node*) ), this, SLOT( nodeInfoStatusBar(Node*) ) ); connect( graphicsWidget, SIGNAL( selectedEdge(Edge*) ), this, SLOT ( edgeInfoStatusBar(Edge*) ) ); connect( graphicsWidget, SIGNAL( windowResized(int, int)), this, SLOT( windowInfoStatusBar(int,int)) ); connect( graphicsWidget, SIGNAL( windowResized(int, int)), &activeGraph, SLOT( setCanvasDimensions(int,int)) ); connect( graphicsWidget, SIGNAL( userDoubleClicked(int, QPointF) ), this, SLOT( addNodeWithMouse(int,QPointF) ) ) ; connect( graphicsWidget, SIGNAL( userMiddleClicked(int, int, float) ), this, SLOT( addEdge(int, int, float) ) ); connect( graphicsWidget, SIGNAL( openNodeMenu() ), this, SLOT( openNodeContextMenu() ) ) ; connect( graphicsWidget, SIGNAL( openEdgeMenu() ), this, SLOT( openEdgeContextMenu() ) ) ; connect (graphicsWidget, &GraphicsWidget::openContextMenu, this, &MainWindow::openContextMenu); connect( graphicsWidget, SIGNAL(updateNodeCoords(int, int, int)), this, SLOT( updateNodeCoords(int, int, int) ) ); connect( graphicsWidget, SIGNAL(zoomChanged(int)), zoomCombo, SLOT( setCurrentIndex(int)) ); connect( &activeGraph, SIGNAL( addGuideCircle(int, int, int) ), graphicsWidget, SLOT( addGuideCircle(int, int, int) ) ) ; connect( &activeGraph, SIGNAL( addGuideHLine(int) ), graphicsWidget, SLOT( addGuideHLine(int) ) ) ; connect( &activeGraph, SIGNAL( moveNode(int, qreal, qreal) ), graphicsWidget, SLOT( moveNode(int, qreal, qreal) ) ) ; connect( &activeGraph, SIGNAL( drawNode( int ,int, QString, QString, int, QString, QString, int, QPointF, QString, bool, bool, bool) ), graphicsWidget, SLOT( drawNode( int ,int, QString, QString, int, QString, QString, int, QPointF, QString, bool, bool, bool) ) ) ; connect( &activeGraph, SIGNAL( eraseEdge(int, int)), graphicsWidget, SLOT( eraseEdge(int, int) ) ); connect( &activeGraph, SIGNAL( graphChanged() ), this, SLOT( graphChanged() ) ) ; connect( &activeGraph, SIGNAL( signalFileType(int , QString , int , int, bool) ), this, SLOT( fileType(int , QString , int , int, bool) ) ) ; connect( &activeGraph, SIGNAL( drawEdge( int, int, float, bool, bool, QString, bool) ), graphicsWidget, SLOT( drawEdge( int, int,float, bool, bool, QString, bool) ) ) ; connect( &activeGraph, SIGNAL( drawEdgeReciprocal(int, int) ), graphicsWidget, SLOT( drawEdgeReciprocal(int, int) ) ); connect( &activeGraph, SIGNAL( changeEdgeColor(long int,long int,QString)), graphicsWidget, SLOT( setEdgeColor(long int,long int,QString) ) ); connect( &activeGraph, SIGNAL( statusMessage (QString) ), this, SLOT( statusMessage (QString) ) ) ; connect( &activeGraph, SIGNAL( describeDataset (QString) ), this, SLOT( showMessageToUser (QString) ) ) ; connect( &activeGraph, SIGNAL( eraseNode(long int) ), graphicsWidget, SLOT( eraseNode(long int) ) ); connect( &activeGraph, &Graph::signalNodeSizesByInDegree, this, &MainWindow::slotLayoutNodeSizesByInDegree ); //connect some signals/slots with MW widgets connect( addNodeBt,SIGNAL(clicked()), this, SLOT( addNode() ) ); connect( addEdgeBt,SIGNAL(clicked()), this, SLOT( slotAddEdge() ) ); connect( removeNodeBt,SIGNAL(clicked()), this, SLOT( slotRemoveNode() ) ); connect( removeEdgeBt,SIGNAL(clicked()), this, SLOT( slotRemoveEdge() ) ); connect( zoomCombo, SIGNAL(currentIndexChanged(const int &)), graphicsWidget, SLOT( changeZoom(const int &)) ); connect( zoomOutAct, SIGNAL(triggered()), graphicsWidget, SLOT( zoomOut() ) ); connect( zoomInAct, SIGNAL(triggered()), graphicsWidget, SLOT( zoomIn() ) ); connect( rotateSpinBox, SIGNAL(valueChanged(int)), graphicsWidget, SLOT( rot(int) ) ); connect( nextRelationAct, SIGNAL(triggered()), this, SLOT( nextRelation() ) ); connect( prevRelationAct, SIGNAL(triggered()), this, SLOT( prevRelation() ) ); connect( addRelationAct, SIGNAL(triggered()), this, SLOT( addRelation() ) ); connect( changeRelationCombo , SIGNAL( currentIndexChanged(int) ) , &activeGraph, SLOT( changeRelation(int) ) ); connect( this , SIGNAL(addRelationToGraph(QString)), &activeGraph, SLOT( addRelationFromUser(QString) ) ); connect ( &activeGraph, SIGNAL(addRelationToMW(QString)), this, SLOT(addRelation(QString))); connect( &activeGraph, SIGNAL(relationChanged(int)), graphicsWidget, SLOT( changeRelation(int)) ) ; connect( &m_filterEdgesByWeightDialog, SIGNAL( userChoices( float, bool) ), &activeGraph, SLOT( filterEdgesByWeight (float, bool) ) ); connect( &m_WebCrawlerDialog, &WebCrawlerDialog::userChoices, this, &MainWindow::slotWebCrawl ); connect( &m_datasetSelectDialog, SIGNAL( userChoices( QString) ), this, SLOT( slotRecreateDataSet(QString) ) ); connect( &activeGraph, SIGNAL( setEdgeVisibility (int, int, int, bool) ), graphicsWidget, SLOT( setEdgeVisibility (int, int, int, bool) ) ); connect( &activeGraph, SIGNAL( setVertexVisibility(long int, bool) ), graphicsWidget, SLOT( setNodeVisibility (long int , bool) ) ); connect( &activeGraph, SIGNAL( setNodeSize(long int, int) ), graphicsWidget, SLOT( setNodeSize (long int , int) ) ); connect( &activeGraph, SIGNAL( setNodeShape(const long int, const QString) ), graphicsWidget, SLOT( setNodeShape (const long int , const QString) ) ); connect( &activeGraph, SIGNAL( setNodeColor(long int,QString)) , graphicsWidget, SLOT( setNodeColor(long int, QString) ) ); connect( &activeGraph, &Graph::setNodeLabel , graphicsWidget, &GraphicsWidget::setNodeLabel ); connect( clearGuidesAct, SIGNAL(triggered()), graphicsWidget, SLOT(clearGuides())); connect(toolBoxAnalysisGeodesicsSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisGeodesicsSelectChanged(int) ) ); connect(toolBoxAnalysisConnectivitySelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisConnectivitySelectChanged(int) ) ); connect(toolBoxAnalysisClusterabilitySelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisClusterabilitySelectChanged(int) ) ); connect(toolBoxAnalysisProminenceSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisProminenceSelectChanged(int) ) ); connect(toolBoxLayoutByIndexButton, SIGNAL (clicked() ), this, SLOT(toolBoxLayoutByIndexButtonPressed() ) ); connect( layoutGuidesBx, SIGNAL(stateChanged(int)), this, SLOT(slotLayoutGuides(int))); //create an horizontal layout for the toolbox and the canvas. // This will be our MW layout. QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(toolBox); //add them layout->addWidget(graphicsWidget); //create a dummy widget, for the above layout QWidget *widget = new QWidget; widget->setLayout(layout); //now set this as central widget of MW setCentralWidget(widget); /* initialise default network parameters */ qDebug()<<" initialise default network parameters"; initNet(); /* * DEFAULTING HERE DOES NOT CHANGE BOOL VALUE EVERY TIME INITNET IS CALLED */ bezier=false; firstTime=true; graphicsWidget->setInitNodeColor(initNodeColor); graphicsWidget->setInitNumberDistance(numberDistance); graphicsWidget->setInitLabelDistance(labelDistance); graphicsWidget->setInitNodeSize(initNodeSize); graphicsWidget->setBackgroundBrush(QBrush(initBackgroundColor)); //Qt::gray dataDir= QDir::homePath() +QDir::separator() + "socnetv-data" + QDir::separator() ; lastUsedDirPath = "socnetv-initial-none"; if (firstTime) { createFortuneCookies(); createTips(); QDir ourDir(dataDir); if ( !ourDir.exists() ) { ourDir.mkdir(dataDir); QMessageBox::information(this, "SocNetV Data Directory", tr("SocNetV saves reports and files in the " "directory %1") .arg (ourDir.absolutePath()) , QMessageBox::Ok, 0); } } qDebug() << "MW::MainWindow() call findCodecs" ; findCodecs(); qDebug() << "MW::MainWindow() create PreviewForm object and set codecs" ; previewForm = new PreviewForm(this); previewForm->setCodecList(codecs); connect (previewForm, &PreviewForm::userCodec, this, &MainWindow::userCodec ); qDebug() << "MW::MainWindow() Try load *graphml* file on exec time "; if (!m_fileName.isEmpty()) { fileName=m_fileName; fileNameNoPath=fileName.split ("/"); previewNetworkFile( fileName, 0 ); } graphicsWidget->setFocus(); statusMessage( tr("Welcome to Social Network Visualizer, Version ")+VERSION); } MainWindow::~MainWindow() { delete printer; delete scene; delete graphicsWidget; } /** initializes all QActions of the application */ void MainWindow::initActions(){ printer = new QPrinter; /** File menu actions */ fileNew = new QAction(QIcon(":/images/new.png"), tr("&New"), this); fileNew->setShortcut(tr("Ctrl+N")); fileNew->setStatusTip(tr("Creates a new network")); fileNew->setToolTip(tr("New network (Ctrl+N)")); fileNew->setWhatsThis(tr("New\n\nCreates a new network")); connect(fileNew, SIGNAL(triggered()), this, SLOT(slotCreateNew())); fileOpen = new QAction(QIcon(":/images/open.png"), tr("&Open"), this); fileOpen->setShortcut(tr("Ctrl+O")); fileOpen->setToolTip(tr("Open network (Ctrl+O)")); fileOpen->setStatusTip(tr("Open a GraphML-formatted file of an existing network")); fileOpen->setWhatsThis(tr("Open\n\nOpens a file of an existing network in GraphML format")); connect(fileOpen, SIGNAL(triggered()), this, SLOT(slotImportGraphML())); importPajek = new QAction( QIcon(":/images/open.png"), tr("&Pajek"), this); importPajek->setStatusTip(tr("Import a Pajek-formatted file")); importPajek->setWhatsThis(tr("Import Pajek \n\n Imports a network from a Pajek-formatted file")); connect(importPajek, SIGNAL(triggered()), this, SLOT(slotImportPajek())); importSM = new QAction( QIcon(":/images/open.png"), tr("&Adjacency Matrix"), this); importSM->setStatusTip(tr("Import an Adjacency matrix file")); importSM->setWhatsThis(tr("Import Sociomatrix \n\n Imports a network from an Adjacency matrix-formatted file")); connect(importSM, SIGNAL(triggered()), this, SLOT(slotImportSM())); importDot = new QAction( QIcon(":/images/open.png"), tr("GraphViz (.dot)"), this); importDot->setStatusTip(tr("Import an dot file")); importDot->setWhatsThis(tr("Import GraphViz \n\n Imports a network from an GraphViz formatted file")); connect(importDot, SIGNAL(triggered()), this, SLOT(slotImportDot())); importDL = new QAction( QIcon(":/images/open.png"), tr("UCINET (.dl)..."), this); importDL->setStatusTip(tr("Import network to a DL-formatted file (UCINET)")); importDL->setWhatsThis(tr("Import UCINET\n\nImports a network from a DL-formatted file")); connect(importDL, SIGNAL(triggered()), this, SLOT(slotImportDL())); importList = new QAction( QIcon(":/images/open.png"), tr("&Edge list"), this); importList->setStatusTip(tr("Import network from an edge list file. ")); importList->setWhatsThis(tr("Import edge list\n\n" "Import a network from an edgelist file. " " The file can be unvalued or valued (see manual)" )); connect(importList, SIGNAL(triggered()), this, SLOT(slotImportEdgeList())); importTwoModeSM = new QAction( QIcon(":/images/open.png"), tr("&Two Mode Sociomatrix"), this); importTwoModeSM->setStatusTip(tr("Imports a two mode sociomatrix (affiliation network) file")); importTwoModeSM->setWhatsThis(tr("Import Sociomatrix \n\n Imports a two mode network from a sociomatrix file. Two-mode networks are described by affiliation network matrices, where A(i,j) codes the events/organizations each actor is affiliated with.")); connect(importTwoModeSM, SIGNAL(triggered()), this, SLOT(slotImportTwoModeSM())); fileSave = new QAction(QIcon(":/images/save.png"), tr("&Save"), this); fileSave->setShortcut(tr("Ctrl+S")); fileSave->setToolTip(tr("Save network (Ctrl+S)")); fileSave->setStatusTip(tr("Saves the actual network to the current file")); fileSave->setWhatsThis(tr("Save.\n\nSaves the actual network")); connect(fileSave, SIGNAL(triggered()), this, SLOT(slotFileSave())); fileSaveAs = new QAction(QIcon(":/images/save.png"), tr("Save &As..."), this); fileSaveAs->setShortcut(tr("Ctrl+Shift+S")); fileSaveAs->setStatusTip(tr("Saves the actual network under a new filename")); fileSaveAs->setWhatsThis(tr("Save As\n\nSaves the actual network under a new filename")); connect(fileSaveAs, SIGNAL(triggered()), this, SLOT(slotFileSaveAs())); exportBMP = new QAction(QIcon(":/images/save.png"), tr("&BMP..."), this); exportBMP->setStatusTip(tr("Export network to a BMP image")); exportBMP->setWhatsThis(tr("Export BMP \n\n Export network to a BMP image")); connect(exportBMP, SIGNAL(triggered()), this, SLOT(slotExportBMP())); exportPNG = new QAction( QIcon(":/images/save.png"), tr("&PNG..."), this); exportPNG->setStatusTip(tr("Export network to a PNG image")); exportPNG->setWhatsThis(tr("Export PNG \n\n Export network to a PNG image")); connect(exportPNG, SIGNAL(triggered()), this, SLOT(slotExportPNG())); exportPDF = new QAction( QIcon(":/images/save.png"), tr("&PDF..."), this); exportPDF->setStatusTip(tr("Export network to a PDF file")); exportPDF->setWhatsThis(tr("Export PDF\n\n Export network to a PDF document")); connect(exportPDF, SIGNAL(triggered()), this, SLOT(slotExportPDF())); exportSM = new QAction( QIcon(":/images/save.png"), tr("&Adjacency Matrix"), this); exportSM->setStatusTip(tr("Export network to an adjacency matrix file")); exportSM->setWhatsThis(tr("Export Sociomatrix \n\n Export network to a adjacency matrix-formatted file")); connect(exportSM, SIGNAL(triggered()), this, SLOT(slotExportSM())); exportPajek = new QAction( QIcon(":/images/save.png"), tr("&Pajek"), this); exportPajek->setStatusTip(tr("Export network to a Pajek-formatted file")); exportPajek->setWhatsThis(tr("Export Pajek \n\n Export network to a Pajek-formatted file")); connect(exportPajek, SIGNAL(triggered()), this, SLOT(slotExportPajek())); exportList = new QAction( QIcon(":/images/save.png"), tr("&List"), this); exportList->setStatusTip(tr("Export network to a List-formatted file. ")); exportList->setWhatsThis(tr("Export List\n\nExport network to a List-formatted file")); connect(exportList, SIGNAL(triggered()), this, SLOT(slotExportList())); exportDL = new QAction( QIcon(":/images/save.png"), tr("&DL..."), this); exportDL->setStatusTip(tr("Export network to a DL-formatted file")); exportDL->setWhatsThis(tr("Export DL\n\nExport network to a DL-formatted")); connect(exportDL, SIGNAL(triggered()), this, SLOT(slotExportDL())); exportGW = new QAction( QIcon(":/images/save.png"), tr("&GW..."), this); exportGW->setStatusTip(tr("Export network to a GW-formatted file")); exportGW->setWhatsThis(tr("Export\n\nExport network to a GW formatted file")); connect(exportGW, SIGNAL(triggered()), this, SLOT(slotExportGW())); fileClose = new QAction( tr("&Close"), this); fileClose->setStatusTip(tr("Closes the actual network")); fileClose->setWhatsThis(tr("Close \n\nCloses the actual network")); connect(fileClose, SIGNAL(triggered()), this, SLOT(slotFileClose())); printNetwork = new QAction(QIcon(":/images/print.png"), tr("&Print"), this); printNetwork->setShortcut(tr("Ctrl+P")); printNetwork->setStatusTip(tr("Prints whatever is viewable on the canvas.")); printNetwork->setWhatsThis(tr("Printing \n\n This function prints whatever is viewable on the canvas. \nTo print the whole network, you might want to zoom-out.")); connect(printNetwork, SIGNAL(triggered()), this, SLOT(slotPrintView())); fileQuit = new QAction(QIcon(":/images/exit.png"), tr("E&xit"), this); fileQuit->setShortcut(tr("Ctrl+Q")); fileQuit->setStatusTip(tr("Quits the application")); fileQuit->setWhatsThis(tr("Exit\n\nQuits the application")); connect(fileQuit, SIGNAL(triggered()), this, SLOT(close())); openTextEditorAct = new QAction(QIcon(":/images/texteditor.png"), tr("Open Text Editor"),this); openTextEditorAct ->setShortcut(tr("Shift+F5")); openTextEditorAct->setStatusTip(tr("Opens the SocNetV text editor." "You can copy/paste network data, save and then import them...")); openTextEditorAct->setWhatsThis(tr("Open Text Editor\n\nOpens the SocNetV text editor where you can copy paste network data, of any supported format, and save to a file. Then you can import that file to SocNetV...")); connect(openTextEditorAct, SIGNAL(triggered()), this, SLOT(slotOpenTextEditor())); viewNetworkFileAct = new QAction(QIcon(":/images/networkfile.png"), tr("View Loaded File"),this); viewNetworkFileAct ->setShortcut(tr("F5")); viewNetworkFileAct->setStatusTip(tr("Displays the loaded network file")); viewNetworkFileAct->setWhatsThis(tr("View Loaded File\n\nDisplays the file of the loaded network")); connect(viewNetworkFileAct, SIGNAL(triggered()), this, SLOT(slotViewNetworkFile())); viewSociomatrixAct = new QAction(QIcon(":/images/sm.png"), tr("View Adjacency Matrix"), this); viewSociomatrixAct ->setShortcut(tr("F6")); viewSociomatrixAct->setStatusTip(tr("Displays the adjacency matrix of the active network. See manual or online help for more...")); viewSociomatrixAct->setWhatsThis(tr("View Adjacency Matrix\n\nDisplays the adjacency matrix of the active network. \n\n The adjacency matrix of a network is a matrix where each element a(i,j) is equal to the weight of the Edge from node i to node j. If the nodes are not connected, then a(i,j)=0. ")); connect(viewSociomatrixAct, SIGNAL(triggered()), this, SLOT(slotViewAdjacencyMatrix())); recreateDataSetAct = new QAction(QIcon(":/images/sm.png"), tr("Create Known Data Sets"), this); recreateDataSetAct ->setShortcut(tr("F7")); recreateDataSetAct->setStatusTip(tr("Recreates a variety of known data sets.")); recreateDataSetAct->setWhatsThis(tr("Known Data Sets\n\nRecreates some of the most widely used data sets in network analysis studies")); connect(recreateDataSetAct, SIGNAL(triggered()), this, SLOT(slotShowDataSetSelectDialog())); createErdosRenyiRandomNetworkAct = new QAction(QIcon(":/images/erdos.png"), tr("Erdős–Rényi"), this); createErdosRenyiRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_E) ); createErdosRenyiRandomNetworkAct->setStatusTip(tr("Creates a random network according to the Erdős–Rényi model")); createErdosRenyiRandomNetworkAct->setWhatsThis( tr("Erdős–Rényi \n\n") + tr("Creates a random network either of G(n, p) model or G(n,M) model.\n") + tr("In the first, edges are created with Bernoulli trials (probability p).\n") + tr("In the second, a graph of exactly M edges is created.")); connect(createErdosRenyiRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotCreateRandomErdosRenyi())); createLatticeNetworkAct = new QAction( QIcon(":/images/net1.png"), tr("Ring Lattice"), this); createLatticeNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_L) ); createLatticeNetworkAct->setStatusTip(tr("Creates a ring lattice random network")); createLatticeNetworkAct->setWhatsThis( tr("Ring Lattice \n\n")+ tr("A ring lattice is a graph with N nodes each connected to d neighbors, d / 2 on each side.")); connect(createLatticeNetworkAct, SIGNAL(triggered()), this, SLOT(slotCreateRandomRingLattice())); createRegularRandomNetworkAct = new QAction(QIcon(":/images/net.png"), tr("d-Regular"), this); createRegularRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_R) ); createRegularRandomNetworkAct->setStatusTip(tr("Creates a random network where every node has the same degree d.")); createRegularRandomNetworkAct->setWhatsThis( tr("d-Regular \n\n") + tr("Creates a random network where each node have the same number of neighbours, aka the same degree d ")); connect(createRegularRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotCreateRegularRandomNetwork())); createGaussianRandomNetworkAct = new QAction(tr("Gaussian"), this); createGaussianRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_G) ); createGaussianRandomNetworkAct->setStatusTip(tr("Creates a Gaussian distributed random network")); createGaussianRandomNetworkAct->setWhatsThis(tr("Gaussian \n\nCreates a random network of Gaussian distribution")); connect(createGaussianRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotCreateRandomGaussian())); createSmallWorldRandomNetworkAct = new QAction(QIcon(":/images/sw.png"), tr("Small World"), this); createSmallWorldRandomNetworkAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_W) ); createSmallWorldRandomNetworkAct->setStatusTip(tr("Creates a random network with small world properties")); createSmallWorldRandomNetworkAct -> setWhatsThis( tr("Small World \n\n") + tr("A Small World, according to the Watts and Strogatz model, " "is a random network with short average path lengths and high clustering coefficient.")); connect(createSmallWorldRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotCreateRandomSmallWorld())); createScaleFreeRandomNetworkAct = new QAction( QIcon(":/images/scalefree.png"), tr("Scale-free"), this); createScaleFreeRandomNetworkAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_S) ); createScaleFreeRandomNetworkAct->setStatusTip( tr("Creates a random network with power-law degree distribution.")); createScaleFreeRandomNetworkAct-> setWhatsThis( tr("Scale-free (power-law)\n\n") + tr("A scale-free network is a network whose degree distribution follows a power law." " This method generates random scale-free networks according to the " " Barabási–Albert (BA) model using a preferential attachment mechanism.")); connect(createScaleFreeRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotCreateRandomScaleFree())); webCrawlerAct = new QAction(QIcon(":/images/webcrawler.png"), tr("Web Crawler"), this); webCrawlerAct->setShortcut(tr("Shift+C")); webCrawlerAct->setEnabled(true); webCrawlerAct->setStatusTip(tr("Creates a network from all links found in a given website")); webCrawlerAct->setWhatsThis(tr("Web Crawler \n\nA Web crawler is a built-in bot, which starts with a given URL (website or webpage) to visit. As the algorithm crawls this webpage, it identifies all the links in the page and adds them to a list of URLs (called frontier). Then, all the URLs from the frontier are recursively visited. You must provide maximum recursion level (how many URLs from the frontier will be visited) and maximum running time, along with the initial web address...")); connect(webCrawlerAct, SIGNAL(triggered()), this, SLOT(slotShowWebCrawlerDialog())); /** Edit menu actions */ selectAllAct = new QAction(QIcon(":/images/selectall.png"), tr("Select All"), this); selectAllAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A)); selectAllAct->setStatusTip(tr("Selects all nodes")); selectAllAct->setWhatsThis(tr("Select All\n\nSelects all nodes in the network")); connect(selectAllAct, SIGNAL(triggered()), this, SLOT(slotSelectAll())); selectNoneAct = new QAction(QIcon(":/images/selectnone.png"), tr("Deselect all"), this); selectNoneAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A)); selectNoneAct->setStatusTip(tr("Deselects all nodes")); selectNoneAct->setWhatsThis(tr("Deselect all\n\n Clears the node selection")); connect(selectNoneAct, SIGNAL(triggered()), this, SLOT(slotSelectNone())); findNodeAct = new QAction(QIcon(":/images/find.png"), tr("Find Node"), this); findNodeAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F)); findNodeAct->setStatusTip(tr("Finds and highlights a node by number or label. Press Ctrl+F again to undo.")); findNodeAct->setWhatsThis(tr("Find Node\n\nFinds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node")); connect(findNodeAct, SIGNAL(triggered()), this, SLOT(slotFindNode()) ); addNodeAct = new QAction(QIcon(":/images/add.png"), tr("Add Node"), this); addNodeAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_A)); addNodeAct->setStatusTip(tr("Adds a node")); addNodeAct->setWhatsThis(tr("Add Node\n\nAdds a node to the network")); connect(addNodeAct, SIGNAL(triggered()), this, SLOT(addNode())); removeNodeAct = new QAction(QIcon(":/images/remove.png"),tr("Remove Node"), this); removeNodeAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_Backspace)); //Single key shortcuts with backspace or del do no work in Mac http://goo.gl/7hz7Dx removeNodeAct->setStatusTip(tr("Removes a node")); removeNodeAct->setWhatsThis(tr("Remove Node\n\nRemoves a node from the network")); connect(removeNodeAct, SIGNAL(triggered()), this, SLOT(slotRemoveNode())); propertiesNodeAct = new QAction(QIcon(":/images/properties.png"),tr("Node Properties"), this); propertiesNodeAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_P)); propertiesNodeAct->setStatusTip(tr("Open node properties")); propertiesNodeAct->setWhatsThis(tr("Node Properties\n\nOpens node properties to edit label, size, color, shape etc")); connect(propertiesNodeAct, SIGNAL(triggered()), this, SLOT(slotChangeNodeProperties())); changeAllNodesSizeAct = new QAction(QIcon(":/images/resize.png"), tr("Change all Nodes Size"), this); changeAllNodesSizeAct->setStatusTip(tr("This option lets you change the size of all nodes")); changeAllNodesSizeAct->setWhatsThis(tr("Nodes Size\n\nThis option lets you change the size of all nodes")); connect(changeAllNodesSizeAct, SIGNAL(triggered()), this, SLOT(slotChangeAllNodesSize()) ); changeAllNodesShapeAct = new QAction( tr("Change all Nodes Shape"), this); changeAllNodesShapeAct->setStatusTip(tr("This option lets you change the shape of all nodes")); changeAllNodesShapeAct->setWhatsThis(tr("Nodes Shape\n\nThis option lets you change the shape of all nodes")); connect(changeAllNodesShapeAct, SIGNAL(triggered()), this, SLOT(slotChangeAllNodesShape()) ); changeNumbersSizeAct = new QAction( tr("Change all Numbers Size"), this); changeNumbersSizeAct->setStatusTip(tr("It lets you change the font size of the numbers of all nodes")); changeNumbersSizeAct->setWhatsThis(tr("Numbers Size\n\nChanges the size of the numbers of all nodes")); connect(changeNumbersSizeAct, SIGNAL(triggered()), this, SLOT(slotChangeNumbersSize()) ); changeLabelsSizeAct = new QAction( tr("Change all Labels Size"), this); changeLabelsSizeAct->setStatusTip(tr("You can change the font size of the labels of all nodes")); changeLabelsSizeAct->setWhatsThis(tr("Labels Size\n\nChange the fontsize of the labels of all nodes")); connect(changeLabelsSizeAct, SIGNAL(triggered()), this, SLOT(slotChangeLabelsSize()) ); addEdgeAct = new QAction(QIcon(":/images/plines.png"), tr("Add Edge"),this); addEdgeAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_A)); addEdgeAct->setStatusTip(tr("Adds a directed edge from a node to another")); addEdgeAct->setWhatsThis(tr("Add Edge\n\nAdds a directed edge from a node to another")); connect(addEdgeAct, SIGNAL(triggered()), this, SLOT(slotAddEdge())); removeEdgeAct = new QAction(QIcon(":/images/disconnect.png"), tr("Remove"), this); //removeEdgeAct ->setShortcut(QKeySequence(Qt::SHIFT+Qt::Key_Backspace)); removeEdgeAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_Backspace)); removeEdgeAct->setStatusTip(tr("Removes an Edge")); removeEdgeAct->setWhatsThis(tr("Remove Edge\n\nRemoves an Edge from the network")); connect(removeEdgeAct, SIGNAL(triggered()), this, SLOT(slotRemoveEdge())); changeEdgeLabelAct = new QAction(QIcon(":/images/letters.png"), tr("Change Label"), this); changeEdgeLabelAct->setStatusTip(tr("Changes the Label of an Edge")); changeEdgeLabelAct->setWhatsThis(tr("Change Label\n\nChanges the label of an Edge")); connect(changeEdgeLabelAct, SIGNAL(triggered()), this, SLOT(slotChangeEdgeLabel())); changeEdgeLabelAct->setEnabled(false); changeEdgeColorAct = new QAction(QIcon(":/images/colorize.png"),tr("Change Color"), this); changeEdgeColorAct->setStatusTip(tr("Changes the Color of an Edge")); changeEdgeColorAct->setWhatsThis(tr("Change Color\n\nChanges the Color of an Edge")); connect(changeEdgeColorAct, SIGNAL(triggered()), this, SLOT(slotChangeEdgeColor())); changeEdgeWeightAct = new QAction(tr("Change Weight"), this); changeEdgeWeightAct->setStatusTip(tr("Changes the Weight of an Edge")); changeEdgeWeightAct->setWhatsThis(tr("Change Value\n\nChanges the Weight of an Edge")); connect(changeEdgeWeightAct, SIGNAL(triggered()), this, SLOT(slotChangeEdgeWeight())); filterNodesAct = new QAction(tr("Filter Nodes"), this); filterNodesAct -> setEnabled(false); //filterNodesAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_F)); filterNodesAct->setStatusTip(tr("Filters Nodes of some value out of the network")); filterNodesAct->setWhatsThis(tr("Filter Nodes\n\nFilters Nodes of some value out of the network.")); connect(filterNodesAct, SIGNAL(triggered()), this, SLOT(slotFilterNodes())); filterIsolateNodesAct = new QAction(tr("Filter Isolate Nodes"), this); filterIsolateNodesAct -> setEnabled(true); filterIsolateNodesAct -> setCheckable(true); filterIsolateNodesAct -> setChecked(false); filterIsolateNodesAct -> setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_F)); filterIsolateNodesAct -> setStatusTip(tr("Filters nodes with no edges")); filterIsolateNodesAct -> setWhatsThis(tr("Filter Isolate Nodes\n\n Enables or disables displaying of isolate nodes. Isolate nodes are those with no edges...")); connect(filterIsolateNodesAct, SIGNAL(toggled(bool)), this, SLOT(slotFilterIsolateNodes(bool))); filterEdgesAct = new QAction(tr("Filter Edges by weight"), this); filterEdgesAct -> setEnabled(true); filterEdgesAct -> setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_F)); filterEdgesAct -> setStatusTip(tr("Filters Edges of some weight out of the network")); filterEdgesAct -> setWhatsThis(tr("Filter Edges\n\nFilters Edge of some specific weight out of the network.")); connect(filterEdgesAct , SIGNAL(triggered()), this, SLOT(slotShowFilterEdgesDialog())); changeBackColorAct = new QAction(QIcon(":/images/color.png"), tr("Change Background Color"), this); changeBackColorAct->setStatusTip(tr("Click to change the background color")); changeBackColorAct->setWhatsThis(tr("Background\n\nChanges background color")); connect(changeBackColorAct, SIGNAL(triggered()), this, SLOT(slotBackgroundColor())); changeAllNodesColorAct = new QAction(QIcon(":/images/nodecolor.png"), tr("Change all Nodes Colors"), this); changeAllNodesColorAct->setStatusTip(tr("Click to choose a new color for all nodes.")); changeAllNodesColorAct->setWhatsThis(tr("All Nodes\n\nChanges all nodes color at once.")); connect(changeAllNodesColorAct, SIGNAL(triggered()), this, SLOT(slotAllNodesColor()) ); changeAllNumbersColorAct = new QAction( tr("Change all Numbers Colors"), this); changeAllNumbersColorAct->setStatusTip(tr("Click to change the color of all numbers.")); changeAllNumbersColorAct->setWhatsThis(tr("Numbers\n\nChanges the color of all numbers.")); connect(changeAllNumbersColorAct, SIGNAL(triggered()), this, SLOT(slotAllNumbersColor())); changeAllLabelsColorAct = new QAction( tr("Change all Labels Colors"), this); changeAllLabelsColorAct->setStatusTip(tr("Click to change the color of all node labels.")); changeAllLabelsColorAct->setWhatsThis(tr("Numbers\n\nChanges the color of all node labels.")); connect(changeAllLabelsColorAct, SIGNAL(triggered()), this, SLOT(slotAllLabelsColor())); changeAllEdgesColorAct = new QAction( tr("Change all Edges Colors"), this); changeAllEdgesColorAct->setStatusTip(tr("Click to change the color of all Edges.")); changeAllEdgesColorAct->setWhatsThis(tr("Background\n\nChanges all Edges color")); connect(changeAllEdgesColorAct, SIGNAL(triggered()), this, SLOT(slotAllEdgesColor())); transformNodes2EdgesAct = new QAction( tr("Transform Nodes to Edges"),this); transformNodes2EdgesAct->setStatusTip(tr("Transforms the network so that nodes become Edges and vice versa")); transformNodes2EdgesAct->setWhatsThis(tr("Transform Nodes EdgesAct\n\nTransforms network so that nodes become Edges and vice versa")); connect(transformNodes2EdgesAct, SIGNAL(triggered()), this, SLOT(slotTransformNodes2Edges())); symmetrizeAct= new QAction(QIcon(":/images/symmetrize.png"), tr("Symmetrize Edges"), this); symmetrizeAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_S)); symmetrizeAct->setStatusTip(tr("Makes all edges reciprocal (thus, a symmetric graph).")); symmetrizeAct->setWhatsThis(tr("Symmetrize Edges\n\nTransforms all directed arcs to undirected edges. The result is a symmetric network")); connect(symmetrizeAct, SIGNAL(triggered()), this, SLOT(slotSymmetrize())); /** Layout menu actions */ strongColorationAct = new QAction ( tr("Strong Structural"), this); strongColorationAct -> setStatusTip( tr("Nodes are assigned the same color if they have identical in and out neighborhoods") ); strongColorationAct -> setWhatsThis( tr("Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods")); connect(strongColorationAct, SIGNAL(triggered() ), this, SLOT(slotColorationStrongStructural()) ); regularColorationAct = new QAction ( tr("Regular"), this); regularColorationAct -> setStatusTip( tr("Nodes are assigned the same color if they have " "neighborhoods of the same set of colors") ); regularColorationAct -> setWhatsThis( tr("Click this to colorize nodes; " "Nodes are assigned the same color if they have neighborhoods " "of the same set of colors")); connect(regularColorationAct, SIGNAL(triggered() ), this, SLOT(slotColorationRegular()) );//TODO randLayoutAct = new QAction( tr("Random"),this); randLayoutAct -> setShortcut(Qt::CTRL+Qt::Key_0); randLayoutAct -> setStatusTip(tr("Repositions all nodes in random places")); randLayoutAct -> setWhatsThis(tr("Random Layout\n\n Repositions all nodes in random places")); connect(randLayoutAct, SIGNAL(triggered()), this, SLOT(slotLayoutRandom())); randCircleLayoutAct = new QAction(tr("Random Circles"), this); randCircleLayoutAct -> setShortcut(Qt::CTRL+Qt::ALT+Qt::Key_0); randCircleLayoutAct ->setStatusTip(tr("Repositions the nodes randomly on circles")); randCircleLayoutAct-> setWhatsThis( tr("Random Circles Layout\n\n Repositions the nodes randomly on circles")); connect(randCircleLayoutAct, SIGNAL(triggered()), this, SLOT(slotLayoutCircularRandom())); layoutCircular_DC_Act = new QAction( tr("Degree Centrality"), this); layoutCircular_DC_Act ->setShortcut(tr("Ctrl+Alt+1")); layoutCircular_DC_Act ->setStatusTip( tr("Layout all nodes on concentric circles of radius inversely " "proportional to their Degree Centrality.")); layoutCircular_DC_Act-> setWhatsThis( tr( "Degree Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their Degree Centrality" "Nodes with higher DC score are closer to the centre." ) ); connect(layoutCircular_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex()) ); layoutCircular_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutCircular_CC_Act ->setShortcut(tr("Ctrl+Alt+2")); layoutCircular_CC_Act -> setStatusTip( tr("Layout all nodes on concentric circles of radius inversely " "proportional to their CC index.")); layoutCircular_CC_Act-> setWhatsThis( tr( "Closeness Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their CC index." "Nodes having higher CC score are closer to the centre." )); connect(layoutCircular_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutCircular_IRCC_Act ->setShortcut(tr("Ctrl+Alt+3")); layoutCircular_IRCC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their IRCC index.")); layoutCircular_IRCC_Act-> setWhatsThis( tr( "Influence Range Closeness Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their IRCC index." "Nodes having higher IRCC score are closer to the centre." )); connect(layoutCircular_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutCircular_BC_Act ->setShortcut(tr("Ctrl+Alt+4")); layoutCircular_BC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their BC index.")); layoutCircular_BC_Act-> setWhatsThis( tr( "Betweenness Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their BC index." "Nodes having higher BC score are closer to the centre." )); connect(layoutCircular_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_SC_Act = new QAction( tr("Stress Centrality"), this); layoutCircular_SC_Act ->setShortcut(tr("Ctrl+Alt+5")); layoutCircular_SC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their SC index.")); layoutCircular_SC_Act-> setWhatsThis( tr( "Stress Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their SC index." "Nodes having higher SC score are closer to the centre." )); connect(layoutCircular_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutCircular_EC_Act ->setShortcut(tr("Ctrl+Alt+6")); layoutCircular_EC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their EC index.")); layoutCircular_EC_Act-> setWhatsThis( tr( "Eccentricity Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their EC index." "Nodes having higher EC score are closer to the centre." )); connect(layoutCircular_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_PC_Act = new QAction( tr("Power Centrality"), this); layoutCircular_PC_Act ->setShortcut(tr("Ctrl+Alt+7")); layoutCircular_PC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their PC index.")); layoutCircular_PC_Act-> setWhatsThis( tr( "Power Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their PC index." "Nodes having higher PC score are closer to the centre." )); connect(layoutCircular_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_IC_Act = new QAction( tr("Information Centrality"), this); layoutCircular_IC_Act ->setEnabled(true); layoutCircular_IC_Act ->setShortcut(tr("Ctrl+Alt+8")); layoutCircular_IC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their IC index.")); layoutCircular_IC_Act-> setWhatsThis( tr( "Information Centrality Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their IC index." "Nodes having higher IC score are closer to the centre." )); connect(layoutCircular_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_DP_Act = new QAction( tr("Degree Prestige"), this); layoutCircular_DP_Act ->setShortcut(tr("Ctrl+Alt+I")); layoutCircular_DP_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their DP index.")); layoutCircular_DP_Act-> setWhatsThis( tr( "Degree Prestige Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their DP index." "Nodes having higher DP score are closer to the centre." )); connect(layoutCircular_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutCircular_PRP_Act ->setEnabled(true); layoutCircular_PRP_Act ->setShortcut(tr("Ctrl+Alt+K")); layoutCircular_PRP_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their PRP index.")); layoutCircular_PRP_Act-> setWhatsThis( tr( "PageRank Prestige Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their PRP index." "Nodes having higher PRP score are closer to the centre." )); connect(layoutCircular_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutCircular_PP_Act ->setShortcut(tr("Ctrl+Alt+Y")); layoutCircular_PP_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their PP index.")); layoutCircular_PP_Act-> setWhatsThis( tr( "Proximity Prestige Circular Layout\n\n " "Repositions all nodes on concentric circles of radius " "inversely proportional to their PP index." "Nodes having higher PP score are closer to the centre." )); connect(layoutCircular_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); clearGuidesAct = new QAction(QIcon(":/images/gridlines.png"), tr("Remove Layout GuideLines"), this); clearGuidesAct ->setStatusTip(tr("Removes all layout guideLines from the canvas.")); clearGuidesAct->setWhatsThis(tr("Remove GuideLines\n\n Removes any guidelines (circles or horizontal lines) created for the network layout.")); layoutLevel_DC_Act = new QAction( tr("Degree Centrality"), this); layoutLevel_DC_Act ->setShortcut(tr("Ctrl+Shift+1")); layoutLevel_DC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their DC index.")); layoutLevel_DC_Act-> setWhatsThis( tr( "Degree Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their DC index." "Nodes having higher DC score are closer to the top.\n\n" ) ); connect(layoutLevel_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex()) ); layoutLevel_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutLevel_CC_Act ->setShortcut(tr("Ctrl+Shift+2")); layoutLevel_CC_Act -> setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their CC index.")); layoutLevel_CC_Act-> setWhatsThis( tr( "Closeness Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their CC index." "Nodes having higher CC score are closer to the top.\n\n" "This layout can be computed only for connected graphs. " )); connect(layoutLevel_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutLevel_IRCC_Act ->setShortcut(tr("Ctrl+Shift+3")); layoutLevel_IRCC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their IRCC index.")); layoutLevel_IRCC_Act-> setWhatsThis( tr( "Influence Range Closeness Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their IRCC index." "Nodes having higher IRCC score are closer to the top.\n\n" "This layout can be computed for not connected graphs. " )); connect(layoutLevel_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutLevel_BC_Act ->setShortcut(tr("Ctrl+Shift+4")); layoutLevel_BC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their BC index.")); layoutLevel_BC_Act-> setWhatsThis( tr( "Betweenness Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their BC index." "Nodes having higher BC score are closer to the top." )); connect(layoutLevel_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_SC_Act = new QAction( tr("Stress Centrality"), this); layoutLevel_SC_Act ->setShortcut(tr("Ctrl+Shift+5")); layoutLevel_SC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their SC index.")); layoutLevel_SC_Act-> setWhatsThis( tr( "Stress Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their SC index." "Nodes having higher SC score are closer to the top." )); connect(layoutLevel_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutLevel_EC_Act ->setShortcut(tr("Ctrl+Shift+6")); layoutLevel_EC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their EC index.")); layoutLevel_EC_Act-> setWhatsThis( tr( "Eccentricity Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their EC index." "Nodes having higher EC score are closer to the top." )); connect(layoutLevel_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_PC_Act = new QAction( tr("Power Centrality"), this); layoutLevel_PC_Act ->setShortcut(tr("Ctrl+Shift+7")); layoutLevel_PC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their PC index.")); layoutLevel_PC_Act-> setWhatsThis( tr( "Power Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their PC index." "Nodes having higher PC score are closer to the top." )); connect(layoutLevel_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_IC_Act = new QAction( tr("Information Centrality"), this); layoutLevel_IC_Act ->setEnabled(true); layoutLevel_IC_Act ->setShortcut(tr("Ctrl+Shift+8")); layoutLevel_IC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their IC index.")); layoutLevel_IC_Act-> setWhatsThis( tr( "Information Centrality Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their IC index." "Nodes having higher IC score are closer to the top." )); connect(layoutLevel_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_DP_Act = new QAction( tr("Degree Prestige"), this); layoutLevel_DP_Act ->setShortcut(tr("Ctrl+Shift+I")); layoutLevel_DP_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their DP index.")); layoutLevel_DP_Act-> setWhatsThis( tr( "Degree Prestige Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their DP index." "Nodes having higher DP score are closer to the top." )); connect(layoutLevel_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutLevel_PRP_Act ->setEnabled(true); layoutLevel_PRP_Act ->setShortcut(tr("Ctrl+Shift+K")); layoutLevel_PRP_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their PRP index.")); layoutLevel_PRP_Act-> setWhatsThis( tr( "PageRank Prestige Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their PRP index." "Nodes having higher PRP score are closer to the top." )); connect(layoutLevel_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutLevel_PP_Act ->setEnabled(true); layoutLevel_PP_Act ->setShortcut(tr("Ctrl+Shift+Y")); layoutLevel_PP_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their PP index.")); layoutLevel_PP_Act-> setWhatsThis( tr( "Proximity Prestige Levels Layout\n\n " "Repositions all nodes on horizontal levels of height" "proportional to their PP index." "Nodes having higher PP score are closer to the top." )); connect(layoutLevel_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); springLayoutAct= new QAction(tr("Spring Embedder (Eades)"), this); springLayoutAct->setShortcut(tr("Alt+1")); springLayoutAct->setCheckable(true); springLayoutAct->setChecked(false); springLayoutAct->setStatusTip(tr("All nodes repel each other while the connected ones are attracted as if connected by springs.")); springLayoutAct->setWhatsThis(tr("Spring Embedder Layout\n\n In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. ")); connect(springLayoutAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutSpringEmbedder(bool))); FRLayoutAct= new QAction( tr("Fruchterman-Reingold"), this); FRLayoutAct->setShortcut(tr("Alt+2")); FRLayoutAct->setCheckable(true); FRLayoutAct->setChecked(false); FRLayoutAct->setStatusTip(tr("Repelling forces between all nodes, and attracting forces between adjacent nodes.")); FRLayoutAct->setWhatsThis(tr("Fruchterman-Reingold Layout\n\n Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other.")); connect(FRLayoutAct, SIGNAL(triggered()), this, SLOT(slotLayoutFruchterman())); zoomInAct = new QAction(QIcon(":/images/zoomin.png"), tr("Zoom &in"), this); zoomInAct->setShortcut(Qt::CTRL + Qt::Key_Plus); zoomInAct->setToolTip(tr("Zoom in (Ctrl++)")); zoomInAct->setStatusTip(tr("Zooms inside the actual network.")); zoomInAct->setWhatsThis(tr("Zoom In.\n\nZooms in. What else did you expect?")); zoomOutAct = new QAction(QIcon(":/images/zoomout.png"), tr("Zoom &out"), this); zoomOutAct->setShortcut(Qt::CTRL + Qt::Key_Minus); zoomOutAct->setToolTip(tr("Zoom out (Ctrl+-)")); zoomOutAct->setStatusTip(tr("Zooms out of the actual network.")); zoomOutAct->setWhatsThis(tr("Zoom out.\n\nZooms out. What else did you expect?")); nextRelationAct = new QAction(QIcon(":/images/nextrelation.png"), tr("Next Relation"), this); nextRelationAct->setShortcut(Qt::CTRL + Qt::Key_Right); nextRelationAct->setToolTip(tr("Goto next graph relation (Ctrl+Right)")); nextRelationAct->setStatusTip(tr("Loads the next relation of the network (if any).")); nextRelationAct->setWhatsThis(tr("Next Relation\n\nLoads the next relation of the network (if any)")); prevRelationAct = new QAction(QIcon(":/images/prevrelation.png"), tr("Previous Relation"), this); prevRelationAct->setShortcut(Qt::CTRL + Qt::Key_Left); prevRelationAct->setToolTip( tr("Goto previous graph relation (Ctrl+Left)")); prevRelationAct->setStatusTip( tr("Loads the previous relation of the network (if any).")); prevRelationAct->setWhatsThis( tr("Previous Relation\n\n" "Loads the previous relation of the network (if any)")); addRelationAct = new QAction(QIcon(":/images/addrelation.png"), tr("Add New Relation"), this); addRelationAct->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_N); addRelationAct->setToolTip( tr("Add a new relation to the active graph (Ctrl+Shift+N)")); addRelationAct->setStatusTip( tr("Adds a new relation to the network. " "Nodes will be preserved, edges will be removed. ")); addRelationAct->setWhatsThis( tr("Add New Relation\n\n" "Adds a new relation to the active network. " "Nodes will be preserved, edges will be removed. ")); nodeSizesByOutDegreeAct= new QAction(QIcon(":/images/nodeout.png"), tr("Node sizes by OutDegree"), this); nodeSizesByOutDegreeAct->setShortcut(tr("Alt+3")); nodeSizesByOutDegreeAct-> setStatusTip(tr("Resizes all nodes according to their outDegree.")); nodeSizesByOutDegreeAct ->setWhatsThis(tr("Node sizes by OutDegree) \n\n" "Adjusts the size of each node according to its " "OutDegree. The more out-linked a node is, " "the bigger will appear...")); nodeSizesByOutDegreeAct->setCheckable(true); nodeSizesByOutDegreeAct->setChecked(false); connect(nodeSizesByOutDegreeAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutNodeSizesByOutDegree(bool))); nodeSizesByInDegreeAct= new QAction( QIcon(":/images/nodein.png"),tr("Node sizes by InDegree"), this); nodeSizesByInDegreeAct->setShortcut(tr("Alt+4")); nodeSizesByInDegreeAct->setStatusTip( tr("Resizes all nodes according to their InDegree.")); nodeSizesByInDegreeAct-> setWhatsThis(tr("Node sizes by InDegree) \n\n " "This method adjusts the size of each node according " "to its InDegree. The more in-linked a node is, " "the bigger will appear...")); nodeSizesByInDegreeAct->setCheckable(true); nodeSizesByInDegreeAct->setChecked(false); connect(nodeSizesByInDegreeAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutNodeSizesByInDegree(bool))); /** Analysis menu actions */ symmetryAct = new QAction( QIcon(":/images/symmetry.png"), tr("Symmetry Test"), this); symmetryAct ->setShortcut(tr("Shift+S")); symmetryAct->setStatusTip(tr("Checks whether the network is symmetric or not")); symmetryAct->setWhatsThis( tr("Symmetry\n\n " "Checks whether the network is symmetric or not. \n" "A network is symmetric when all edges are reciprocal, or, " "in mathematical language, when the adjacency matrix is " "symmetric.") ); connect(symmetryAct, SIGNAL(triggered()), this, SLOT(slotCheckSymmetry())); invertAdjMatrixAct = new QAction( QIcon(":/images/symmetry.png"), tr("Invert Adjacency Matrix"), this); invertAdjMatrixAct ->setShortcut(tr("Shift+I")); invertAdjMatrixAct->setStatusTip(tr("Inverts the adjacency matrix")); invertAdjMatrixAct->setWhatsThis(tr("Invert Adjacency Matrix \n\n Inverts the adjacency matrix using linear algebra methods.")); connect(invertAdjMatrixAct, SIGNAL(triggered()), this, SLOT(slotInvertAdjMatrix())); graphDistanceAct = new QAction( QIcon(":/images/distance.png"), tr("Distance"), this ); graphDistanceAct ->setShortcut(tr("Ctrl+G")); graphDistanceAct->setStatusTip( tr("Calculates the length of the shortest path between two nodes...")); graphDistanceAct->setWhatsThis( tr("Distance\n\n " "In graph theory, the distance (geodesic distance) of two " "nodes is the length (number of edges) of the shortest path " "between them.")); connect(graphDistanceAct, SIGNAL(triggered()), this, SLOT(slotGraphDistance())); distanceMatrixAct = new QAction(QIcon(":/images/dm.png"), tr("Distances Matrix"),this); distanceMatrixAct ->setShortcut(tr("Ctrl+Shift+G")); distanceMatrixAct-> setStatusTip( tr("The matrix of geodesic distances between all pair of nodes.") ); distanceMatrixAct-> setWhatsThis( tr("Distances Matrix\n\n" "Calculates and displays the matrix of distances between all " "possible pair of nodes in the social network." "A distances matrix is a n x n square matrix, in which the " "(i,j) element is the distance from node i to node j" "The distance of two nodes is the length of the shortest path between them.") ); connect(distanceMatrixAct, SIGNAL(triggered()), this, SLOT( slotDistancesMatrix() ) ); geodesicsMatrixAct = new QAction(QIcon(":/images/dm.png"), tr("Geodesics Matrix"),this); geodesicsMatrixAct ->setShortcut(tr("Ctrl+Alt+G")); geodesicsMatrixAct->setStatusTip(tr("The number of geodesic paths between each pair of nodes ")); geodesicsMatrixAct->setWhatsThis( tr( "Geodesics Matrix\n\n" "Displays a n x n square matrix, where the (i,j) element " "is the number of geodesics between node i and node j. " "A geodesic of two nodes is the shortest path between them.") ); connect(geodesicsMatrixAct, SIGNAL(triggered()), this, SLOT( slotGeodesicsMatrix()) ); diameterAct = new QAction(QIcon(":/images/diameter.png"), tr("Diameter"),this); diameterAct ->setShortcut(tr("Ctrl+D")); diameterAct->setStatusTip(tr("The diameter of the network.")); diameterAct->setWhatsThis(tr("Diameter\n\n The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network.")); connect(diameterAct, SIGNAL(triggered()), this, SLOT(slotDiameter())); averGraphDistanceAct = new QAction(QIcon(":/images/avdistance.png"), tr("Average Distance"),this); averGraphDistanceAct ->setShortcut(tr("Ctrl+B")); averGraphDistanceAct->setStatusTip(tr("The average shortest path length.")); averGraphDistanceAct->setWhatsThis(tr("Average Distance\n\n This the average length of all shortest paths (geodesics) between the connected pair of nodes of the network.")); connect(averGraphDistanceAct, SIGNAL(triggered()), this, SLOT(slotAverageGraphDistance())); eccentricityAct = new QAction(QIcon(":/images/eccentricity.png"), tr("Eccentricity"),this); eccentricityAct->setShortcut(tr("")); eccentricityAct->setStatusTip(tr("Eccentricity indices for each node and group Eccentricity")); eccentricityAct->setWhatsThis(tr("Eccentricity\n\n The eccentricity or association number of each node i is the largest geodesic distance (i,j) between node i and every other node j. Therefore, it reflects how far, at most, is each node from every other node. \n\nThis index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); connect(eccentricityAct, SIGNAL(triggered()), this, SLOT(slotEccentricity())); connectednessAct = new QAction(QIcon(":/images/distance.png"), tr("Connectedness"), this); connectednessAct ->setShortcut(tr("Ctrl+Shift+C")); connectednessAct->setStatusTip(tr("Checks whether the network is a connected " "graph, a weakly connected digraph or " "a disconnected graph/digraph...")); connectednessAct->setWhatsThis(tr("Connectedness\n\n In graph theory, a " "graph is connected if there is a " "path between every pair of nodes. \n" "A digraph is strongly connected " "if there the a path from i to j and " "from j to i for all pairs (i,j).\n" "A digraph is weakly connected if at least " "a pair of nodes are joined by a semipath.\n" "A digraph or a graph is disconnected if " "at least one node is isolate." )); connect(connectednessAct, SIGNAL(triggered()), this, SLOT(slotConnectedness())); walksAct = new QAction(QIcon(":/images/walk.png"), tr("Walks of a given length"),this); walksAct->setShortcut(tr("Ctrl+W")); walksAct->setStatusTip(tr("The number of walks of a given length between any nodes.")); walksAct->setWhatsThis(tr("Walks of a given length\n\n A walk is a sequence of alternating vertices and edges such as v0e1, v1e2, v2e3, …, ekvk, where each edge, ei is defined as ei = {vi-1, vi}. This function counts the number of walks of a given length between each pair of nodes, by studying the powers of the sociomatrix.\n ")); connect(walksAct, SIGNAL(triggered()), this, SLOT(slotWalksOfGivenLength() ) ); totalWalksAct = new QAction(QIcon(":/images/walk.png"), tr("Total Walks"),this); totalWalksAct->setShortcut(tr("Ctrl+Shift+W")); totalWalksAct->setStatusTip(tr("Calculates the total number of walks of every possible length between all nodes")); totalWalksAct->setWhatsThis(tr("Total Walks\n\n A walk is a sequence of alternating vertices and edges such as v0e1, v1e2, v2e3, …, ekvk, where each edge, ei is defined as ei = {vi-1, vi}. This function counts the number of walks of any length between each pair of nodes, by studying the powers of the sociomatrix\n ")); connect(totalWalksAct, SIGNAL(triggered()), this, SLOT(slotTotalWalks() ) ); reachabilityMatrixAct = new QAction(QIcon(":/images/walk.png"), tr("Reachability Matrix"),this); reachabilityMatrixAct->setShortcut(tr("Ctrl+Shift+R")); reachabilityMatrixAct->setStatusTip(tr("Calculates the Reachability Matrix for the loaded network.")); reachabilityMatrixAct->setWhatsThis(tr("Reachability Matrix\n\n Calculates the reachability matrix XR of the graph where the {i,j} element is 1 if the vertices i and j are reachable. \n\n Actually, this just checks whether the corresponding element of Distances matrix is not zero.\n ")); connect(reachabilityMatrixAct, SIGNAL(triggered()), this, SLOT(slotReachabilityMatrix() ) ); cliquesAct = new QAction(QIcon(":/images/clique.png"), tr("Clique Census (clique number <= 4) "),this); cliquesAct->setShortcut(tr("Ctrl+T")); cliquesAct->setStatusTip(tr("Computes a partial clique census report (for cliques up to 4 vertices).")); cliquesAct->setWhatsThis(tr("Clique Census\n\n Computes aggregate counts of cliques (up to clique number 4), along with disaggregation by vertex and co-membership information. ")); connect(cliquesAct, SIGNAL(triggered()), this, SLOT(slotCliqueCensus() ) ); clusteringCoefAct = new QAction(QIcon(":/images/clique.png"), tr("Clustering Coefficient"),this); clusteringCoefAct ->setShortcut(tr("Ctrl+C")); clusteringCoefAct->setStatusTip(tr("The average Clustering Coefficient of the network.")); clusteringCoefAct->setWhatsThis(tr("Clustering Coefficient\n\n The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. \n ")); connect(clusteringCoefAct, SIGNAL(triggered()), this, SLOT(slotClusteringCoefficient() ) ); triadCensusAct = new QAction(QIcon(":/images/triad.png"), tr("Triad Census"),this); triadCensusAct->setShortcut(tr("Ctrl+Shift+T")); triadCensusAct->setStatusTip(tr("Conducts a triad census for the active network.")); triadCensusAct->setWhatsThis(tr("Triad Census\n\n A triad census counts all the different kinds of observed triads within a network and codes them according to their number of mutual, asymmetric and non-existent dyads. \n ")); connect(triadCensusAct, SIGNAL(triggered()), this, SLOT(slotTriadCensus() ) ); cDegreeAct = new QAction(tr("Degree Centrality (DC)"),this); cDegreeAct->setShortcut(tr("Ctrl+1")); cDegreeAct ->setStatusTip(tr("Degree Centrality indices and group Degree Centralization.")); cDegreeAct ->setWhatsThis( tr( "Degree Centrality (DC)\n\n " "For each node v, the DC index is the number of edges " "attached to it (in undirected graphs) or the total number " "of arcs (outLinks) starting from it (in digraphs).\n" "This is often considered a measure of actor activity. \n\n" "This index can be calculated in both graphs and digraphs " "but is usually best suited for undirected graphs. " "It can also be calculated in weighted graphs. " "In weighted relations, DC is the sum of weights of all " "edges/outLinks attached to v.")); connect(cDegreeAct, SIGNAL(triggered()), this, SLOT(slotCentralityDegree())); cClosenessAct = new QAction(tr("Closeness Centrality (CC)"), this); cClosenessAct->setShortcut(tr("Ctrl+2")); cClosenessAct ->setStatusTip( tr( "Closeness Centrality indices and group Closeness Centralization.")); cClosenessAct ->setWhatsThis( tr("Closeness Centrality (CC)\n\n " "For each node v, CC the inverse sum of " "the shortest distances between v and every other node. CC is " "interpreted as the ability to access information through the " "\"grapevine\" of network members. Nodes with high closeness " "centrality are those who can reach many other nodes in few steps. " "\n\nThis index can be calculated in both graphs and digraphs. " "It can also be calculated in weighted graphs although the weight of " "each edge (v,u) in E is always considered to be 1. ")); connect(cClosenessAct, SIGNAL(triggered()), this, SLOT(slotCentralityCloseness())); cInfluenceRangeClosenessAct = new QAction(tr("Influence Range Closeness Centrality (IRCC)"), this); cInfluenceRangeClosenessAct->setShortcut(tr("Ctrl+3")); cInfluenceRangeClosenessAct ->setStatusTip( tr("Closeness Centrality indices focusing on how proximate each node is" "to the nodes in its influence range")); cInfluenceRangeClosenessAct ->setWhatsThis( tr("Influence Range Closeness Centrality (IRCC)\n\n " "For each node v, IRCC is the standardized inverse average distance " "between v and every reachable node.\n" "This improved CC index is optimized for graphs and directed graphs which " "are not strongly connected. Unlike the ordinary CC, which is the inverted " "sum of distances from node v to all others (thus undefined if a node is isolated " "or the digraph is not strongly connected), IRCC considers only " "distances from node v to nodes in its influence range J (nodes reachable from v). " "The IRCC formula used is the ratio of the fraction of nodes reachable by v " "(|J|/(n-1)) to the average distance of these nodes from v (sum(d(v,j))/|J|")); connect(cInfluenceRangeClosenessAct, SIGNAL(triggered()), this, SLOT(slotCentralityClosenessInfluenceRange())); cBetweennessAct = new QAction(tr("Betweenness Centrality (BC)"), this); cBetweennessAct->setShortcut(tr("Ctrl+4")); cBetweennessAct->setWhatsThis(tr("Betweenness Centrality (BC)\n\n For each node v, BC is the ratio of all geodesics between pairs of nodes which run through v. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. A node which lies between many others is assumed to have a higher likelihood of being able to control information flow in the network. \n\n Note that betweenness centrality assumes that all geodesics have equal weight or are equally likely to be chosen for the flow of information between any two nodes. This is reasonable only on \"regular\" networks where all nodes have similar degrees. On networks with significant degree variance you might want to try informational centrality instead. \n\nThis index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); cBetweennessAct->setStatusTip(tr("Betweenness Centrality indices and group Betweenness Centralization.")); connect(cBetweennessAct, SIGNAL(triggered()), this, SLOT(slotCentralityBetweenness())); cStressAct = new QAction(tr("Stress Centrality (SC)"), this); cStressAct->setShortcut(tr("Ctrl+5")); cStressAct->setStatusTip(tr("Stress Centrality indices and group Stress Centralization.")); cStressAct->setWhatsThis(tr("Stress Centrality (SC)\n\n For each node v, SC is the total number of geodesics between all other nodes which run through v. A node with high SC is considered 'stressed', since it is traversed by a high number of geodesics. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality. \n\nThis index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); connect(cStressAct, SIGNAL(triggered()), this, SLOT(slotCentralityStress())); cEccentAct = new QAction(tr("Eccentricity Centrality (EC)"), this); cEccentAct->setShortcut(tr("Ctrl+6")); cEccentAct->setStatusTip(tr("Eccentricity Centrality indices for each node.")); cEccentAct->setWhatsThis( tr("Eccentricity Centrality (EC)\n\n For each node i, " "the EC is the inverse of the maximum geodesic distance " "of that v to all other nodes in the network. \n" "Nodes with high EC have short distances to all other nodes " "This index can be calculated in both graphs and digraphs " "but is usually best suited for undirected graphs. " "It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); connect(cEccentAct, SIGNAL(triggered()), this, SLOT(slotCentralityEccentricity())); cPowerAct = new QAction(tr("Power Centrality (PC)"), this); cPowerAct->setShortcut(tr("Ctrl+7")); cPowerAct->setStatusTip(tr("Calculate and display Power Centrality indices (aka Gil-Schmidt Power Centrality) and group Power Centralization")); cPowerAct->setWhatsThis(tr("Power Centrality (PC)\n\n For each node v, this index sums its degree (with weight 1), with the size of the 2nd-order neighbourhood (with weight 2), and in general, with the size of the kth order neighbourhood (with weight k). Thus, for each node in the network the most important other nodes are its immediate neighbours and then in decreasing importance the nodes of the 2nd-order neighbourhood, 3rd-order neighbourhood etc. For each node, the sum obtained is normalised by the total numbers of nodes in the same component minus 1. Power centrality has been devised by Gil-Schmidt. \n\nThis index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1 (therefore not considered).")); connect(cPowerAct, SIGNAL(triggered()), this, SLOT(slotCentralityPower())); cInformationAct = new QAction(tr("Information Centrality (IC)"), this); cInformationAct->setShortcut(tr("Ctrl+8")); cInformationAct->setEnabled(true); cInformationAct->setStatusTip(tr("Calculate and display Information Centrality indices and group Information Centralization")); cInformationAct->setWhatsThis(tr("Information Centrality (IC)\n\n Information centrality counts all paths between nodes weighted by strength of tie and distance. This centrality measure developed by Stephenson and Zelen (1989) focuses on how information might flow through many different paths. \n\nThis index should be calculated only for graphs. \n\n Note: To compute this index, SocNetV drops all isolated nodes.")); connect(cInformationAct, SIGNAL(triggered()), this, SLOT(slotCentralityInformation())); cInDegreeAct = new QAction(tr("Degree Prestige (DP)"), this); cInDegreeAct->setStatusTip(tr("Degree Prestige (InDegree) indices ")); cInDegreeAct->setShortcut(tr("Ctrl+I")); cInDegreeAct->setWhatsThis(tr("InDegree (Degree Prestige)\n\n For each node k, this the number of arcs ending at k. Nodes with higher in-degree are considered more prominent among others. In directed graphs, this index measures the prestige of each node/actor. Thus it is called Degree Prestige. Nodes who are prestigious tend to receive many nominations or choices (in-links). The largest the index is, the more prestigious is the node. \n\nThis index can be calculated only for digraphs. In weighted relations, DP is the sum of weights of all arcs/inLinks ending at node v.")); connect(cInDegreeAct, SIGNAL(triggered()), this, SLOT(slotPrestigeDegree())); cPageRankAct = new QAction(tr("PageRank Prestige (PRP)"), this); cPageRankAct->setShortcut(tr("Ctrl+K")); cPageRankAct->setEnabled(true); cPageRankAct->setStatusTip(tr("Calculate and display PageRank Prestige")); cPageRankAct->setWhatsThis(tr("PageRank Prestige\n\n An importance ranking for each node based on the link structure of the network. PageRank, developed by Page and Brin (1997), focuses on how nodes are connected to each other, treating each edge from a node as a citation/backlink/vote to another. In essence, for each node PageRank counts all backlinks to it, but it does so by not counting all edges equally while it normalizes each edge from a node by the total number of edges from it. PageRank is calculated iteratively and it corresponds to the principal eigenvector of the normalized link matrix. \n\nThis index can be calculated in both graphs and digraphs but is usually best suited for directed graphs since it is a prestige measure. It can also be calculated in weighted graphs. In weighted relations, each backlink to a node v from another node u is considered to have weight=1 but it is normalized by the sum of outLinks weights (outDegree) of u. Therefore, nodes with high outLink weights give smaller percentage of their PR to node v.")); connect(cPageRankAct, SIGNAL(triggered()), this, SLOT(slotPrestigePageRank())); cProximityPrestigeAct = new QAction(tr("Proximity Prestige (PP)"), this); cProximityPrestigeAct->setShortcut(tr("Ctrl+Y")); cProximityPrestigeAct->setEnabled(true); cProximityPrestigeAct->setStatusTip(tr("Calculate and display Proximity Prestige (digraphs only)")); cProximityPrestigeAct ->setWhatsThis( tr("Proximity Prestige (PP) \n\n " "This index measures how proximate a node v is to the nodes " "in its influence domain I (the influence domain I of a node " "is the number of other nodes that can reach it).\n " "In PP calculation, proximity is based on distances to rather " "than distances from node v. \n" "To put it simply, in PP what matters is how close are all " "the other nodes to node v. \n\n" "The algorithm takes the average distance to node v of all " "nodes in its influence domain, standardizes it by " "multiplying with (N-1)/I and takes its reciprocal. " "In essence, the formula SocNetV uses to calculate PP " "is the ratio of the fraction of nodes that can reach node v, " "to the average distance of that nodes to v: \n" "PP = (I/(N-1))/(sum{d(u,v)}/I) \n" "where the sum is over all nodes in I.")); connect(cProximityPrestigeAct, SIGNAL(triggered()), this, SLOT(slotPrestigeProximity())); /** Options menu actions */ displayNodeNumbersAct = new QAction( tr("Display Numbers"), this ); displayNodeNumbersAct->setStatusTip(tr("Toggles displaying of node numbers")); displayNodeNumbersAct->setWhatsThis(tr("Display Numbers\n\nEnables/disables node numbers")); displayNodeNumbersAct->setCheckable (true); displayNodeNumbersAct->setChecked(true); connect(displayNodeNumbersAct, SIGNAL(toggled(bool)), this, SLOT(slotDisplayNodeNumbers(bool))); displayNodeLabelsAct= new QAction(tr("Display Labels"), this ); displayNodeLabelsAct->setStatusTip(tr("Toggles displaying of node labels")); displayNodeLabelsAct->setWhatsThis(tr("Display Labels\n\nEnables/disables node labels")); displayNodeLabelsAct->setCheckable (true); displayNodeLabelsAct->setChecked(false); connect(displayNodeLabelsAct, SIGNAL(toggled(bool)), this, SLOT(slotDisplayNodeLabels(bool))); displayNumbersInsideNodesAct= new QAction(tr("Display Numbers Inside Nodes"), this ); displayNumbersInsideNodesAct->setStatusTip(tr("Toggles displaying numbers inside nodes")); displayNumbersInsideNodesAct->setWhatsThis(tr("Display Numbers Inside Nodes\n\nTurns on/off displaying nodenumbers inside nodes")); displayNumbersInsideNodesAct->setCheckable (true); displayNumbersInsideNodesAct->setChecked(false); connect(displayNumbersInsideNodesAct, SIGNAL(toggled(bool)), this, SLOT(slotDisplayNumbersInsideNodes(bool))); displayEdgesAct = new QAction(tr("Display Edges"), this); displayEdgesAct->setStatusTip(tr("Toggle to display or not Edges")); displayEdgesAct->setWhatsThis(tr("Display Edges\n\nClick to enable or disable displaying of Edges")); displayEdgesAct->setCheckable(true); displayEdgesAct->setChecked(true); connect(displayEdgesAct, SIGNAL(toggled(bool)), this, SLOT(slotDisplayEdges(bool)) ); displayEdgesWeightNumbersAct = new QAction(tr("Display Edge Weights"), this); displayEdgesWeightNumbersAct->setStatusTip(tr("Toggles displaying of numbers of Edges weights")); displayEdgesWeightNumbersAct->setWhatsThis(tr("Display Weight Numbers\n\nClick to enable or disable displaying numbers of Edges weight")); displayEdgesWeightNumbersAct->setCheckable(true); displayEdgesWeightNumbersAct->setChecked(false); connect(displayEdgesWeightNumbersAct, SIGNAL(toggled(bool)), this, SLOT(slotDisplayEdgesWeightNumbers(bool)) ); considerEdgeWeightsAct = new QAction(tr("Consider Weights in calculcations"), this); considerEdgeWeightsAct-> setStatusTip( tr("Toggles considering Edge weights during calculations (i.e. distances, centrality, etc)")); considerEdgeWeightsAct-> setWhatsThis( tr("Display Weight Numbers\n\n" "Click to enable or disable considering edge weights during " "calculations (i.e. distances, centrality, etc)")); considerEdgeWeightsAct->setCheckable(true); considerEdgeWeightsAct->setChecked(false); connect(considerEdgeWeightsAct, SIGNAL(triggered(bool)), this, SLOT(slotConsiderEdgeWeights(bool)) ); displayEdgesArrowsAct = new QAction( tr("Display Arrows"),this); displayEdgesArrowsAct->setStatusTip(tr("Toggles displaying of arrows on edges")); displayEdgesArrowsAct->setWhatsThis(tr("Display Arrows\n\nClick to enable or disable displaying of arrows on edges")); displayEdgesArrowsAct->setCheckable(true); displayEdgesArrowsAct->setChecked(true); connect(displayEdgesArrowsAct, SIGNAL(toggled(bool)), this, SLOT(slotDisplayEdgesArrows(bool)) ); drawEdgesWeightsAct = new QAction( tr("Thickness=Weight"), this); drawEdgesWeightsAct->setStatusTip(tr("Draws edges as thick as their weights (if specified)")); drawEdgesWeightsAct->setWhatsThis(tr("Draw As Thick As Weights\n\nClick to toggle having all edges as thick as their weight (if specified)")); drawEdgesWeightsAct->setCheckable(true); drawEdgesWeightsAct->setChecked(false); drawEdgesWeightsAct->setEnabled(false); connect(drawEdgesWeightsAct, SIGNAL(toggled(bool)), this, SLOT(slotDrawEdgesThickAsWeights()) ); drawEdgesBezier = new QAction( tr("Bezier Curves"), this); drawEdgesBezier->setStatusTip(tr("Draws Edges as Bezier curves")); drawEdgesBezier->setWhatsThis(tr("Edges Bezier\n\nEnables/Disables drawing Edges as Bezier curves.")); drawEdgesBezier->setCheckable(true); drawEdgesBezier->setChecked (false); drawEdgesBezier->setEnabled(false); connect(drawEdgesBezier, SIGNAL(toggled(bool)), this, SLOT(slotDrawEdgesBezier(bool)) ); /** Options > View menu actions */ antialiasingAct = new QAction(tr("Anti-Aliasing"), this); antialiasingAct ->setShortcut(tr("F8")); antialiasingAct ->setStatusTip(tr("Enables/disables anti-aliasing")); antialiasingAct ->setWhatsThis(tr("Enable or disable Anti-Aliasing\n\n Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed...")); antialiasingAct ->setCheckable(true); antialiasingAct ->setChecked (true); connect(antialiasingAct , SIGNAL(toggled(bool)), this, SLOT(slotAntialiasing(bool))); showProgressBarAct = new QAction(tr("Progress Bars"), this); showProgressBarAct ->setShortcut(tr("F10")); showProgressBarAct->setStatusTip(tr("Enables/disables Progress Bars")); showProgressBarAct->setWhatsThis(tr("Enable or disable Progress Bars\n\nProgress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation.")); showProgressBarAct->setCheckable(true); showProgressBarAct->setChecked (true); connect(showProgressBarAct, SIGNAL(toggled(bool)), this, SLOT(slotShowProgressBar(bool))); printDebugAct = new QAction(tr("Debug Messages"), this); printDebugAct ->setShortcut(tr("F9")); printDebugAct->setStatusTip(tr("Enables/disables printing debug messages to stdout")); printDebugAct->setWhatsThis(tr("Enables or disable Debug Messages\n\nPrinting debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.")); printDebugAct->setCheckable(true); printDebugAct->setChecked (false); printDebug=false; connect(printDebugAct, SIGNAL(toggled(bool)), this, SLOT(slotPrintDebug(bool))); viewToolBar = new QAction(tr("Toolbar"), this); viewToolBar->setStatusTip(tr("Enables/disables the toolbar")); viewToolBar->setWhatsThis(tr("Enable or disable Toolbar\n\nThe toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like...")); viewToolBar->setCheckable(true); viewToolBar->setChecked(true); connect(viewToolBar, SIGNAL(toggled(bool)), this, SLOT(slotViewToolBar(bool))); viewStatusBar = new QAction(tr("Statusbar"), this); viewStatusBar->setStatusTip(tr("Enables/disables the statusbar")); viewStatusBar->setWhatsThis(tr("Enable or disable Statusbar\n\nThe statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it...")); viewStatusBar->setCheckable(true); viewStatusBar->setChecked(true); connect(viewStatusBar, SIGNAL(toggled(bool)), this, SLOT(slotViewStatusBar(bool))); backgroundImageAct = new QAction(tr("Background Image"), this); backgroundImageAct->setStatusTip(tr("Enables/disables displaying a user-defined custom image in the background")); backgroundImageAct->setWhatsThis(tr("Enable or disable background image\n\n If you enable it, you will be asked for a image file, which will be displayed in the background instead of plain color..")); backgroundImageAct->setCheckable(true); backgroundImageAct->setChecked(false); connect(backgroundImageAct, SIGNAL(toggled(bool)), this, SLOT(slotBackgroundImage(bool))); /** Help menu actions */ helpApp = new QAction(QIcon(":/images/help.png"), tr("Manual"), this); helpApp ->setShortcut(tr("F1")); helpApp->setStatusTip(tr("Read the manual...")); helpApp->setWhatsThis(tr("Manual\n\nDisplays the documentation of SocNetV")); connect(helpApp, SIGNAL(triggered()), this, SLOT(slotHelp())); tipsApp = new QAction(tr("Tip of the Day"), this); tipsApp->setStatusTip(tr("Read useful tips")); tipsApp->setWhatsThis(tr("Quick Tips\n\nDisplays some useful and quick tips")); connect(tipsApp, SIGNAL(triggered()), this, SLOT(slotTips())); helpAboutApp = new QAction(tr("About SocNetV"), this); helpAboutApp->setStatusTip(tr("About SocNetV")); helpAboutApp->setWhatsThis(tr("About\n\nBasic information about SocNetV")); connect(helpAboutApp, SIGNAL(triggered()), this, SLOT(slotHelpAbout())); helpAboutQt = new QAction(tr("About Qt"), this); helpAboutQt->setStatusTip(tr("About Qt")); helpAboutQt->setWhatsThis(tr("About\n\nAbout Qt")); connect(helpAboutQt, SIGNAL(triggered()), this, SLOT(slotAboutQt() ) ); } /** Creates and populates the MenuBar */ void MainWindow::initMenuBar() { /** menuBar entry networkMenu */ networkMenu = menuBar()->addMenu(tr("&Network")); networkMenu -> addAction(fileNew); networkMenu -> addAction(fileOpen); importSubMenu = new QMenu(tr("Import ...")); importSubMenu -> setIcon(QIcon(":/images/import.png")); importSubMenu -> addAction(importPajek); importSubMenu -> addAction(importSM); importSubMenu -> addAction(importTwoModeSM); importSubMenu -> addAction(importList); importSubMenu -> addAction(importDL); importSubMenu -> addAction(importDot); networkMenu ->addMenu (importSubMenu); networkMenu -> addSeparator(); networkMenu -> addAction (openTextEditorAct); networkMenu -> addAction (viewNetworkFileAct); networkMenu -> addSeparator(); networkMenu -> addAction (viewSociomatrixAct); networkMenu -> addSeparator(); networkMenu -> addAction (recreateDataSetAct); networkMenu -> addSeparator(); randomNetworkMenu = new QMenu(tr("Create Random Network...")); randomNetworkMenu -> setIcon(QIcon(":/images/random.png")); networkMenu ->addMenu (randomNetworkMenu); randomNetworkMenu -> addAction (createScaleFreeRandomNetworkAct); randomNetworkMenu -> addAction (createSmallWorldRandomNetworkAct); randomNetworkMenu -> addAction (createErdosRenyiRandomNetworkAct ); // createGaussianRandomNetworkAct -> addTo(randomNetworkMenu); randomNetworkMenu -> addAction (createLatticeNetworkAct); randomNetworkMenu -> addAction (createRegularRandomNetworkAct); networkMenu->addSeparator(); networkMenu -> addAction(webCrawlerAct); networkMenu -> addSeparator(); networkMenu -> addAction(fileSave); networkMenu -> addAction(fileSaveAs); networkMenu -> addSeparator(); exportSubMenu = networkMenu -> addMenu(tr("Export...")); exportSubMenu -> addAction (exportBMP); exportSubMenu -> addAction (exportPNG); exportSubMenu -> addAction (exportPDF); exportSubMenu -> addSeparator(); exportSubMenu -> addAction (exportSM); exportSubMenu -> addAction (exportPajek); // exportList->addTo(exportSubMenu); // exportDL->addTo(exportSubMenu); // exportGW->addTo(exportSubMenu); networkMenu -> addSeparator(); networkMenu -> addAction(printNetwork); networkMenu -> addSeparator(); networkMenu -> addAction(fileClose); networkMenu -> addAction(fileQuit); /** menuBar entry editMenu */ editMenu = menuBar()->addMenu(tr("&Edit")); editNodeMenu = new QMenu(tr("Node...")); editNodeMenu -> setIcon(QIcon(":/images/node.png")); editMenu -> addMenu ( editNodeMenu ); editNodeMenu -> addAction (selectAllAct); editNodeMenu -> addAction (selectNoneAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (findNodeAct); editNodeMenu -> addAction (addNodeAct); editNodeMenu -> addAction (removeNodeAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (propertiesNodeAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (changeAllNodesSizeAct); editNodeMenu -> addAction (changeAllNodesShapeAct); editNodeMenu -> addAction (changeNumbersSizeAct); editNodeMenu -> addAction (changeLabelsSizeAct); editEdgeMenu = new QMenu(tr("Edge...")); editEdgeMenu -> setIcon(QIcon(":/images/line.png")); editMenu-> addMenu (editEdgeMenu); editEdgeMenu -> addAction(addEdgeAct); editEdgeMenu -> addAction(removeEdgeAct); editEdgeMenu ->addSeparator(); editEdgeMenu -> addAction(changeEdgeLabelAct); editEdgeMenu -> addAction(changeEdgeColorAct); editEdgeMenu -> addAction(changeEdgeWeightAct); editEdgeMenu ->addSeparator(); // transformNodes2EdgesAct -> addTo (editMenu); editEdgeMenu -> addAction (symmetrizeAct); editMenu ->addSeparator(); filterMenu = new QMenu ( tr("Filter...")); filterMenu -> setIcon(QIcon(":/images/filter.png")); editMenu ->addMenu(filterMenu); filterMenu -> addAction(filterNodesAct ); filterMenu -> addAction(filterIsolateNodesAct ); filterMenu -> addAction(filterEdgesAct ); editNodeMenu -> addSeparator(); colorOptionsMenu=new QMenu(tr("Colors")); colorOptionsMenu -> setIcon(QIcon(":/images/colorize.png")); editMenu -> addMenu (colorOptionsMenu); colorOptionsMenu -> addAction (changeBackColorAct); colorOptionsMenu -> addAction (changeAllNodesColorAct); colorOptionsMenu -> addAction (changeAllEdgesColorAct); colorOptionsMenu -> addAction (changeAllNumbersColorAct); colorOptionsMenu -> addAction (changeAllLabelsColorAct); /** menuBar entry layoutMenu */ layoutMenu = menuBar()->addMenu(tr("&Layout")); // colorationMenu = new QPopupMenu(); // layoutMenu -> insertItem (tr("Colorization"), colorationMenu); // strongColorationAct -> addTo(colorationMenu); // regularColorationAct-> addTo(colorationMenu); // layoutMenu->insertSeparator(); randomLayoutMenu = new QMenu(tr("Random...")); layoutMenu -> addMenu (randomLayoutMenu ); randomLayoutMenu -> addAction(randLayoutAct); randomLayoutMenu -> addAction( randCircleLayoutAct ); layoutMenu->addSeparator(); circleLayoutMenu = new QMenu(tr("Circular by prominence index...")); circleLayoutMenu -> setIcon(QIcon(":/images/circular.png")); layoutMenu -> addMenu (circleLayoutMenu); circleLayoutMenu -> addAction (layoutCircular_DC_Act); circleLayoutMenu -> addAction (layoutCircular_CC_Act); circleLayoutMenu -> addAction (layoutCircular_IRCC_Act); circleLayoutMenu -> addAction (layoutCircular_BC_Act); circleLayoutMenu -> addAction (layoutCircular_SC_Act); circleLayoutMenu -> addAction (layoutCircular_EC_Act); circleLayoutMenu -> addAction (layoutCircular_PC_Act); circleLayoutMenu -> addAction (layoutCircular_IC_Act); circleLayoutMenu -> addAction (layoutCircular_DP_Act); circleLayoutMenu -> addAction (layoutCircular_PRP_Act); circleLayoutMenu -> addAction (layoutCircular_PP_Act); levelLayoutMenu = new QMenu (tr("On levels by prominence index...")); levelLayoutMenu -> setIcon(QIcon(":/images/net3.png")); layoutMenu -> addMenu (levelLayoutMenu); levelLayoutMenu -> addAction (layoutLevel_DC_Act); levelLayoutMenu -> addAction (layoutLevel_CC_Act); levelLayoutMenu -> addAction (layoutLevel_IRCC_Act); levelLayoutMenu -> addAction (layoutLevel_BC_Act); levelLayoutMenu -> addAction (layoutLevel_SC_Act); levelLayoutMenu -> addAction (layoutLevel_EC_Act); levelLayoutMenu -> addAction (layoutLevel_PC_Act); levelLayoutMenu -> addAction (layoutLevel_IC_Act); levelLayoutMenu -> addAction (layoutLevel_DP_Act); levelLayoutMenu -> addAction (layoutLevel_PRP_Act); levelLayoutMenu -> addAction (layoutLevel_PP_Act); layoutMenu->addSeparator(); physicalLayoutMenu = new QMenu (tr("Force-Directed...")); physicalLayoutMenu -> setIcon(QIcon(":/images/force.png")); layoutMenu -> addMenu (physicalLayoutMenu); physicalLayoutMenu -> addAction (springLayoutAct); physicalLayoutMenu -> addAction (FRLayoutAct); layoutMenu->addSeparator(); layoutMenu->addAction(nodeSizesByOutDegreeAct); layoutMenu->addAction(nodeSizesByInDegreeAct); layoutMenu->addSeparator(); layoutMenu -> addAction (clearGuidesAct); /** menuBar entry: statistics menu */ statMenu = menuBar()->addMenu(tr("&Analyze")); statMenu -> addAction (symmetryAct); statMenu -> addAction (invertAdjMatrixAct); // statMenu -> addAction (netDensity); statMenu -> addSeparator(); statMenu -> addAction (graphDistanceAct); statMenu -> addAction (averGraphDistanceAct); statMenu -> addAction (distanceMatrixAct); statMenu -> addAction (geodesicsMatrixAct); statMenu -> addAction (eccentricityAct); statMenu -> addAction (diameterAct); statMenu -> addSeparator(); statMenu -> addAction(connectednessAct); statMenu -> addAction (walksAct); statMenu -> addAction (totalWalksAct); statMenu -> addAction (reachabilityMatrixAct); statMenu -> addSeparator(); statMenu -> addAction (cliquesAct); statMenu -> addAction (clusteringCoefAct); statMenu -> addSeparator(); statMenu -> addAction (triadCensusAct); statMenu->addSeparator(); centrlMenu = new QMenu(tr("Centrality and Prestige indices...")); centrlMenu -> setIcon(QIcon(":/images/centrality.png")); statMenu->addMenu(centrlMenu); centrlMenu -> addSection(QIcon(":/images/centrality.png"), tr("Centrality")); centrlMenu -> addAction (cDegreeAct); centrlMenu -> addAction (cClosenessAct); centrlMenu -> addAction (cInfluenceRangeClosenessAct); centrlMenu -> addAction (cBetweennessAct); centrlMenu -> addAction (cStressAct); centrlMenu -> addAction (cEccentAct); centrlMenu -> addAction (cPowerAct); centrlMenu -> addAction (cInformationAct); centrlMenu -> addSection(QIcon(":/images/prestige.png"), tr("Prestige")); centrlMenu -> addAction (cInDegreeAct); centrlMenu -> addAction (cPageRankAct); centrlMenu -> addAction (cProximityPrestigeAct); /** menuBar entry optionsMenu */ optionsMenu = menuBar()->addMenu(tr("&Options")); nodeOptionsMenu=new QMenu(tr("Nodes...")); nodeOptionsMenu -> setIcon(QIcon(":/images/nodes.png")); optionsMenu -> addMenu (nodeOptionsMenu); nodeOptionsMenu -> addAction (displayNodeNumbersAct); nodeOptionsMenu -> addAction (displayNodeLabelsAct); nodeOptionsMenu -> addAction (displayNumbersInsideNodesAct); edgeOptionsMenu=new QMenu(tr("Edges...")); edgeOptionsMenu -> setIcon(QIcon(":/images/line.png")); optionsMenu -> addMenu (edgeOptionsMenu); edgeOptionsMenu -> addAction (displayEdgesAct); edgeOptionsMenu -> addAction (displayEdgesWeightNumbersAct); edgeOptionsMenu -> addAction (considerEdgeWeightsAct); edgeOptionsMenu -> addAction (displayEdgesArrowsAct ); edgeOptionsMenu -> addSeparator(); edgeOptionsMenu -> addAction (drawEdgesWeightsAct); edgeOptionsMenu -> addAction (drawEdgesBezier); viewOptionsMenu = new QMenu (tr("&View...")); viewOptionsMenu -> setIcon(QIcon(":/images/view.png")); optionsMenu -> addMenu (viewOptionsMenu); viewOptionsMenu-> addAction (backgroundImageAct); viewOptionsMenu-> addAction (antialiasingAct); viewOptionsMenu-> addAction (printDebugAct); viewOptionsMenu-> addAction (showProgressBarAct); viewOptionsMenu-> addAction (viewToolBar); viewOptionsMenu-> addAction (viewStatusBar); /** menuBar entry helpMenu */ helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu -> addAction (helpApp); helpMenu -> addAction (tipsApp); helpMenu -> addSeparator(); helpMenu-> addAction (helpAboutApp); helpMenu-> addAction (helpAboutQt); } /** Initializes the toolbar */ void MainWindow::initToolBar(){ toolBar = addToolBar("operations"); toolBar -> addAction (fileNew); toolBar -> addAction (fileOpen); toolBar -> addAction (fileSave); toolBar -> addAction (printNetwork); toolBar -> addSeparator(); toolBar -> addAction(zoomInAct); //Create zooming widget zoomCombo = new QComboBox; QStringList scales; scales << tr("25%") << tr("50%") << tr("75%") << tr("100%") << tr("125%") << tr("150%")<addItems(scales); zoomCombo->setCurrentIndex(3); toolBar -> addWidget(zoomCombo); toolBar -> addAction(zoomOutAct); toolBar -> addSeparator(); QLabel *labelRotateSpinBox= new QLabel; labelRotateSpinBox ->setText(tr("Rotation:")); rotateSpinBox = new QSpinBox; rotateSpinBox ->setRange(-360, 360); rotateSpinBox->setSingleStep(1); rotateSpinBox->setValue(0); toolBar -> addWidget(labelRotateSpinBox); toolBar -> addWidget(rotateSpinBox); toolBar -> addSeparator(); //Create relation select widget QLabel *labelRelationSelect= new QLabel; labelRelationSelect ->setText(tr("Relation:")); toolBar -> addWidget (labelRelationSelect); toolBar -> addAction (prevRelationAct); changeRelationCombo = new QComboBox; changeRelationCombo->setCurrentIndex(0); toolBar -> addWidget(changeRelationCombo); toolBar -> addAction (nextRelationAct); toolBar -> addAction (addRelationAct); toolBar -> addSeparator(); toolBar -> addAction ( QWhatsThis::createAction (this)); } //Creates a dock widget for instant menu access void MainWindow::initToolBox(){ toolBox = new QTabWidget; //toolBox->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored)); /* * create widgets for the Controls Tab */ // create 4 buttons for the Edit groupbox addNodeBt= new QPushButton(QIcon(":/images/add.png"),tr("&Add Node")); addNodeBt->setFocusPolicy(Qt::NoFocus); addNodeBt->setToolTip( tr("Add a new node to the network (Ctrl+X, Ctrl+A). \n\n " "Alternately, you can create a new node \n" "in a specific position by double-clicking \n" "on that spot of the canvas.") ); removeNodeBt= new QPushButton(QIcon(":/images/remove.png"),tr("&Remove Node")); removeNodeBt->setFocusPolicy(Qt::NoFocus); removeNodeBt->setToolTip( tr("Remove a node from the network. \n\n " "Alternately, you can remove a node \n" "by right-clicking on it.") ); addEdgeBt= new QPushButton(QIcon(":/images/connect.png"),tr("Add &Edge")); addEdgeBt->setFocusPolicy(Qt::NoFocus); addEdgeBt->setToolTip( tr("Add a new Edge from a node to another (Ctrl+E,Ctrl+A).\n\n " "Alternately, you can create a new edge between two \n" "nodes by middle-clicking on them consecutively.") ); removeEdgeBt= new QPushButton(QIcon(":/images/disconnect.png"),tr("Remove Edge")); removeEdgeBt->setFocusPolicy(Qt::NoFocus); removeEdgeBt->setToolTip( tr("Remove an Edge from the network \n\n " "Alternately, you can remove an Edge \n" "by right-clicking on it." ) ); //create a grid layout for these buttons QGridLayout *buttonsGrid = new QGridLayout; buttonsGrid -> addWidget(addNodeBt, 0,0); buttonsGrid -> addWidget(removeNodeBt, 0,1); buttonsGrid -> addWidget(addEdgeBt,1,0); buttonsGrid -> addWidget(removeEdgeBt,1,1); buttonsGrid->setSpacing(10); buttonsGrid->setMargin(0); //create a groupbox "Edit" - Inside, display the vertical layout of widgets QGroupBox *editGroupBox= new QGroupBox(tr("Edit")); editGroupBox->setLayout(buttonsGrid); editGroupBox->setMaximumSize(300,100); //create widgets for the "Analysis" box QLabel *toolBoxAnalysisGeodesicsSelectLabel = new QLabel; toolBoxAnalysisGeodesicsSelectLabel->setText(tr("Distances:")); toolBoxAnalysisGeodesicsSelect = new QComboBox; QStringList geodesicsCommandsList; geodesicsCommandsList << "None selected" << "Distance" << "Average Distance" << "Distances Matrix" << "Geodesics Matrix" << "Eccentricity" << "Diameter"; toolBoxAnalysisGeodesicsSelect->addItems(geodesicsCommandsList); // toolBoxAnalysisGeodesicsSelect->setMaximumHeight(20); QLabel *toolBoxAnalysisConnectivitySelectLabel = new QLabel; toolBoxAnalysisConnectivitySelectLabel->setText(tr("Connectivity:")); toolBoxAnalysisConnectivitySelect = new QComboBox; QStringList connectivityCommands; connectivityCommands << "None selected" << "Connectedness" << "Walks of given length" << "Total Walks" << "Reachability Matrix"; toolBoxAnalysisConnectivitySelect->addItems(connectivityCommands); // toolBoxAnalysisConnectivitySelect->setMaximumHeight(20); QLabel *toolBoxAnalysisClusterabilitySelectLabel = new QLabel; toolBoxAnalysisClusterabilitySelectLabel->setText(tr("Clusterability:")); toolBoxAnalysisClusterabilitySelect = new QComboBox; QStringList clusterabilityCommands; clusterabilityCommands << "None selected" << "Cliques" << "Clustering Coefficient" << "Triad Census"; toolBoxAnalysisClusterabilitySelect->addItems(clusterabilityCommands); QLabel *toolBoxAnalysisProminenceSelectLabel = new QLabel; toolBoxAnalysisProminenceSelectLabel->setText(tr("Prominence:")); toolBoxAnalysisProminenceSelect = new QComboBox; toolBoxAnalysisProminenceSelect -> setToolTip( tr("Various metrics to calculate how 'prominent' or important each actor (node) is inside the network.\n\n") ); toolBoxAnalysisProminenceSelect -> setWhatsThis( tr("Various metrics to calculate how 'prominent' or important each actor (node) is inside the network.\n\n") + tr("Centrality metrics quantify how central is each node by examining its ties and its geodesic distances (shortest path lengths) to other nodes. ")+ tr("Most Centrality indices were designed for undirected graphs.\n\n")+ tr("Prestige indices focus on \"choices received\" to a node. \n")+ tr("These indices measure the nominations or ties to each node from all others (or inLinks). ") + tr("Prestige indices are suitable (and can be calculated only) on directed graphs.") ); QStringList prominenceCommands; prominenceCommands << "None selected" << "Degree Centrality" << "Closeness Centrality" << "Influence Range Closeness Centrality" << "Betweenness Centrality" << "Stress Centrality" << "Eccentricity Centrality" << "Power Centrality" << "Information Centrality" << "Degree Prestige (inDegree)" << "PageRank Prestige" << "Proximity Prestige"; toolBoxAnalysisProminenceSelect->addItems(prominenceCommands); // toolBoxAnalysisProminenceSelect->setMaximumHeight(20); //create layout for analysis options QGridLayout *analysisGrid = new QGridLayout(); analysisGrid -> addWidget(toolBoxAnalysisGeodesicsSelectLabel, 0,0); analysisGrid -> addWidget(toolBoxAnalysisGeodesicsSelect, 0,1); analysisGrid -> addWidget(toolBoxAnalysisConnectivitySelectLabel, 1,0); analysisGrid -> addWidget(toolBoxAnalysisConnectivitySelect, 1,1); analysisGrid -> addWidget(toolBoxAnalysisClusterabilitySelectLabel, 3,0); analysisGrid -> addWidget(toolBoxAnalysisClusterabilitySelect, 3,1); analysisGrid -> addWidget(toolBoxAnalysisProminenceSelectLabel, 4,0); analysisGrid -> addWidget(toolBoxAnalysisProminenceSelect, 4,1); //layoutByIndexGrid -> setRowStretch(0,1); //fix stretch //create a box and set the above layout inside QGroupBox *analysisBox= new QGroupBox(tr("Analyze")); analysisBox->setMaximumWidth(300); analysisBox->setLayout (analysisGrid ); //create widgets for the "Visualization By Index" box QLabel *toolBoxLayoutByIndexSelectLabel = new QLabel; toolBoxLayoutByIndexSelectLabel->setText(tr("Index:")); toolBoxLayoutByIndexSelect = new QComboBox; QStringList indicesList; indicesList << "None/Original"<< "Random" << "Degree Centrality" << "Closeness Centrality" << "Influence Range Closeness Centrality" << "Betweenness Centrality" << "Stress Centrality" << "Eccentricity Centrality" << "Power Centrality" << "Information Centrality" << "Degree Prestige (inDegree)" << "PageRank Prestige" << "Proximity Prestige"; toolBoxLayoutByIndexSelect->addItems(indicesList); toolBoxLayoutByIndexSelect->setMinimumHeight(20); QLabel *toolBoxLayoutByIndexTypeLabel = new QLabel; toolBoxLayoutByIndexTypeLabel->setText(tr("Layout Type:")); toolBoxLayoutByIndexTypeSelect = new QComboBox; QStringList layoutTypes; layoutTypes << "Circular" << "On Levels" << "Nodal size"; toolBoxLayoutByIndexTypeSelect->addItems(layoutTypes); toolBoxLayoutByIndexTypeSelect->setMinimumHeight(20); toolBoxLayoutByIndexButton = new QPushButton(tr("Apply")); toolBoxLayoutByIndexButton->setFocusPolicy(Qt::NoFocus); toolBoxLayoutByIndexButton->setMinimumHeight(20); toolBoxLayoutByIndexButton->setMaximumWidth(60); //create layout for visualisation by index options QGridLayout *layoutByIndexGrid = new QGridLayout(); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexSelectLabel, 0,0); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexSelect, 0,1); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexTypeLabel, 1,0); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexTypeSelect, 1,1); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexButton, 2,1); //layoutByIndexGrid -> setRowStretch(0,1); //fix stretch //create a box and set the above layout inside QGroupBox *layoutByIndexBox= new QGroupBox(tr("By Prominence Index")); layoutByIndexBox->setMaximumWidth(300); layoutByIndexBox->setLayout (layoutByIndexGrid ); // create widgets for the "Force-Directed Models" groupBox layoutEadesBx = new QCheckBox(tr("Spring Embedder (Eades)") ); layoutEadesBx->setEnabled(true); layoutEadesBx->setChecked(false); layoutEadesBx ->setToolTip( tr("Embeds a spring-gravitational model on the network, where \n" "each node is regarded as physical object (ring) repeling all \n" "other nodes, while springs between connected nodes attract them. \n" "This is a very SLOW process on networks with N > 100!")); layoutFruchtermanBx = new QCheckBox(tr("Fruchterman-Reingold") ); layoutFruchtermanBx->setEnabled(true); layoutFruchtermanBx->setChecked(false); layoutFruchtermanBx ->setToolTip( tr("In Fruchterman-Reingold model, the vertices behave as atomic \n" "particles or celestial bodies, exerting attractive and repulsive \n" "forces to each other. Again, only vertices that are neighbours \n" "attract each other but, unlike Eades Spring Embedder, all vertices \n" "repel each other. ")); layoutKamandaBx= new QCheckBox(tr("Kamanda-Kawai") ); layoutKamandaBx->setEnabled(false); layoutKamandaBx->setToolTip(tr("!")); //create layout for dynamic visualisation QGridLayout *layoutForceDirectedGrid = new QGridLayout(); layoutForceDirectedGrid -> addWidget(layoutEadesBx, 0,0); layoutForceDirectedGrid -> addWidget(layoutFruchtermanBx, 1,0); layoutForceDirectedGrid -> addWidget(layoutKamandaBx, 2,0); layoutForceDirectedGrid->setSpacing(10); layoutForceDirectedGrid->setMargin(0); //create a box for dynamic layout options QGroupBox *layoutDynamicBox= new QGroupBox(tr("By Force-Directed Model")); layoutDynamicBox->setMaximumWidth(300); layoutDynamicBox->setLayout (layoutForceDirectedGrid ); //create widgets for additional visualization options box nodeSizesByOutDegreeBx = new QCheckBox( tr("Node sizes by OutDegree") ); nodeSizesByOutDegreeBx ->setEnabled(true); nodeSizesByOutDegreeBx ->setToolTip( tr("If you enable this, all nodes will be resized so that their " "size reflect their out-degree. \n" "Nodes with more directed edges starting from them will be bigger...")); nodeSizesByInDegreeBx = new QCheckBox( tr("Node sizes by InDegree") ); nodeSizesByInDegreeBx ->setEnabled(true); nodeSizesByInDegreeBx ->setToolTip( tr("If you enable this, all nodes will be resized so that their " "size reflect their in-degree. \n" "Nodes with more directed edges ending at them will be bigger...")); layoutGuidesBx = new QCheckBox( tr("Layout guidelines") ); layoutGuidesBx ->setEnabled(true); layoutGuidesBx ->setChecked(true); layoutGuidesBx->setToolTip( tr("Disable to not display layout guidelines")); QGridLayout *layoutOptionsGrid = new QGridLayout(); layoutOptionsGrid -> addWidget(nodeSizesByOutDegreeBx, 0,0); layoutOptionsGrid -> addWidget(nodeSizesByInDegreeBx, 1,0); layoutOptionsGrid -> addWidget(layoutGuidesBx, 2,0); layoutOptionsGrid->setSpacing(10); layoutOptionsGrid->setMargin(0); //Box for additional layout options QGroupBox *layoutOptionsBox= new QGroupBox(tr("Options")); layoutOptionsBox->setMaximumWidth(300); layoutOptionsBox->setLayout (layoutOptionsGrid ); //Parent box with vertical layout for all layout/visualization boxes QVBoxLayout *visualizationBoxLayout = new QVBoxLayout; visualizationBoxLayout -> addWidget(layoutByIndexBox); visualizationBoxLayout -> addWidget(layoutDynamicBox); visualizationBoxLayout -> addWidget(layoutOptionsBox); QGroupBox *visualizationBox= new QGroupBox(tr("Visualize")); visualizationBox->setMaximumWidth(300); visualizationBox->setLayout (visualizationBoxLayout ); //Parent box with vertical layout for all boxes of Controls QVBoxLayout *controlTabVerticalLayout = new QVBoxLayout; controlTabVerticalLayout -> addWidget(editGroupBox); controlTabVerticalLayout -> addWidget(analysisBox); controlTabVerticalLayout -> addWidget(visualizationBox); controlTabVerticalLayout->setSpacing(0); controlTabVerticalLayout->setMargin(0); QGroupBox *controlGroupBox = new QGroupBox; controlGroupBox->setLayout(controlTabVerticalLayout); controlGroupBox->setMaximumWidth(300); controlGroupBox->setContentsMargins(0,0,0,0); toolBox->addTab(controlGroupBox, tr("Controls")); connect(layoutEadesBx, SIGNAL(clicked(bool)), this, SLOT(slotLayoutSpringEmbedder(bool))); connect(layoutFruchtermanBx, SIGNAL(stateChanged(int)), this, SLOT(layoutFruchterman(int))); connect(nodeSizesByOutDegreeBx , SIGNAL(clicked(bool)), this, SLOT(slotLayoutNodeSizesByOutDegree(bool))); connect(nodeSizesByInDegreeBx , SIGNAL(clicked(bool)), this, SLOT(slotLayoutNodeSizesByInDegree(bool))); //create widgets for Properties/Statistics group/tab QLabel *labelNodesLCD = new QLabel; labelNodesLCD->setText(tr("Total Nodes")); QLabel *labelEdgesLCD = new QLabel; labelEdgesLCD->setText(tr("Total Edges (Arcs)")); nodesLCD=new QLCDNumber(7); nodesLCD->setSegmentStyle(QLCDNumber::Flat); nodesLCD->setToolTip(tr("Counts how many nodes (vertices) exist in the whole network.")); edgesLCD=new QLCDNumber(7); edgesLCD->setSegmentStyle(QLCDNumber::Flat); edgesLCD->setToolTip(tr("Counts how many edges (arcs) exist in the whole network.")); QLabel *labelDensityLCD = new QLabel; labelDensityLCD->setText(tr("Density")); densityLCD=new QLCDNumber(7); densityLCD->setSegmentStyle(QLCDNumber::Flat); densityLCD->setToolTip(tr("The density of a network is the ratio of existing edges to all possible edges ( n*(n-1) ) between nodes.")); //create a grid layout QGridLayout *propertiesGrid = new QGridLayout(); propertiesGrid -> setColumnMinimumWidth(0, 10); propertiesGrid -> setColumnMinimumWidth(1, 10); propertiesGrid -> addWidget(labelNodesLCD, 0,0); propertiesGrid -> addWidget(labelEdgesLCD, 0,1); propertiesGrid -> addWidget(nodesLCD,1,0); propertiesGrid -> addWidget(edgesLCD,1,1); propertiesGrid -> addWidget(labelDensityLCD, 2,0); propertiesGrid -> addWidget(densityLCD,2,1); QLabel *dummyLabel = new QLabel; dummyLabel-> setText (" "); QLabel *labelNode = new QLabel; labelNode-> setText (tr("Active Node")); labelNode ->setFont(QFont("sans-serif", 10, QFont::Bold)); QLabel *labelSelectedNodeLCD = new QLabel; labelSelectedNodeLCD -> setText (tr("Node Number:")); labelSelectedNodeLCD -> setToolTip (tr("This is the number of the last selected node.")); selectedNodeLCD =new QLCDNumber(7); selectedNodeLCD ->setSegmentStyle(QLCDNumber::Flat); QLabel *labelInDegreeLCD = new QLabel; labelInDegreeLCD -> setText (tr("Node In-Degree:")); labelInDegreeLCD -> setToolTip (tr("The sum of all in-edge weights of the node you clicked..")); inDegreeLCD=new QLCDNumber(7); inDegreeLCD -> setSegmentStyle(QLCDNumber::Flat); inDegreeLCD -> setToolTip (tr("The sum of all in-edge weights of the node you clicked.")); QLabel *labelOutDegreeLCD = new QLabel; labelOutDegreeLCD -> setText (tr("Node Out-Degree:")); labelOutDegreeLCD -> setToolTip (tr("The sum of all out-edge weights of the node you clicked.")); outDegreeLCD=new QLCDNumber(7); outDegreeLCD -> setSegmentStyle(QLCDNumber::Flat); outDegreeLCD -> setToolTip (tr("The sum of all out-edge weights of the node you clicked.")); QLabel *labelClucofLCD = new QLabel; labelClucofLCD -> setText (tr("Clustering Coef.")); labelClucofLCD -> setToolTip (tr("The Clustering Coefficient quantifies how close the clicked vertex and its neighbors are to being a clique. \nThe value is the proportion of Edges between the vertices within the neighbourhood of the clicked vertex,\n divided by the number of Edges that could possibly exist between them. \n\n WARNING: This value is automatically calculated only if vertices < 500.\n If your network is larger than 500 vertices, compute CluCof from the menu Analysis > Clustering Coefficient ")); clucofLCD = new QLCDNumber(7); clucofLCD -> setSegmentStyle(QLCDNumber::Flat); clucofLCD -> setToolTip (tr("The Clustering Coefficient quantifies how close the clicked vertex and its neighbors are to being a clique. \nThe value is the proportion of Edges between the vertices within the neighbourhood of the clicked vertex,\n divided by the number of Edges that could possibly exist between them. \n\n This value is automatically calculated only if vertices < 500.\n If your network is larger than 500 vertices, compute CluCof from the menu Analysis > Clustering Coefficient ")); propertiesGrid -> addWidget(dummyLabel, 6,0); propertiesGrid -> addWidget(labelNode, 7,0); propertiesGrid -> addWidget(labelSelectedNodeLCD , 8,0); propertiesGrid -> addWidget(selectedNodeLCD ,8,1); propertiesGrid -> addWidget(labelInDegreeLCD, 9,0); propertiesGrid -> addWidget(inDegreeLCD, 9,1); propertiesGrid -> addWidget(labelOutDegreeLCD, 10,0); propertiesGrid -> addWidget(outDegreeLCD,10,1); propertiesGrid -> addWidget(labelClucofLCD, 11,0); propertiesGrid -> addWidget(clucofLCD,11,1); propertiesGrid -> setRowStretch(12,1); //fix stretch //create a box with title QGroupBox *networkPropertiesGroup = new QGroupBox(tr("")); networkPropertiesGroup -> setLayout (propertiesGrid); toolBox->addTab( networkPropertiesGroup, tr("Statistics")); toolBox->setMinimumWidth(controlGroupBox->sizeHint().width()); toolBox->setFixedWidth(300); //toolBox->setStyleSheet("* { background-color:#ededed}}"); } //Called from MW, when user selects something in the Geodesics selectbox // of toolbox void MainWindow::toolBoxAnalysisGeodesicsSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisGeodesicsSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotGraphDistance(); break; case 2: slotAverageGraphDistance(); break; case 3: slotDistancesMatrix(); break; case 4: slotGeodesicsMatrix(); break; case 5: slotEccentricity(); break; case 6: slotDiameter(); break; }; } //Called from MW, when user selects something in the Connectivity selectbox // of toolbox void MainWindow::toolBoxAnalysisConnectivitySelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisConnectivitySelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: qDebug()<< "Connectedness"; slotConnectedness(); break; case 2: qDebug()<< "Walks of given length"; slotWalksOfGivenLength(); break; case 3: qDebug() << "Total Walks selected"; slotTotalWalks(); break; case 4: qDebug() << "Reachability Matrix"; slotReachabilityMatrix(); break; }; } //Called from MW, when user selects something in the Clusterability selectbox // of toolbox void MainWindow::toolBoxAnalysisClusterabilitySelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisClusterabilitySelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: qDebug()<< "Cliques"; slotCliqueCensus(); break; case 2: qDebug()<< "Clustering Coefficient"; slotClusteringCoefficient(); break; case 3: qDebug() << "Triad Census"; slotTriadCensus(); break; }; } //Called from MW, when user selects something in the Prominence selectbox // of toolbox void MainWindow::toolBoxAnalysisProminenceSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisProminenceSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotCentralityDegree(); break; case 2: slotCentralityCloseness(); break; case 3: slotCentralityClosenessInfluenceRange(); break; case 4: slotCentralityBetweenness(); break; case 5: slotCentralityStress(); break; case 6: slotCentralityEccentricity(); break; case 7: slotCentralityPower(); break; case 8: slotCentralityInformation(); break; case 9: slotPrestigeDegree(); break; case 10: slotPrestigePageRank(); break; case 11: slotPrestigeProximity(); break; }; } void MainWindow::toolBoxLayoutByIndexButtonPressed(){ qDebug()<<"MW::toolBoxLayoutByIndexButtonPressed()"; int selectedIndex = toolBoxLayoutByIndexSelect->currentIndex(); QString selectedIndexText = toolBoxLayoutByIndexSelect -> currentText(); int selectedLayoutType = toolBoxLayoutByIndexTypeSelect ->currentIndex(); qDebug() << " selected index is " << selectedIndexText << " : " << selectedIndex << " selected layout type is " << selectedLayoutType; switch(selectedIndex) { case 0: break; case 1: if (selectedLayoutType==0) slotLayoutCircularRandom(); else if (selectedLayoutType==1) slotLayoutRandom(); break; default: if (selectedLayoutType==0) slotLayoutCircularByProminenceIndex(selectedIndexText); else if (selectedLayoutType==1) slotLayoutLevelByProminenceIndex(selectedIndexText); else if (selectedLayoutType==2){ slotLayoutNodeSizesByProminenceIndex(selectedIndexText); // re-init other options for node sizes... nodeSizesByOutDegreeAct->setChecked(false); nodeSizesByOutDegreeBx->setChecked(false); nodeSizesByInDegreeAct->setChecked(false); nodeSizesByInDegreeBx->setChecked(false); } break; }; } //FIXME this is a bug: Graph calls GraphicsWidget which calls this to call Graph! void MainWindow::updateNodeCoords(int nodeNumber, int x, int y){ // qDebug("MW: updateNodeCoords() for %i with x %i and y %i", nodeNumber, x, y); activeGraph.updateVertCoords(nodeNumber, x, y); } /** Initializes the status bar */ void MainWindow::initStatusBar() { statusBarDuration=3000; statusMessage( tr("Ready.")); } /** Initializes the scene and its graphicsWidget, the main widget of SocNetV */ void MainWindow::initView() { qDebug ("MW initView()"); //create a scene scene=new QGraphicsScene(); //create a view widget for this scene graphicsWidget=new GraphicsWidget(scene, this); graphicsWidget->setViewportUpdateMode( QGraphicsView::BoundingRectViewportUpdate ); // FullViewportUpdate // MinimalViewportUpdate //SmartViewportUpdate //BoundingRectViewportUpdate //QGraphicsView can cache pre-rendered content in a QPixmap, which is then drawn onto the viewport. graphicsWidget->setCacheMode(QGraphicsView::CacheNone); //CacheBackground | CacheNone graphicsWidget->setRenderHint(QPainter::Antialiasing, true); graphicsWidget->setRenderHint(QPainter::TextAntialiasing, true); graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, true); //Optimization flags: //if items do restore their state, it's not needed for graphicsWidget to do the same... graphicsWidget->setOptimizationFlag(QGraphicsView::DontSavePainterState, false); //Disables QGraphicsView's antialiasing auto-adjustment of exposed areas. graphicsWidget->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, false); //"QGraphicsScene applies an indexing algorithm to the scene, to speed up item discovery functions like items() and itemAt(). // Indexing is most efficient for static scenes (i.e., where items don't move around). // For dynamic scenes, or scenes with many animated items, the index bookkeeping can outweight the fast lookup speeds." So... scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex); //NoIndex (for anime) | BspTreeIndex graphicsWidget->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); graphicsWidget->setResizeAnchor(QGraphicsView::AnchorViewCenter); // sets dragging the mouse over the scene while the left mouse button is pressed. graphicsWidget->setDragMode(QGraphicsView::RubberBandDrag); graphicsWidget->setFocusPolicy(Qt::StrongFocus); graphicsWidget->setFocus(); this->resize(1024,768); //set minimum size of canvas graphicsWidget->setMinimumSize( (qreal) ( this->width()-toolBox->sizeHint().width() -40 ) , (qreal) ( this->height()-statusBar()->sizeHint().height() -toolBar->sizeHint().height() -menuBar()->sizeHint().height() -20 ) ); qDebug ("MW initView(): now window size %i, %i, graphicsWidget size %i, %i, scene %f,%f",this->width(),this->height(), graphicsWidget->width(),graphicsWidget->height(), graphicsWidget->scene()->width(), graphicsWidget->scene()->height()); } /** Resizes the scene when the window is resized. */ void MainWindow::resizeEvent( QResizeEvent * ){ qDebug ("MW resizeEvent():INITIAL window size %i, %i, graphicsWidget size %i, %i, scene %f,%f",this->width(),this->height(), graphicsWidget->width(),graphicsWidget->height(), graphicsWidget->scene()->width(), graphicsWidget->scene()->height()); //the area of the scene displayed by the CanvasView scene->setSceneRect(0, 0, (qreal) ( graphicsWidget->width() -5 ), (qreal) (graphicsWidget->height() -5 ) ); qDebug ("MW resizeEvent(): now window size %i, %i, graphicsWidget size %i, %i, scene %f,%f",this->width(),this->height(), graphicsWidget->width(),graphicsWidget->height(), graphicsWidget->scene()->width(), graphicsWidget->scene()->height()); } /** Initializes the default network parameters. Also used when erasing a network to start a new one */ void MainWindow::initNet(){ qDebug()<<"MW: initNet() START INITIALISATION"; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // Init basic variables initNodeSize=8; initNodeColor="red"; initEdgeColor="black"; initLabelColor="darkblue"; initLabelSize=7; initNumberSize=7; initNumberColor="black"; initNodeShape="circle"; initBackgroundColor="white"; //"gainsboro"; minDuration=3000; //dialogue duration - obsolete maxNodes=5000; //Max nodes used by createRandomNetwork dialogues labelDistance=8; numberDistance=5; networkName=""; previous_fileName=fileName; fileName=""; pajekFileLoaded=false; adjacencyFileLoaded=false; fileFormat = -1; initFileCodec= "UTF-8"; checkSelectFileType = true; dotFileLoaded=false; fileLoaded=false; networkModified=false; fileSave->setIcon(QIcon(":/images/saved.png")); fileSave->setEnabled(true); markedNodesExist=false; //used by slotFindNode() cursorPosGW=QPointF(-1,-1); clickedJimNumber=-1; edgeClicked=false; nodeClicked=false; considerWeights=false; inverseWeights=false; askedAboutWeights=false; /** Clear previous network data */ activeGraph.clear(); activeGraph.setSocNetV_Version(VERSION); activeGraph.setInitVertexShape(initNodeShape); activeGraph.setInitVertexSize(initNodeSize); activeGraph.setInitVertexColor(initNodeColor); activeGraph.setInitVertexNumberSize(initNumberSize); activeGraph.setInitVertexNumberColor(initNumberColor); activeGraph.setInitVertexLabelColor(initLabelColor); activeGraph.setInitVertexLabelSize(initLabelSize); activeGraph.setInitEdgeColor(initEdgeColor); activeGraph.setShowLabels(this->showLabels()); activeGraph.setShowNumbersInsideNodes( this->showNumbersInsideNodes()); /** Clear scene **/ graphicsWidget->clear(); /** Clear LCDs **/ nodesLCD->display(activeGraph.vertices()); edgesLCD->display(activeEdges()); densityLCD->display(activeGraph.density()); inDegreeLCD->display(0); outDegreeLCD->display(0); clucofLCD->display(0); selectedNodeLCD->display(0); /** Clear toolbox and menu checkboxes **/ toolBoxAnalysisClusterabilitySelect->setCurrentIndex(0); toolBoxAnalysisConnectivitySelect->setCurrentIndex(0); toolBoxAnalysisGeodesicsSelect->setCurrentIndex(0); toolBoxAnalysisProminenceSelect->setCurrentIndex(0); toolBoxLayoutByIndexSelect->setCurrentIndex(0); toolBoxLayoutByIndexTypeSelect ->setCurrentIndex(0); nodeSizesByOutDegreeBx->setChecked(false); nodeSizesByInDegreeBx->setChecked(false); layoutEadesBx->setChecked(false); springLayoutAct->setChecked(false); FRLayoutAct->setChecked(false); displayEdgesWeightNumbersAct->setChecked(false); considerEdgeWeightsAct->setChecked(false); //displayEdgesArrowsAct->setChecked(false); //FIXME: USER PREFS EMITTED TO GRAPH? filterIsolateNodesAct->setChecked(false); // re-init orphan nodes menu item changeRelationCombo->clear(); /** set window title **/ setWindowTitle(tr("Social Network Visualizer ")+VERSION); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready")); qDebug("MW: initNet() INITIALISATION END"); // QTextStream stream(stdout); // Matrix m(10,10); // stream << "(9,1) = " << m[9][1] ; // m[9][1]=1212; // stream<< m; // stream << "(9,1) = " << m[9][1] ; } /* * Slot called by Graph::statusMessage to display some message to the user */ void MainWindow::statusMessage(const QString message){ statusBar()->showMessage( message, statusBarDuration ); } void MainWindow::showMessageToUser(const QString message) { QMessageBox::information(this, tr("Info"), message, QMessageBox::Ok, 0); } /** * Displays a message on the status bar when you resize the window. */ void MainWindow::windowInfoStatusBar(int w, int h){ statusMessage( QString(tr("Window resized to (%1, %2) pixels.")).arg(w).arg(h) ); } /** Closes the application. Asks to write any unsaved network data. */ void MainWindow::closeEvent( QCloseEvent* ce ) { if ( !networkModified ) { ce->accept(); return; } switch( QMessageBox::information( this, "Save file", tr("Do you want to save the changes") + tr(" to the network file?"), tr("Yes"), tr("No"), tr("Cancel"), 0, 1 ) ) { case 0: slotFileSave(); ce->accept(); break; case 1: ce->accept(); break; case 2: default: // just for sanity ce->ignore(); break; } } /** Creates a new network */ void MainWindow::slotCreateNew() { slotFileClose(); } /** * @brief MainWindow::getLastPath * returns the last path used by user to open/save something */ QString MainWindow::getLastPath() { if ( lastUsedDirPath == "socnetv-initial-none") { lastUsedDirPath = QDir::homePath(); } qDebug() << lastUsedDirPath ; return lastUsedDirPath ; } /** * @brief MainWindow::setLastPath * sets the last path used by user to open/save something * @param filePath */ void MainWindow::setLastPath(QString filePath) { lastUsedDirPath = filePath.left( filePath.lastIndexOf("/")); qDebug() << lastUsedDirPath; } /** Prompts the user a directory dialogue to choose a file from. Calls previewNetworkFile() */ void MainWindow::slotChooseFile() { if (firstTime && fileFormat == -500 ) { QMessageBox::information( this, "SocNetV", tr("Attention: \n")+ tr("This menu option is more suitable for loading " "a network file in GraphML format (.graphml), " "which is the default format of SocNetV. \n" "Nevertheless, if you select other supported " "filetype SocNetV will attempt to load it.\n")+ tr("If your file is not GraphML but you know its " "format is supported (i.e. Pajek, UCINET, GraphViz, etc), ")+ tr("please use the options in the Import sub menu. They are more safe.\n")+ tr("\n This warning message will not appear again."), "OK", 0 ); firstTime=false; } if ( fileFormat == -1 ) fileFormat = -1; bool a_file_was_already_loaded=fileLoaded; previous_fileName=fileName; QString m_fileName, fileType_string; int m_fileFormat=fileFormat; statusMessage( tr("Choose a network file...")); switch (m_fileFormat){ case 1: //GraphML fileType_string = tr("GraphML (*.graphml *.xml);;All (*)"); break; case 2: //Pajek fileType_string = tr("Pajek (*.net *.paj *.pajek);;All (*)"); break; case 3: //Adjacency fileType_string = tr("Adjacency (*.csv *.sm *.adj);;All (*)"); break; case 4: //Dot fileType_string = tr("GraphViz (*.dot);;All (*)"); break; case 5: //GML fileType_string = tr("GML (*.gml);;All (*)"); break; case 6: //DL fileType_string = tr("DL (*.dl);;All (*)"); break; case 7: // Weighted List fileType_string = tr("Weighted List (*.wlst *.wlist);;All (*)"); break; case 8: // Simple List fileType_string = tr("List (*.lst *.list);;All (*)"); break; case 9: // Two mode sm fileType_string = tr("Two-Mode Sociomatrix (*.2sm *.aff);;All (*)"); break; default: //All fileType_string = tr("GraphML (*.graphml *.xml);;Pajek (*.net *.pajek *.paj);;DL (*.dl *.dat);;Adjacency (*.csv *.adj *.sm);;GraphViz (*.dot);;List (*.lst *.list);;Weighted List (*.wlst *.wlist);;All (*)"); break; } m_fileName = QFileDialog::getOpenFileName( this, tr("Select one file to open"), getLastPath(), fileType_string ); if (checkSelectFileType) { //check if user has changed the filetype filter and loaded other filetype if (m_fileName.endsWith(".graphml",Qt::CaseInsensitive ) || m_fileName.endsWith(".xml",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=1; } else if (m_fileName.endsWith(".net",Qt::CaseInsensitive ) || m_fileName.endsWith(".paj",Qt::CaseInsensitive ) || m_fileName.endsWith(".pajek",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=2; } else if (m_fileName.endsWith(".sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".dat",Qt::CaseInsensitive ) || m_fileName.endsWith(".adj",Qt::CaseInsensitive ) || m_fileName.endsWith(".txt",Qt::CaseInsensitive )) { fileFormat=m_fileFormat=3; } else if (m_fileName.endsWith(".dot",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=4; } else if (m_fileName.endsWith(".gml",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=5; } else if (m_fileName.endsWith(".dl",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=6; } else if (m_fileName.endsWith(".list",Qt::CaseInsensitive ) || m_fileName.endsWith(".lst",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=7; } else if (m_fileName.endsWith(".wlist",Qt::CaseInsensitive ) || m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=8; } else if (m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".aff",Qt::CaseInsensitive ) ) { fileFormat=m_fileFormat=9; } else fileFormat=m_fileFormat=-1; } if (!m_fileName.isEmpty()) { if (m_fileFormat == -1) { QMessageBox::critical(this, "Unrecognized file extension", tr("Error! \n" "SocNetV supports the following network file" "formats. The filename you selected does not " "end with any of the following extensions:\n" "- GraphML (.graphml or .xml)\n" "- Pajek (.paj or .pajek or .net)\n" "- UCINET (.dl) \n" "- GraphViz (.dot)\n" "- Adjacency Matrix (.sm or .adj or .csv)\n" "- List (.list or .lst)\n" "- Weighted List (.wlist or .wlst)\n" "- Two-Mode / affiliation (.2sm or .aff) \n\n" "If you are sure the file is of a supported " "format, perhaps you should just change its extension..."), QMessageBox::Ok, 0); } qDebug()<<"MW: file selected: " << m_fileName; fileNameNoPath=m_fileName.split ("/"); setLastPath(m_fileName); // store this path previewNetworkFile(m_fileName, m_fileFormat ); } else { statusMessage( tr("Opening aborted")); //if a file was previously opened, get back to it. if (a_file_was_already_loaded) { fileLoaded=true; fileName=previous_fileName; } } } /** Saves the network in the same file */ void MainWindow::slotFileSave() { statusMessage( tr("Saving file...")); if (!fileLoaded && !networkModified ) { statusMessage( QString(tr("No network loaded.")) ); return; } if ( fileName.isEmpty() ) { slotFileSaveAs(); return; } int maxWidth=scene->width(); int maxHeight=scene->height(); fileNameNoPath=fileName.split ("/"); if (pajekFileLoaded) { if ( activeGraph.saveGraph(fileName, 1, networkName, maxWidth,maxHeight) ) networkSaved(1); else networkSaved(0); } else if (adjacencyFileLoaded) { if ( activeGraph.saveGraph(fileName, 2, networkName, maxWidth,maxHeight) ) networkSaved(2); else networkSaved(0); } else if (graphMLFileLoaded || ( !fileLoaded && networkModified) ) { //new file or GraphML if ( activeGraph.saveGraph(fileName, 4, networkName, maxWidth,maxHeight) ) networkSaved(4); else networkSaved(0); } else { switch( QMessageBox::information( this, "GraphML File Format", tr("This network will be saved in GraphML format. \n")+ tr("Is this OK? \n\n") + tr("If not, press Cancel, then go to Network > Export menu..."), "Yes", "No",0,1 ) ) { case 0: if ( activeGraph.saveGraph(fileName, 4, networkName, maxWidth,maxHeight) ) networkSaved(4); else networkSaved(0); break; case 1: statusMessage( tr("Save aborted...") ); break; } } } /** Saves the network in a new file */ void MainWindow::slotFileSaveAs() { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, tr("Save GraphML Network to File Named..."), getLastPath(), tr("GraphML (*.graphml *.xml);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ QMessageBox::information(this, "Missing Extension ",tr("File extension was missing! \nI am appending a standard .graphml to the given filename."), "OK",0); fn.append(".graphml"); } fileName=fn; fileNameNoPath=fileName.split ("/"); setLastPath(fileName); // store this path adjacencyFileLoaded=false; pajekFileLoaded=false; graphMLFileLoaded=false; slotFileSave(); } else { statusMessage( tr("Saving aborted")); return; } statusMessage( tr("Ready.")); } /* * Called from Graph when we try to save file */ void MainWindow::networkSaved(int saved_ok) { if (saved_ok <= 0) { graphChanged(); statusMessage( tr("Error! Could not save this file... ")+fileNameNoPath.last()+tr(".") ); } else { fileSave->setIcon(QIcon(":/images/saved.png")); fileSave->setEnabled(false); fileLoaded=true; networkModified=false; setWindowTitle( fileNameNoPath.last() ); statusMessage( tr("Network saved under filename: ")+fileNameNoPath.last()+tr(".") ); switch (saved_ok){ case 1: adjacencyFileLoaded=false; pajekFileLoaded=true; graphMLFileLoaded=false; break; case 2: adjacencyFileLoaded=true; pajekFileLoaded=false; graphMLFileLoaded=false; break; case 3: adjacencyFileLoaded=false; pajekFileLoaded=false; graphMLFileLoaded=false; break; case 4: adjacencyFileLoaded=false; pajekFileLoaded=false; graphMLFileLoaded=true; break; } } } /** Closes the network. Saves it if necessary. Used by createNew. */ void MainWindow::slotFileClose() { statusMessage( tr("Closing file...")); qDebug()<<"slotFileClose()"; if (networkModified) { switch ( QMessageBox::information (this, "Closing Network...", tr("Network has not been saved. \nDo you want to save before closing it?"), "Yes", "No",0,1)) { case 0: slotFileSave(); break; case 1: break; } } statusMessage( tr("Erasing old network data....")); initNet(); statusMessage( tr("Ready.")); } /** Prints whatever is viewable on the Graphics widget */ void MainWindow::slotPrintView() { statusMessage( tr("Printing...")); QPrintDialog dialog(printer, this); if ( dialog.exec() ) { QPainter painter(printer); graphicsWidget->render(&painter); }; statusMessage( tr("Ready.")); } /** Imports a network from a formatted file */ void MainWindow::slotImportGraphML(){ // fileFormat=-1; // checkSelectFileType = true; this->slotChooseFile(); } /** Imports a network from a formatted file */ void MainWindow::slotImportPajek(){ fileFormat=2; checkSelectFileType = false; this->slotChooseFile(); } /** Imports a network from a Adjacency matrix formatted file */ void MainWindow::slotImportSM(){ fileFormat=3; checkSelectFileType = false; this->slotChooseFile(); } /** Imports a network from a two mode sociomatrix formatted file */ void MainWindow::slotImportTwoModeSM(){ fileFormat=9; checkSelectFileType = false; this->slotChooseFile(); } /** Imports a network from a Dot formatted file */ void MainWindow::slotImportDot(){ fileFormat=4; checkSelectFileType = false; this->slotChooseFile(); } /** Imports a network from a GML formatted file */ void MainWindow::slotImportGML(){ fileFormat=5; checkSelectFileType = false; this->slotChooseFile(); } /** Imports a network from a UCINET formatted file */ void MainWindow::slotImportDL(){ fileFormat=6; checkSelectFileType = false; this->slotChooseFile(); } /** Imports a network from a List formatted file */ void MainWindow::slotImportEdgeList(){ switch( QMessageBox::question( this, "Type of list format", tr("I can parse two kinds of lists: \n\n")+ tr("A. Weighted lists, with each line having exactly 3 columns (source, target, weight), i.e.\n 1 2 5 \n \n")+ tr("B. Simple edge lists, with each line having 2 or more columns (source, target1, target2, ... etc)\n\n")+ tr("Please select the appropriate type of list format for the file you want to load:"), "Weighted", "Simple",0,1 ) ) { case 0: qDebug() << "*** MW: slotImportEdgeList - Weighted list selected! " ; fileFormat = 7; break; case 1: qDebug() << "*** MW: slotImportEdgeList - Simple list selected! " ; fileFormat = 8; break; } checkSelectFileType = false; this->slotChooseFile(); } void MainWindow::findCodecs() { QMap codecMap; QRegExp iso8859RegExp("ISO[- ]8859-([0-9]+).*"); foreach (int mib, QTextCodec::availableMibs()) { QTextCodec *codec = QTextCodec::codecForMib(mib); QString sortKey = codec->name().toUpper(); int rank; if (sortKey.startsWith("UTF-8")) { rank = 1; } else if (sortKey.startsWith("UTF-16")) { rank = 2; } else if (iso8859RegExp.exactMatch(sortKey)) { if (iso8859RegExp.cap(1).size() == 1) rank = 3; else rank = 4; } else { rank = 5; } sortKey.prepend(QChar('0' + rank)); codecMap.insert(sortKey, codec); } codecs = codecMap.values(); } bool MainWindow::previewNetworkFile(QString m_fileName, int m_fileFormat ){ qDebug() << "MW::previewNetworkFile() : "<< m_fileName; if (!m_fileName.isEmpty()) { QFile file(m_fileName); if (!file.open(QFile::ReadOnly)) { QMessageBox::warning(this, tr("previewNetworkFile"), tr("Cannot read file %1:\n%2") .arg(m_fileName) .arg(file.errorString())); return false; } qDebug() << "MW::loadNetworkFile() reading the file now... " ; QByteArray data = file.readAll(); previewForm->setEncodedData(data,m_fileName, m_fileFormat); previewForm->exec(); } return true; } void MainWindow::userCodec(const QString m_fileName, const QString m_codecName, const int m_fileFormat) { loadNetworkFile(m_fileName, m_codecName, m_fileFormat ); } /** * @brief MainWindow::loadNetworkFile * Main network file loader method * First, inits everything to default values. * Then calls activeGraph::loadGraph to actually load the network... * @param m_fileName * @param m_fileFormat * @return */ bool MainWindow::loadNetworkFile(const QString m_fileName, const QString m_codecName, const int m_fileFormat ) { qDebug() << "MW::loadNetworkFile() : "<< m_fileName << " m_codecName " << m_codecName << " m_fileFormat " << m_fileFormat; initNet(); userSelectedCodecName = m_codecName; //var for future use in a Settings dialog int two_sm_mode = 0; if ( m_fileFormat == 9 ) { switch( QMessageBox::information( this, "Two-mode sociomatrix", tr("If this file is in two-mode sociomatrix format, \n")+ tr("please specify which mode to open \n\n") + tr("1st mode: rows are nodes \n") + tr("2nd mode: columns are nodes"), tr("1st Mode"), tr("2nd mode"), 0,1 ) ) { case 0: two_sm_mode = 1; break; case 1: two_sm_mode = 2; break; } } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MW::loadNetworkFile() : calling activeGraph.loadGraph() "; bool loadGraphStatus = activeGraph.loadGraph ( m_fileName, m_codecName, displayNodeLabelsAct->isChecked(), graphicsWidget->width(), graphicsWidget->height(), m_fileFormat, two_sm_mode ); qDebug() << "MW::loadNetworkFile() : loadGraphStatus " << loadGraphStatus; if ( loadGraphStatus ) { fileName=m_fileName; previous_fileName=fileName; fileNameNoPath = fileName.split("/"); Q_ASSERT_X( !fileNameNoPath.isEmpty(), "not empty filename ", "empty filename " ); setWindowTitle("SocNetV "+ VERSION +" - "+fileNameNoPath.last()); QString message=tr("Loaded network: ")+fileNameNoPath.last(); statusMessage( message ); } else { statusMessage( tr("Error loading requested file. Aborted.")); QMessageBox::critical( this, "SocNetV", tr("Error! \n")+ tr("Sorry, the selected file is not in valid format or encoding. \n")+ tr("Try a different codec in the preview window or if you are trying to import legacy formats (i.e. Pajek, UCINET, dot, etc), ")+ tr("please use the options in the Import sub menu. \n"), "OK", 0 ); } QApplication::restoreOverrideCursor(); qDebug() << "MW::loadNetworkFile() : returning " << loadGraphStatus; return loadGraphStatus; } /** * @brief MainWindow::fileType * Called from Parser/Graph when a network file is loaded. * It informs the MW about the type of the network so that it can display the appropiate message. * @param type * @param netName * @param aNodes * @param totalEdges * @param undirected */ void MainWindow::fileType ( int type, QString netName, int aNodes, int totalEdges, bool undirected) { qDebug()<< "MW: fileType() networkName is: " << netName << " type " << type; Q_UNUSED (undirected); if (netName != "") networkName=netName ; else networkName=(fileName.split ("/")).last(); fileFormat=type; switch( type ) { case 0: pajekFileLoaded=false; adjacencyFileLoaded=false; graphMLFileLoaded=false; fileLoaded=false; break; case 1: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=false; graphMLFileLoaded=true; fileLoaded=true; networkModified=false; statusMessage( QString(tr("GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 2: pajekFileLoaded=true; adjacencyFileLoaded=false; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges )); break; case 3: pajekFileLoaded=false; adjacencyFileLoaded=true; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 4: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=true; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("Dot formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 5: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=false; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("DL-formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 6: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=false; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("GML-formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 7: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=false; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("Weighted list-formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 8: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=false; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("Simple list-formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; case 9: pajekFileLoaded=false; adjacencyFileLoaded=false; dotFileLoaded=false; graphMLFileLoaded=false; fileLoaded=true; networkModified=false; statusMessage( QString(tr("Two-mode affiliation network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( networkName ).arg( aNodes ).arg(totalEdges ) ); break; default: // just for sanity pajekFileLoaded=false; adjacencyFileLoaded=false; graphMLFileLoaded=false; fileLoaded=false; QMessageBox::critical(this, "Error","Unrecognized format. \nPlease specify" " which is the file-format using Import Menu.","OK",0); break; } graphChanged(); fileSave->setIcon(QIcon(":/images/saved.png")); fileSave->setEnabled(false); } /** * @brief MainWindow::prevRelation * Decreases the index of changeRelationCombo * which signals to Graph::changeRelation() */ void MainWindow::prevRelation(){ qDebug() << "MW::prevRelation()"; int index=changeRelationCombo->currentIndex(); if (index>0){ --index; filterIsolateNodesAct->setChecked(false); changeRelationCombo->setCurrentIndex(index); } } /** * @brief MainWindow::nextRelation * Increases the index of changeRelationCombo * which signals to Graph::changeRelation() */ void MainWindow::nextRelation(){ qDebug() << "MW::nextRelation()"; int index=changeRelationCombo->currentIndex(); int relationsCounter=changeRelationCombo->count(); if (index< (relationsCounter -1 )){ ++index; filterIsolateNodesAct->setChecked(false); changeRelationCombo->setCurrentIndex(index); } } /** * @brief MainWindow::addRelation * called from activeGraph::addRelationFromGraph(QString) when the parser or a * Graph method demands a new relation to be added in the Combobox. * @param relationName (NULL) */ void MainWindow::addRelation(QString relationName){ qDebug() << "MW::addRelation(string)" << relationName; if ( !relationName.isNull() ){ changeRelationCombo->addItem(relationName); } } /** * @brief MainWindow::addRelation * Called from MW when user clicks New Relation btn * or when the user creates the first edge visually. */ void MainWindow::addRelation(){ qDebug() << "MW::addRelation()"; bool ok; QString newRelationName; int relationsCounter=changeRelationCombo->count(); if (relationsCounter==0) { newRelationName = QInputDialog::getText(this, tr("Add new relation"), tr("Since you have just created the first edge " "of this social network, please enter a name \n" "for this new relation between the actors.\n " "A relation is a collection of ties of a " "specific kind between the network actors.\n" "For instance, enter \"friendship\" if the " "edges of this relation refer to the set of \n" "friendships between pairs of actors."), QLineEdit::Normal, QString::null, &ok ); } else { newRelationName = QInputDialog::getText( this, tr("Add new relation"), tr("Please enter a name for the new relation:"), QLineEdit::Normal,QString::null, &ok ); } if (ok && !newRelationName.isEmpty()){ changeRelationCombo->addItem(newRelationName); emit addRelationToGraph(newRelationName); if (relationsCounter != 0){ //dont do it if its the first relation added qDebug() << "MW::addRelation() - updating combo index"; changeRelationCombo->setCurrentIndex(relationsCounter); } } else if ( newRelationName.isEmpty() && ok ){ QMessageBox::critical(this, tr("Error"), tr("You did not type a name for this new relation"), QMessageBox::Ok, 0); addRelation(); } else { statusMessage( QString(tr("New relation cancelled.")) ); return; } statusMessage( QString(tr("New relation named %1, added.")) .arg( newRelationName ) ); } /** Calls Graph::createVertex method to add a new RANDOM node into the activeGraph. Called when "Create Node" button is clicked on the Main Window. */ void MainWindow::addNode() { qDebug() << "MW::addNode() "; // minus a screen edge offset... activeGraph.createVertex ( -1, graphicsWidget->width()-10, graphicsWidget->height()-10); statusMessage( tr("New node (numbered %1) added.") .arg(activeGraph.lastVertexNumber()) ); } /** Calls Graph::createVertex method to add a new node into the activeGraph. Called on double clicking */ void MainWindow::addNodeWithMouse(int num, QPointF p) { qDebug("MW: addNodeWithMouse(). Calling activeGraph::createVertex() for a vertex named %i", num); activeGraph.createVertex(num, p); statusMessage( tr("New node (numbered %1) added.").arg(activeGraph.lastVertexNumber()) ); } /** Exports the network to a PNG image Mediocre Quality but smaller file */ bool MainWindow::slotExportPNG(){ qDebug("slotExportPNG"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("The canvas is empty!\nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export PNG.") ); return false; } QString fn = QFileDialog::getSaveFileName( this,tr("Save"), getLastPath(), tr("Image Files (*.png)")); if (fn.isEmpty()) { statusMessage( tr("Saving aborted") ); return false; } setLastPath(fn); // store this path tempFileNameNoPath=fn.split ("/"); qDebug("slotExportPNG: grabbing canvas"); QPixmap picture; picture=QPixmap::grabWidget(graphicsWidget, graphicsWidget->rect()); qDebug("slotExportPNG: adding logo"); QPainter p; p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); p.drawText(5,10,"SocNetV: "+tempFileNameNoPath.last()); p.end(); qDebug("slotExportPNG: checking filename"); if (fn.contains("png", Qt::CaseInsensitive) ) { picture.toImage().save(fn, "PNG"); QMessageBox::information(this, "Export to PNG...", tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); } else { picture.toImage().save(fn+".png", "PNG"); QMessageBox::information(this, "Export to PNG...", tr("Image Saved as: ")+tempFileNameNoPath.last()+".png" , "OK",0); } statusMessage( tr("Exporting completed") ); return true; } /** Exports the network to a BMP image Better Quality but larger file */ bool MainWindow::slotExportBMP(){ qDebug( "slotExportBMP()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to export! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export BMP.") ); return false; } QString format="bmp"; QString fn = QFileDialog::getSaveFileName( this,tr("Save Image as"), getLastPath(),tr("Image Files (*.bmp)")); if (fn.isEmpty()) { statusMessage( tr("Saving aborted") ); return false; } setLastPath(fn); // store this path tempFileNameNoPath=fn.split ("/"); QPixmap picture; qDebug("slotExportBMP: grabbing canvas"); picture=QPixmap::grabWidget(graphicsWidget, graphicsWidget->viewport()->rect()); QPainter p; qDebug("slotExportBMP: adding logo"); p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); p.drawText(5,10,"SocNetV: "+tempFileNameNoPath.last()); p.end(); qDebug("slotExportBMP: checking file"); if (fn.contains(format, Qt::CaseInsensitive) ) { picture.toImage().save(fn, format.toLatin1()); QMessageBox::information(this, tr("Export to BMP..."), tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); } else { picture.toImage().save(fn+"."+format, format.toLatin1()); QMessageBox::information(this, tr("Export to BMP..."), tr("Image Saved as: ")+tempFileNameNoPath.last()+"."+format , "OK",0); } qDebug()<< "Exporting BMP to "<< fn; statusMessage( tr("Exporting completed") ); qDebug("Export finished!"); return true; } /** Exports the network to a PDF Document Best Quality */ bool MainWindow::slotExportPDF(){ qDebug( "slotExportPDF()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("The canvas is empty!\nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export PDF.") ); return false; } QString m_fileName = QFileDialog::getSaveFileName( this, tr("Export to PDF"), getLastPath(), tr("Portable Document Format files (*.pdf)")); if (m_fileName.isEmpty()) { statusMessage( tr("Saving aborted")); return false; } else { if (QFileInfo(m_fileName).suffix().isEmpty()) m_fileName.append(".pdf"); QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(m_fileName); QPainter painter(&printer); graphicsWidget->render(&painter); } qDebug()<< "Exporting PDF to "<< m_fileName; tempFileNameNoPath=m_fileName.split ("/"); setLastPath(m_fileName); QMessageBox::information(this, tr("Export to PDF..."),tr("File saved as: ")+tempFileNameNoPath.last() , "OK",0); statusMessage( tr("Exporting completed") ); return true; } /** Exports the network to a Pajek-formatted file Calls the relevant Graph method. */ void MainWindow::slotExportPajek() { qDebug ("MW: slotExportPajek"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to export! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export to Pajek.") ); return; } statusMessage( tr("Exporting active network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, tr("Export Network to File Named..."), getLastPath(), tr("Pajek (*.paj *.net *.pajek);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ QMessageBox::information(this, "Missing Extension ",tr("File extension was missing! \nI am appending a standard .paj to the given filename."), "OK",0); fn.append(".paj"); } fileName=fn; setLastPath(fileName); fileNameNoPath=fileName.split ("/"); } else { statusMessage( tr("Saving aborted")); return; } int maxWidth=scene->width(); int maxHeight=scene->height(); if ( activeGraph.saveGraph(fileName, 1, networkName, maxWidth,maxHeight ) ) networkSaved(1); else networkSaved(0); } /** Exports the network to a adjacency matrix-formatted file Calls the relevant Graph method. */ void MainWindow::slotExportSM(){ qDebug("MW: slotExportSM()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to export!\nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export to Adjacency Matrix.") ); return; } statusMessage( tr("Exporting active network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, tr("Export Network to File Named..."), getLastPath(), tr("Adjacency (*.adj *.sm *.txt *.csv *.net);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ QMessageBox::information(this, "Missing Extension ",tr("File extension was missing! \nI am appending a standard .adj to the given filename."), "OK",0); fn.append(".adj"); } fileName=fn; setLastPath(fileName); fileNameNoPath=fileName.split ("/"); } else { statusMessage( tr("Saving aborted")); return; } QMessageBox::information(this, "Warning",tr("Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. \n If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1."), "OK",0); int maxWidth=scene->width(); int maxHeight=scene->height(); if ( activeGraph.saveGraph(fileName, 2, networkName,maxWidth,maxHeight ) ) networkSaved(1); else networkSaved(0); } /** Exports the network to a DL-formatted file TODO slotExportDL */ bool MainWindow::slotExportDL(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to export!\nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export to DL.") ); return false; } if (fileName.isEmpty()) { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, "Export UCINET", getLastPath(), 0); if (!fn.isEmpty()) { fileName=fn; setLastPath(fileName); } else { statusMessage( tr("Saving aborted")); return false; } } return true; } /** Exports the network to a GW-formatted file TODO slotExportGW */ bool MainWindow::slotExportGW(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to export!\nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("Cannot export to GW.") ); return false; } if (fileName.isEmpty()) { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, "Export GW", getLastPath(), 0); if (!fn.isEmpty()) { fileName=fn; setLastPath(fileName); } else { statusMessage( tr("Saving aborted")); return false; } } return true; } /** Exports the network to a list-formatted file TODO slotExportList */ bool MainWindow::slotExportList(){ if (fileName.isEmpty()) { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, "Export List", getLastPath(), 0); if (!fn.isEmpty()) { fileName=fn; setLastPath(fileName); } else { statusMessage( tr("Saving aborted")); return false; } } return true; } /** Displays the file of the loaded network. Network _must_ be unchanged since last save/load. Otherwise it will ask the user to first save the network, then view its file. */ void MainWindow::slotViewNetworkFile(){ qDebug() << "slotViewNetworkFile() : " << fileName.toLatin1(); if ( fileLoaded && !networkModified ) { //file network unmodified QFile f( fileName ); if ( !f.open( QIODevice::ReadOnly ) ) { qDebug ("Error in open!"); return; } TextEditor *ed = new TextEditor(fileName);//OPEN A TEXT EDITOR WINDOW ed->setWindowTitle(tr("Viewing network file - ") + fileNameNoPath.last() ); ed->show(); statusMessage( tr("Loaded network text file " )+ fileNameNoPath.last() ); } else if (fileName.isEmpty() && networkModified) { //New network + something QMessageBox::information (this, "Viewing network file", tr("This network has not been saved yet. \nI will open a dialog for you to save it now. \nPlease choose a filename..."), "OK",0); slotFileSaveAs(); } else if (fileLoaded && networkModified ) { //file network + modified QMessageBox::information (this, "Viewing network file", //FIXME maybe better to save automagically rather than asking? tr("The network has been modified. \nI will save it to the original file for you now."), "OK",0); networkModified = false; slotFileSave(); slotViewNetworkFile(); } else { QMessageBox::critical(this, "Error", tr("Empty network! \nLoad a network file first or create and save a new one..."), "OK",0); statusMessage( tr("Nothing here. Not my fault, though!") ); } } /** Opens the embedded text editor */ void MainWindow::slotOpenTextEditor(){ qDebug() << "slotOpenTextEditor() : "; TextEditor *ed = new TextEditor("", this); ed->setWindowTitle(tr("New Network File")); ed->show(); statusMessage( tr("Enter your network data here" ) ); } /** Displays the adjacency matrix of the network. It uses a different method for writing the matrix to a file. While slotExportSM uses << operator of Matrix class (via adjacencyMatrix of Graph class), this is using directly the writeAdjacencyMatrix method of Graph class */ void MainWindow::slotViewAdjacencyMatrix(){ if ( !fileLoaded && !networkModified) { QMessageBox::critical (this, "Error", tr("Empty network! \nLoad a network file or create something by double-clicking on the canvas!"), "OK",0); statusMessage( tr("Nothing to show!") ); return; } int aNodes=activeNodes(); statusBar() -> showMessage ( QString (tr ("creating adjacency adjacency matrix of %1 nodes")).arg(aNodes) ); qDebug ("MW: calling Graph::writeAdjacencyMatrix with %i nodes", aNodes); QString fn = dataDir + "socnetv-report-adjacency-matrix.dat"; activeGraph.writeAdjacencyMatrix(fn, networkName.toLocal8Bit()) ; //Open a text editor window for the new file created by graph class TextEditor *ed = new TextEditor(fn); tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); statusMessage(tr("Adjacency Matrix saved at ") + tempFileNameNoPath.last()); } /* * Calls the m_datasetSelectionDialog to display the datasetselection dialog */ void MainWindow::slotShowDataSetSelectDialog(){ qDebug()<< "slotShowDataSetSelectDialog()"; m_datasetSelectDialog.exec(); } /* * Recreates some of the most famous and widely used data sets * in network analysis studies */ void MainWindow::slotRecreateDataSet (QString m_fileName) { int m_fileFormat=0; qDebug()<< "slotRecreateDataSet() fileName: " << m_fileName; //initNet(); qDebug()<< "slotRecreateDataSet() datadir+fileName: " << dataDir+m_fileName; activeGraph.writeDataSetToFile(dataDir, m_fileName); if (m_fileName.endsWith(".graphml")) { m_fileFormat=1; } else if (m_fileName.endsWith(".pajek") || m_fileName.endsWith(".paj") || m_fileName.endsWith(".net")) { m_fileFormat=2; } else if (m_fileName.endsWith(".sm") || m_fileName.endsWith(".adj")) { m_fileFormat=3; } else if (m_fileName.endsWith(".dot")) { m_fileFormat=4; } else if (m_fileName.endsWith(".gml")) { m_fileFormat=5; } else if (m_fileName.endsWith(".dl")) { m_fileFormat=6; } else if (m_fileName.endsWith(".list")) { m_fileFormat=7; } else if (m_fileName.endsWith(".lst")) { m_fileFormat=8; } else if (m_fileName.endsWith(".2sm")) { m_fileFormat=9; } checkSelectFileType = false; if ( loadNetworkFile(dataDir+m_fileName, "UTF-8", m_fileFormat) ) { qDebug() << "slotRecreateDataSet() loaded file " << m_fileName; fileName=m_fileName; previous_fileName=fileName; setWindowTitle("SocNetV "+ VERSION +" - "+fileName); QString message=tr("Dataset loaded. Dataset file saved as ") + fileName; statusMessage( message ); } else { statusMessage( "Could not read new network data file. Aborting."); } } /** Calls activeGraph.createRandomNetErdos () to create a symmetric network Edge existance is controlled by a user specified possibility. */ void MainWindow::slotCreateRandomErdosRenyi(){ statusMessage( "Creating a random symmetric network... "); m_randErdosRenyiDialog = new RandErdosRenyiDialog(this); connect( m_randErdosRenyiDialog, &RandErdosRenyiDialog::userChoices, this, &MainWindow::createRandomNetErdos ); m_randErdosRenyiDialog->exec(); } void MainWindow::createRandomNetErdos( const int newNodes, const QString model, const int edges, const float eprob, const QString mode, const bool diag) { qDebug() << "MW::createRandomNetErdos()"; statusMessage( "Erasing any existing network. "); initNet(); statusMessage( tr("Creating random network. Please wait... ") ); if (showProgressBarAct->isChecked() && newNodes > 500 && eprob > 0.06){ progressDialog= new QProgressDialog( "Creating random network. \n " " Please wait (or disable me from Options > View > ProgressBar, next time ;)).", "Cancel", 0, newNodes+newNodes, this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.createRandomNetErdos ( newNodes, model, edges, eprob, mode, diag); QApplication::restoreOverrideCursor(); if (showProgressBarAct->isChecked() && newNodes > 500 && eprob > 0.06) progressDialog->deleteLater(); fileLoaded=false; graphChanged(); setWindowTitle("Untitled"); double threshold = log(newNodes)/newNodes; //float avGraphDistance=activeGraph.averageGraphDistance(); float clucof=activeGraph.clusteringCoefficient(); if ( (eprob ) > threshold ) QMessageBox::information( this, "New Random Network", tr("Random network created. \n")+ tr("\nNodes: ")+ QString::number(activeNodes())+ tr("\nEdges: ")+ QString::number( (mode == "graph") ? activeEdges()/2.0 : activeEdges() ) + //tr("\nAverage path length: ") + QString::number(avGraphDistance)+ tr("\nClustering coefficient: ")+QString::number(clucof)+ tr("\n\nOn the average, edges should be ") + QString::number( eprob * newNodes*(newNodes-1)) + tr("\nThis graph is almost surely connected because: \nprobability > ln(n)/n, that is: \n") + QString::number(eprob)+ tr(" bigger than ")+ QString::number(threshold) , "OK",0); else QMessageBox::information( this, "New Random Network", tr("Random network created. \n")+ tr("\nNodes: ")+ QString::number(activeNodes())+ tr("\nEdges: ")+ QString::number( (mode == "graph") ? activeEdges()/2.0 : activeEdges() )+ //tr("\nAverage path length: ") + QString::number(avGraphDistance)+ tr("\nClustering coefficient: ")+QString::number(clucof)+ tr("\n\nOn the average, edges should be ") + QString::number(eprob * newNodes*(newNodes-1)) + tr("\nThis graph is almost surely not connected because: \nprobability < ln(n)/n, that is: \n") + QString::number(eprob)+ " smaller than "+ QString::number(threshold) , "OK",0); statusMessage( "Random network created. "); } /** Creates a pseudo-random network where every node has the same degree */ void MainWindow::slotCreateRegularRandomNetwork(){ bool ok; statusMessage( "Creating a pseudo-random network where each node has the same degree... "); int newNodes= QInputDialog::getInt( this, tr("Create k-regular network"), tr("This will create a network with nodes of the same degree d.") + tr("\nPlease enter the number of nodes:"), 100, 1, maxNodes, 1, &ok ) ; if (!ok) { statusMessage( "You did not enter an integer. Aborting."); return; } int degree = QInputDialog::getInt( this, tr("Create k-regular network..."), tr("Now, select an even number d. \nThis will be the degree (number of edges) of each node:"), 2, 2, newNodes-1, 2, &ok ); if ( (degree% 2)==1 ) { QMessageBox::critical( this, "Error", tr(" Sorry. I cannot create such a network. Degree must be even number"), "OK",0 ); return; } statusMessage( "Erasing any existing network. "); initNet(); statusMessage( "Creating a pseudo-random network where each node has the same degree... "); if (showProgressBarAct->isChecked() && newNodes > 300){ progressDialog= new QProgressDialog ( "Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ;)).", "Cancel", 0, (int) (newNodes+newNodes), this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.createSameDegreeRandomNetwork (newNodes, degree); QApplication::restoreOverrideCursor(); if (showProgressBarAct->isChecked() && newNodes > 300) progressDialog->deleteLater(); fileLoaded=false; graphChanged(); setWindowTitle("Untitled"); statusMessage( "Uniform random network created: " +QString::number(activeNodes())+" Nodes, " +QString::number( activeEdges())+" Edges"); } void MainWindow::slotCreateRandomGaussian(){ graphChanged(); } void MainWindow::slotCreateRandomScaleFree() { qDebug() << "MW;:slotCreateRandomScaleFree()"; m_randScaleFreeDialog = new RandScaleFreeDialog(this); connect( m_randScaleFreeDialog, &RandScaleFreeDialog::userChoices, this, &MainWindow::createScaleFreeNetwork); m_randScaleFreeDialog->exec(); } void MainWindow::createScaleFreeNetwork ( const int &nodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode) { qDebug() << "MW;:createScaleFreeNetwork()"; statusMessage( tr("Erasing any existing network. ")); initNet(); statusMessage( tr("Creating small world network. Please wait...")); double x0=scene->width()/2.0; double y0=scene->height()/2.0; double radius=(graphicsWidget->height()/2.0)-50; //pixels if (showProgressBarAct->isChecked() && nodes > 300){ progressDialog= new QProgressDialog( tr("Creating random network. Please wait \n (or disable me from Options > View > ProgressBar, next time )."), "Cancel", 0, (int) (2* nodes), this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.createRandomNetScaleFree( nodes, power, initialNodes, edgesPerStep, zeroAppeal, mode, x0, y0, radius); QApplication::restoreOverrideCursor(); if (showProgressBarAct->isChecked() && nodes > 300 ) progressDialog->deleteLater(); fileLoaded=false; graphChanged(); setWindowTitle("Untitled"); statusMessage( tr("Scale-free random network created: ") + QString::number(activeNodes()) + " nodes, "+QString::number( activeEdges())+" edges"); //float avGraphDistance=activeGraph.averageGraphDistance(); float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "New scale-free network", tr("Scale-free random network created.\n")+ tr("\nNodes: ")+ QString::number(activeNodes())+ tr("\nEdges: ") + QString::number( (mode == "graph" ) ? activeEdges()/2.0 : activeEdges()) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) + tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); } void MainWindow::slotCreateRandomSmallWorld() { qDebug() << "MW;:slotCreateRandomSmallWorld()"; m_randSmallWorldDialog = new RandSmallWorldDialog(this); connect( m_randSmallWorldDialog, &RandSmallWorldDialog::userChoices, this, &MainWindow::createSmallWorldNetwork); m_randSmallWorldDialog->exec(); } void MainWindow::createSmallWorldNetwork (const int &nodes, const int °ree, const float &beta, const QString &mode, const bool &diag) { Q_UNUSED(diag); qDebug() << "MW;:createSmallWorldNetwork()"; statusMessage( tr("Erasing any existing network. ")); initNet(); statusMessage( tr("Creating small world network. Please wait...")); double x0=scene->width()/2.0; double y0=scene->height()/2.0; double radius=(graphicsWidget->height()/2.0)-50; //pixels if (showProgressBarAct->isChecked() && nodes > 300){ progressDialog= new QProgressDialog( tr("Creating random network. Please wait \n (or disable me from Options > View > ProgressBar, next time )."), "Cancel", 0, (int) (2* nodes), this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.createRandomNetSmallWorld(nodes, degree, beta, x0, y0, radius); activeGraph.symmetrize(); QApplication::restoreOverrideCursor(); if (showProgressBarAct->isChecked() && nodes > 300 ) progressDialog->deleteLater(); fileLoaded=false; graphChanged(); setWindowTitle("Untitled"); statusMessage( tr("Small world random network created: ")+QString::number(activeNodes())+" nodes, "+QString::number( activeEdges())+" edges"); //float avGraphDistance=activeGraph.averageGraphDistance(); float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "New Small World", tr("Small world network created.\n")+ tr("\nNodes: ")+ QString::number(activeNodes())+ tr("\nEdges: ") + QString::number( (mode == "graph" ) ? activeEdges()/2.0 : activeEdges()) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) + tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); } /** Creates a lattice network, i.e. a connected network where every node has the same degree and is Edgeed with its neighborhood. */ void MainWindow::slotCreateRandomRingLattice(){ bool ok; statusMessage( "You have selected to create a ring lattice network. "); int newNodes=( QInputDialog::getInt( this, tr("Create ring lattice"), tr("This will create a ring lattice network, where each node has degree d:\n d/2 edges to the right and d/2 to the left.\n Please enter the number of nodes you want:"), 100, 4, maxNodes, 1, &ok ) ) ; if (!ok) { statusMessage( "You did not enter an integer. Aborting."); return; } int degree = QInputDialog::getInt( this, tr("Create ring lattice..."), tr("Now, enter an even number d. \nThis is the total number of edges each new node will have:"), 2, 2, newNodes-1, 2, &ok); if ( (degree% 2)==1 ) { QMessageBox::critical(this, "Error",tr(" Sorry. I cannot create such a network. Degree must be even number"), "OK",0); return; } statusMessage( "Erasing any existing network. "); initNet(); statusMessage( "Creating ring lattice network. Please wait..."); double x0=scene->width()/2.0; double y0=scene->height()/2.0; double radius=(graphicsWidget->height()/2.0)-50; //pixels if (showProgressBarAct->isChecked() && newNodes > 300){ progressDialog= new QProgressDialog("Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ;)).", "Cancel", 0, (int) (newNodes+newNodes), this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.createRandomNetRingLattice(newNodes, degree, x0, y0, radius ); QApplication::restoreOverrideCursor(); if (showProgressBarAct->isChecked() && newNodes > 300) progressDialog->deleteLater(); fileLoaded=false; // graphChanged(); statusMessage( "Ring lattice random network created: "+QString::number(activeNodes())+" nodes, "+QString::number( activeEdges())+" edges"); setWindowTitle("Untitled"); //float avGraphDistance=activeGraph.averageGraphDistance(); //float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "Ring Lattice", tr("Ring lattice network created.\n")+ tr("\nNodes: ")+ QString::number(activeNodes())+ tr("\nEdges: ")+ QString::number( activeEdges()/2.0) // + tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); } /** * Shows a dialog from where the user * creates a new network by crawling a given website */ void MainWindow::slotShowWebCrawlerDialog() { qDebug () << "MW: slotShowWebCrawlerDialog() - sending canvasWidth and Height"; activeGraph.setCanvasDimensions(graphicsWidget->width(), graphicsWidget->height()); m_WebCrawlerDialog.exec() ; } /** * Called from m_WebCrawlerDialog * Clears the loaded network (saving if needed) * then passes parameters to webCrawl of ActiveGraph class. */ void MainWindow::slotWebCrawl ( QString seed, int maxNodes, int maxRecursion, bool extLinks, bool intLinks) { this->slotFileClose(); activeGraph.webCrawl( seed, maxNodes, maxRecursion, extLinks, intLinks) ; } /** Calls GW: findNode() to find a node by its number or label. The node is then marked. */ void MainWindow::slotFindNode(){ qDebug ("MW: slotFindNode()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, tr("Find Node"), tr("No nodes present! \nLoad a network file first or create some nodes..."), tr("OK"),0 ); statusMessage( QString(tr("Nothing to find!")) ); return; } if ( markedNodesExist ) { // if a node has been already marked graphicsWidget->setMarkedNode(""); // call setMarkedNode to just unmark it. markedNodesExist=false; statusMessage( tr("Node unmarked.") ); return; // and return to MW } bool ok=false; QString nodeText = QInputDialog::getText(this, tr("Find Node"), tr("Enter node label or node number:"), QLineEdit::Normal,QString::null, &ok ); if (!ok) { statusMessage( tr("Find node operation cancelled.") ); return; } else { if ( graphicsWidget->setMarkedNode(nodeText) ) { markedNodesExist=true; statusMessage( tr("Node found and marked. Press Ctrl+F again to unmark...") ); } else { QMessageBox::information(this, tr("Find Node"), tr("Sorry. There is no such node in this network. \n Try again."), "OK",0); } } } /** * A slot activated when something has been changed in the graph. Makes the fileSave icon active and refreshes any LCD values. Also called from graphicsWidget. */ void MainWindow::graphChanged(){ qDebug("MW: graphChanged"); networkModified=true; fileSave->setIcon(QIcon(":/images/save.png")); fileSave->setEnabled(true); nodesLCD->display(activeGraph.vertices()); edgesLCD->display(activeEdges()); densityLCD->display( activeGraph.density() ); } void MainWindow::slotSelectAll(){ qDebug() << "MainWindow::slotSelectAll()"; graphicsWidget->selectAll(); statusMessage( QString(tr("Selected nodes: %1") ) .arg( selectedNodes().count() ) ); } void MainWindow::slotSelectNone(){ qDebug() << "MainWindow::slotSelectNone()"; graphicsWidget->selectNone(); statusMessage( QString(tr("Selection cleared") ) ); } /** Popups a context menu with some options when the user right-clicks on a node */ void MainWindow::openNodeContextMenu() { clickedJimNumber=clickedJim->nodeNumber(); qDebug("MW: openNodeContextMenu() for node %i at %i, %i", clickedJimNumber, QCursor::pos().x(), QCursor::pos().y()); QMenu *nodeContextMenu = new QMenu(QString::number(clickedJimNumber), this); Q_CHECK_PTR( nodeContextMenu ); //displays "out of memory" if needed if ( selectedNodes().count() == 1) { nodeContextMenu -> addAction( tr("## NODE ") + QString::number(clickedJimNumber) + " ## "); } else { nodeContextMenu -> addAction( tr("## NODE ") + QString::number(clickedJimNumber) + " ## " + tr(" (selected nodes: ") + QString::number (selectedNodes().count() ) + ")"); } nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(addEdgeAct); nodeContextMenu -> addAction(removeNodeAct ); nodeContextMenu -> addAction(propertiesNodeAct ); //QCursor::pos() is good only for menus not related with node coordinates nodeContextMenu -> exec(QCursor::pos() ); delete nodeContextMenu; clickedJimNumber=-1; //undo node selection } /** Popups a context menu with some options when the user right-clicks on an Edge */ void MainWindow::openEdgeContextMenu() { int source=clickedEdge->sourceNodeNumber(); int target=clickedEdge->targetNodeNumber(); qDebug("MW: openEdgeContextMenu() for edge %i-%i at %i, %i",source, target, QCursor::pos().x(), QCursor::pos().y()); QString edgeName=QString::number(source)+QString("->")+QString::number(target); //make the menu QMenu *edgeContextMenu = new QMenu(edgeName, this); edgeContextMenu -> addAction( "## EDGE " + edgeName + " ## "); edgeContextMenu -> addSeparator(); edgeContextMenu -> addAction( removeEdgeAct ); edgeContextMenu -> addAction( changeEdgeWeightAct ); edgeContextMenu -> addAction( changeEdgeColorAct ); edgeContextMenu -> exec(QCursor::pos() ); delete edgeContextMenu; } /** Popups a context menu with some options when the user right-clicks on the scene */ void MainWindow::openContextMenu( const QPointF &mPos) { cursorPosGW=mPos; QMenu *contextMenu = new QMenu(" Menu",this); Q_CHECK_PTR( contextMenu ); //displays "out of memory" if needed contextMenu -> addAction( "## Selected nodes: " + QString::number( selectedNodes().count() ) + " ## "); contextMenu -> addSeparator(); contextMenu -> addAction( addNodeAct ); if (selectedNodes().count()) { contextMenu -> addAction(propertiesNodeAct ); } contextMenu -> addAction( addEdgeAct ); QMenu *options=new QMenu("Options", this); contextMenu -> addMenu(options ); options -> addAction (changeBackColorAct ); options -> addAction (backgroundImageAct ); options -> addAction (changeAllNodesSizeAct ); options -> addAction (changeAllNodesShapeAct ); options -> addAction (changeAllNodesColorAct ); options -> addAction (changeAllEdgesColorAct ); options -> addAction (displayNodeNumbersAct); options -> addAction (displayNodeLabelsAct); //QCursor::pos() is good only for menus not related with node coordinates contextMenu -> exec(QCursor::pos() ); delete contextMenu; cursorPosGW=QPoint(-1,-1); } QList MainWindow::selectedNodes() { return graphicsWidget->selectedItems(); } /** * When the user clicks on a node, displays some information about it on the status bar. */ void MainWindow::nodeInfoStatusBar ( Node *jim) { qDebug ("MW: NodeInfoStatusBar()"); edgeClicked=false; nodeClicked=true; clickedJim=jim; clickedJimNumber=clickedJim->nodeNumber(); int inDegree=activeGraph.inDegree(clickedJimNumber); int outDegree=activeGraph.outDegree(clickedJimNumber); selectedNodeLCD->display (clickedJimNumber); inDegreeLCD->display (inDegree); outDegreeLCD->display (outDegree); if (activeGraph.vertices() < 500) clucofLCD->display(activeGraph.localClusteringCoefficient(clickedJimNumber)); statusMessage( QString(tr("(%1, %2); Node %3, label %4 - " "In-Degree: %5, Out-Degree: %6")).arg( ceil( clickedJim->x() ) ) .arg( ceil( clickedJim->y() )).arg( clickedJimNumber ).arg( clickedJim->labelText() ) .arg(inDegree).arg(outDegree) ); } /** * When the user clicks on an Edge, displays some information about it on the status bar. */ void MainWindow::edgeInfoStatusBar (Edge* edge) { clickedEdge=edge; edgeClicked=true; nodeClicked=false; if (edge->isReciprocal()) { float outbound = activeGraph.hasArc (edge->sourceNodeNumber(), edge->targetNodeNumber()); float inbound = activeGraph.hasArc (edge->targetNodeNumber(), edge->sourceNodeNumber()); if (outbound==inbound) statusMessage( QString (tr("Symmetric edge %1 <--> %2 of weight %3 has been selected. " "Click again to unselect it.")) .arg( edge->sourceNodeNumber() ).arg(edge->targetNodeNumber()) .arg(edge->weight()) ) ; else statusMessage( QString (tr("Arc %1 --> %2 of weight %3 " " and Arc %4 --> %5 of weight %6" " have been selected. " "Click again to unselect them.")) .arg(edge->sourceNodeNumber() ) .arg(edge->targetNodeNumber()) .arg(outbound) .arg( edge->targetNodeNumber() ) .arg(edge->sourceNodeNumber()) .arg(inbound) ) ; } else { statusMessage( QString(tr("Arc %1 --> %2 of weight %3 has been selected. " "Click again to unselect it.")) .arg( edge->sourceNodeNumber() ).arg(edge->targetNodeNumber()) .arg(edge->weight()) ) ; } } /** * Deletes a node and the attached objects (edges, etc). * It deletes clickedJim (signal from GraphicsView or set by another function) * or else asks for a nodeNumber to remove. The nodeNumber is doomedJim. Called from nodeContextMenu */ void MainWindow::slotRemoveNode() { qDebug() << "MW: slotRemoveNode()"; if (!activeGraph.vertices()) { QMessageBox::critical( this, "Error", tr("Nothing to do! \n" "Load a network file or add some nodes first."), "OK",0); statusMessage( tr("Nothing to remove.") ); return; } if (activeGraph.relations() > 1){ QMessageBox::critical( this, "Error", tr("Cannot remove node! \n" "This a network with more than 1 relations. If you remove " "a node from the active relation, and then ask me to go " "to the previous or the next relation, then I would crash " "because I would try to display edges from a delete node." "You can only add nodes in multirelational networks."), "OK",0); statusMessage( tr("Nothing to remove.") ); return; } int doomedJim=-1, min=-1, max=-1; bool ok=false; min = activeGraph.firstVertexNumber(); max = activeGraph.lastVertexNumber(); qDebug("MW: min is %i and max is %i", min, max); if (min==-1 || max==-1 ) { qDebug("ERROR in finding min max nodeNumbers. Abort"); return; } else if (nodeClicked && clickedJimNumber >= 0 && clickedJimNumber<= max ) { doomedJim=clickedJimNumber ; } else if (!nodeClicked ) { doomedJim = QInputDialog::getInt(this,"Remove node",tr("Choose a node to remove between (" + QString::number(min).toLatin1()+"..."+QString::number(max).toLatin1()+"):"),min, 1, max, 1, &ok); if (!ok) { statusMessage( "Remove node operation cancelled." ); return; } } qDebug ("MW: removing vertex with number %i from Graph", doomedJim); activeGraph.removeVertex(doomedJim); clickedJimNumber=-1; nodeClicked=false; graphChanged(); qDebug("MW: removeNode() completed. Node %i removed completely.",doomedJim); statusMessage( tr("Node removed completely. Ready. ") ); } void MainWindow::slotChangeNodeProperties() { qDebug() << "MW::slotChangeNodeProperties()"; // if (!fileLoaded && !networkModified ) { if (!activeGraph.vertices()) { QMessageBox::critical( this, "Error", tr("Nothing to do! \n" "Load a network file or add some nodes first."), "OK",0); statusMessage( tr("Nothing to remove.") ); return; } int min=-1, max=-1, size = initNodeSize; QColor color = QColor(initNodeColor); QString shape= initNodeShape, label=""; bool ok=false; if ( selectedNodes().count() == 0) { min = activeGraph.firstVertexNumber(); max = activeGraph.lastVertexNumber(); qDebug("MW: min is %i and max is %i", min, max); if (min==-1 || max==-1 ) { qDebug("ERROR in finding min max nodeNumbers. Abort"); return; } clickedJimNumber = QInputDialog::getInt( this, "Node Properties", tr("Choose a node between (" + QString::number(min).toLatin1() +"..." + QString::number(max).toLatin1()+"):"),min, 1, max, 1, &ok); if (!ok) { statusMessage( "Node properties cancelled." ); return; } } else { foreach (QGraphicsItem *item, selectedNodes() ) { if ( (clickedJim = qgraphicsitem_cast(item) )) { if ( selectedNodes().count() > 1 ) { clickedJimNumber = clickedJim->nodeNumber(); color = activeGraph.vertexColor( clickedJimNumber ); shape = activeGraph.vertexShape( clickedJimNumber); size = activeGraph.vertexSize ( clickedJimNumber); } else { clickedJimNumber = clickedJim->nodeNumber(); label = activeGraph.vertexLabel( clickedJimNumber ); color = activeGraph.vertexColor( clickedJimNumber ); shape = activeGraph.vertexShape( clickedJimNumber); size = activeGraph.vertexSize ( clickedJimNumber); } } } } qDebug ()<< "MW: changing properties for "<< clickedJimNumber ; m_nodeEditDialog = new NodeEditDialog(this, label, size, color, shape) ; connect( m_nodeEditDialog, &NodeEditDialog::userChoices, this, &MainWindow::slotNodeProperties ); m_nodeEditDialog->exec(); statusMessage( tr("Node properties dialog opened. Ready. ") ); } void MainWindow::slotNodeProperties( const QString label, const int size, const QString value, const QColor color, const QString shape) { qDebug()<< "MW::slotNodeProperties() " << " label " << label << " size " << size << "value " << value << " color " << color << " shape " << shape << " clickedJimNumber " <(item) )) { clickedJimNumber = clickedJim->nodeNumber(); if ( selectedNodes().count() > 1 ) { activeGraph.setVertexLabel( clickedJimNumber, label + QString::number(clickedJimNumber) ); } else activeGraph.setVertexLabel( clickedJimNumber, label ); if (!showLabels()) displayNodeLabelsAct->setChecked(true); qDebug () << clickedJimNumber; qDebug()<<"MW: updating color "; activeGraph.setVertexColor( clickedJimNumber, color.name()); qDebug()<<"MW: updating size "; activeGraph.setVertexSize(clickedJimNumber,size); qDebug()<<"MW: updating shape "; activeGraph.setVertexShape( clickedJimNumber, shape); clickedJim->setShape(shape); } } clickedJim=0; clickedJimNumber=-1; graphChanged(); statusMessage( tr("Ready. ")); } /** * Adds a new edge between two nodes specified by the user. Called when user clicks on the MW button "Add edge". */ void MainWindow::slotAddEdge(){ qDebug ("MW: slotAddEdge()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("No nodes!! \nCreate some nodes first."), "OK",0); statusMessage( tr("There are no nodes yet...") ); return; } int sourceNode=-1, targetNode=-1, sourceIndex=-1, targetIndex=-1; float weight=1; //weight of this new edge should be one... bool ok=false; int min=activeGraph.firstVertexNumber(); int max=activeGraph.lastVertexNumber(); if (min==max) return; //if there is only one node -> no edge if (!nodeClicked) { sourceNode=QInputDialog::getInt(this, "Create new edge, Step 1",tr("This will draw a new edge between two nodes. \nEnter source node ("+QString::number(min).toLatin1()+"..."+QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Add edge operation cancelled." ); return; } } else sourceNode=clickedJimNumber; qDebug () << "sourceNode=clickedJimNumber " << clickedJimNumber; if ( (sourceIndex =activeGraph.hasVertex(sourceNode)) ==-1 ) { statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","No such node.", "OK",0); qDebug ("MW: slotAddEdge: Cant find sourceNode %i.", sourceNode); return; } targetNode=QInputDialog::getInt (this, "Create new edge, Step 2", tr("Source node accepted. \nNow enter target node ("+ QString::number(min).toLatin1()+"..."+QString::number(max) .toLatin1()+"):"),min, min, max , 1, &ok) ; if (!ok) { statusMessage( "Add edge target operation cancelled." ); return; } if ( (targetIndex=activeGraph.hasVertex(targetNode)) ==-1 ) { statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","No such node.", "OK",0); qDebug ("MW: slotAddEdge: Cant find targetNode %i",targetNode); return; } weight=QInputDialog::getDouble( this, "Create new edge, Step 3", tr("Source and target nodes accepted. \n " "Please, enter the weight of new edge: "),1.0, -100.0, 100.0, 1, &ok); if (!ok) { statusMessage( "Add edge operation cancelled." ); return; } //Check if this edge already exists... if (activeGraph.hasArc(sourceNode, targetNode)!=0 ) { qDebug("edge exists. Aborting"); statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","edge already exists.", "OK",0); return; } addEdge(sourceNode, targetNode, weight); graphChanged(); statusMessage( tr("Ready. ") ); } /** helper to slotAddEdge() above Also called from GW::userMiddleClicked() signal when user creates edges with middle-clicks Calls Graph::createEdge method to add the new edge to the active Graph */ void MainWindow::addEdge (int v1, int v2, float weight) { qDebug("MW: addEdge() - setting user preferences and calling Graph::createEdge(...)"); bool drawArrows=displayEdgesArrowsAct->isChecked(); int reciprocal=0; bool bezier = false; activeGraph.createEdge(v1, v2, weight, reciprocal, drawArrows, bezier); if ( activeEdges() == 1 && changeRelationCombo->count() == 0 ) { addRelation(); } } /** * Erases the clicked edge. Otherwise asks the user to specify one edge. * First deletes arc reference from object nodeVector * then deletes arc item from scene **/ void MainWindow::slotRemoveEdge(){ if ( (!fileLoaded && !networkModified) || activeEdges() ==0 ) { QMessageBox::critical(this, "Error",tr("There are no edges! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("No edges to remove - sorry.") ); return; } int min=0, max=0, sourceNode=-1, targetNode=-1; bool ok=false; min=activeGraph.firstVertexNumber(); max=activeGraph.lastVertexNumber(); if (!edgeClicked) { sourceNode=QInputDialog::getInt( this,tr("Remove edge"), tr("Source node: (")+QString::number(min)+ "..."+QString::number(max)+"):", min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Remove edge operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, tr("Remove edge"), tr("Target node: (")+QString::number(min)+"..."+QString::number(max)+"):",min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Remove edge operation cancelled." ); return; } if ( activeGraph.hasArc(sourceNode, targetNode)!=0 ) { if (activeGraph.symmetricEdge(sourceNode, targetNode) ) graphicsWidget->unmakeEdgeReciprocal(targetNode, sourceNode); graphicsWidget->eraseEdge(sourceNode, targetNode); activeGraph.removeEdge(sourceNode, targetNode); } else { QMessageBox::critical(this, "Remove edge",tr("There is no such edge."), "OK",0); statusMessage( tr("There are no nodes yet...") ); return; } } else { sourceNode = clickedEdge->sourceNodeNumber(); targetNode = clickedEdge->targetNodeNumber(); if (activeGraph.symmetricEdge(sourceNode, targetNode) ) { QString s=QString::number(sourceNode); QString t=QString::number(targetNode); switch (QMessageBox::information( this, tr("Remove edge"), tr("This edge is directed. \n") + tr("Select what Direction to delete or Both..."), s+" -> "+ t, t+" -> "+s, tr("Both"), 0, 1 )) { case 0: graphicsWidget->removeItem(clickedEdge); activeGraph.removeEdge(sourceNode, targetNode); //make new edge // graphicsWidget->unmakeEdgeReciprocal(clickedEdge->targetNodeNumber(), clickedEdge->sourceNodeNumber()); //FIXME weight should be the same graphicsWidget->drawEdge(targetNode, sourceNode, 1, false, displayEdgesArrowsAct->isChecked(), initEdgeColor, false); break; case 1: clickedEdge->unmakeReciprocal(); //graphicsWidget->removeItem(clickedEdge); activeGraph.removeEdge(targetNode, sourceNode); // graphicsWidget->drawEdge(i, j, false, drawArrowsAct->isChecked(), initEdgeColor, false); break; case 2: graphicsWidget->removeItem(clickedEdge); activeGraph.removeEdge(sourceNode, targetNode); activeGraph.removeEdge(targetNode, sourceNode); } } else { graphicsWidget->removeItem(clickedEdge); activeGraph.removeEdge(sourceNode, targetNode); } } graphChanged(); qDebug("MW: View items now: %i ", graphicsWidget->items().size()); qDebug("MW: Scene items now: %i ", scene->items().size()); } /** * Changes the color of all nodes */ void MainWindow::slotAllNodesColor(){ QColor color = QColorDialog::getColor( Qt::red, this, "Change the color of all nodes" ); if (color.isValid()) { initNodeColor=color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MainWindow::slotAllNodesColor() : " << initNodeColor; activeGraph.setAllVerticesColor(initNodeColor); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Nodes color change aborted. ") ); } } //TODO slotChangeEdgeLabel void MainWindow::slotChangeEdgeLabel(){ graphChanged(); } /** * Changes the colour of the clicked edge. * If no edge is clicked, then it asks the user to specify one. */ void MainWindow::slotChangeEdgeColor(){ qDebug() << "MW::slotChangeEdgeColor()"; if ( ( !fileLoaded && !networkModified) || activeEdges() ==0 ) { QMessageBox::critical(this, "Error", tr("There are no edges! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("No edges present...") ); return; } int sourceNode=-1, targetNode=-1; bool ok=false; int min=activeGraph.firstVertexNumber(); int max=activeGraph.lastVertexNumber(); if (!edgeClicked) { //no edge clicked. Ask user to define an edge. sourceNode=QInputDialog::getInt(this, "Change edge color", tr("Select edge source node: ("+ QString::number(min).toLatin1()+ "..."+QString::number(max).toLatin1()+ "):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge color operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, "Change edge color...", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..." + QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge color operation cancelled." ); return; } if ( ! activeGraph.hasArc (sourceNode, targetNode ) ) { statusMessage( tr("There is no such edge. ") ); QMessageBox::critical(this, "Error", tr("No edge! \nNo such edge found in current network."), "OK",0); return; } } else { //edge has been clicked. sourceNode = clickedEdge->sourceNodeNumber(); targetNode = clickedEdge->targetNodeNumber(); } QColor color = QColorDialog::getColor( Qt::black, this, tr("Select new color....") ); if ( color.isValid()) { QString newColor=color.name(); qDebug() << "MW::slotChangeEdgeColor() - " << sourceNode << " -> " << targetNode << " newColor " << newColor; activeGraph.setEdgeColor( sourceNode, targetNode, newColor); statusMessage( tr("Ready. ") ); } else { statusMessage( tr("Change edge color aborted. ") ); } } /** * Changes the weight of the clicked edge. * If no edge is clicked, asks the user to specify an Edge. */ void MainWindow::slotChangeEdgeWeight(){ if ( ( !fileLoaded && !networkModified) || activeEdges() ==0 ) { QMessageBox::critical(this, "Error",tr("There are no edges! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("No edges present...") ); return; } qDebug("MW::slotChangeEdgeWeight()"); int sourceNode=-1, targetNode=-1; float newWeight=1.0; int min=activeGraph.firstVertexNumber(); int max=activeGraph.lastVertexNumber(); bool ok=false; if (!edgeClicked) { sourceNode=QInputDialog::getInt(this, "Change edge weight",tr("Select edge source node: ("+QString::number(min).toLatin1()+"..."+QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge weight operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, "Change edge weight...", tr("Select edge target node: ("+QString::number(min).toLatin1()+"..."+QString::number(max).toLatin1()+"):"),min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge weight operation cancelled." ); return; } qDebug("source %i target %i",sourceNode, targetNode); QList list=scene->items(); for (QList::iterator it=list.begin(); it!= list.end() ; it++) if ( (*it)->type()==TypeEdge) { Edge *edge=(Edge*) (*it); qDebug ("MW: searching edge..."); if ( edge->sourceNodeNumber()==sourceNode && edge->targetNodeNumber()==targetNode ) { qDebug("MW: edge found"); newWeight=(float) QInputDialog::getDouble(this, "Change edge weight...",tr("New edge Weight: "), 1, -100, 100 ,1, &ok ) ; if (ok) { edge->setWeight(newWeight); edge->update(); activeGraph.setArcWeight(sourceNode, targetNode, newWeight); statusMessage( QString(tr("Ready.")) ); return; } else { statusMessage( QString(tr("input error. Abort.")) ); return; } } } } else { //edgeClicked qDebug() << "MW: slotChangeedgeWeight() - an Edge has already been clicked"; sourceNode=clickedEdge->sourceNodeNumber(); targetNode=clickedEdge->targetNodeNumber(); qDebug() << "MW: slotChangeEdgeWeight() from " << sourceNode << " to " << targetNode; if ( activeGraph.symmetricEdge(sourceNode, targetNode) ) { QString s=QString::number(sourceNode); QString t=QString::number(targetNode); switch (QMessageBox::information( this, tr("Change edge weight"), tr("This edge is reciprocal. \n") + tr("Select what Direction to change or Both..."), s+" -> "+ t, t+" -> "+s, tr("Both"), 0, 1 )) { case 0: qDebug("MW: slotChangeEdgeWeight() real edge %i -> %i", sourceNode, targetNode); newWeight=QInputDialog::getDouble(this, "Change edge weight...",tr("New edge weight: "), 1.0, -100.0, 100.00 ,1, &ok) ; if (ok) { clickedEdge->setWeight(newWeight); clickedEdge->update(); qDebug()<<"MW: newWeight will be "<< newWeight; activeGraph.setArcWeight(sourceNode, targetNode, newWeight); statusMessage( QString(tr("Ready.")) ); return; } else { statusMessage( QString(tr("Change edge weight cancelled.")) ); return; } break; case 1: qDebug("MW: slotChangeEdgeWeight() virtual edge %i -> %i",targetNode , sourceNode); newWeight=(float) QInputDialog::getDouble(this, "Change edge weight...",tr("New edge Weight: "), 1, -100, 100 ,1, &ok ) ; if (ok) { qDebug()<<"MW: newWeight will be "<< newWeight; activeGraph.setArcWeight( targetNode, sourceNode, newWeight); statusMessage( QString(tr("Ready.")) ); return; } else { statusMessage( QString(tr("Change edge weight cancelled.")) ); return; } break; case 2: qDebug("MW: slotChangeEdgeWeight() both directions %i <-> %i",targetNode , sourceNode); newWeight=(float) QInputDialog::getDouble(this, "Change edge weight...",tr("New edge Weight: "), 1, -100, 100 ,1, &ok ) ; if (ok) { qDebug()<<"MW: Changing first direction. NewWeight will be "<< newWeight; activeGraph.setArcWeight(sourceNode, targetNode, newWeight); qDebug()<<"MW: Changing opposite direction. NewWeight will be "<< newWeight; activeGraph.setArcWeight( targetNode, sourceNode, newWeight); statusMessage( QString(tr("Ready.")) ); return; } else { statusMessage( QString(tr("Change edge weight cancelled.")) ); return; } break; } } else { qDebug() << "MW: slotChangeEdgeWeight() real edge " << sourceNode << " -> " <setWeight(newWeight); qDebug() << "MW: slotChangeEdgeWeight() calling update "; clickedEdge->update(); qDebug()<<"MW: newWeight will be "<< newWeight; activeGraph.setArcWeight(sourceNode, targetNode, newWeight); statusMessage( QString(tr("Ready.")) ); return; } else { statusMessage( QString(tr("Change edge weight cancelled.")) ); return; } } edgeClicked=false; } } /** * Filters Nodes by their value TODO slotFilterNodes * */ void MainWindow::slotFilterNodes(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to filter! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to filter!")) ); return; } } /** * @brief MainWindow::slotFilterIsolateNodes *Calls Graph::filterIsolateVertices to toggle visibility of isolated vertices */ void MainWindow::slotFilterIsolateNodes(bool checked){ Q_UNUSED(checked); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to filter! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to filter!")) ); return; } qDebug()<< "MW: slotFilterIsolateNodes"; activeGraph.filterIsolateVertices( ! filterIsolateNodesAct->isChecked() ); statusMessage( QString(tr("Isolate nodes visibility toggled!")) ); } /** * Shows a dialog from where the user may * filter edges according to their weight * All edges weighted more (or less) than the specified weight will be disabled. */ void MainWindow::slotShowFilterEdgesDialog() { if (!fileLoaded && !networkModified ) { statusMessage( QString(tr("Load a network file first. \nThen you may ask me to compute something!")) ); return; } m_filterEdgesByWeightDialog.exec() ; } /** * Transforms all nodes to edges TODO slotTransformNodes2Edges */ void MainWindow::slotTransformNodes2Edges(){ graphChanged(); } /** * Converts all edges to double edges, so that the network becomes undirected (symmetric adjacency matrix). */ void MainWindow::slotSymmetrize(){ if ( ( !fileLoaded && !networkModified) || activeEdges() ==0 ) { QMessageBox::critical(this, "Error",tr("There are no edges! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("No edges present...") ); return; } qDebug("MW: slotSymmetrize() calling symmetrize"); activeGraph.symmetrize(); QMessageBox::information(this, "Symmetrize",tr("All edges are reciprocal. \nYour network is symmetric..."), "OK",0); statusBar()->showMessage (QString(tr("Ready")), statusBarDuration) ; } /** TODO slotColorationStrongStructural */ void MainWindow::slotColorationStrongStructural() { } /** TODO slotColorationRegular */ void MainWindow::slotColorationRegular() { } /** * @brief MainWindow::slotLayoutRandom * to reposition all nodes on a circular layout randomly */ void MainWindow::slotLayoutRandom(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. \n" "Then we can talk about layouts!"), "OK",0); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } double maxWidth=graphicsWidget->width(); double maxHeight=graphicsWidget->height(); statusMessage( QString(tr("Randomizing nodes positions. Please wait...")) ); graphicsWidget->clearGuides(); createProgressBar(); activeGraph.layoutRandom(maxWidth, maxHeight); destroyProgressBar(); statusMessage( tr("Node positions are now randomized.") ); } /** * @brief MainWindow::slotLayoutCircularRandom */ void MainWindow::slotLayoutCircularRandom(){ qDebug() << "MainWindow::slotLayoutCircularRandom()"; if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. \n" "Then we can talk about layouts!"), "OK",0); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } double x0=scene->width()/2.0; double y0=scene->height()/2.0; double maxRadius=(graphicsWidget->height()/2.0)-50; //pixels statusMessage( QString(tr("Calculating new nodes positions. Please wait...")) ); graphicsWidget->clearGuides(); createProgressBar(); activeGraph.layoutCircularRandom(x0, y0, maxRadius); destroyProgressBar(); statusMessage( tr("Nodes in random circles.") ); } /** slotLayoutSpringEmbedder called from menu or toolbox checkbox */ void MainWindow::slotLayoutSpringEmbedder(bool state ){ qDebug()<< "MW:slotLayoutSpringEmbedder"; if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are node nodes yet!\nLoad a network file or create a new network first. \nThen we can talk about layouts!"), "OK",0); statusMessage( tr("I am really sorry. You must really load a file first... ") ); layoutEadesBx->setCheckState(Qt::Unchecked); return; } //Stop any other layout running layoutFruchtermanBx->setCheckState(Qt::Unchecked); activeGraph.nodeMovement(!state, 2, graphicsWidget->width(), graphicsWidget->height()); scene->setItemIndexMethod (QGraphicsScene::NoIndex); //best when moving items if (state){ statusMessage( tr("Embedding a spring-gravitational model on the network.... ") ); layoutEadesBx->setCheckState(Qt::Checked); activeGraph.nodeMovement(state, 1, graphicsWidget->width(), graphicsWidget->height()); statusMessage( tr("Click on the checkbox \"Spring-Embedder\" to stop movement!") ); } else { layoutEadesBx->setCheckState(Qt::Unchecked); activeGraph.nodeMovement(state, 1, graphicsWidget->width(), graphicsWidget->height()); statusMessage( tr("Movement stopped!") ); } scene->setItemIndexMethod (QGraphicsScene::BspTreeIndex); //best when not moving items } /** slotLayoutFruchterman called from menu */ void MainWindow::slotLayoutFruchterman(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes yet!\nLoad a network file or create a new network first. \nThen we can talk about layouts!"), "OK",0); statusMessage( tr("I am really sorry. You must really load a file first... ") ); return; } if (layoutFruchtermanBx->checkState() == Qt::Unchecked){ statusMessage( tr("Embedding a repelling-attracting forces model on the network.... ") ); layoutFruchtermanBx->setCheckState(Qt::Checked); statusMessage( tr("Click on the checkbox \"Fruchterman-Reingold\" to stop movement!") ); } else { layoutFruchtermanBx->setCheckState(Qt::Unchecked); statusMessage( tr("Movement stopped!") ); } } /** Called when user presses button. Calls Graph::startNodeMovement to embed a repelling-attracting forces layout... */ void MainWindow::layoutFruchterman (int state){ qDebug("MW: layoutFruchterman ()"); layoutEadesBx->setChecked(false); scene->setItemIndexMethod (QGraphicsScene::NoIndex); //best when moving items activeGraph.nodeMovement(state, 2, graphicsWidget->width(), graphicsWidget->height()); scene->setItemIndexMethod (QGraphicsScene::BspTreeIndex); //best when not moving items } /** * @brief MainWindow::slotLayoutNodeSizesByOutDegree * Resizes all nodes according to their outDegree * Called when user selects the relevant menu entry or the option in the toolbox * @param checked */ void MainWindow::slotLayoutNodeSizesByOutDegree(bool checked){ if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("There are no nodes yet!\n" "Load a network file or create a new network first. " "Then we can talk about layouts!"), "OK",0); statusMessage( tr("I am really sorry. You must really load a file first... ") ); return; } qDebug("MW: slotLayoutNodeSizesByOutDegree()"); if (checked != true) { qDebug("MW: slotLayoutNodeSizesByOutDegree() resetting size"); nodeSizesByOutDegreeAct->setChecked(false); nodeSizesByOutDegreeBx->setChecked(false); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 0, false, false, false); QApplication::restoreOverrideCursor(); return; } qDebug("MW: slotLayoutNodeSizesByOutDegree() setting size"); nodeSizesByOutDegreeAct->setChecked(true); nodeSizesByOutDegreeBx->setChecked(true); nodeSizesByInDegreeAct->setChecked(false); nodeSizesByInDegreeBx->setChecked(false); askAboutWeights(); statusMessage( tr("Embedding node size model on the network.... ") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 1,considerWeights,inverseWeights, filterIsolateNodesAct->isChecked()); QApplication::restoreOverrideCursor( ); } /** * @brief MainWindow::slotLayoutNodeSizesByInDegree * Resizes all nodes according to their inDegree * Called when user selects the relevant menu entry or the option in the toolbox * @param checked */ void MainWindow::slotLayoutNodeSizesByInDegree(bool checked){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("You must be dreaming! \nLoad a network file or create a new network first. \nThen we can talk about layouts!"), "OK",0); statusMessage( tr("I am really sorry. You must really load a file first... ") ); return; } qDebug("MW: slotLayoutNodeSizesByInDegree()"); if (checked != true) { qDebug("MW: slotLayoutNodeSizesByInDegree() resetting size"); nodeSizesByInDegreeAct->setChecked(false); nodeSizesByInDegreeBx->setChecked(false); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 0, false,false, false); QApplication::restoreOverrideCursor(); return; } qDebug("MW: slotLayoutNodeSizesByInDegree() setting size"); nodeSizesByOutDegreeAct->setChecked(false); nodeSizesByOutDegreeBx->setChecked(false); nodeSizesByInDegreeAct->setChecked(true); nodeSizesByInDegreeBx->setChecked(true); askAboutWeights(); statusMessage( tr("Embedding node size model on the network.... ") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 9, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); QApplication::restoreOverrideCursor( ); } /** * @brief MainWindow::slotLayoutGuides * @param state */ void MainWindow::slotLayoutGuides(int state){ qDebug()<< "MW:slotLayoutGuides()"; if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are node nodes yet!\nLoad a network file or create a new network first. \nThen we can talk about layouts!"), "OK",0); statusMessage( tr("I am really sorry. You must really load a file first... ") ); //layoutGuidesBx->setCheckState(Qt::Unchecked); return; } if (state){ qDebug()<< "MW:slotLayoutGuides() - will be displayed"; statusMessage( tr("Layout Guides will be displayed") ); } else { qDebug()<< "MW:slotLayoutGuides() - will NOT be displayed"; graphicsWidget->clearGuides(); statusMessage( tr("Layout Guides will not be displayed") ); } } /** * @brief MainWindow::slotLayoutCircularByProminenceIndex * Checks sender text() to find out who QMenu item was pressed * calls slotLayoutCircularByProminenceIndex(QString) */ void MainWindow::slotLayoutCircularByProminenceIndex(){ qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex()"; if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. \n" "Then we can talk about layouts!"), "OK",0); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText=menuitem->text(); qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutCircularByProminenceIndex(menuItemText); } /** * @brief MainWindow::slotLayoutCircularByProminenceIndex * Overloaded - called when selectbox changes in the toolbox * or from slotLayoutCircularByProminenceIndex() when the user click on menu * Repositions all nodes on a Circular layout based on that index * More prominent nodes are closer to the centre of the screen. */ void MainWindow::slotLayoutCircularByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex() "; if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. \n" "Then we can talk about layouts!"), "OK",0); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } int userChoice = 0; QString prominenceIndexName = choice; if ( prominenceIndexName.contains("Degree Centrality") ) userChoice=1; else if ( prominenceIndexName == "Closeness Centrality") userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centrality")) userChoice=4; else if (prominenceIndexName.contains("Stress Centrality")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centrality")) userChoice=6; else if (prominenceIndexName.contains("Power Centrality")) userChoice=7; else if (prominenceIndexName.contains("Information Centrality")) userChoice=8; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=9; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=10; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=11; qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(0); bool dropIsolates=false; //check if CC was selected and the graph is disconnected. if (userChoice == 2 ) { int connectedness=activeGraph.connectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations" "otherwise Closeness Centrality " "index can not be defined, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can conside using the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is VERY SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:" "Aii=1+weighted_degree_ni" "Aij=1 if (i,j)=0" "Aij=1-wij if (i,j)=wij" "Next, it will compute the inverse matrix C of A." "The computation of the inverse matrix is VERY CPU intensive function." "because it uses the Gauss-Jordan elimination algorithm.\n\n " "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); double x0=scene->width()/2.0; double y0=scene->height()/2.0; double maxRadius=(graphicsWidget->height()/2.0)-50; //pixels statusMessage( QString(tr("Calculating new nodes positions. Please wait...")) ); graphicsWidget->clearGuides(); createProgressBar(); activeGraph.layoutCircularByProminenceIndex( x0, y0, maxRadius, userChoice, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked() || dropIsolates); destroyProgressBar(); statusMessage( tr("Nodes in inner circles have greater prominence index.") ); } /** * @brief MainWindow::slotLayoutNodeSizesByProminenceIndex * Called when selectbox changes in the toolbox */ void MainWindow::slotLayoutNodeSizesByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutNodeSizesByProminenceIndex() "; if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. \n" "Then we can talk about layouts!"), "OK",0); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } int userChoice = 0; QString prominenceIndexName = choice; if ( prominenceIndexName.contains("Degree Centrality") ) userChoice=1; else if ( prominenceIndexName == "Closeness Centrality") userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centrality")) userChoice=4; else if (prominenceIndexName.contains("Stress Centrality")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centrality")) userChoice=6; else if (prominenceIndexName.contains("Power Centrality")) userChoice=7; else if (prominenceIndexName.contains("Information Centrality")) userChoice=8; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=9; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=10; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=11; qDebug() << "MainWindow::slotLayoutNodeSizesByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(0); //check if CC was selected and the graph is disconnected. bool dropIsolates=false; if (userChoice == 2 ) { int connectedness=activeGraph.connectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is VERY SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:" "Aii=1+weighted_degree_ni" "Aij=1 if (i,j)=0" "Aij=1-wij if (i,j)=wij" "Next, it will compute the inverse matrix C of A." "The computation of the inverse matrix is VERY CPU intensive function." "because it uses the Gauss-Jordan elimination algorithm.\n\n " "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); statusMessage( QString(tr("Calculating new node sizes. Please wait...")) ); graphicsWidget->clearGuides(); createProgressBar(); activeGraph.layoutVerticesSizeByProminenceIndex( userChoice, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked() || dropIsolates); destroyProgressBar(); statusMessage( tr("Bigger nodes have greater prominence index.") ); } /** * @brief MainWindow::slotLayoutLevelByProminenceIndex * Checks sender text() to find out who QMenu item was pressed * and what prominence index was chosen * calls slotLayoutLevelByProminenceIndex(QString) */ void MainWindow::slotLayoutLevelByProminenceIndex(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical (this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. " "\nThen we can talk about layouts!" ), "OK",0 ); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText = menuitem->text(); qDebug() << "MainWindow::slotLayoutLevelByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutLevelByProminenceIndex(menuItemText); } /** * @brief MainWindow::slotLayoutLevelByProminenceIndex(QString) * Overloaded - called when user clicks on toolbox options and when * the user selects a menu option (called by slotLayoutLevelByProminenceIndex()) * Repositions all nodes on different top-down levels according to the * chosen prominence index. * More prominent nodes are closer to the top of the canvas */ void MainWindow::slotLayoutLevelByProminenceIndex(QString choice=""){ if (!fileLoaded && !networkModified ) { QMessageBox::critical (this, "Error", tr("Sorry, I can't follow! " "\nLoad a network file or create a new network first. " "\nThen we can talk about layouts!" ), "OK",0 ); statusMessage( QString(tr("Nothing to layout! Are you dreaming?")) ); return; } int userChoice = 0; QString prominenceIndexName = choice; if (prominenceIndexName == "Degree Centrality") userChoice=1; else if (prominenceIndexName == "Closeness Centrality") userChoice=2; else if (prominenceIndexName == "Influence Range Closeness Centrality") userChoice=3; else if (prominenceIndexName == "Betweenness Centrality") userChoice=4; else if (prominenceIndexName == "Stress Centrality") userChoice=5; else if (prominenceIndexName == "Eccentricity Centrality") userChoice=6; else if (prominenceIndexName == "Power Centrality") userChoice=7; else if (prominenceIndexName == "Information Centrality") userChoice=8; else if (prominenceIndexName == "Degree Prestige") userChoice=9; else if (prominenceIndexName == "PageRank Prestige") userChoice=10; else if (prominenceIndexName == "Proximity Prestige") userChoice=11; qDebug() << "MainWindow::slotLayoutLevelByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(1); bool dropIsolates=false; //check if CC was selected and the graph is disconnected. if (userChoice == 2 ) { int connectedness=activeGraph.connectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is VERY SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:" "Aii=1+weighted_degree_ni" "Aij=1 if (i,j)=0" "Aij=1-wij if (i,j)=wij" "Next, it will compute the inverse matrix C of A." "The computation of the inverse matrix is VERY CPU intensive function." "because it uses the Gauss-Jordan elimination algorithm.\n\n " "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); double maxWidth=scene->width(); double maxHeight=scene->height(); //pixels statusMessage( QString(tr("Calculating new nodes positions. Please wait...")) ); graphicsWidget->clearGuides(); createProgressBar(); activeGraph.layoutLevelByProminenceIndex( maxWidth, maxHeight, userChoice, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked() || dropIsolates); destroyProgressBar(); statusMessage( tr("Nodes in upper levels are more prominent. ") ); } /** * Returns the amount of enabled/active edges on the scene. */ int MainWindow::activeEdges(){ qDebug () << "MW::activeEdges()"; return activeGraph.enabledEdges(); } /** * Returns the number of active nodes on the scene. */ int MainWindow::activeNodes(){ return activeGraph.vertices(); } /** * Displays a box informing the user about the symmetry or not of the adjacency matrix */ void MainWindow::slotCheckSymmetry(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("There is no network!")) ); return; } if (activeGraph.isSymmetric()) QMessageBox::information(this, "Symmetry", tr("The adjacency matrix is symmetric." ),"OK",0); else QMessageBox::information(this, "Symmetry", tr("The adjacency matrix is not symmetric." ),"OK",0); statusMessage (QString(tr("Ready")) ); } void MainWindow::slotInvertAdjMatrix(){ if ( !fileLoaded && !networkModified) { QMessageBox::critical (this, "Error", tr("Empty network! \nLoad a network file or create something by double-clicking on the canvas!"), "OK",0); statusMessage( tr("Nothing to show!") ); return; } int aNodes=activeNodes(); statusBar() -> showMessage ( QString (tr ("inverting adjacency adjacency matrix of %1 nodes")).arg(aNodes) ); qDebug ("MW: calling Graph::writeInvertAdjacencyMatrix with %i nodes", aNodes); QString fn = dataDir + "socnetv-report-invert-adjacency-matrix.dat"; QTime timer; timer.start(); activeGraph.writeInvertAdjacencyMatrix(fn, networkName, QString("lu")) ; int msecs = timer.elapsed(); statusMessage (QString(tr("Ready.")) + QString(" Time: ") + QString::number(msecs) ); //Open a text editor window for the new file created by graph class TextEditor *ed = new TextEditor(fn); tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tr("View Adjacency Matrix - ") + tempFileNameNoPath.last()); ed->show(); } void MainWindow::askAboutWeights(){ if (!activeGraph.isWeighted() ){ considerWeights=false; return; } if (askedAboutWeights) return; if ( ! considerEdgeWeightsAct->isChecked() && !considerWeights){ switch( QMessageBox::information( this, "Edge weights and Distances", tr("This network is weighted.\n" "Take edge weights into account (Default: No)?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) ) { case QMessageBox::Yes: considerWeights=true; considerEdgeWeightsAct->setChecked(true); break; case QMessageBox::No: considerWeights=false; considerEdgeWeightsAct->setChecked(false); break; default: // just for sanity considerWeights=false; considerEdgeWeightsAct->setChecked(false); return; break; } } if (considerWeights){ switch( QMessageBox::information ( this, "Edge weights and Distances", tr("Inverse edge weights during calculations? (Default: Yes)?\n\n" "If the weights denote cost (i.e. ), press No, since the " "distance between two nodes should be the quickest or cheaper one. \n\n" "If the weights denote value or strength (i.e. votes or interaction), " "press Yes to inverse the weights, since the distance between two " "nodes should be the most valuable one."), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes) ) { case QMessageBox::Yes: inverseWeights=true; break; case QMessageBox::No: inverseWeights=false; break; default: // just for sanity inverseWeights=true; return; break; } } askedAboutWeights=true; } /** * Displays the graph distance (geodesic distance) between two user-specified nodes This is the length of the shortest path between them. */ void MainWindow::slotGraphDistance(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("There are no nodes. Nothing to do...")) ); return; } bool ok=false; long int min=1, max=1, i=-1, j=-1; QList list=scene->items(); for (QList ::iterator it=list.begin(); it!=list.end(); it++) { if ( (*it) -> type() == TypeNode ){ Node *jim = (Node*) (*it); if ( min>jim->nodeNumber() && jim->isEnabled() ) min=jim->nodeNumber(); if ( maxnodeNumber() && jim->isEnabled() ) max=jim->nodeNumber(); } } i=QInputDialog::getInt(this, tr("Distance between two nodes"), tr("Select source node: (" +QString::number(min).toLatin1() +"..."+QString::number(max).toLatin1() +"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Distance calculation operation cancelled." ); return; } j=QInputDialog::getInt(this, tr("Distance between two nodes"), tr("Select target node: (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1() +"):"),min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( tr("Distance calculation operation cancelled.") ); return; } qDebug() << "source " << i << " target" << j; if (activeGraph.isSymmetric() && i>j) { qSwap(i,j); } askAboutWeights(); int distance = activeGraph.distance(i,j, considerWeights, inverseWeights); if ( distance > 0 && distance < RAND_MAX) QMessageBox::information(this, tr("Distance"), tr("Network distance (") +QString::number(i)+", "+QString::number(j) +") = "+QString::number(distance) +tr("\nThe nodes are connected."),"OK",0); else QMessageBox::information(this, tr("Distance"), tr("Network distance (") +QString::number(i)+", "+QString::number(j) +") = "+ QString("\xE2\x88\x9E") +tr("\nThe nodes are not connected."),"OK",0); } /** * Invokes calculation of the matrix of geodesic distances for the loaded network, then displays it. */ void MainWindow::slotDistancesMatrix(){ qDebug("MW: slotDistancesMatrix()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes nor edges!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to do!")) ); return; } statusMessage( tr("Creating distance matrix. Please wait...") ); QString fn = dataDir + "socnetv-report-distance-matrix.dat"; askAboutWeights(); createProgressBar(); activeGraph.writeDistanceMatrix(fn, networkName.toLocal8Bit(), considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); //Open a text editor window for the new file created by graph class TextEditor *ed = new TextEditor(fn); tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Distance matrix saved as: ")+tempFileNameNoPath.last()); } /** * Invokes calculation of the sigmas matrix (the number of geodesic paths between each pair of nodes in the loaded network), then displays it. */ void MainWindow::slotGeodesicsMatrix(){ qDebug("MW: slotViewNumberOfGeodesics()"); if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes nor edges!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to do!")) ); return; } QString fn = dataDir + "socnetv-report-sigmas-matrix.dat"; askAboutWeights(); statusMessage( tr("Creating number of geodesics matrix. Please wait...") ); createProgressBar(); activeGraph.writeNumberOfGeodesicsMatrix(fn, networkName.toLocal8Bit(), considerWeights, inverseWeights); destroyProgressBar(); //Open a text editor window for the new file created by graph class TextEditor *ed = new TextEditor(fn); tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Matrix of geodesic path counts saved as: ") + tempFileNameNoPath.last()); } /** Displays the network diameter (largest geodesic) */ void MainWindow::slotDiameter() { if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error" ,tr("There are no nodes nor edges!\n" "Load a network file or create a new network. \n" "Then ask me to compute something!"), "OK",0); statusMessage( QString(tr("Cannot find the diameter of nothing...")) ); return; } askAboutWeights(); createProgressBar(); int netDiameter=activeGraph.diameter(considerWeights, inverseWeights); destroyProgressBar(); if ( activeGraph.isWeighted() && considerWeights ) QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nSince this is a weighted network \n" "the diameter can be more than N"), "OK",0); else if ( activeGraph.isWeighted() && !considerWeights ) QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nThis is the diameter of the \n" "corresponding network without weights"), "OK",0); else QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nSince this is a non-weighted network, \n" "the diameter is always less than N-1."), "OK",0); statusMessage( tr("Diameter calculated. Ready.") ); } /** Displays the average shortest path length (average graph distance) */ void MainWindow::slotAverageGraphDistance() { if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes nor edges!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Cannot find the diameter of nothing...")) ); return; } askAboutWeights(); createProgressBar(); float averGraphDistance=activeGraph.averageGraphDistance( considerWeights, inverseWeights, filterIsolateNodesAct->isChecked() ); destroyProgressBar(); QMessageBox::information(this, "Average Graph Distance", "The average shortest path length is = " + QString::number(averGraphDistance), "OK",0); statusMessage( tr("Average distance calculated. Ready.") ); } /** * Writes Eccentricity indices into a file, then displays it. */ void MainWindow::slotEccentricity(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do...")) ); return; } QString fn = dataDir + "socnetv-report-eccentricity.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writeEccentricity( fn, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Eccentricity report saved as: ") + tempFileNameNoPath.last()); } /** * @brief MainWindow::slotConnectedness */ void MainWindow::slotConnectedness(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes nor edges!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to do...")) ); return; } createProgressBar(); int connectedness=activeGraph.connectedness(); qDebug () << "MW::connectedness result " << connectedness; destroyProgressBar(); switch ( connectedness ) { case 1: QMessageBox::information(this, "Connectedness", "This undirected graph " "is connected.", "OK",0); break; case 0: QMessageBox::information(this, "Connectedness", tr("This undirected graph " " is not connected."), "OK",0); break; case 2: QMessageBox::information(this, "Connectedness", tr("This directed graph " "is strongly connected."), "OK",0); break; case -1: QMessageBox::information(this, "Connectedness", tr("This undirected graph " "is disconnected because isolate nodes exist. \n" "It can become connected by dropping isolates."), "OK",0); break; case -2: QMessageBox::information(this, "Connectedness", tr("This directed graph " "is unilaterally connected. \n" "For every pair of " "nodes (u,v) there is a path either from u to v or " "from v to u, but not always both."), "OK",0); break; case -3: QMessageBox::information(this, "Connectedness", "This directed graph " "is disconnected because isolate nodes exist. \n" "It can become strongly connected by dropping isolates.", "OK",0); break; case -4: QMessageBox::information(this, "Connectedness", "This directed graph " "is disconnected. \nThere are pairs of nodes that " "are disconnected.", "OK",0); break; default: QMessageBox::critical(this, "Connectedness", "Something went wrong!.", "OK",0); break; }; statusMessage( tr("Connectedness calculated. Ready.") ); } /** * Calls Graph:: writeNumberOfWalks() to calculate and print * the number of walks of a given length , between each pair of nodes. */ void MainWindow::slotWalksOfGivenLength(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } QString fn = dataDir + "socnetv-report-number-of-walks.dat"; bool ok=false; createProgressBar(); int length = QInputDialog::getInt(this, "Number of walks", tr("Select desired length of walk: (2 to %1)").arg(activeNodes()-1),2, 2, activeNodes()-1, 1, &ok ); if (!ok) { statusMessage( "Cancelled." ); return; } activeGraph.writeNumberOfWalksMatrix(fn, networkName, length); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Number of walks saved as: ") + tempFileNameNoPath.last()); } /** * Calls Graph:: writeTotalNumberOfWalksMatrix() to calculate and print * the total number of walks of any length , between each pair of nodes. */ void MainWindow::slotTotalWalks(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } if (activeNodes() > 50) { switch( QMessageBox::critical(this, "Slow function warning",tr("Please note that this function is VERY SLOW on large networks (n>50), since it will calculate all powers of the sociomatrix up to n-1 in order to find out all possible walks. \n\nIf you need to make a simple reachability test, we advise to use the Reachability Matrix function instead. \n\n Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } QString fn = dataDir + "socnetv-report-total-number-of-walks.dat"; createProgressBar(); int maxLength=activeNodes()-1; activeGraph.writeTotalNumberOfWalksMatrix(fn, networkName, maxLength); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle( tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage("Total number of walks saved as: " + tempFileNameNoPath.last()); } /** * Calls Graph:: writeReachabilityMatrix() to calculate and print * the Reachability Matrix of the network. */ void MainWindow::slotReachabilityMatrix(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } QString fn = dataDir + "socnetv-report-reachability-matrix.dat"; createProgressBar(); activeGraph.writeReachabilityMatrix(fn, networkName); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage("Reachability Matrix saved as: " + tempFileNameNoPath.last()); } /** * Calls Graph:: writeCliqueCensus() to write the number of cliques (triangles) * of each vertex into a file, then displays it. */ void MainWindow::slotCliqueCensus(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } QString fn = dataDir + "socnetv-report-clique-census.dat"; bool considerWeights=true; createProgressBar(); activeGraph.writeCliqueCensus(fn, considerWeights); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage("Clique Census saved as: " + tempFileNameNoPath.last()); } /** * Writes Clustering Coefficients into a file, then displays it. */ void MainWindow::slotClusteringCoefficient (){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } QString fn = dataDir + "socnetv-report-clustering-coefficients.dat"; bool considerWeights=true; createProgressBar(); activeGraph.writeClusteringCoefficient(fn, considerWeights); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage("Clustering Coefficients saved as: " + tempFileNameNoPath.last()); } /** * Calls Graph to conduct and write a triad census into a file, then displays it. */ void MainWindow::slotTriadCensus() { if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } QString fn = dataDir + "socnetv-report-triad-census.dat"; bool considerWeights=true; createProgressBar(); activeGraph.writeTriadCensus(fn, considerWeights); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage("Triad Census saved as: " + tempFileNameNoPath.last()); } /** * Writes Out-Degree Centralities into a file, then displays it. */ void MainWindow::slotCentralityDegree(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do! \nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" No network here. Sorry. Nothing to do.")) ); return; } bool considerWeights=false; if ( activeGraph.isWeighted()) { switch( QMessageBox::information( this, "Centrality Out-Degree", tr("Graph edges have weights. \nTake weights into account (Default: No)?"), tr("Yes"), tr("No"), 0, 1 ) ) { case 0: considerWeights=true; break; case 1: considerWeights=false; break; default: // just for sanity considerWeights=false; return; break; } } QString fn = dataDir + "socnetv-report-centrality-out-degree.dat"; createProgressBar(); activeGraph.writeCentralityDegree(fn, considerWeights, filterIsolateNodesAct->isChecked() ); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Out-Degree Centralities saved as: ") + tempFileNameNoPath.last()); } /** * Writes Closeness Centralities into a file, then displays it. */ void MainWindow::slotCentralityCloseness(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network" " file or create a new network manually. " "\nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to do...")) ); return; } int connectedness=activeGraph.connectedness(); bool dropIsolates=false; switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; askAboutWeights(); QString fn = dataDir + "socnetv-report-centrality_closeness.dat"; createProgressBar(); activeGraph.writeCentralityCloseness( fn, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked() || dropIsolates); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle( tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Closeness Centralities saved as: ") + tempFileNameNoPath.last()); } /** * @brief MainWindow::slotCentralityClosenessInfluenceRange * Writes Centrality Closeness (based on Influence Range) indices into a file, * then displays it. */ void MainWindow::slotCentralityClosenessInfluenceRange(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network" " file or create a new network manually. " "\nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to do...")) ); return; } QString fn = dataDir + "socnetv-report-centrality_closeness_influence_range.dat"; askAboutWeights(); createProgressBar(); activeGraph.writeCentralityClosenessInfluenceRange( fn, considerWeights,inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); statusMessage( QString(tr(" displaying file..."))); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Influence Range Closeness Centrality saved as: ")+tempFileNameNoPath.last()); } /** * Writes Betweenness Centralities into a file, then displays it. */ void MainWindow::slotCentralityBetweenness(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do...")) ); return; } QString fn = dataDir + "socnetv-report-centrality_betweenness.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writeCentralityBetweenness( fn, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); statusMessage( QString(tr(" displaying file..."))); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last() ); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Betweenness Centralities saved as: ")+tempFileNameNoPath.last()); } /** * Writes Degree Prestige indices (In-Degree Centralities) into a file, then displays it. */ void MainWindow::slotPrestigeDegree(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("Nothing to do!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr("Nothing to do...")) ); return; } if (activeGraph.isSymmetric()) { QMessageBox::warning( this, "Warning", tr("Undirected graph!\n" "Degree Prestige counts inbound edges, therefore is more " "meaningful on directed graphs.\n" "For undirected graphs, the DP scores are the same as " "Degree Centrality..."), "OK",0); } bool considerWeights=false; if ( activeGraph.isWeighted()) { switch( QMessageBox::information( this, "Degree Prestige (In-Degree)", tr("Graph edges have weights. \nTake weights into account (Default: No)?"), tr("Yes"), tr("No"), 0, 1 ) ) { case 0: considerWeights=true; break; case 1: considerWeights=false; break; default: // just for sanity considerWeights=false; return; break; } } QString fn = dataDir + "socnetv-report-degree-prestige.dat"; createProgressBar(); activeGraph.writePrestigeDegree(fn, considerWeights, filterIsolateNodesAct->isChecked() ); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle( tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Degree Prestige (in-degree) saved as: ") + tempFileNameNoPath.last()); } /** * Writes PageRank Prestige indices into a file, then displays it. */ void MainWindow::slotPrestigePageRank(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do...")) ); return; } QString fn = dataDir + "socnetv-report-prestige_pagerank.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writePrestigePageRank(fn, filterIsolateNodesAct->isChecked()); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("PageRank Prestige indices saved as: ")+ tempFileNameNoPath.last()); } /** * Writes Proximity Prestige indices into a file, then displays them. */ void MainWindow::slotPrestigeProximity(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("There are no nodes!\n" "Load a network file or create a new network. \n" "Then ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do...")) ); return; } QString fn = dataDir + "socnetv-report-centrality_proximity_prestige.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writePrestigeProximity(fn, true, false , filterIsolateNodesAct->isChecked()); destroyProgressBar(); statusMessage( QString(tr(" displaying file..."))); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle( tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Proximity Prestige Centralities saved as: ")+ tempFileNameNoPath.last()); } /** * Writes Informational Centralities into a file, then displays it. */ void MainWindow::slotCentralityInformation(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("There are no nodes!\n" "Load a network file or create a new network. \n" "Then ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do...")) ); return; } if (activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:" "Aii=1+weighted_degree_ni" "Aij=1 if (i,j)=0" "Aij=1-wij if (i,j)=wij\n" "Next, it will compute the inverse matrix C of A." "The computation of the inverse matrix is a CPU intensive function." "although it uses LU decomposition.\n\n " "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } QString fn = dataDir + "socnetv-report-centrality_information.dat"; statusMessage( QString(tr(" Please wait..."))); askAboutWeights(); createProgressBar(); activeGraph.writeCentralityInformation(fn,considerWeights, inverseWeights); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Information Centralities saved as: ")+ tempFileNameNoPath.last()); } /** * Writes Stress Centralities into a file, then displays it. */ void MainWindow::slotCentralityStress(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical( this, "Error", tr("There are no nodes!\n" "Load a network file or create a new network. \n" "Then ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do! Why don't you try creating something first?")) ); return; } QString fn = dataDir + "socnetv-report-centrality_stress.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writeCentralityStress( fn, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Stress Centralities saved as: ")+ tempFileNameNoPath.last()); } /** * Writes Power Centralities into a file, then displays it. */ void MainWindow::slotCentralityPower(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do! Why don't you try creating something first?")) ); return; } QString fn = dataDir + "socnetv-report-centrality_power.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writeCentralityPower( fn, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Stress Centralities saved as: ")+ tempFileNameNoPath.last()); } /** * Writes Eccentricity Centralities into a file, then displays it. */ void MainWindow::slotCentralityEccentricity(){ if (!fileLoaded && !networkModified ) { QMessageBox::critical(this, "Error",tr("There are no nodes!\nLoad a network file or create a new network. \nThen ask me to compute something!"), "OK",0); statusMessage( QString(tr(" Nothing to do...")) ); return; } QString fn = dataDir + "socnetv-report-centrality_eccentricity.dat"; askAboutWeights(); statusMessage( QString(tr(" Please wait..."))); createProgressBar(); activeGraph.writeCentralityEccentricity( fn, considerWeights, inverseWeights, filterIsolateNodesAct->isChecked()); destroyProgressBar(); TextEditor *ed = new TextEditor(fn); //OPEN A TEXT EDITOR WINDOW tempFileNameNoPath=fn.split( "/"); ed->setWindowTitle(tempFileNameNoPath.last()); ed->show(); QApplication::restoreOverrideCursor(); statusMessage(tr("Eccentricity Centralities saved as: ")+ tempFileNameNoPath.last()); } void MainWindow::createProgressBar(){ if (showProgressBarAct->isChecked() || activeEdges() > 2000){ progressDialog= new QProgressDialog("Please wait....", "Cancel", 0, activeGraph.vertices(), this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); } void MainWindow::destroyProgressBar(){ QApplication::restoreOverrideCursor(); if (showProgressBarAct->isChecked() || activeEdges() > 1000) progressDialog->deleteLater(); } /** * Called from Graph:: */ bool MainWindow::showNumbers(){ return displayNodeNumbersAct->isChecked(); } /** * Turns on/off displaying the numbers of nodes (outside ones) */ void MainWindow::slotDisplayNodeNumbers(bool toggle) { if (!fileLoaded && ! networkModified) { QMessageBox::critical(this, "Error",tr("There are no nodes! \nLoad a network file or create a new network."), "OK",0); statusMessage( tr("Errr...no nodes here. Sorry!") ); return; } statusMessage( tr("Toggle Nodes Numbers. Please wait...") ); if (!toggle) { graphicsWidget->setAllItemsVisibility(TypeNumber, false); statusMessage( tr("Node Numbers are invisible now. Click the same option again to display them.") ); return; } else{ graphicsWidget->setAllItemsVisibility(TypeNumber, true); statusMessage( tr("Node Numbers are visible again...") ); } } /** * Called by Graph:: and this->initNet() */ bool MainWindow::showLabels(){ return displayNodeLabelsAct->isChecked(); } /** * Called by Graph:: and this->initNet() */ bool MainWindow::showNumbersInsideNodes(){ return displayNumbersInsideNodesAct->isChecked(); } /** * Turns on/off displaying the nodenumbers inside the nodes. */ void MainWindow::slotDisplayNumbersInsideNodes(bool toggle){ statusMessage( tr("Toggle Numbers inside nodes. Please wait...") ); if ( showNumbers() ) { // ? } else{ displayNodeNumbersAct->setChecked(true); } activeGraph.setShowNumbersInsideNodes(toggle); graphicsWidget -> setNumbersInsideNodes(toggle); if (toggle){ statusMessage( tr("Numbers inside nodes...") ); } else { statusMessage( tr("Numbers outside nodes...") ); } } /** * Turns on/off displaying labels */ void MainWindow::slotDisplayNodeLabels(bool toggle){ if (!fileLoaded && ! networkModified) { QMessageBox::critical(this, "Error",tr("There are no nodes! \nLoad a network file or create a new network first. "), "OK",0); statusMessage( tr("No nodes found. Sorry...") ); return; } statusMessage( tr("Toggle Nodes Labels. Please wait...") ); if (!toggle) { graphicsWidget->setAllItemsVisibility(TypeLabel, false); statusMessage( tr("Node Labels are invisible now. Click the same option again to display them.") ); return; } else{ graphicsWidget->setAllItemsVisibility(TypeLabel, true); statusMessage( tr("Node Labels are visible again...") ); } activeGraph.setShowLabels(toggle); } /** * Changes the size of all nodes */ void MainWindow::slotChangeAllNodesSize() { bool ok=false; int newSize = QInputDialog::getInt( this, "Change node size", tr("Select new size for all nodes: (1-16)"), initNodeSize, 1, 16, 1, &ok ); if (!ok) { statusMessage( "Change node size operation cancelled." ); return; } qDebug ("MW: slotChangeAllNodesSize:"); changeAllNodesSize(newSize); graphChanged(); statusBar()->showMessage (QString(tr("Ready")), statusBarDuration) ; return; } /** * Changes the size of nodes. */ void MainWindow::changeAllNodesSize(int size) { qDebug ("MW: changeAllNodesSize:"); if (size == 0 ) { if (activeNodes() < 200) { return; } else if (activeNodes() >= 200 && activeNodes() < 500){ size = 4; } else if (activeNodes() >= 500 && activeNodes() < 1000) { size = 3; } else if (activeNodes() >= 1000) { size = 2; } } initNodeSize = size; activeGraph.setAllVerticesSize(size); } /** * Changes the shape of all nodes. */ void MainWindow::slotChangeAllNodesShape() { bool ok=false; QStringList lst; lst << "box"<< "circle"<< "diamond"<< "ellipse"<< "triangle"; QString newShape = QInputDialog::getItem(this, "Node shapes", "Select a shape for all nodes: ", lst, 1, true, &ok); if ( ok ) { //user selected an item and pressed OK QList list=scene->items(); for (QList::iterator it=list.begin(); it!=list.end(); it++) if ( (*it) -> type() == TypeNode ){ Node *jim = (Node*) (*it); (*jim).setShape(newShape); activeGraph.setVertexShape ((*jim).nodeNumber(), newShape); } graphChanged(); activeGraph.setInitVertexShape(newShape); statusBar()->showMessage (QString(tr("All shapes have been changed. Ready")), statusBarDuration) ; } else { //user pressed Cancel statusBar()->showMessage (QString(tr("Change node shapes aborted...")), statusBarDuration) ; } } /** * Change size of all nodes' numbers (outside ones) */ void MainWindow::slotChangeNumbersSize() { bool ok=false; int newSize; newSize = QInputDialog::getInt(this, "Change text size", tr("Change all nodenumbers size to: (1-16)"),initNumberSize, 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change font size: Aborted.") ); return; } QList list=scene->items(); for (QList::iterator it2=list.begin();it2!=list.end(); it2++) if ( (*it2)->type()==TypeNumber) { NodeNumber * number= (NodeNumber*) (*it2); qDebug ("MW: slotChangeNumbersSize Found"); number->setFont( QFont (number->font().family(), newSize, QFont::Light, false) ); } activeGraph.setInitVertexNumberSize(newSize); statusMessage( tr("Changed numbers size. Ready.") ); } /** * Changes size of all nodes' labels */ void MainWindow::slotChangeLabelsSize() { bool ok=false; int newSize; newSize = QInputDialog::getInt(this, "Change text size", tr("Change all node labels size to: (1-16)"),initNumberSize, 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change font size: Aborted.") ); return; } QList list=scene->items(); for (QList::iterator it2=list.begin();it2!=list.end(); it2++) if ( (*it2)->type()==TypeLabel) { NodeLabel *label= (NodeLabel*) (*it2); qDebug ("MW: slotChangeLabelsSize Found"); label->setFont( QFont (label->font().family(), newSize, QFont::Light, false) ); activeGraph.setVertexLabelSize ( (label->node())->nodeNumber(), newSize); } activeGraph.setInitVertexLabelSize(newSize); statusMessage( tr("Changed labels size. Ready.") ); } /** Turns on/off drawing edges as thick as their weights. TODO */ void MainWindow::slotDrawEdgesThickAsWeights() { } /** * Turns on/off displaying edge weight numbers */ void MainWindow::slotDisplayEdgesWeightNumbers(bool toggle) { if (!fileLoaded && ! networkModified) { QMessageBox::critical(this, "Error",tr("There are no edges! \nLoad a network file or create a new network first."), "OK",0); statusMessage( tr("No nodes or edges found. Sorry...") ); return; } qDebug() << "MW::slotDisplayEdgesWeightNumbers - Toggling Edges Weights. Please wait..."; statusMessage( tr("Toggle Edges Weights. Please wait...") ); if (!toggle) { graphicsWidget->setAllItemsVisibility(TypeEdgeWeight, false); statusMessage( tr("Edge weights are invisible now. Click the same option again to display them.") ); return; } else{ graphicsWidget->setAllItemsVisibility(TypeEdgeWeight, true); statusMessage( tr("Edge weights are visible again...") ); } activeGraph.setShowLabels(toggle); } /** * @brief MainWindow::slotConsiderEdgeWeights * @param toggle */ void MainWindow::slotConsiderEdgeWeights(bool toggle) { if (toggle) { considerWeights=true; askedAboutWeights=false; askAboutWeights(); // will only ask about inversion } else considerWeights=false; } /** * Turns on/off displaying edges */ void MainWindow::slotDisplayEdges(bool toggle){ if (!fileLoaded && ! networkModified) { QMessageBox::critical(this, "Error",tr("There are no nodes nor edges! \nLoad a network file or create a new network first!"), "OK",0); statusMessage( tr("No edges found...") ); return; } statusMessage( tr("Toggle Edges Arrows. Please wait...") ); if (!toggle) { graphicsWidget->setAllItemsVisibility(TypeEdge, false); statusMessage( tr("Edges are invisible now. Click again the same menu to display them.") ); return; } else{ graphicsWidget->setAllItemsVisibility(TypeEdge, true); statusMessage( tr("Edges visible again...") ); } } /** * Turns on/off the arrows of edges */ void MainWindow::slotDisplayEdgesArrows(bool toggle){ if (!fileLoaded && ! networkModified) { QMessageBox::critical(this, "Error",tr("There are no edges! \nLoad a network file or create a new network first!"), "OK",0); statusMessage( tr("No edges found...") ); return; } statusMessage( tr("Toggle Edges Arrows. Please wait...") ); if (!toggle) { QList list = scene->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { if ( (*item)->type() ==TypeEdge){ Edge *edge = (Edge*) (*item); edge->showArrows(false); } } return; } else{ QList list = scene->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) if ( (*item)->type() ==TypeEdge){ Edge *edge = (Edge*) (*item); edge->showArrows(true); } } statusMessage( tr("Ready.")); } /** * FIXME edges Bezier */ void MainWindow::slotDrawEdgesBezier(bool toggle){ if (!fileLoaded && ! networkModified) { QMessageBox::critical(this, "Error",tr("There are no edges! \nLoad a network file or create a new network!"), "OK",0); statusMessage( tr("There are NO edges here!") ); return; } statusMessage( tr("Toggle edges bezier. Please wait...") ); // // graphicsWidget->setBezier(toggle); if (!toggle) { // QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // QList list = scene->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++) { // if ( (*item)->type() ==TypeEdge ){ // Edge *edge = (Edge*) (*item); // // edge->toggleBezier(false); // (*item)->hide();(*item)->show(); // } // // } // QApplication::restoreOverrideCursor(); // return; } else{ // QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // QList list = scene->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++){ // if ( (*item)->type() ==TypeEdge ){ // Edge *edge = (Edge*) (*item); // // edge->toggleBezier(true); // (*item)->hide();(*item)->show(); // } // } // QApplication::restoreOverrideCursor(); } } /** * Changes the background color of the scene */ void MainWindow::slotBackgroundColor () { qDebug("MW: slotBackgroundColor "); QColor backgrColor = QColorDialog::getColor( initBackgroundColor, this ); graphicsWidget ->setBackgroundBrush(QBrush(backgrColor)); statusMessage( tr("Ready. ") ); } /** * Changes the color of all edges */ void MainWindow::slotAllEdgesColor(){ QColor color = QColorDialog::getColor( Qt::red, this, "Change the color of all nodes" ); if (color.isValid()) { initNodeColor=color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MainWindow::slotAllEdgesColor() : " << initNodeColor; //createProgressBar(); activeGraph.setAllEdgesColor(initNodeColor); //destroyProgressBar(); QApplication::restoreOverrideCursor(); graphChanged(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Nodes color change aborted. ") ); } } /** * Changes the color of nodes' numbers */ void MainWindow::slotAllNumbersColor(){ QColor textColor = QColorDialog::getColor( Qt::black, this ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug ("MW: Will change color"); QList list= scene->items(); for (QList::iterator it=list.begin(); it!=list.end(); it++) { if ( (*it)->type() == TypeNumber) { NodeNumber *jimNumber = (NodeNumber *) (*it); jimNumber->update(); jimNumber->setDefaultTextColor(textColor); } } activeGraph.setInitVertexNumberColor( textColor.name() ); QApplication::restoreOverrideCursor(); statusMessage( tr("Numbers' colors changed. Ready. ") ); } /** * Changes the color of nodes labels */ void MainWindow::slotAllLabelsColor(){ QColor textColor = QColorDialog::getColor( Qt::black, this ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug ("MW: Will change label color"); QList list= scene->items(); for (QList::iterator it=list.begin(); it!=list.end(); it++) if ( (*it)->type() == TypeNode ) { Node *jim = (Node *) (*it); jim->label()->update(); jim->label()->setDefaultTextColor(textColor); qDebug ("MW: Changed color"); activeGraph.setVertexLabelColor (jim->nodeNumber(), textColor.name()); } activeGraph.setInitVertexLabelColor(textColor.name()); QApplication::restoreOverrideCursor(); statusMessage( tr("Label colors changed. Ready. ") ); } /** * turns antialiasing on or off */ void MainWindow::slotAntialiasing(bool toggle) { statusMessage( tr("Toggle anti-aliasing. This will take some time if the network is large (>500)...") ); //Inform graphicsWidget about the change QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); graphicsWidget->setRenderHint(QPainter::Antialiasing, toggle); graphicsWidget->setRenderHint(QPainter::TextAntialiasing, toggle); graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, toggle); QApplication::restoreOverrideCursor(); if (!toggle) statusMessage( tr("Anti-aliasing off.") ); else statusMessage( tr("Anti-aliasing on.") ); } /** * turn progressbar on or off */ void MainWindow::slotShowProgressBar(bool toggle) { statusMessage( tr("Toggle progressbar...")); if (!toggle) { statusMessage( tr("Progress bars off.") ); } else { statusMessage( tr("Progress bars on.") ); } } /** * Turns debugging messages on or off */ void MainWindow::slotPrintDebug(bool toggle){ if (!toggle) { printDebug=false; statusMessage( tr("Debug messages off.") ); } else { printDebug=true; statusMessage( tr("Debug messages on.") ); } } /** * Turns Toolbar on or off */ void MainWindow::slotViewToolBar(bool toggle) { statusMessage( tr("Toggle toolbar...")); if (toggle== false) { toolBar->hide(); statusMessage( tr("Toolbar off.") ); } else { toolBar->show(); statusMessage( tr("Toolbar on.") ); } } /** * Turns Statusbar on or off */ void MainWindow::slotViewStatusBar(bool toggle) { statusMessage( tr("Toggle statusbar...")); if (toggle == false) { statusBar()->hide(); statusMessage( tr("Status bar off.") ); } else { statusBar()->show(); statusMessage( tr("Status bar on.") ); } } /* * Enables/disables displaying a user-defined custom image in the background */ void MainWindow::slotBackgroundImage(bool toggle) { statusMessage( tr("Toggle BackgroundImage...")); QString m_fileName ; if (toggle == false) { statusMessage( tr("BackgroundImage off.") ); graphicsWidget->setBackgroundBrush(QBrush(initBackgroundColor)); } else { m_fileName = QFileDialog::getOpenFileName( this, tr("Select one image"), getLastPath(), tr("All (*);;PNG (*.png);;JPG (*.jpg)") ); if (!m_fileName.isEmpty()) { setLastPath(m_fileName); graphicsWidget->setBackgroundBrush(QImage(m_fileName)); graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); statusMessage( tr("BackgroundImage on.") ); } } } /** * Displays a random tip */ void MainWindow::slotTips() { int randomTip=rand()%tipsCounter; //Pick a tip. QMessageBox::about( this, tr("Tip Of The Day"), tips[randomTip]); } /** Creates our tips. */ void MainWindow::createTips(){ tips+=tr("You can add a new node by double-clicking on the scene."); tips+=tr("You can add a new node by clicking on Add button."); tips+=tr("You can remove a node by clicking on Remove button."); tips+=tr("You can rotate the network by selecting a new angle on the dock."); tips+=tr("You can add a new edge between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node."); tips+=tr("You can remove a node by right-clicking on it and selecting Remove."); tips+=tr("You can change background color (from the menu Edit > Colors)."); tips+=tr("Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette."); tips+=tr("The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering)."); tips+=tr("You can move a node easily by dragging it with your mouse."); tips+=tr("SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format."); tips+=tr("You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes"); tips+=tr("You can change the label of node by right-clicking on it, and selecting Options > Change Label."); tips+=tr("All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or an Edge."); tips+=tr("Node information is displayed on the Status bar, when you left-click on it."); tips+=tr("Edge information is displayed on the Status bar, when you left-click on it."); tipsCounter = 16; } /** Loads the HTML Help file and displays it via system browser */ void MainWindow::slotHelp(){ QString helpPath; bool manualFound = false; QDir d( QCoreApplication::applicationDirPath() ); qDebug()<< QCoreApplication::applicationDirPath().toLatin1(); if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); } else { if (d.dirName()=="bin") { d.cdUp(); } if (d.cd("./manual") ) { if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); manualFound = true; } else { qDebug()<< "help file does not exist here."; manualFound = false; } } // MacOS: assumes manual dir in socnetv.app/Contents/ // before deploy copy there the manual dir if (d.cd("../manual") ) { // for Mac if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); manualFound = true; } else { qDebug()<< "help file does not exist here."; manualFound = false; } } if (!manualFound && d.cd("../trunk/manual") ) { if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); manualFound = true; } else { qDebug()<< "help file does not exist here."; manualFound = false; } } if ( !manualFound && d.cd("/usr/local/share/doc/socnetv/") ) { //for compile installation if (d.exists("manual/")) d.cd("manual/"); if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); qDebug()<< "path" << helpPath.toLatin1(); manualFound = true; } else { qDebug()<< "help file does not exist."; manualFound = false; } } if (!manualFound && d.cd("/usr/share/doc/socnetv/") ) { //for Debian Ubuntu if (d.exists("manual/")) d.cd("manual/"); if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); manualFound = true; } else { qDebug("help file does not exist in /usr/share/doc/socnetv/."); manualFound = false; } } if ( !manualFound && d.cd("/usr/share/doc/packages/socnetv/") ) { //for opensuse file hierarchy if (d.exists("manual/")) d.cd("manual/"); if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); manualFound = true; } else { qDebug("help file does not exist."); } } QString fedoraPath = "/usr/share/doc/socnetv-" + VERSION; if ( !manualFound && d.cd(fedoraPath) ) { //for Fedora file hierarchy if (d.exists("manual/")) d.cd("manual/"); if ( d.exists("manual.html") ) { helpPath=d.filePath("manual.html"); manualFound = true; } else { qDebug("help file does not exist."); } } } qDebug () << "help path is: " << helpPath.toLatin1(); QDesktopServices::openUrl(QUrl::fromLocalFile(helpPath)); } /** Displays the following message!! */ void MainWindow::slotHelpAbout(){ int randomCookie=rand()%fortuneCookiesCounter;//createFortuneCookies(); QString BUILD="Tue Jun 23 18:52:00 EEST 2015"; QMessageBox::about( this, "About SocNetV", "Social Network Visualizer (SocNetV)" "

Version: " + VERSION + "

" "

Build: " + BUILD + "

" "

(C) 2005-2015 by Dimitris V. Kalamaras" "
dimitris.kalamaras@gmail.com" "

Fortune cookie:
\"" + fortuneCookie[randomCookie] +"\"" "

License:
" "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 3 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, see http://www.gnu.org/licenses/

"); } /** Creates the fortune cookies displayed on the above message. */ void MainWindow::createFortuneCookies(){ fortuneCookie+="sic itur ad astra / sic transit gloria mundi ?
--Unknown"; fortuneCookie+="losers of yesterday, the winners of tomorrow...
--B.Brecht"; fortuneCookie+="Patriotism is the virtue of the wicked...
--O. Wilde"; fortuneCookie+="No tengo nunca mas, no tengo siempre. En la arena
" "la victoria dejo sus piers perdidos.
" "Soy un pobre hombre dispuesto a amar a sus semejantes.
" "No se quien eres. Te amo. No doy, no vendo espinas.
--Pablo Neruda" ; fortuneCookie+="I will never apologize for the United States of America. I don't care what it has done. I don't care what the facts are.
--Vice President George H.W. Bush, after the Iranian airliner flight IR655 (an Airbus A300) was shot down by a U.S. missile cruiser (USS Vincennes), killing all 290 civilian passengers..."; fortuneCookie+="Man must not check reason by tradition, but contrawise, must check tradition by reason.
--Leo Tolstoy"; fortuneCookie+="Only after the last tree has been cut down,
only after the last river has been poisoned,
only after the last fish has been caught,
only then will you realize that money cannot be eaten.
--The Cree People"; fortuneCookie+="Stat rosa pristina nomine, nomina nuda tenemus
--Unknown"; fortuneCookie+="Jupiter and Saturn, Oberon, Miranda
" "And Titania, Neptune, Titan.
" "Stars can frighten.
Syd Barrett"; fortuneCookiesCounter=9; // return fortuneCookie.count(); } /** Displays a short message about the Qt Toolbox. */ void MainWindow::slotAboutQt(){ QMessageBox::aboutQt(this, "About Qt - SocNetV"); } socnetv-1.9/src/node.cpp0000775000175000017500000002762012542300240015522 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt node.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "node.h" #include #include #include #include #include //for QT_VERSION #include #include "graphicswidget.h" #include "edge.h" #include "nodelabel.h" #include "nodenumber.h" #include //sqrt Node::Node( GraphicsWidget* gw, int num, int size, QString col, QString shape, bool numIn, int ldist, int ndist, QPointF p ) : graphicsWidget (gw) { Q_UNUSED(p); graphicsWidget->scene()->addItem(this); //Without this nodes don't appear on the screen... //ItemSendsGeometryChanges introduced in Qt 4.6... #if QT_VERSION >= 0x040600 setFlags(ItemSendsGeometryChanges | ItemIsSelectable | ItemIsMovable); setCacheMode(QGraphicsItem::ItemCoordinateCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events //DeviceCoordinateCache #else setFlags(ItemIsSelectable | ItemIsMovable ); //Without this, the node cannot move nor be selected ... setCacheMode(QGraphicsItem::NoCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events #endif setAcceptHoverEvents(true); m_num=num; m_size=size; m_hasLabel=false; m_hasNumber=false; m_isNumberInside = numIn; m_shape=shape; m_col_str=col; m_col=QColor(col); m_nd=ndist; m_ld=ldist; m_poly_t=new QPolygon(3); m_poly_d=new QPolygon(4); qDebug()<< "Node: constructor: initial position at: " << this->x()<<", "<y() << " Will move at: "<< p.x()<<", "<setEndOffset(size); } foreach (Edge *edge, outEdgeList) { qDebug("Node: updating edges in outEdgeList"); edge->setStartOffset(size); } setShape(m_shape); } /** Used by MainWindow::findNode() and Edge::Edge() */ int Node::size(){ qDebug("size()"); return m_size; } /** Called every time the user needs to change the shape of an node. */ void Node::setShape(const QString shape) { qDebug("Node: setShape()"); prepareGeometryChange(); m_shape=shape; qDebug ("Node: setShape(): node is at x=%f and y=%f", x(), y()); update(); } /* * Returns the shape of the node as a path (an accurate outline of the item's shape) * Used by the collision algorithm in collidesWithItem() */ QPainterPath Node::shape() const { //qDebug ("Node: shape()"); return (*m_path); } /* * Returns the bounding rectangle of the node * That is the rectangle where all painting will take place. */ QRectF Node::boundingRect() const { qreal adjust = 6; return QRectF(-m_size -adjust , -m_size-adjust , 2*m_size+adjust , 2*m_size +adjust); } /** Does the actual painting. Called by GraphicsView in every update() */ void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { // painter->setClipRect( option->exposedRect ); //if the node is being dragged around, darken it! if (option->state & QStyle::State_Selected) { //qDebug()<< " node : selected "; painter->setBrush(m_col.dark(150)); } else if (option->state & QStyle::State_MouseOver) { //qDebug()<< " node : mouse over"; painter->setBrush(m_col.dark(150)); setZValue(255); } //else if (option->state & QStyle::State_Sunken) { //qDebug()<< " node : sunken "; //setZValue(255); //painter->setBrush(m_col_dark.dark(160)); //} else { //no, just paint it with the usual color. //qDebug()<< " node : nothing"; setZValue(254); painter->setBrush(m_col); } painter->setPen(QPen(Qt::black, 0)); m_path = new QPainterPath; if ( m_shape == "circle") { m_path->addEllipse (-m_size, -m_size, 2*m_size, 2*m_size); } else if ( m_shape == "ellipse") { m_path->addEllipse(-m_size, -m_size, 2*m_size, 1.5* m_size); } else if ( m_shape == "box" || m_shape == "rectangle" ) { //rectangle: for GraphML compliance m_path->addRect (-m_size , -m_size , 1.8*m_size , 1.8*m_size ); } else if (m_shape == "roundrectangle" ) { //roundrectangle: GraphML only m_path->addRoundedRect (-m_size , -m_size , 1.8*m_size , 1.8*m_size, 60.0, 60.0, Qt::RelativeSize ); } else if ( m_shape == "triangle") { m_poly_t -> setPoints (3, 0,-m_size, -m_size,m_size, m_size,+m_size); m_path->addPolygon(*m_poly_t); m_path->closeSubpath(); } else if ( m_shape == "diamond"){ m_poly_d -> setPoints (4, 0,-m_size, -m_size,0, 0,+m_size, +m_size,0); m_path->addPolygon(*m_poly_d); m_path->closeSubpath(); } painter->drawPath (*m_path); } void Node::setLabelText ( QString label) { prepareGeometryChange(); m_label->setPlainText(label); m_hasLabel=true; } QString Node::labelText ( ) { if (m_hasLabel) { return m_label->toPlainText(); } else return ""; } void Node::setNumberInside (bool numIn){ m_isNumberInside = numIn; //Move its graphic number QPointF myPos = this->pos(); if ( m_hasNumber ) { if (!m_isNumberInside) { //move it outside m_number -> setZValue(254); m_number -> setPos( myPos.x()+m_nd, myPos.y() ); } else { //move it inside node this->setSize(m_size+2); //increase size to display nicely the number m_number -> setZValue(255); m_number->setPos( myPos.x() - m_size-2, myPos.y() - m_size-2 ); } } } /** * Propagates the changes to connected elements, i.e. edges, numbers, etc. * Checks if the node is inside the scene. */ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value) { QPointF newPos = value.toPointF(); switch (change) { case ItemPositionHasChanged : { foreach (Edge *edge, inEdgeList) //Move each inEdge of this node edge->adjust(); foreach (Edge *edge, outEdgeList) //Move each outEdge of this node edge->adjust(); //Move its graphic number if ( m_hasNumber ) { if (!m_isNumberInside) { //move it outside m_number -> setZValue(254); m_number -> setPos( m_size+m_nd, 0); } else { //move it inside node m_number -> setZValue(255); m_number -> setPos( - m_size, - m_size-3); } } if (m_hasLabel) { m_label->setPos( -2, m_ld+m_size); } if ( newPos.x() !=0 && newPos.y() != 0 ){ graphicsWidget->nodeMoved(nodeNumber(), (int) newPos.x(), (int) newPos.y()); } else qDebug()<< "Node: ItemChange(): Not emitting nodeMoved. Node " << nodeNumber()<<" is at 0,0"; break; } case ItemEnabledHasChanged:{ if (ItemEnabledHasChanged) { return 1; } else{ return 0; } } case ItemVisibleHasChanged: { if (ItemVisibleHasChanged){ return 1; } else{ return 0; } } default: { break; } }; return QGraphicsItem::itemChange(change, value); } /** handles the events of a click on a node */ void Node::mousePressEvent(QGraphicsSceneMouseEvent *event) { qDebug() << "Node::mousePressEvent() " << " set selected and emitting nodeClicked"; this->setSelected(true); // emit nodeClicked(this); graphicsWidget->nodeClicked(this); if ( event->button()==Qt::LeftButton ) { qDebug("Node::mousePressEvent() left click "); } if ( event->button()==Qt::RightButton ) { qDebug("Node: Right-click on node, at %i, %i", event->screenPos().x(), event->screenPos().y()); // emit openNodeContextMenu(); graphicsWidget->openNodeContextMenu(); /** Update commented out - caused segmentation fault when removing node */ // update(); // QGraphicsItem::mousePressEvent(event); } if ( event->button()==Qt::MidButton) { qDebug("Node: Middle-Click on a node. Calling GraphicsWidget startEdge()"); // emit startEdge(this); graphicsWidget->startEdge(this); } } void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { update(); QGraphicsItem::mouseReleaseEvent(event); } void Node::addInLink( Edge *edge ) { qDebug() << "Node: addInLink() for "<< m_num; inEdgeList.push_back( edge); //qDebug ("Node: %i inEdgeList has now %i edges", m_num, inEdgeList.size()); } void Node::deleteInLink( Edge *link ){ qDebug () << "Node: deleteInLink for "<< m_num; //qDebug ("Node: %i inEdgeList has %i edges", m_num, inEdgeList.size()); inEdgeList.remove( link); //qDebug ("Node: %i inEdgeList has now %i edges", m_num, inEdgeList.size()); } void Node::addOutLink( Edge *edge ) { qDebug("Node: addOutLink()"); outEdgeList.push_back( edge); // qDebug ("Node: outEdgeList has now %i edges", outEdgeList.size()); } void Node::deleteOutLink(Edge *link){ qDebug () << "Node: deleteOutLink() from " << m_num; // qDebug ("Node: %i outEdgeList has %i edges", m_num, outEdgeList.size()); outEdgeList.remove( link); // qDebug ("Node: %i outEdgeList has now %i edges", m_num, outEdgeList.size()); } void Node::addLabel (NodeLabel* gfxLabel ) { //qDebug("NODE: add label"); m_label=gfxLabel ; m_hasLabel=true; } NodeLabel* Node::label(){ return m_label; } void Node::deleteLabel(){ qDebug ("Node: deleteLabel "); m_hasLabel=false; m_label->hide(); graphicsWidget->removeItem(m_label); } void Node::clearLabel(){ m_hasLabel=false; } void Node::addNumber (NodeNumber *gfxNum ) { m_number=gfxNum ; m_hasNumber=true; } void Node::deleteNumber( ){ qDebug ("Node: deleteNumber "); m_number->hide(); graphicsWidget->removeItem(m_number); } Node::~Node(){ qDebug() << "\n\n\n *** ~Node() "<< nodeNumber(); foreach (Edge *edge, inEdgeList) { qDebug("~Node: removing edges in inEdgeList"); //edge->remove(); graphicsWidget->removeItem(edge); } foreach (Edge *edge, outEdgeList) { qDebug("~Node: removing edges in outEdgeList"); //edge->remove(); graphicsWidget->removeItem(edge); } this->deleteNumber(); this->deleteLabel(); inEdgeList.clear(); outEdgeList.clear(); this->hide(); graphicsWidget->removeItem(this); } socnetv-1.9/src/edgeweight.h0000775000175000017500000000367612542300240016363 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt edgeweight.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef EDGEWEIGHT_H #define EDGEWEIGHT_H #include class Edge; static const int TypeEdgeWeight = QGraphicsItem::UserType+5; class EdgeWeight: public QGraphicsTextItem { public: EdgeWeight(Edge * , int, QString); void removeRefs(); void move(double x, double y); enum { Type = UserType + 5 }; int type() const { return Type; } ~EdgeWeight(); private: }; #endif socnetv-1.9/src/randerdosrenyidialog.h0000664000175000017500000000477212542300240020452 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt randerdosrenyidialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef RANDERDOSRENYIDIALOG_H #define RANDERDOSRENYIDIALOG_H #include #include "ui_randerdosrenyidialog.h" class RandErdosRenyiDialog : public QDialog { Q_OBJECT public: explicit RandErdosRenyiDialog(QWidget *parent=0); public slots: void checkErrors(); void gatherData(); void gnmModel(); void gnpModel(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int nodes, const QString model, const int edges, const float eprob, const QString mode, const bool diag); private: QString model; QString mode; int nodes, edges; float eprob; bool diag; Ui::RandErdosRenyiDialog ui; }; #endif // RANDERDOSRENYIDIALOG_H socnetv-1.9/src/filteredgesbyweightdialog.h0000775000175000017500000000371112542300240021455 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt filteredgesbyweightdialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef FILTEREDGESBYWEIGHTDIALOG_H #define FILTEREDGESBYWEIGHTDIALOG_H #include #include "ui_filteredgesbyweightdialog.h" class FilterEdgesByWeightDialog : public QDialog { Q_OBJECT public: FilterEdgesByWeightDialog (QWidget *parent = 0); public slots: void gatherData (); signals: void userChoices( float, bool); private: Ui::FilterEdgesByWeightDialog ui; }; #endif socnetv-1.9/src/randsmallworlddialog.cpp0000664000175000017500000001124512542300240020773 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt randsmallworlddialog.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include #include #include #include #include #include #include "randsmallworlddialog.h" RandSmallWorldDialog::RandSmallWorldDialog(QWidget *parent) : QDialog(parent) { qDebug() << "::RandSmallWorldDialog() " ; ui.setupUi(this); nodes = 0; degree = 0; bprob = 0; mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &RandSmallWorldDialog::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); ui.probDoubleSpinBox->setEnabled(true); ui.degreeSpinBox-> setEnabled(true); ui.undirectedRadioButton->setChecked(true); ui.directedRadioButton->setEnabled(false); ui.diagCheckBox ->setChecked(false); ui.diagCheckBox -> setEnabled(false); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &RandSmallWorldDialog::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &RandSmallWorldDialog::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &RandSmallWorldDialog::setDiag); } void RandSmallWorldDialog::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void RandSmallWorldDialog::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void RandSmallWorldDialog::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void RandSmallWorldDialog::checkErrors() { qDebug()<< " RandSmallWorldDialog::checkErrors()" ; // if ( !ui.gnpRadioButton->isChecked() && !ui.gnmRadioButton->isChecked()) // { // QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; // effect->setColor(QColor("red")); // ui.gnpRadioButton->setGraphicsEffect(effect); // (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); // } // else { // ui.gnpRadioButton->setGraphicsEffect(0); // ui.gnmRadioButton->setGraphicsEffect(0); // (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); // } //gatherData(); } void RandSmallWorldDialog::gatherData() { qDebug() << "RandSmallWorldDialog::gatherData() " ; nodes = ui.nodesSpinBox->value(); bprob = ui.probDoubleSpinBox->value(); degree= ui.degreeSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "bprob " << bprob; qDebug() << "degree" << degree; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, degree, bprob, mode, diag); } socnetv-1.9/src/edge.cpp0000775000175000017500000003342612542300240015502 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt edge.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include #include #include #include #include //used for qDebug messages #include #include "graphicswidget.h" #include "edge.h" #include "node.h" #include "edgeweight.h" static const double Pi = 3.14159265; static double TwoPi = 2.0 * Pi; Edge::Edge( GraphicsWidget *gw, Node *from, Node *to, const Qt::PenStyle &style, const float &weight, const int &nodeSize, const QString &color, const bool &reciprocal, const bool &drawArrows, const bool &bez ) : graphicsWidget(gw) { qDebug("Edge: Edge()"); Q_UNUSED(nodeSize); graphicsWidget->scene()->addItem(this); //add edge to scene to be displayed from->addOutLink( this ); //adds this Edge to sourceNode to->addInLink( this ); //adds this Edge to targetNode source=from; //saves the sourceNode target=to; //Saves the targetNode m_style = style; m_color=color; m_drawArrows=drawArrows; m_reciprocal=reciprocal; m_startOffset=source->size(); //used to offset edge from the centre of node m_endOffset=target->size(); //used to offset edge from the centre of node // qDebug("Edge() m_startOffset %i",(int) m_startOffset); // qDebug("Edge() m_endOffset %i",(int) m_endOffset); m_arrowSize=4; //controls the width of the edge arrow eFrom = source->nodeNumber() ; eTo = target->nodeNumber() ; m_weight = weight ; m_Bezier = bez; adjust(); } void Edge::showArrows(bool drawArrows){ prepareGeometryChange(); m_drawArrows=drawArrows; } void Edge::removeRefs(){ //FIXME Need to disconnect signals from node to this "erased" edge qDebug("Edge: removeRefs()"); source->deleteOutLink(this); target->deleteInLink(this); } void Edge::setColor( const QString &str) { m_color=str; prepareGeometryChange(); } QString Edge::color() const{ return m_color; } /** * @brief Edge::colorToPajek * Called from Graph::saveGraphToPajekFormat() * @return */ QString Edge::colorToPajek() { if (m_color.startsWith("#")) { return ("RGB"+m_color.right( m_color.size()-1 )).toUpper() ; } return m_color; } /** * @brief Edge::setWeight * Called from MW when user wants to change an edge's weight. Updates both the width and the EdgeWeight * @param w */ void Edge::setWeight(const float &w) { m_weight = w; foreach (EdgeWeight *wgt, weightList) ///update the edgeWeight of this edge as well. wgt->setPlainText (QString::number(w)); } float Edge::weight() const { return m_weight; } void Edge::setStartOffset(int offset){ m_startOffset=offset; } void Edge::setEndOffset(int offset){ m_endOffset=offset; } Node *Edge::sourceNode() const { return source; } void Edge::setSourceNode(Node *node) { source = node; adjust(); } Node *Edge::targetNode() const { return target; } void Edge::setTargetNode(Node *node){ target = node; adjust(); } int Edge::sourceNodeNumber () { return eFrom; } int Edge::targetNodeNumber() { return eTo; } /** * @brief Edge::addWeight * Called from EdgeWeight objects to 'connect' them to this edge. * @param canvasWeight */ void Edge::addWeight (EdgeWeight* canvasWeight ) { weightList.push_back( canvasWeight) ; } void Edge::clearWeightList(){ foreach (EdgeWeight *wgt, weightList) //Delete this weight wgt->deleteLater(); weightList.clear(); } /** * @brief Edge::adjust * leaves some empty space (offset) from node - * make the edge weight appear on the centre of the edge */ void Edge::adjust(){ // qDebug("Edge: adjust()"); if (!source || !target) return; QLineF line(mapFromItem(source, 0, 0), mapFromItem(target, 0, 0)); QPointF edgeOffset; if (source!=target) { qreal length = line.length(); edgeOffset = QPointF((line.dx() * m_endOffset) / length, (line.dy() *m_endOffset) / length); } else edgeOffset = QPointF(0, 0); prepareGeometryChange(); sourcePoint = line.p1() + edgeOffset; targetPoint = line.p2() - edgeOffset; // qDebug()<<"----Edge: adjust() "<< sourcePoint.x()<< " "<setPos( (source->x()+target->x())/2.0, (source->y()+target->y())/2.0 ); } /** * @brief Edge::shape * Returns the shape of this edge as a QPainterPath in local coordinates. * The shape is used for many things, including collision detection, hit tests, * and for the QGraphicsScene::items() functions. * The default implementation calls boundingRect() to return a simple rectangular shape, * but we reimplement it to return a more accurate shape for non-rectangular items. * @return QPainterPath */ QPainterPath Edge::shape () const { //qDebug()<<"Edge::shape()"; //too many debug messages... QPainterPath path; qreal extra = ( width() + m_arrowSize); QLineF line(sourcePoint, targetPoint); QPolygonF poly; line.translate(extra,extra); poly.push_back(line.p1()); poly.push_back(line.p2()); line.translate(-extra,-extra); poly.push_back(line.p1()); poly.push_back(line.p2()); path.addPolygon(poly); path.closeSubpath(); //path.addRegion(boundingRegion(QTransform())); // path.addRect(boundingRect()); return path; } /** * @brief Edge::boundingRect * Defines the outer bounds of the edge as a rectangle; * All painting will be restricted to inside the edge's bounding rect. * Qt uses this bounding rect to determine whether the edge requires redrawing. * @return */ QRectF Edge::boundingRect() const { if (!source || !target) return QRectF(); qreal penWidth = 1; qreal extra = ( penWidth + m_arrowSize) / 2.0; QRectF a = QRectF ( sourcePoint, QSizeF( targetPoint.x() - sourcePoint.x(), targetPoint.y() - sourcePoint.y()) ).normalized().adjusted(-extra, -extra, extra, extra); //qDebug()<<"Edge::boundingRect() extra = " << extra << "QSizeF width "<< a.width() << " QSizeF height "<< a.height(); if (source==target) { //self-edge has different bounding rect. return QRectF ( sourcePoint-QPointF(30,30), QSizeF(60,30)).normalized().adjusted(-extra, -extra, extra, extra); } return a; } void Edge::makeReciprocal(){ qDebug("Edge::makeReciprocal()"); prepareGeometryChange(); m_reciprocal= true; } void Edge::unmakeReciprocal(){ qDebug("Edge::unmakeReciprocal()"); prepareGeometryChange(); m_reciprocal= false; } bool Edge::isReciprocal() { return m_reciprocal; } void Edge::setStyle( const Qt::PenStyle &style ) { m_style = style; } Qt::PenStyle Edge::style() const{ return m_style; } QPen Edge::pen() const { if (m_weight < 0 ){ return QPen(QColor("red"), width(), Qt::DashLine, Qt::RoundCap, Qt::RoundJoin); } return QPen(QColor(m_color), width(), style(), Qt::RoundCap, Qt::RoundJoin); } void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){ if (!source || !target) return; Q_UNUSED(option); // painter->setClipRect( option->exposedRect ); // qDebug() <<"@@@ Edge::paint()"; // qDebug()<x() <<","<< (sourceNode())->y() // << ") to node "<x() // <<","<< (targetNode())->y() << ") of weight "<< m_weight; //Define the path upon which we' ll draw the line QPainterPath line(sourcePoint); //Construct the path if (source!=target) { if ( !m_Bezier){ // qDebug()<< "*** Edge::paint(). Constructing a line"; line.lineTo(targetPoint); } else { qDebug() << "*** Edge::paint(). Constructing a bezier curve"; } } else { //self-link QPointF c1 = QPointF( targetPoint.x() -30, targetPoint.y() -30 ); QPointF c2 = QPointF( targetPoint.x() +30, targetPoint.y() -30 ); // qDebug()<< "*** Edge::paint(). Constructing a bezier self curve c1 " // <setPen( pen() ); //Draw the arrows only if we have different nodes. if (m_drawArrows && source!=target) { angle = 0; line_length = line.length(); line_dx = targetPoint.x()-sourcePoint.x(); line_dy = targetPoint.y()-sourcePoint.y(); if ( line.length() >0 ) angle = ::acos( line_dx / line_length ); // qDebug() << " acos() " << ::acos( line_dx / line_length ) ; if ( line_dy >= 0) angle = TwoPi - angle; // qDebug() << "*** Edge::paint(). Constructing arrows. First Arrow at target node" // << "target-source: " << line_dx // << " length: " << line_length // << " angle: "<< angle; QPointF destArrowP1 = targetPoint + QPointF(sin(angle - Pi / 3) * m_arrowSize, cos(angle - Pi / 3) * m_arrowSize); QPointF destArrowP2 = targetPoint + QPointF(sin(angle - Pi + Pi / 3) * m_arrowSize, cos(angle - Pi + Pi / 3) * m_arrowSize); // qDebug() << "*** Edge::paint() destArrowP1 " // << destArrowP1.x() << "," << destArrowP1.y() // << " destArrowP2 " << destArrowP2.x() << "," << destArrowP2.y(); painter->setBrush(QColor(m_color)); QPolygonF destP; destP << targetPoint << destArrowP1 << destArrowP2; line.addPolygon ( destP); //painter->drawPolygon(QPolygonF() << line.p2() << destArrowP1 << destArrowP2); if (m_reciprocal) { // qDebug() << "**** Edge::paint() This edge is SYMMETRIC! " // << " So, we need to create Arrow at src node as well"; QPointF srcArrowP1 = sourcePoint + QPointF(sin(angle +Pi / 3) * m_arrowSize, cos(angle +Pi / 3) * m_arrowSize); QPointF srcArrowP2 = sourcePoint + QPointF(sin(angle +Pi - Pi / 3) * m_arrowSize, cos(angle +Pi - Pi / 3) * m_arrowSize); // qDebug() << "*** Edge::paint() srcArrowP1 " << srcArrowP1.x() << "," << srcArrowP1.y() // << " srcArrowP2 " << srcArrowP2.x() << "," << srcArrowP2.y(); QPolygonF srcP; srcP << sourcePoint<< srcArrowP1<< srcArrowP2; line.addPolygon ( srcP); // painter->drawPolygon(QPolygonF() << line.p1() << srcArrowP1 << srcArrowP2); } else { // qDebug() << "*** Edge::paint() Not symmetric edge. Finish"; } } else { // qDebug()<< "*** Edge::paint(). This edge is self-link - CONTINUE!"; } // qDebug()<< "### Edge::paint(). DrawPath now...."; painter->drawPath(line); } /** Controls the width of the edge; is a function of edge weight */ float Edge::width() const{ // qDebug()<< "Edge::width() will return "<< fabs(m_weight); if ( fabs(m_weight) > 1 ) return 1 + fabs(m_weight)/10; return 1; // Default, if m_weight in (-1, 1) space } /** handles the events of a click on an edge*/ void Edge::mousePressEvent(QGraphicsSceneMouseEvent *event) { qDebug("Edge: pressEvent() emitting edgeClicked"); graphicsWidget->edgeClicked(this); if ( event->button()==Qt::LeftButton ) { qDebug() << "Edge: edge pressEvent() left click > "; // graphicsWidget->startNodeMovement(0); } if ( event->button()==Qt::RightButton ) { qDebug("Edge: Right-click on an edge, at %i, %i", event->screenPos().x(), event->screenPos().y()); graphicsWidget->openEdgeContextMenu(); } } Edge::~Edge(){ qDebug() << "\n\n\n *** ~EDGE() " << sourceNodeNumber()<< "->" << targetNodeNumber(); removeRefs(); weightList.clear(); this->hide(); } socnetv-1.9/src/node.h0000775000175000017500000000761712542300240015173 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt node.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef NODE_H #define NODE_H using namespace std; #include #include #include class GraphicsWidget; class QGraphicsSceneMouseEvent; class Edge; class NodeLabel; class NodeNumber; static const int TypeNode = QGraphicsItem::UserType+1; /** * This is actually a container-class. * Contains the graphical objects called Nodes, * which are displayed as triangles, boxes, circles, etc, on the canvas. * Each node "knows" the others with which she is connected. */ // class Node : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: Node(GraphicsWidget*, int num, int size, QString col, QString shape, bool, int, int, QPointF p) ; ~Node(); enum { Type = UserType + 1 }; int type() const { return Type; } QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); long int nodeNumber() {return m_num;} void setSize(int); int size(); void setShape (const QString); QString nodeShape() {return m_shape;} void setColor(QString str); void setColor(QColor color); QString color (); void setLabelText ( QString label) ; QString labelText () ; // Used by GW:: hasNode() NodeLabel* label(); void addLabel (NodeLabel* gfxLabel ) ; void deleteLabel(); void clearLabel(); void addInLink( Edge *edge ) ; void deleteInLink(Edge*); void addOutLink( Edge *edge ) ; void deleteOutLink(Edge*); void setNumberInside(bool); void addNumber (NodeNumber *gfxNum ) ; void deleteNumber(); void toggleAntialiasing(bool); protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); signals: void nodeClicked(Node*); void openNodeContextMenu(); void startEdge(Node *); void adjustOutEdge(); void adjustInEdge(); void removeOutEdge(); void removeInEdge(); private: GraphicsWidget *graphicsWidget; QPainterPath *m_path; QPointF newPos; QPolygon *m_poly_t, *m_poly_d; int m_size, m_nd, m_ld; long int m_num; QString m_shape, m_col_str, m_labelIn; QColor m_col; bool m_hasNumber, m_hasLabel, m_isNumberInside; /**Lists of elements attached to this node */ list inEdgeList, outEdgeList; NodeLabel *m_label; NodeNumber *m_number; }; #endif socnetv-1.9/src/matrix.h0000775000175000017500000001223312542300240015540 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt matrix.h - description ------------------- copyright : (C) 2005-2015 by dimitris kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef MATRIX_H #define MATRIX_H #include //used for qDebug function #include #include #include // std::pair, std::make_pair using namespace std; //or else compiler groans for nothrow class Row { public: Row (int cols=0) { cell=new (nothrow) float [m_cols=cols]; Q_CHECK_PTR( cell ); for (register int i=0;i. * ********************************************************************************/ #ifndef PARSER_H #define PARSER_H using namespace std; #include #include #include #include #include #include class QXmlStreamReader; class QXmlStreamAttributes; /** Main class for network file parsing and loading Currently, it supports Pajek, Adjacency, Graphviz, GraphML */ class Parser : public QObject { Q_OBJECT public: Parser(const QString fn, const QString codec, const int iNS, const QString iNC, const QString iNSh, const QString iNNC, const int iNNS, const QString iNLC, const int iNLS , const QString iEC, const int w, const int h, const int format, const int sm_mode); ~Parser(); bool run(); bool loadPajek(); bool loadAdjacency(); bool loadDot(); bool loadGraphML(); bool loadGML(); bool loadGW(); bool loadDL(); bool loadSimpleList(); bool loadWeighedList(); bool loadTwoModeSociomatrix(); void dotProperties(QString str, float &, QString &label, QString &shape, QString &color, QString &fontName, QString &fontColor ); void readGraphML (QXmlStreamReader &); void readGraphMLElementGraph(QXmlStreamReader &); void readGraphMLElementNode (QXmlStreamReader &); void endGraphMLElementNode (QXmlStreamReader &); void readGraphMLElementEdge (QXmlStreamAttributes &); void endGraphMLElementEdge (QXmlStreamReader &); void readGraphMLElementData (QXmlStreamReader &); void readGraphMLElementUnknown (QXmlStreamReader &); void readGraphMLElementKey (QXmlStreamAttributes &); bool xmlStreamHasAttribute( QXmlStreamAttributes &, QString ) const ; void readGraphMLElementDefaultValue(QXmlStreamReader &); void readGraphMLElementNodeGraphics (QXmlStreamReader &); void readGraphMLElementEdgeGraphics (QXmlStreamReader &); void createEdgesMissingNodes(); bool isComment(QString str); void createRandomNodes(int, QString, int); signals: void addRelation( QString ); void changeRelation( int ); void createNode( int num, int size, QString color, QString numColor, int numSize, QString label, QString lColor, int lSize, QPointF p, QString shape, bool signalMW); void createEdge (int, int, float, QString, int, bool, bool); void fileType(int, QString, int, int, bool); void removeDummyNode (int); void finished(QString); protected: private: QHash nodeNumber; QHash keyFor, keyName, keyType, keyDefaultValue ; QHash edgesMissingNodesHash; QStringList edgeMissingNodesList,edgeMissingNodesListData; QMultiMap firstModeMultiMap, secondModeMultiMap; QXmlStreamReader *xml; QString fileName, userSelectedCodecName, networkName, initNodeColor; QString initEdgeColor, initNodeShape, initNodeNumberColor, initNodeLabelColor; QString nodeColor, edgeColor, edgeType, nodeShape, nodeLabel, edgeLabel; QString nodeNumberColor, nodeLabelColor; QString key_id, key_value, key_name, key_what, key_type; QString node_id, edge_id, edge_source, edge_target; int gwWidth, gwHeight; int totalLinks, aNodes, fileFormat, two_sm_mode, undirected; int initNodeSize, initNodeNumberSize, nodeNumberSize, initNodeLabelSize; int nodeLabelSize, source, target, nodeSize; float initEdgeWeight, edgeWeight, arrowSize; float bez_p1_x,bez_p1_y, bez_p2_x, bez_p2_y; bool missingNode; bool arrows, bezier, conv_OK; bool bool_key, bool_node, bool_edge, fileContainsNodeColors; bool fileContainsNodeCoords, fileContainsLinkColors; double randX, randY; }; #endif socnetv-1.9/src/webcrawlerdialog.cpp0000775000175000017500000001175712542300240020116 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt webcrawlerdialog.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "webcrawlerdialog.h" #include #include #include WebCrawlerDialog::WebCrawlerDialog(QWidget *parent) : QDialog (parent) { ui.setupUi(this); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.seedUrlEdit)->setFocus(); connect (ui.extLinksCheckBox, &QCheckBox::stateChanged, this, &WebCrawlerDialog::checkErrors); connect (ui.intLinksCheckBox, &QCheckBox::stateChanged, this, &WebCrawlerDialog::checkErrors); if ( !ui.extLinksCheckBox->isChecked() &&!ui.intLinksCheckBox->isChecked() ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); } } void WebCrawlerDialog::checkErrors(){ qDebug()<< "WebCrawlerDialog::checkErrors..."; if ( !ui.extLinksCheckBox->isChecked() && !ui.intLinksCheckBox->isChecked() ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); } else (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); } void WebCrawlerDialog::gatherData(){ qDebug()<< "WebCrawlerDialog::gatherData()..."; bool extLinks=true, intLinks=false; QString seedUrl = (ui.seedUrlEdit)->text(); qDebug()<< "WebCrawlerDialog::gatherData() initial seed url " << seedUrl << " simplifying and lowering it"; seedUrl = seedUrl.simplified().toLower() ; qDebug()<< "WebCrawlerDialog::gatherData() adding / to seed url "; seedUrl = seedUrl + "/"; QUrl newUrl(seedUrl); qDebug()<< "WebCrawlerDialog::gatherData() QUrl " << newUrl.toString() << " scheme " << newUrl.scheme() << " host " << newUrl.host() << " path " << newUrl.path(); if ( newUrl.scheme() != "http" && newUrl.scheme() != "https" && newUrl.scheme() != "ftp" && newUrl.scheme() != "ftps") { qDebug()<< "WebCrawlerDialog::gatherData() URL scheme missing " << newUrl.scheme() << " setting the default scheme http "; newUrl = QUrl ("http://" + seedUrl); qDebug() << newUrl; } if (! newUrl.isValid() || newUrl.host() == "") { emit webCrawlerDialogError(seedUrl); qDebug()<< "WebCrawlerDialog::gatherData() not valid URL"; return; } seedUrl = newUrl.toString(); qDebug()<< "WebCrawlerDialog::gatherData() final seed url " << newUrl << " scheme " << newUrl.scheme() << " host " << newUrl.host() << " path " << newUrl.path(); int maxLinksPerPage = (ui.maxLinksPerPageSpinBox) -> value(); int totalUrlsToCrawl = (ui.totalUrlsToCrawlSpinBox) -> value(); if ( ui.extLinksCheckBox -> isChecked() ) { qDebug()<< " External links will be crawled... " ; extLinks = true; } else { qDebug()<< " No external links... "; extLinks = false; } if ( ui.intLinksCheckBox -> isChecked() ) { qDebug()<< " Internal links will be crawled too. " ; intLinks = true; } else { qDebug()<< " No internal links. "; intLinks = false; if (!intLinks && !extLinks) return; } qDebug()<< " seedUrl: " << seedUrl; qDebug()<< " maxLinksPerPage " << maxLinksPerPage << " totalUrlsToCrawl " << totalUrlsToCrawl ; emit userChoices( seedUrl, totalUrlsToCrawl, maxLinksPerPage, extLinks, intLinks ); } socnetv-1.9/src/src.qrc0000775000175000017500000000546312534331160015376 0ustar dimitrisdimitris images/properties.png images/selectall.png images/selectnone.png images/new.png images/open.png images/save.png images/saved.png images/exit.png images/add.png images/remove.png images/print.png images/help.png images/color.png images/colorize.png images/find.png images/letters.png images/line.png images/node.png images/nodes.png images/sm.png images/dm.png images/socnetv.png images/socnetv-32px.png images/net.png images/net1.png images/net2.png images/net3.png images/box.png images/circle.png images/diamond.png images/triangle.png images/ellipse.png images/symmetry.png images/plines.png images/zoomin.png images/zoomout.png images/connect.png images/disconnect.png images/resize.png images/nodecolor.png images/view.png images/diameter.png images/distance.png images/back.png images/forward.png images/home.png images/sw.png images/erdos.png images/circular.png images/clique.png images/walk.png images/triad.png images/avdistance.png images/symmetrize.png images/nodeout.png images/nodein.png images/gridlines.png images/webcrawler.png images/prestige.png images/centrality.png images/eccentricity.png images/prevrelation.png images/addrelation.png images/nextrelation.png images/texteditor.png images/networkfile.png images/random.png images/filter.png images/import.png images/force.png images/scalefree.png socnetv-1.9/src/webcrawlerdialog.h0000775000175000017500000000411712542300240017553 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt webcrawlerdialog.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef WEBCRAWLERDIALOG_H #define WEBCRAWLERDIALOG_H #include #include "ui_webcrawlerdialog.h" class WebCrawlerDialog: public QDialog { Q_OBJECT public: WebCrawlerDialog (QWidget *parent = 0); public slots: void checkErrors (); void gatherData (); signals: void userChoices( QString, int, int, bool, bool); void webCrawlerDialogError(QString); private: Ui::WebCrawlerDialog ui; }; #endif socnetv-1.9/src/main.cpp0000775000175000017500000000651012542300240015514 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt main.cpp - description ------------------- begin : 9 21:10:04 EET 2005 copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include //core Qt functionality #include //for text translations #include #include //used for cout #include "mainwindow.h" //main application window using namespace std; int main(int argc, char *argv[]) { Q_INIT_RESOURCE(src); QApplication app(argc, argv); QTranslator tor( 0 ); QLocale locale; // set the location where your .qm files are in load() below as the last parameter instead of "." // for development, use "/" to use the english original as // .qm files are stored in the base project directory. tor.load( QString("socnetv.") + locale.name(), "." ); app.installTranslator( &tor ); //Check if a filename is passed when this program is called. QString option; if ( argc > 1 ) { option = argv[1]; if (option=="--help" || option=="-h" || option=="--h" ) { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION)<< "\n" <<"\nUsage: socnetv [flags] [file]\n" <<"-h, --help Displayes this help message\n" <<"-V, --version Displays version number\n\n" <<"You can load a network from a file using \n" <<"socnetv file.net \n" <<"where file.net/csv/dot/graphml must be of valid format. See README\n\n" <<"Please send any bug reports to dimitris.kalamaras@gmail.com.\n\n"; return -1; } else if (option=="-V" || option=="--version") { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION) << "\nCopyright Dimitris V. Kalamaras, \nLicense: GPL3\n\n"; return -1; } else { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION); cout<<"\nLoading file: " << qPrintable(option) << "\n\n"; } } MainWindow *socnetv=new MainWindow(option); socnetv->show(); return app.exec(); } socnetv-1.9/src/icon.rc0000775000175000017500000000006112377612576015365 0ustar dimitrisdimitrisIDI_ICON1 ICON DISCARDABLE "images/socnetv.ico" socnetv-1.9/src/previewform.h0000664000175000017500000000474112542300240016603 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt previewform.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef PREVIEWFORM_H #define PREVIEWFORM_H #include #include class QComboBox; class QDialogButtonBox; class QLabel; class QTextCodec; class QTextEdit; class PreviewForm : public QDialog { Q_OBJECT public: explicit PreviewForm(QWidget *parent = 0); void setCodecList(const QList &list); void setEncodedData(const QByteArray &data, const QString, const int ); QString decodedString() const { return decodedStr; } signals: void userCodec(const QString, const QString, const int); private slots: void updateTextEdit(); void accept(); private: QByteArray encodedData; QString decodedStr, fileName; int format; QComboBox *encodingComboBox; QLabel *encodingLabel; QTextEdit *textEdit; QDialogButtonBox *buttonBox; }; #endif // PREVIEWFORM_H socnetv-1.9/src/randscalefreedialog.h0000664000175000017500000000156112542300240020211 0ustar dimitrisdimitris#ifndef RANDSCALEFREEDIALOG_H #define RANDSCALEFREEDIALOG_H #include #include "ui_randscalefreedialog.h" class RandScaleFreeDialog : public QDialog { Q_OBJECT public: explicit RandScaleFreeDialog(QWidget *parent = 0); public slots: void checkErrors(); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int &nodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode); private: QString mode; int nodes; // n int initialNodes; // m0 int edgesPerStep; //m int power; float zeroAppeal; // a bool diag; Ui::RandScaleFreeDialog ui; }; #endif // RANDSCALEFREEDIALOG_H socnetv-1.9/src/graph.h0000775000175000017500000006047412542300240015347 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt graph.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef GRAPH_H #define GRAPH_H #include #include // used in exporting centrality files #include #include #include #include //FYI: stack is a wrapper around in C++, see: www.cplusplus.com/reference/stl/stack #include #include #include "vertex.h" #include "matrix.h" #include "parser.h" #include "webcrawler.h" using namespace std; class QPointF; /** This is the main class for a Graph, used in conjuction with Vertex, Parser and Matrix objects. Graph class has the interface and the various network algorithms Vertex class holds each vertex data (colors, strings, statistics, etc) Matrix class holds the adjacency matrix of the network. Parser class loads files of networks. */ typedef QList Vertices; typedef QHash H_StrToInt; typedef QHash H_Int; typedef QPair pair_f_b; typedef QPair rel_w_bool; typedef QHash < int, rel_w_bool > H_edges; typedef QHash H_StrToBool; class Distance { public: int target; int distance; Distance(int t, int dist) : target(t), distance(dist) { } }; struct Distance1 { int target; int distance; }; // implement a min-priority queue class CompareDistances { public: bool operator()(Distance& t1, Distance& t2) { if (t1.distance == t2.distance) return t1.target > t2.target; return t1.distance > t2.distance; //minimum priority // Returns true if t1 is closer than t2 // else } }; class Graph: public QObject{ Q_OBJECT QThread file_parserThread; QThread wc_parserThread; QThread wc_spiderThread; public slots: int currentRelation(); /** Slots to signals from Parser */ void addRelationFromParser(QString); void createVertex( int i, int size, QString nodeColor, QString numColor, int numSize, QString label, QString lColor, int lSize, QPointF p, QString nodeShape, bool signalMW );//Main vertex creation call void setFileType(int, QString, int,int, bool); void removeDummyNode(int); void terminateParserThreads (QString reason); /** Slots to signals from GraphicsWidget and Parser*/ void createEdge (int, int, float, QString, int, bool, bool); //GW and Parser. void createEdge (int, int, float, int, bool, bool); //GW void createEdgeWebCrawler (int, int); //WebCrawler void nodeMovement(bool state, int type, int cW, int cH); //Called by MW to start movement void slotSetEdgeVisibility(int relation, int, int, bool); //auxiliary createVertex functions void createVertex(int i, QPointF p); //Called by GW void createVertex(int i, int canvasWidth, int canvasHeight); //Called by MW void createVertexWebCrawler(QString label, int i) ; /** Slots to signals from MainWindow */ void changeRelation(int); void addRelationFromUser(QString relation); void setCanvasDimensions(int w, int h); void filterIsolateVertices ( bool ); //Called by MW to filter orphan vertices void filterEdgesByWeight (float, bool); //Called by MW to filter edges over/under a weight void filterEdgesByRelation(int relation, bool status); void webCrawl(QString, int, int, bool extLinks, bool intLinks); //Called by MW to start a web crawler... void setGraphChanged(bool changed) { graphModified = changed; } signals: /** Signals to MainWindow */ void updateProgressDialog(int ); void graphChanged(); //call to update MW widgets void signalFileType (int, QString, int,int, bool); //notifies MW what we have loaded. void statusMessage (QString message); //updates statusbar message void addRelationToMW(QString newRelation); void describeDataset(QString); void signalNodeSizesByOutDegree(bool); void signalNodeSizesByInDegree(bool); /** Signals to GraphicsWidget */ void drawNode( int ,int, QString, QString, int, QString, QString, int, QPointF, QString, bool, bool, bool); //call GW to draw a node void eraseNode (long int); //erase node from GW void drawEdge(int, int, float, bool, bool, QString, bool); //call GW to draw an edge void eraseEdge(int, int); //emited from removeEdge() to GW to clear the edge item. void setEdgeVisibility (int, int, int, bool); // emitted from each Vertex void setVertexVisibility(long int, bool); //notifies GW to disable a node void setNodeSize(long int, int); void setNodeShape(const long int, const QString); void setNodeColor(long int, QString); void setNodeLabel(long int, QString); void drawEdgeReciprocal(int, int); //call GW to draw the edge as symmetric one void changeEdgeColor(long int, long int, QString); void addGuideCircle(int, int, int); //call GW to draw a circular layout line somewhere. void addGuideHLine (int); //call GW to draw a horizontal layout line somewhere. void moveNode(int, qreal, qreal); /** Signals to Vertice */ void relationChanged(int); /** Signals to Crawler threads */ void operateSpider(); public: /* INIT AND CLEAR*/ Graph(); void clear(); ~Graph(); //destroy object void setSocNetV_Version (QString ver) { VERSION = ver; } void setShowLabels(bool toggle); void setShowNumbersInsideNodes(bool toggle); /*FILES (READ AND WRITE)*/ bool loadGraph (const QString, const QString m_codecName, const bool, const int maxWidth, const int maxHeight, const int format, const int two_sm_mode); bool saveGraph( QString fileName, int fileType, QString networkName, int maxWidth, int maxHeight ); bool saveGraphToPajekFormat (QString fileName,QString networkName, int maxWidth, int maxHeight); bool saveGraphToAdjacencyFormat (QString fileName); bool saveGraphToDotFormat (QString fileName, QString networkName, int maxWidth, int maxHeight); bool saveGraphToGraphMLFormat (QString fileName,QString networkName, int maxWidth, int maxHeight); /* VERTICES */ int lastVertexNumber(); int firstVertexNumber(); int hasVertex(long int ); int hasVertex(QString); void removeVertex (long int ); void setInitVertexSize (const long int); void setVertexSize(const long int &v, const int &newsize ); void setAllVerticesSize(const int &newsize); int vertexSize(const long int &v); void setInitVertexShape (const QString); void setVertexShape(const int v, const QString shape); void setAllVerticesShape(const QString shape); QString vertexShape(const int &v); void setInitVertexColor (const QString &color); void setVertexColor(const long &v, const QString &color); void setAllVerticesColor(const QString &color); QColor vertexColor(const long int &v); void setInitVertexNumberColor ( QString color); void setInitVertexNumberSize (int size); void setInitVertexLabelSize(int newSize); void setVertexLabelSize(int v, int newSize); void setInitVertexLabelColor(QString color); void setVertexLabel(int v, QString label); void setVertexLabelColor(int v1, QString color); QString vertexLabel(const long int &v1); void updateVertCoords(int v, int x, int y); int vertices(const bool dropIsolates=false, const bool countAll=false) ; int outboundEdges (int i) ; int inboundEdges (int i) ; int outDegree(int); int inDegree(int); int verticesWithOutboundEdges(); int verticesWithInboundEdges(); int verticesWithReciprocalEdges(); QList verticesIsolated(); qreal euclideian_distance(const QPointF &a, const QPointF &b); qreal euclideian_distance(const QPointF &a); int sign(const qreal &D); qreal layoutForceDirected_F_rep(const qreal &dist,const qreal &optimalDistance) ; qreal layoutForceDirected_F_att(const qreal &dist,const qreal &optimalDistance) ; void layoutForceDirected_Eades_moveNodes(const qreal &c4); void layoutForceDirected_FR_moveNodes(const qreal &temperature) ; qreal computeOptimalDistance(const int &Vertices); void compute_angles( const QPointF &Delta, const qreal &dist, qreal &angle1, qreal &angle2, qreal °rees1, qreal °rees2 ); /* EDGES */ int enabledEdges(); void edges(); float hasArc (const long &v1, const long &v2); bool hasEdge (const int &v1, const long int &v2); void removeEdge (int v1, int v2); bool isWeighted(); void setArcWeight (const long int &v1, const long int &v2, const float &w); void setInitEdgeColor(const QString &); void setEdgeColor(const long int &v1, const long int &v2, const QString &color); QString edgeColor (const long int &v1, const long int &v2); bool setAllEdgesColor(const QString &color); float density(); bool symmetricEdge(int v1, int v2); bool isSymmetric(); void symmetrize(); void createAdjacencyMatrix(const bool dropIsolates=false, const bool considerWeights=true, const bool inverseWeights=false, const bool symmetrize=false ); bool invertAdjacencyMatrix(const QString &method); /* PRINT OUT TO FILES*/ void writeDataSetToFile(const QString dir, const QString ); void writeAdjacencyMatrixTo(QTextStream& os); void writeAdjacencyMatrix(const QString, const char*); void writeInvertAdjacencyMatrix(const QString &filename, const QString &, const QString &); void writeDistanceMatrix(const QString fn, const char*, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void writeNumberOfGeodesicsMatrix(const QString fn, const char*, const bool considerWeights, const bool inverseWeights); void writeEccentricity(const QString, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); // friend QTextStream& operator << (QTextStream& os, Graph& m); void writeCentralityDegree(const QString, const bool weights, const bool dropIsolates); void writeCentralityCloseness(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writeCentralityClosenessInfluenceRange(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writeCentralityBetweenness(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writeCentralityPower(const QString, const bool weigths, const bool inverseWeights, const bool dropIsolates); void writeCentralityStress(const QString, const bool weigths, const bool inverseWeights, const bool dropIsolates); void writeCentralityEccentricity(const QString, const bool weigths, const bool inverseWeights, const bool dropIsolates); void writeCentralityInformation(const QString, const bool weigths, const bool inverseWeights); void writePrestigeDegree(const QString, const bool weights, const bool dropIsolates); void writePrestigeProximity(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writePrestigePageRank(const QString, const bool Isolates=false); void writeCliqueCensus( const QString fileName, const bool considerWeights ); void writeClusteringCoefficient(const QString, const bool); void writeTriadCensus(const QString, const bool); /* DISTANCES, CENTRALITIES & PROMINENCE MEASURES */ int distance(const int, const int, const bool considerWeights, const bool inverseWeights); int diameter(const bool considerWeights, const bool inverseWeights); float averageGraphDistance(const bool considerWeights, const bool inverseWeights, const bool dropIsolates); int connectedness(); void createDistanceMatrix(const bool centralities=false, const bool considerWeights=false, const bool inverseWeights=true, const bool dropIsolates=false); void centralityDegree(const bool weights, const bool dropIsolates=false); void centralityInformation(const bool considerWeights=false, const bool inverseWeights=false); void centralityClosenessInfluenceRange(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); void prestigeDegree(bool, bool); void prestigePageRank(const bool dropIsolates=false); void prestigeProximity(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); /* REACHABILTY AND WALKS */ int numberOfWalks(int v1, int v2,int length); void createNumberOfWalksMatrix(int length); void writeTotalNumberOfWalksMatrix(QString fn, QString netName, int length); void writeNumberOfWalksMatrix(QString fn, QString netName, int length); int reachable(int v1, int v2) ; QList influenceRange(int v1); QList influenceDomain(int v2); void reachabilityMatrix(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); void writeReachabilityMatrix(QString fn, QString netName, const bool dropIsolates=false); float numberOfTriples(int v1); float countCliquesWith(int source, int size=0); bool addClique (const QList &list); float countCliquesOfSize(int size ); float localClusteringCoefficient(const long int &v1); float clusteringCoefficient (); bool triadCensus(); void examine_MAN_label(int, int, int, Vertex*, Vertex*, Vertex* ); // void eccentr_JordanCenter(); // TODO /* LAYOUTS */ void layoutRandom( double maxWidth, double maxHeight ); void layoutCircularRandom(double x0, double y0, double maxRadius); void layoutCircularByProminenceIndex(double x0, double y0, double maxRadius, int type, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void layoutLevelByProminenceIndex(double maxWidth, double maxHeight, int type, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void layoutVerticesSizeByProminenceIndex(int index, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void layoutForceDirectedSpringEmbedder(bool &dynamicMovement); void layoutForceDirectedFruchtermanReingold(bool dynamicMovement); /* CRAWLER */ void terminateCrawlerThreads (QString reason); /**RANDOM NETWORKS*/ void makeThingsLookRandom(); void createRandomNetErdos ( const int &vert, const QString &model, const int &edges, const float &eprob, const QString &mode, const bool &diag); void createRandomNetRingLattice (int, int, double, double, double); void createSameDegreeRandomNetwork (int, int); void createRandomNetScaleFree (const int &n, const int &power, const int &m0, const int &m, const float &alpha, const QString &mode, const double &x0, const double &y0, const double &radius); void createRandomNetSmallWorld (int, int, double, double, double, double); int factorial (int); int relations(); void addRelationFromGraph(QString relationName); /** index stores the real position of each vertex inside m_graph. * It starts at zero (0). * We need to know the place of a vertex inside m_graph after adding * or removing many vertices */ H_Int index; // Stores the number of vertices at distance n from a given vertex H_Int sizeOfNthOrderNeighborhood; /* maps have O(logN) lookup complexity */ /* Consider using tr1::hashmap which has O(1) lookup, but this is not ISO C++ yet :( */ protected: // Called from nodeMovement when a timerEvent occurs void timerEvent(QTimerEvent *event); private: /** * List of pointers to the vertices. * A vertex stores all the info: links, colours, etc */ Vertices m_graph; Parser *file_parser; //file loader threaded class. WebCrawler_Parser *wc_parser; WebCrawler_Spider *wc_spider; /** private member functions */ void addVertex ( int v1, int val, int size, QString color, QString numColor, int numSize, QString label, QString labelColor, int labelSize, QPointF p, QString shape ); void addEdge (int v1, int v2, float w, QString color, int reciprocal); /** methods used by createDistanceMatrix() */ void BFS(const int s, const bool computeCentralities, const bool dropIsolates); void dijkstra(const int s,const bool computeCentralities, const bool inverseWeights, const bool dropIsolates); void minmax( float C, Vertex *v, float &max, float &min, int &maxNode, int &minNode ) ; void resolveClasses (float C, H_StrToInt &discreteClasses, int &classes); void resolveClasses ( float C, H_StrToInt &discreteClasses, int &classes, int name ); QList m_relationsList; QList triadTypeFreqs; //stores triad type frequencies QList m_isolatedVerticesList; QHash influenceRanges, influenceDomains; QHash disconnectedVertices; QHash unilaterallyConnectedVertices; H_StrToBool cliques_2_Vertex; H_StrToBool cliques_3_Vertex; H_StrToBool cliques_4_Vertex; Matrix TM, DM, sumM, invAM, AM, invM; Matrix XM, XSM, XRM; stack Stack; /** used in resolveClasses and createDistanceMatrix() */ H_StrToInt discreteDPs, discreteDCs, discreteCCs, discreteBCs, discreteSCs; H_StrToInt discreteIRCCs, discreteECs, discreteEccentricities; H_StrToInt discretePCs, discreteICs, discretePRPs, discretePPs; bool calculatedDP, calculatedDC, calculatedCentralities, dynamicMovement; bool calculatedPP, calculatedIRCC, calculatedIC, calculatedPRP; bool calculatedTriad; int m_precision, m_curRelation; float meanDC, varianceDC; float meanCC, varianceCC; float meanIRCC, varianceIRCC; float meanBC, varianceBC; float meanSC, varianceSC; float meanEC, varianceEC; float meanPC, variancePC; float meanIC, varianceIC; float meanDP, varianceDP; float meanPP, variancePP; float meanPRP, variancePRP; float minEccentricity, maxEccentricity, sumEccentricity; float minDP, maxDP, t_sumDP, sumDP, groupDP; float minDC, maxDC, t_sumDC, sumDC, groupDC; float minCC, maxCC, nomCC, denomCC, sumCC, groupCC, maxIndexCC; float minIRCC, maxIRCC, nomIRCC, denomIRCC, sumIRCC, groupIRCC; float minBC, maxBC, nomBC, denomBC, sumBC, groupBC, maxIndexBC; float minPC, maxPC, nomPC, denomPC, t_sumIC, sumPC, groupPC, maxIndexPC; float minSC, maxSC, nomSC, denomSC, sumSC, groupSC, maxIndexSC; float minEC, maxEC, nomEC, denomEC, sumEC, groupEC, maxIndexEC; float minIC, maxIC, nomIC, denomIC, sumIC, maxIndexIC; float minPRP, maxPRP, nomPRC, denomPRC, t_sumPC, t_sumPRP, sumPRP; float minPP, maxPP, nomPP, denomPP, sumPP, groupPP; float minCLC, maxCLC, averageCLC, d_factor; int maxNodeCLC, minNodeCLC; int classesDP, maxNodeDP, minNodeDP; int classesDC, maxNodeDC, minNodeDC; int classesCC, maxNodeCC, minNodeCC; int classesIRCC, maxNodeIRCC, minNodeIRCC; int classesBC, maxNodeBC, minNodeBC; int classesPC, maxNodePC, minNodePC; int classesSC, maxNodeSC, minNodeSC; int classesEC, maxNodeEC, minNodeEC; int classesEccentricity, maxNodeEccentricity, minNodeEccentricity; int classesIC, maxNodeIC, minNodeIC; int classesPRP, maxNodePRP, minNodePRP; int classesPP, maxNodePP, minNodePP; int sizeOfComponent; /** General & initialisation variables */ long int m_totalVertices, graphDiameter, initVertexSize; int initVertexLabelSize, initVertexNumberSize; int isolatedVertices; float averGraphDistance, nonZeroDistancesCounter; int outboundEdgesVert, inboundEdgesVert, reciprocalEdgesVert; int timerId, layoutType, canvasWidth, canvasHeight; bool order, initShowLabels, initNumbersInsideNodes; bool adjacencyMatrixCreated, symmetricAdjacencyMatrix, graphModified, distanceMatrixCreated; bool reachabilityMatrixCreated; bool m_undirected; QString VERSION, networkName, initEdgeColor, initVertexColor, initVertexNumberColor, initVertexLabelColor, initVertexShape; QDateTime actualDateTime; }; #endif socnetv-1.9/src/images/0000775000175000017500000000000012542300363015332 5ustar dimitrisdimitrissocnetv-1.9/src/images/selectnone.png0000664000175000017500000000076612527557765020237 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  tIME 1ըXtEXtCommentCreated with GIMPW^IDATXJ@wA<*سҳO QڃkТ%6^nZc7C~X2d2vhp@(0PsdQs@h3YRy}8T|V(&u@ $_p`)vcDO"#WRbiSf_n+":^LdM?*پ)U8\$<:^sjttMavkK+ ֬^═=ȧ! pw+%ǒ˛ǒyCqnNԔ#=ǁ7E`x~1{o_[S&|ӯҕ\-Y3߫L],)M.;)bMZcIR?i(hP |i`?r%Xzc-g^m-|-# mhrd'Wj$_*Ͼ<=Zx藋-ޢ3}+ˆ, uCXmYT `b$*ޏ|YcI0L-i*/X4t编q8n5lѕ ^͚΢iP]@U\gu_.9Wg8smk?H+59e 8x  /d_g[1[ʿ%{33ŗ(`6Gj}ůqz̀wZ <|[}"^R^ρ|2gžO`hI~UE!syf$Jr~ڶ-Zz6+? z|@oah*{]wٖ:TA5e t:h,˒LNfB5P_ҟR0蚲 /8Rk S̅%'4=<0 U۶gB۶,+v3.PٶQUtuHy|%k?+CuJ+m- ʗ@|:IENDB`socnetv-1.9/src/images/resize.png0000644000175000017500000000231112410526714017340 0ustar dimitrisdimitrisPNG  IHDR M )sBIT|d pHYs--; tEXtSoftwarewww.inkscape.org<FIDATH[Legfwg˲\ʂ-P eD6yM->Xk &M4ƶIզ/Q,XTD(r۬n..A\Y?9ΜbfQI#z--cICN79VDhNY OP^2":-d/.t^o9f~ HEVllzkzuŽWzž翑.y/3dvo>)3tJGP/r6@Dzf)]A~jDp8FFo=̭b:dC*JV .>Qs "+uI٨Ȯ@yiILqe+<؀Z-ܑ[(7=R 6 zbkZ\X R#O(Gq@pv`t̽V>'QӸ҄Eִ$(30|Eņ×Q\̡$̼ 6x +!zok>JIHd\EVSUFi"%YXe+@Dj&f^4hnyٷbuT|03S+)w'odb` &̼AĚRY ܹh0횸qHoVxj353__-xɔPP74p_G>~vdu!X 3{@(.JʘpfP ƒ⍇b3d3`p,F()gD$ DT@fkRԂZ@ J}1kӈ=뗉P 2ǑmXyc8dq[+F:`~!,шKKB%3 -IJ9"g(jGͨ(-1' N t;Tcu`gĎ:6 Hiꀄj lJhjꀯ+25D. pR1qPzahhx! PPPxxxLnڴivmcc+,,Lr\|իWFGG?IݍJ68p`u9r$O&Щ`C&}666s 0ykk(TY{Um VOU;aB*y`---ɓ'x1=z$Z gSىDsqJRwL ;`&H0n޼) [n߿ P8K?jΕK WLvv(d???899رci!&&HIIUEp01ϒCbpubqyxz 233 Bt]QdHOOXzLM yu21 4%+Wؼy+a7oj);c!"bsDy㜫 {KxMOO 5ǏG̹*9]pT[\3gЎi8;wJXwMEɹ~P߷hM }D4C idP?xaKs$N:jd}...s myS1DFНq}\WWc˖-rvE `mm=+97-᫩ 'O%|h6_|͚5fff#‰Rw=eس dQ|ޓ˗&55K֒TY~Ajžu֕ڵkw@vx- ŋմJ[vrI4Eoŕ*"u%<@[[qmVVV-۶m=ZYGG|aBOOOU@N(Ή+Y+h ,(D;ʕ+V^@Yp7" 5򵤘 ",Sre~TJGcIENDB`socnetv-1.9/src/images/sm.png0000644000175000017500000001032012410526714016455 0ustar dimitrisdimitrisPNG  IHDR szzsRGBbKGD pHYs  tIME &G?tEXtCommentCreated with GIMPW+IDATX  AAPPIAAAAPPIAAAAAAPPPPIIAAAA ^ ',yIENDB`socnetv-1.9/src/images/socnetv-32px.png0000644000175000017500000000271212410526714020317 0ustar dimitrisdimitrisPNG  IHDR VόbKGD pHYsS`tIME [zWIDATHǽ[lTest:m4KE J)Ң@J1%<@h / jLM`b$R-R%(P^hL{|8^HC8ə3^{?c0HdtUΓ鼰2?8]~UXU5㒗}&zYf5qI⧂.x/S{r;*} ]ABGR@SoqF0P \0M3#O|Š Hܭ1q{H+zLBfbG "JxJs-6/52ONK 2omP xR`oVct(ژXWL%Q`2׀Iy+wd>k$ÙMy,/N!|0(Z 7`&qrk+WZF 8*a]lWj-R?tӁB`sBd,}ÖA00RV@#P%%"0`'s MxZFo$4sjIeHD e)܇?y>CRka<Ӏf[3F{X.6U-Z0XǁtɓxHn4(+Q'HGDP70G~/1GvP;dT0pC*26;; )[%9.A0̆x*8akHl nPNԡx0rH~% Tʎ2K: (Yq#"bRt`z^ T`/P4k^Q\*h4#C#겝N Q)Z/[,y!V9LEdt| u=5P#'t6Ixzf yUVh˾N@ jeZj} @)(m3Q"̧˧Fc2XO'oo0"eʣp/lV :?NHOځJ"A#{ FѼ4Jz :8G ѧudF +Wi=΂*-!(Q5Ɖe6V&[䧺aH 1%$A QI7JjS,X01f2b]K`f򩣆8XEh6J|Gl+6K JXuVY`8,|6)lAww ׊],θؙ5q.lxW -- QqhVS]v{)p 19EX-|X#u^^pPYXa>g~L~C*d^Ҫⲏc-UqZzSX:0+mwb FqO(u[?W(,ȸEX3,|c cNݰwĠZƓxYTU'IENDB`socnetv-1.9/src/images/circular.png0000644000175000017500000000410012410526714017641 0ustar dimitrisdimitrisPNG  IHDR szzsBIT|d pHYspUtEXtSoftwarewww.inkscape.org<IDATXyp?~YHBB=@ e+RY:X@TbN 8PdK2B(İC;# Z3w~=|={*?MxڙMC\,忶}s/L₱iC=brMMCD@TmFCtml '?SU'۞ϴ,5,K 4**4(yY"-]wԻ 24 ==zHqqeOY ˲Fq"ZQQQU0S^{F OݲEprd M@DBk 4hްkaѥ_~kGE5z2M_~m-msDo i>q@?,wjί^ѳ#"BFx0h 4ӟ> %b J3q3㉪RrNs~MO.jtya `\wXVTd=. 9uJo߳l=#&UD\٧OlZrd8 @ pEd SU|\]SO`f5,%%2LUED4:c8VD; p\ئGU@!qMS\.eĶ 2vTEEU*csCEiVykn>QGDU=E1}Us z`ФqZl^RRYZrzwg !"l >UśLD"`:|g[4H+ > ,]n+0{69kײqfJD`Z A+ar8(@DO~U7&Mc؛^Tt50 T 3Ğ\Th("Ȭua^-"|/_)"mRR";^δvψ `:te]RE4:Uz]XtcvIQ0m$ h&>7Eu7m@+R֍#DDnaI81QU:wj8sF.@|jDR~`40ƻXl_Θo?y{q9L<v+T[Ps 'xB#w=շOFF}FRÛgtԓ!b._ `p=cϟ1dNgte'tͥĄ63))rpee-MWSͦ<\_Ar"ȋxg%466Y]]Uނa?uf2֛C W?Q=ӓ`6ǡsxAҌG7y! @A8zc0X{5?$P}{0*)%H@J(O{IHL"~ j`[ |>ءđOBo"(f~;i<8P[[d2caaKKK" zR'Wדxxy@- `ra /'7C}}}ƆYP}ͅO=gAI!3vkMǟDmX J~Vzom)sTˏ]'N:RTeYbB `ŋ.~DaXU6%߆ J) RJ@Dy1lƵk*@___i!ؙ@b9#D5+@Jض]sb@k^o)ǘ9w1/_hC`h4Z "bfR S@)%wQ\ pmDbfH) )esFFFB6\R ~8;Q!MXV: pF ADKEɓER˲Na۶5=={Ie(@6 r\  >_UnbbDA5Z&.z'_ ~Q0c%|pkxj8T*1:XIENDB`socnetv-1.9/src/images/sw.png0000644000175000017500000001025312410526714016474 0ustar dimitrisdimitrisPNG  IHDR szzsRGBbKGDMM pHYs  tIME u+IDATX  NbNFiOmAQA KiOX8s OKK Ojyjjyj  jyjjyjF f2";A:6) %}<Q'z2mκ~J]J ? *˪N%r m-(  %'Vx9 a TP^/'E~  D ^3Y|wJDž2~qDA  ! 8@8 t?/"oK tto})&&/AQA?  H"*ǹdz[^^ <p?#_ ?PP$4**7|@@d )%9 0<9й ShSjyj58    %"  Ʒo Y0--]u jyg :J~dl&%&l@@NX$eqDUDL hE 6 UIt ȸk  " *^R;ܱ\  P#K$m /ϴ  & 8Q I1Vjyjߨ. 1 ȡ#?  J]J ?κf  @  JɜPIENDB`socnetv-1.9/src/images/box.png0000664000175000017500000000044112527107060016631 0ustar dimitrisdimitrisPNG  IHDRVΎWbKGD pHYs+tIME 8%1tEXtCommentCreated with GIMPWIDAT8픱 @,9T3߀ ܁P*w!8΁mPo>`na IB8DT՞tM+%L < KN :&p4/Xg$h}rp iRS 3\IENDB`socnetv-1.9/src/images/line.png0000644000175000017500000000134712410526714016776 0ustar dimitrisdimitrisPNG  IHDR @sBIT|d pHYsO¢tEXtSoftwarewww.inkscape.org<dIDATHMKQ#.$J,~EIJE5Y"]tmS议*E B AB-֖()j)DhHx0$a{9gFT_^|\;;t{T-]@U&9TՌDLyp_o679.rzlw7anccrx\I2 jkKKT7 VDLs+ F;9C!I E TU`g͸ON8`}LF?ǘS<>`UƠ'@ xT05E$TuκXDGvuE;"PwT{-P`ै:U[PhBD<3(sb-bH} <75p.C^[Zp75јqNaeEDdGu;8`2%[K&)BNSN/wR8xd+h)GG 8L*b ;dWűChyn093CJNsHQU-k U%d"^801tU_vokIc>ϟTOިb1_zVuIENDB`socnetv-1.9/src/images/gridlines.png0000644000175000017500000000203412410526714020021 0ustar dimitrisdimitrisPNG  IHDR M )sBIT|d pHYsktEXtSoftwarewww.inkscape.org<IDATHYUE_aaApdD B 0lИOQ#&/"HHB e5\ӷ9w^+UuUWUWIJ)-V gpqpgύlr)UՈA~#}|)1o^W؇gD"vb U]0 l?<[N5oQTz psyn,=#${Vct^E؀w ۺ ZF-|8 ޛr8֯hqPW 4Wg("f983«V&z7YD<(rcmM⯈Q[bvP[ˊۃ)Ý:][~ ^(Zh+֯5) -2Şr? #_a<"' l''5up&-Oa}&s*,̕"{\D<ׁΫCq5.Ÿ{ {*e*ur+A7b=7#_oԁ8.?aF}֗nP=NeOLUJJiYY ;t>^RQ<=JUejfSRMp:Wq]ӑ:W1RZapJi '<>k;.Dl<"ntFl#3at 6a\#">/bUD|aqzLcyڂzt1ͦZ68cY0=xK ށRmٰROwbãuZ[)3Q0&}KG>_vqIm^5R6L&#؏(S?'">)ˁlحi\)SSȯfu|élx^]Qԏu q/GIENDB`socnetv-1.9/src/images/erdos.png0000644000175000017500000000344012410526714017157 0ustar dimitrisdimitrisPNG  IHDR szzsBIT|d pHYs<tEXtSoftwarewww.inkscape.org<IDATXWyPw@%b-axKǮu+2ng[tjoi;㺳C ԊNJ:c]Zd.r! "D#㾙L~~C O@D<$KMTOTȾv uu8R[{4 ,z6+qѓwН~B>{ ``BpgSH=~u\ 9v;|?~a:ɐfgc3&@$#""'zr9u1˃~֎O8qMM-Οǧn\_c{ @ ̌B0.ZSǿOx'sx`*lMI1ݨ(71g4GOCp^m/5 3_{$wQrʤ8Y O|l|2=0k)L284 t`2hIaR fL'.QV ~>CWUaC7鋘%IA䭘P\FDDtwmvF0bOnIHOG kAvJz𹧒]s_[lO 0\f~ojIѴ`i-^󸈲Q0wTK$ZJLxvZ;GWFDm-LDADy\0,3]3>WC"[?{H1lӹ24iY@S?3CBHnP:q{pI8uuo0kt "-hԸM Mǎ '}R1T*)`RR5^_ QZV]^Ve\L^b'@ Nf%&Ҝ |"06oF=x%3ЃDgjui_ ߶46Z>01sPJ>vDېG\A"_68 xVW`6bdt^Fq&t:ߋ ;r\1^]W1k[R3]e.'bU%3 ekyS3V(z"%'CӃN3bw^L>wʜ\'UAB|XLk>rgO4" 3ҋSm#@u>'?d2w ܴX :eE3¦'t/z.0;}xA>dg=5Uߡˮ46`a4vU`ܯG^ ??Y[d,SDibTU7aLWF48 ͽn)7uM]nZSzd/Lu#Gm6$RI*^9 jZ? u *y3)-Excn4< .{|鬍kynjaiGn /CCY"o_ŞQH` Z(&]~S==ڈ;!!;zoZXҠֆW NJ*"y͙,cUr2`ȉ_΋ȗB><2ghf@+gyڛs@& CUW諬aQF*g|fF99}̤,+srHJ6z/L@C]GYjoU|p aB GXfRU]/\jq\XYMMe8?4DZKFD4OLDpVv8̦(g* R[N;)-LK@U!mQjbEEK>Ƹ%+$@R(`لvZzgs{z%-6Orr·=yyaAl[usfGuBL cLð[;boXyoup`JzvoD1c@ (cb\p=˻;EtT=ci}a6{ [fաʼn0=.!$0m11/;P*gw4hקx`D~Ջ0:BbRycLU d:f;:  H!l/o" wՆ{q^|6[>ƩQ5;e^PsB1SH@k0Kt#0DU]-ibI:m wy/C%o/^8Sr?9`Dhc0B`0QĈTAAܒ,JailJiIbem{~4 Xu;v$k}H a$JPTb1*Q^@drqݮ.:]"26D2L@"P!(qۢ7.OwO?-Ơ'{[ZlXRX2?g(~kbu׸wM:jP<ҺMW_j뺯+O !n  $3 "?T o;CR^J=d%6pKx-GY=曫b2&td> jOBKڸZǙWW(WB\c:"VIuˇ+3ĝ|O[ 0$;[~e{査I+@)$^T Ba"J_ |IlE+s{'馊8ubJ2>tUk.ŠBM- 6p٦;6d,"}2̣Ef|>p4GF#p {df6 >f8ٝWm,;RGd4FGH!{FPw\!(s~ɚ6qq>IRˎR+$tWk6o}rbxbw f|"!BVb5)d2{5 b;^c˵6_?7Tuޒ(.xnF'N\_(] \ ԖB쎈MA4U]1IT u(XR!wms\#ddY&fLd>v3KΦ&iK M>ϯ#۳u8՜!M9~JjsSu7l-*MR }2 "᾽EOx CWxgyKNg$N6083-+zb$s㌜xn \W3dt.cJexK#@yoDiM-K&v+)[ynqB%PNSb^fV+}E,n0\O Fˆb'սe^A:LT[F^"`xJV4?ZyZ D{q+9w}̫F8ۣM2tIǭռcL%G{:f+hZCȏisxϟrj(6[wyY%,f jM"BLc؁\ v^l|9O(0ԑG#A (\s͆ l^۷Jź2[$P -iI,x= !Fyh0wd-Lh7[1` V8@ЬWiM!Sckym Dz`­3㇂G֘ @H5sb &`D}fDv& >==Cǭ1̘xrcJnJ]wH-6qVt]F۶'jiP.*mAx{ A -ILuAI4ƻ; C!QlXѴ{3\x%õ BBt݀"olGN&1/m{HZry;"Nـ& ňsT0OvJ%]KVauL³m'!t= FcY2}f 8@XvvT˺^\  @*tV%!*0a4*4V3aђ 8BcZO}_i0PGIENDB`socnetv-1.9/src/images/exit.png0000644000175000017500000000273012410526714017015 0ustar dimitrisdimitrisPNG  IHDR szzgAMA a pHYs )ItIME CJbKGDUIDATXWkOTW7K|J[HtZKB_֠@AX!0<ûDAPã ܙ( c41-U}䞹{bRSo3=gݷe}^a?.n/:qk~L G~"8{`I{v<ز֌3`5FR,`I+0CCaRD|d"u^Ѓ&`0ANc˚&W6~W-/ EgQ6mg]!zz_?OFC9}qG9'|?@?O?]8;r0{IL/|85,p`5@kLJiZؚ+nES% +d dG@W? hn,"u=q"Y j'i/4^Ϊ+jH{LMZԾ7?2`D~殚LxW Zj_4]kcO=$Gڐ:& Y~I:|7].ط(6h`j}a7XlPCVJpOJH _@ # ,{ZNJ92UK;_Rb[_c# p0&@v8p]t}>ZN4T"۫@@^׬ 8 <&w_჆۾&@#N+_V^ek6b돜 |2 :A9SYlۮ}~C60kx " }X8޾kS7652T$ 'i$CaB)8a̍ 4\r8xE->7o P6[ 'jvgfbӃ0|&^σMJ| HF@#L;{?SO Gݠ0Rd暊|B AS '7(:Szd|^NeU7ԛi,\}Z|TL۟ PV]v8wHBWUr@EC|uu,WPnLULEeTBNJG=Ί^dHpt ߎ(StJ2(J2&hHFLZN3v/q sM]IENDB`socnetv-1.9/src/images/socnetv.xpm0000644000175000017500000000362512410526714017551 0ustar dimitrisdimitris/* XPM */ static char * socnetv_xpm[] = { "32 32 49 1", " c None", ". c #B40B00", "+ c #B50A00", "@ c #AB0E00", "# c #CC0100", "$ c #B00C00", "% c #CE0100", "& c #D10000", "* c #941700", "= c #A61000", "- c #9D1300", "; c #A21200", "> c #B10C00", ", c #CD0100", "' c #000000", ") c #950F00", "! c #CF0000", "~ c #CE0000", "{ c #A71000", "] c #B30B00", "^ c #D30000", "/ c #4A1100", "( c #850D00", "_ c #9D0C00", ": c #761200", "< c #7C0F00", "[ c #8D1000", "} c #B60A00", "| c #A40C00", "1 c #A20C00", "2 c #A11200", "3 c #D00000", "4 c #C20400", "5 c #5F1400", "6 c #4B1400", "7 c #6C1100", "8 c #671500", "9 c #D20000", "0 c #671300", "a c #8D0E00", "b c #9D1400", "c c #CD0000", "d c #930F00", "e c #A60C00", "f c #3A1500", "g c #CB0200", "h c #9D1500", "i c #B20B00", "j c #B20C00", " ", " .+ ", " @##$ ", " %&* ", " ", " = -; ", " >,& ' )!~{ ", " ]#^/ (!~= ", " _: ' <[ ", " ", " ", " ", " ' ", " ' ", " ", " }| 1} ", "}#! !#}", "234 432", " ", " ' ", " ", " ", " ", " ", " 56 78 ", " ;&90 a3&b ", " >#cd ' ' e,~{ ", " $@ @= ", " f ", " g9h ", " =,#i ", " j+ "}; socnetv-1.9/src/images/nodecolor.png0000644000175000017500000000403012410526714020023 0ustar dimitrisdimitrisPNG  IHDR szzsBIT|d pHYsiZBtEXtSoftwarewww.inkscape.org<IDATXWkP~wY]...( *A(oxS;qV֙j3mmEM$ډI'&֚Ԫ4QT  r~e/}?F g9y=̘̈H`"fG웙$B/usIP DDFB2NC[S >nDd\sk3Rh2>?TT6}) s"#u1}Wb{պP'_[[L8v3,j]ϕ +?} &.QUX|ԾZ!O HH#҈ȇ"k3Lپ|HӲ?z{H1#`x p؀2zD_Eq>A#(/?64ǂL"|):彄ڪRuɳ ;v͹˭A*-w H`J*q {*1Ձ8}9-΁w ^a᳈r PV/lgf'0V%Ӓgsz=VvhMC{Gg%X(;jXY+jhltbOn8p}GG*= "cI-ӦaJ0Mp0s`4۸1%7/o8r>=/`Viup"?c5(q@Q!+ ( i^BwBkeRScV)F#bf~dV݂>Ž;H$ɲ(( ( M]pA0PlH &AK&f~5>~?PUnԩtUP\ݺo_/uTшdYV+럐&}wX:) C"ЇhK7[%} 7bRV1r35*{br9-cʨ Gb7K’4&kx E x c:@.,4ҧeF0alЈbNuBeeݻ Jև oė]D^U,!&7;RiK0RzKp+8p'1$=uϽUaVEG$C|笼`" ia=f\/&e3<Dx̶ᔵw_:_Z+2Ѫ3kpAqC#w,(#'$d^ˠ7AU05)rk.h_tok_?r nܩukf,$BV$aj\iwblGXRXU((S6ё%r]u{Qs]fB"J0 7wY8?5l=ŅU ["5VbUvƙxpsZ҂ 돼Цgzf~ޟcJw TIT=ζQg\~-&^,&Rレ(FW`1C1cCos:^gf0sE>.<ҷ9{' |`X՝c%u 3OGvTɖzAnt˛)Y(Vlm= U M=;cCIENDB`socnetv-1.9/src/images/back.png0000644000175000017500000000313112410526714016740 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs ,tIME *IMIDATxŗ][Gwڬ#.Iv6P%6{D " )/-m +CߪTBJ""%* UԈ .]lM^3<ڎl 33yhx.PGA(ٴAw|tZ},L$X@:8K&+9K?0vfx%!A"8;%>@فFj,e8O[Io%|?'EDۚR}[L'5鴿ur= yx`MFh;<[gLn.,_XWH]ɍ;vle}nvl}QZ0rh>5P!gYdT3o0ϜTU̕9u$;\Z>5s$ >v]sCӗ ~u$?]oMr(C,)<}4 ل6sZƵNG{/P(`gJfҶ[f,rP 4-^8TX:GK6a{ˀm}]abe?Z lٕWWJ咋BfF'k'Z  2H*Yb{Ӎ?/u%^XqpEjn1bfA`jM(>VOrkWWs#-h@D=@ܥ|O=ݢ<^&] "؍$ȧ"F2Ru܍9Q=FsJxnk( v CzJCz18kQuz.ς@yY@aAө>ޚCg]7hM/]ˢw%em8^.]t a 7yčv|'={-X<AVOH_(pOG_9T_(mlRT[d2f_+oO $Čvq׬D旦ow_]o\{S&Pl>] @v>G荺mF]bGv>W7t/z ɨϫ Ǘ5vE:Ϳ9*5>)Pu>>.Ĥ#DϢcQSɭ IueMx Uj_Cw 6]PD T>{M]G WK>۴X-߇m (tښk{1""{I /KP2a wjV9 az2`p6jN JWIENDB`socnetv-1.9/src/images/webcrawler.png0000644000175000017500000001005212410526714020175 0ustar dimitrisdimitrisPNG  IHDR VόsRGBbKGD pHYs B(xtIME * &]  އe3:5IH+  (% ׾QTf oaEC,FF) "'$ DO7B g2ehtJgdA ,. +'#"   Vܟ#w&76'??%//  +,&%N%%I+.SDE):9#y ("  q wbDB,dIF* <9#(%   E .;i12!(*,)30"   ҍ  % 10        ,7&!+ E_ v  " \     e',c ('   ?1:-  ?3=?7!  %  +$    >1    &. H;!<- `  ) ))'$ ;#.>%N/0    9%R19 V1 , r 5 J C(  ' " 6 K_G(  .{Z     6  h܋^  <m~ z( Frʼ!޼(Z$2((" . ! <mnی') ȧ^#]Un% L+% ؈"rގ_ , & 3 o6-lIENDB`socnetv-1.9/src/images/socnetv.png0000644000175000017500000001074612410526714017533 0ustar dimitrisdimitrisPNG  IHDR@>AosBIT|d pHYsS`tEXtSoftwarewww.inkscape.org<cIDAThśyt?ͽNA@D ʈ;qܪ訴u\jVO:q:ւ=-"".e č"*Aٓ{1dђs[>y_>N;ڕ%1Hla;ZܥsrwP1o?[oKyb}m̘ywfZky+iadvE<Ї88K4u<XSbAy :=[kI1?) 9{l{; FOh`ā?%A~[5*6~o k?@.P n6Pr;=P>~< |Hwށ)8"/]k{Xw Dm3EԽ?SĎc%#)ȂȊ@)pȳJ"3SBA| =:WdWO{c'KrO"tNseZfZb> YLcF֎}l40Rd Q~ .?8l?;Kp჉72y(?ڰn=`-/X|!df y)+*wNT l}(\(^ǁBC1#mJ jwو$~{](nE>Yߛm À[!O8= 3xUf(ab{M2Svf" )fعrDj)r|Á8dNdR&p3SJ ǔ}xC qDZ]<8_ đ8h9g;S6ežC\E@eK9q{n{߂g Bzn=.b]e¦s.idM Zhe yǞUh CB2v$MU|ρ3'<0U;Fjϛ<^V_S,M!5HA62[E|tD}G ЊB}\r1 O!BFHvF+Qb(,>A4s'")'®r M0Ij({0n-dhqXgL5J.ӈ]#BeB=` F#f XȲ `eLB!Kz!/<u;LE(4t|D@GV=;Y$\2[[3Q)aRo 5Mvȃ!`{P$mk(UFgѣ} sPR-pOy[|fr@kh9ȂE.A`P6fHKꑇ6]/D}1zz;FY~B2s8[BdQ8BTZd6HBTl=dbjA FS7b#@|d6^Xi#"|Sf%C6\ c& K^Y[BmRḆ󐇔6e4kAӢ4Vڙh۫%2d6=dZQA׶BCYnF;9vIh\5mG^ `C_a;"}/@3Di<[(s'Tsh[t{AB5ܞ&u2j wqsv"̶(ދBΡfC>c+I.sNQe&Vmtqt&o庨Ħ0r"mwQbFVlA^pUw,Qgd عvSގr6u_Pξ/k&k L}24y`;ɰ*,.z2owX;v]wlΥc/ A\O%{4/4#kJfxR\=b97_ .NZm'+Tm6W4*lWqϪž/6EVhrGnF1> xl{.cSUqNj+gts-Oto} Úa _w{#moz˯э]mJ~0IuV?eS˯G"08ot)Oϥ8Mp Xn5௽7eùjޤ^̻eҕ:~$'6puP*?R1mLUA}-O9|1X߶9BV`_u&BjHP;j%ҔeSxbmKHfG{IENDB`socnetv-1.9/src/images/addrelation.png0000644000175000017500000000141112410526714020325 0ustar dimitrisdimitrisPNG  IHDR D&PLTEѯѢ½½þe[tRNSo_}IDATxyS@n;;+5-e%@xo+3L3}TzDf([TJӂ@c(Xʲ\TdQpWU\ŧ:w' ZMh4j`"=6h7qWm_91VMt0VaTMԄAmq= ر4Y4p„`K م`P@ 7!p !:t:1UgRJ =܆ Řmۦ9(M|AGQAnw>zeqƘzO!4 M$놧(? qA0.# 8! I (XTgIENDB`socnetv-1.9/src/images/triad.png0000664000175000017500000000315312527107060017147 0ustar dimitrisdimitrisPNG  IHDR szzsBIT|d pHYsetEXtSoftwarewww.inkscape.org<IDATXmLWaWk^Tdm(.unkmiҤIA6nnٗik+M *(bZi2t e^`vēy=sJDRʴvWss΋}~{$П VIAVy^ޢ .}/h=tyHpl /܋ԵKGrʶZӔR"2`SJ-xr |>&zztp"4cc^/Ӑ5yV>mldwS|UGnYCspС:!yGGLU~U;pkz:iPTDL}=*"i]lٚ-[ٓ'ݵ{l*+H {NM}׍nGrr8bɋ8 b^~+3je%"024ҙdAr2LNLl,thТ9Dz39IO1e2miR l]~.~"2:A)u5+RS+u3~gl6ټ xųe #@|E9?-"2Iⓚog:M;"2MR*PϢ!vF;;y]jBiӮ˗& |m3g()#u}>RF=Nn}e'=i_/ZgZI3oV׉ub4۹HE*Rn^kUŋ;yn @08LRJEI͟qlqZZr1iL3a2-˅SBs<Ϙpx,AcON҂W{z #vB3"BI GvƌWիiNہ( C%ŕy9O~o~gƍoRyy63u [TEJmu {),ٙe @fX8SzPa^DwS[J8yWgKlF]uS| ^"wj=0W-/7LN2மnS 7~_ "GZɶXol)),6m%ŋJKk} ; c40J=㏇CG_W?ѿc6!N'O< E<' sW4ʾ}쬩Dyp?">,Ą39ϷXF?.QJܷGx^D @@Ѯ:z{ʹ>MP KAKkk~mX8ҹJ}o`zzTyyZV4IENDB`socnetv-1.9/src/images/colorize.png0000644000175000017500000000350312410526714017671 0ustar dimitrisdimitrisPNG  IHDRw=gAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?-@1b< +``db`,dɫ*&-[/^oo^n?{2E P-fS1̐4S+KԔ[ Y߮exq0G'سgs S`8B/W.1>Y2 \2 1000p00\~R7y l`Vn$*>Ͽs/34 GIXzMR *d+ }c` {6>cS ~o;@wejo V! quS&g,>>}G ^˶>9(okX>/~`O)I{o~f,qH2%o9!ba`KQ򊗾kO d8Ozܴ:Z?0b` ,!1 k320 h,!Nq]3]KWV)_ӀAu:߳?bc`fV` X9~b`a` u 6`X~z .` @,rʼ 2(81 }DU們0B0Ȓ| 6_N 0}\g`S ^2z<07`p<B_o@0rPA-+@`xaE9Č ׵ $>q)s񲂃? $3fX3xAN ^q _3D<óE3oo~{=TN'㟿 oz1<{3Ó =d0Zq a3|S 3eX @j#`x(+05'vOq-//2H2 ^``Z.Çfc8CϿ @ b_- r32q^fA{ j`ed T{X./BT#)2'39td`gca`?SGRPm~ 5",CIENDB`socnetv-1.9/src/images/random.png0000664000175000017500000000425012534331160017321 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  ~tIME/e &iTXtCommentCreated with GIMP on a Mac_[IDATXÕW]~9dͺ1bVe&TPm b@}K7i"IR("DxXm^BJ[lw眷o2yp<;0<@^{5LNNA.ogϞ?b /_}t[%`GP!c۶m<%ɕy>x@__]ׯr·]E\ƾ}Ç}T*h|~~J)Aru]h4P(LZ穧2⋇bZ<\. dY "LOO0 G:F.iw-h4h4y\eZΝ;[n]<ɓ`Y\Uc^#N͛í[P.q)y4 p!D&AVaBJ )% Peٷz/\ 6SNa}8CD"R aba0 X mZR`ii \BE8pl !d<G?̄}.Bhmoe컘$"41>>5k}4 ,--P(@)"B: 4ݍ{evbڵ{wBW_< "_+"0,R ˗/ǎ;ՅGBu!`&n݊%t/[lJE0׈VJ@80s!ˁs~o47zzz c axۋ۷o_+J̙3}dx<΅)! j#| "ʕ+ʼn'􁁁ܸq҉hR-J["J#8~cmts`Ϟ=۷R z7+SSSrAG?;w*w1Q"޽{9ǭ[ZWN۶MbSSS}?^ʶ4mg(ǁipcbbo&z{{SRZ\Ջ"۷_pⱞfbYYpD'@OO4MjR1ƴrjzmT:_F "G5sP hXX 0 0ԝ;wjV9̀xM@ dmD6`١\m{PJ`MI)RJCJi4thֆTℐnhIENDB`socnetv-1.9/src/images/centrality.png0000644000175000017500000000203312410526714020216 0ustar dimitrisdimitrisPNG  IHDR szzbKGD.l pHYs  tIME *$&iTXtCommentCreated with GIMP on a Mac_[vIDATX͗MHTQ9jCT"'l>F61D`h}BAD)ZYQ(O/ǡPAQTVNEczMЁ{9{F \+A=_ԣĸ'j",Q)P7ojqЊ΁E@EB+m^cbz/*:旺c@b[_O\;0 oa{W(f^nM -i+ RR{ |6g( -*ၤ%EXH7$ n?t6yL Ѐ&Xq`IC +r ?e8/ǛS@Lq[ćaH,6< cYZa= &+́ϋlXif9pɂ(#twT P g sW P[*> "SiRLZ;KE0R)0yO+ڠ~(2Iu5Zpfiz_ L|X{hǎUWm3ݺ:Pm1 sP㭛_:w$bU_-gA+dDl~\CZJYUK׀6WҔob6=Ç>'X[uEZ$}Yp-!Y:H "`2^0s|]˩@Z9"E*\FyeS_o.cě5"BwŲ ;mQ(FG{WEkK!JL ;wmyURBPEk.9|G5Y>PCSEXcDIENDB`socnetv-1.9/src/images/circle.png0000664000175000017500000000072512527107060017307 0ustar dimitrisdimitrisPNG  IHDRVΎWbKGD pHYs+tIME 8HjtEXtCommentCreated with GIMPW=IDAT8˭Qj@EK8 BJ$+(nA%~+T nEKD6 ~A1F0c81%[SJaQeBps粖1WIx:tE8KHg)=)2xl69,n%pp'(,H]q߱l-Z`$mlT}n>VFiʎq.¢DP|ჵՒcfIBW77|FRņ<8Fk!pzIENDB`socnetv-1.9/src/images/net.png0000644000175000017500000000243612410526714016635 0ustar dimitrisdimitrisPNG  IHDR ꂣAsBIT|d pHYsֲ4tEXtSoftwarewww.inkscape.org<IDATHŖ]Pe|  )@yQ~:]_!!MdT3 cZMy3I4cMYڠ3Nڎ ˢ.Xe̹߭x?9W`nd,DKKMADDnM̦;{i]qֲg}GT͎*^T+@2`0\6`#Vk\4A,$D?ol[mytӾQJ5+n[vu>t?t_~/77e XyD,^xlvEI ^ ԀY?}-"~CRj` e2,_t5C)ޏ[V_L%%hh7C8A~GV32-fMNNj x?SjbH+@उg +0"R=oo"*DdXlPW_M.VU} d_FA)2yBDzN6wUv‚ EnۺhiF$zmajj&[m6>yi<] [6n.=|gK!hIw6`SlloH{ =IF/z>H)""CD6<5',$..k)#+8QJ@XRP4/wewjoUQQe: -TRy_rd>LNV&O>mmǎ9@]6ק.5ǏsGh=tKR&OLlƓ^fstهkoW=GZsTTǬCxHssҢw*hl#_seVBELNe tz?z+?E渳3p11z FD*1^O.Wg'fo6p< ee4-~0//}dY`:pxFwK2o5sׯćiqƊ v(PJ5{~>-a;J2ro,܇䦋]zZDx~N8_IENDB`socnetv-1.9/src/images/nextrelation.png0000644000175000017500000000145212410526714020560 0ustar dimitrisdimitrisPNG  IHDR szzIDATxNXJ6L< TiNpqc)*"cf}$%LwٺOx7UiOȌa$H.W5)N:tϗ(K39wV+pzzFIL]\. 5TH..PXU(yD"A> F5QC0m^_7I&bbIIF dYfM\D:݄T";( 7 ؑTi8U͉D&yzzB|_/c vqA .z3L݆a]ւ %UA(16^^^Pnl|qf9L>_U77yCau^__"BJ`xs}Plt,l6Kָ 1bi:hX0ڌHRĈe+xMpryUٜ1n 츈vEZ4Y,.bGы`8#xD'nw6|LdkKX\xecYdv5B8䣸^@TUL=-M;;{7#ZNAI|* ͦL!8"2' ~? [ÝooG~xx25B!1!_]<D5@fQeRtIENDB`socnetv-1.9/src/images/force.png0000664000175000017500000000341712534331160017143 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  tIME4%StEXtCommentCreated with GIMPWwIDATXýkl\W޻{ڎk'I[HhV%UKT AˇFQT U<  uBJh@#E u3o:ٵ vqF0v5i?r Ejk&s! [" r. tm/>U.^k+c1PĮƱ@Y`͜ 0b^z5>o)0.( VG,6aU1|lH ;lUUT*T H/ ce(kae;R(=/ru=4Ew2@&_Un`KFhZ1  LiBJ5Z۸Ƣ72))G0d]M59rD,PsrGj*[khlt^Y2R)l.6FHO (83pe^vȊ7,7Y| .6)I L6i`p1ɄTDAK]4hUj!`@)q ԰$n}*ebqƃl]-N_,j45<*"jMIVڡj8|"ݒ@C1uFDbdJ14aH ;~>Ng/c|9kQCtNf D7V;G&EiqxDfR(.d!Q43?{1ǎ7XHl'`4&ocOE/J1W_Xꏳgn-Es]g- +Fzy_1ƘN uHlA{!gKġaRgrZgp7EJjS܂%Xwϊ7~{J < 'ч,6Hzw:02bA38nE8wyYq;Ç2t'ˍ17".Q6' 9a޽a```uh-ed_lr>Y\UHp?E[@׍wڬJU@)ᒯ93V?)@m6]T5=N`0u^?D>L{z+WrğQE)bN0&iU_2cZ3:2BP~=#倯۴΍?@K]"sQxϖ>t^SL\'JL&D"}v}ѿep 1jzy0W/$>N$ mmm}ܙsԑ#G^>QxWWWm?e?$eКcc ԟËe2Fֶ46O<ĉw=rߋ| "^ZY VطҖ{%-kT ӳ Ay}opbCr |Dֲ,. 14|4q%pu<6qiTjyn5m'oٲ_ߙ.R uB)$;RooqN]vz7m +߹S=q7re_5}}}ض}ŪԲ{Z767_S]B[n}{-MQz-ztJR:N /9H 6IENDB`socnetv-1.9/src/images/delete.png0000644000175000017500000000147712410526714017315 0ustar dimitrisdimitrisPNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDAT8}KhSAI([5ZPڅ]v)-BIW΍ ຫ+QDE UV#Ŷj4ndN>?fc<&~Pgk m ʜ!Q^laawJ"5鿡!*uwS,f6(WiS)*P)T8hp`ZBK ]oUʴ4Rx Iз>R}mV:ֲڥ֠H;5.)mB4MNቬiY֨SykuO>ڠhM=+IENDB`socnetv-1.9/src/images/open.png0000644000175000017500000000403112410526714017001 0ustar dimitrisdimitrisPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATXíW[Pgb/nٛκ;{z:vTpuֶT mQB!@Hs$!grB GTx~0X}g/>ߗ]v uH W*e`[ '3">vH~Y2ϭB9DQ臆={XSR,::NI1>I,`]YSMM4 1y`Ż?ū)LOϮ׼tCO3@{=.4W S2^5C`\&K|H$8As''H׻ "U77N,V>V:q,k,'6?F}oY1~ Z{?0L(CF&e-,!4{Z;~m$~CF=5iu?PZlY]/sq 1% cbjC'ݼ-Ӱ;3&SXO!,CPF BBsZ*tȼE lϛ'Ào%Q(/bM$"R-BySt < ] /yƪԳs LŔbK˫#7G<<b~/=/-:C(QNܿ}3i 3Th58 X\3>GelAC'b ER$nPD΋J~ m['Ѡe")(Ȑx5 >c f]KcNzI1N[;|%jJ0("3 t.| J*qQF@7 jH7'@L{RE qAAͷ]lC(N#KQWHoXQG `4QɫgA@?noiқ*/ru)S)+vQŕky8n3cjs@"mDžPt +4X$ |J8aVU;:!z/]&o8=cn*F׃H:381 ^QO`+] 9g=JUmCeS@%`+njǩD9|6ZZ8/95j'zLIdnFڍv9,9=NrG ;Fc9=pOJnX,P(@!F.[! o<<5iEX?7Ǎ: !p5U!4[7*4nI:rsm$Jesp)>|c.2*\"]~M 6tvw䈶"I m2JxLKIõ.|me6Y\Q{ %&6ǝՂ^NEXx]\UPuTA/ rjQCO-H/5z-Wxܯ\u˯Ev2g`§ҩ;P 9 /"mak^2/f_=IENDB`socnetv-1.9/src/images/properties.png0000664000175000017500000000201112527107060020230 0ustar dimitrisdimitrisPNG  IHDRĴl;bKGD pHYs ,tIME :IDAT8Օ]h\E?&$I4Zc$ҨXD /' O+"(Qg-QlJMmv&ݻwƇI_v{`~mҏ+epnhlS@.ŹBF\Ƭ2Kv]d@G֎m,BIڪC6"E};j^~| Pw]{x0{O$Tq]yw^:UGZn&YUk[/RݢXNO-drqMJv{Qr43ޘk]֟T%tVf@ -nǪڊQ =U"iw*l7ƥ" -6! V5#`"i{y`Wlu>UրK 29s dFcǶHT-lѝK,hUNnn64I| 1:z{_t7ӓWãH+?OMT}oiF"Jt ?~咙HmjgW^kG\Zvz/K#M|nxz7QND褍Qőצӳa].Y=}oea;,0 8A%=ZpK?t~}ӻʾy].( ө_}r~BcUNm 3^3+ >DsE}|ͶpV[l`6UOho4lt,bn`˿q*m.yK?=v>+IENDB`socnetv-1.9/src/images/plines.png0000644000175000017500000000214512410526714017336 0ustar dimitrisdimitrisPNG  IHDR szzgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxW]hE6_m+,$mR/FЗBQ B̋PQ >V}!b&Jcvv2wgs؅?3;w79,Dw OpfM=p@X3cP'A:-X:u׃z/dKq;bC#[ZJ?=y~ l0OӟxhDt_n}`9_r+S';!ޙXW\.m^=z2|Օ20lތn2F#cO!#qeyyb\<-+ v D&GH[6hq3uA ̑<,hvp}̎?paZQ@->Odö~k=̖3nTtѷ$DV *  h^#/ߜ;r߫ e]58Fcp \5?u쁵 2ݐVJQlܮN0};DUm_wD ܏jX/IpAS7!R <EOd?΁pͨVp,eaTZ>hWf]P0hkƴf\HQkM:62) FIj!.? 0%ʟIENDB`socnetv-1.9/src/images/letters.png0000644000175000017500000000167012410526714017530 0ustar dimitrisdimitrisPNG  IHDRĴl;sBIT|dtEXtSoftwarewww.inkscape.org<JIDAT8Mhe~tdMWSiC^4R(XzH"$o zSO([!f76ݙdg `xwhh4H$b8uIXŚ]bPJ;???82}/"Q뺔eYzbYV7xRsv8rC=C} uR*P|+\)5(  "   .     | 5S###)d   ԫo1>khB!uN/ @/b(C8O#'#0&+>- " 8  ή2! #$(*+dUU@@K&"/s??|@'"]))O/!+ 8< $|%T j/`( 'H}IENDB`socnetv-1.9/src/images/ellipse.png0000664000175000017500000000074012527107060017500 0ustar dimitrisdimitrisPNG  IHDRVΎWbKGD pHYs+tIME 82GtEXtCommentCreated with GIMPWHIDAT8?kPL &C6L3,"-tZkBBn%/  .3= )' (J%b(}VMM($$MY4VMcNŽ $xj1+k 3ab={dk6|>煢$F9W.K @c]#uj@, '|LQZ>&B$Fބ}aOfw8u1'+]g^opHǶSU6izLOPUTmy4G;!04߲`Yo7*-/!IENDB`socnetv-1.9/src/images/find.png0000644000175000017500000000237612410526714016772 0ustar dimitrisdimitrisPNG  IHDRĴl;bKGD pHYs  tIME ;[IDATxڕ[lTE3綗>A/hAc&Hd[yC- F(4 IPBmK[ m^9Ci |ɼo?,u2;ctjsHY$B:B7t$#hYYt]G)%J)#J[kׯv[\EJ0lMp=T6iqשtuHdGgv,q=\o4,@uN40<H~?3,4)ƛO{[;[(-}zp\pcDů̑) nUこwXt&;L,/LNE]j Q׽|r{kUb?8WVV_-ϫߎSq H|V&uZ9X';n3y/f}gzNؽ~R3gP҈d*3y͌g[z,ױzTZ[/d1/G̏RT̝}ZQ>)˨2{GVIENDB`socnetv-1.9/src/images/home.png0000644000175000017500000000331012410526714016767 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs ,tIME  3VUIDATxŗo?sf粻wqbƉb'M=-Rڢ h6}X>BJxmQʦ6I|]ۻ33sNvCHR#}3].;!<3{i8JPr_p8d:V&>< < Lv@zz[>@o8w?N˻`x %LًiEYཅ^*ୂz/8{I6Z :ٵ=ı]oHNKɆ .aLrY4Ȼ6Q <#fUyT|s|thRb"N*M6s5ށɩ߶_Msb'}DRK2RBsh:%7#ޮh8WJvҿ@u!D4qo&& z 2҃;f lch> y'EݲDo zaԛLoivgJ6A NZ>tאOª67v@+Mª~ |ⵚתOE]\zI7|^}o|<_m +Mڏ{Ph2z҉rkPW  '40t4[s){>T/9_r@!N;p8 {v\<~2];g\1'08N8lN-R%_ w "-40}̥O lenV״cY>55%tG0/铖!7蠤7~a賶Vk/d0{ٱޚk~(lLB@jZ=<>5/Qbd0ON JїfSSSZ=āt1 yaz:񳄕,|dJVկԜ֭a-îωF0ݮ9/B6z:IENDB`socnetv-1.9/src/images/prestige.png0000644000175000017500000000171512410526714017670 0ustar dimitrisdimitrisPNG  IHDR szzbKGD.l pHYs  tIME &-4I&iTXtCommentCreated with GIMP on a Mac_[(IDATXŗMHTQY4w>6@c~Hd3f A]-2(hQ0ТE;Q,L (kef͝zμ<{s{d-"GW"^/tj23: sj<+x8Kf\kH?Ho\@/`:Ju$*=*-c2iV- [},Bi $0eD\uBp[v_.6%ՙz3׮TfMFZwU"$lzG>56Uy9`bSȂnI&nIԪ F@7  ]le0VX8(Roȣ`*rd F* l3M!ߠr`PefQ]S>Ʈ4,͚ƒ7.o fS>jԇ bi d΁C=/1hl A6y-fY sA`Kk?6$$@6sYK<LԉW7(ʼns,e8% ei +a~]+{naL͙u1%PXG{XÙx29Ȝ G9%@]H <~)ԻåπI@^l})\ L2 x:Dxx׉w/V,hAm1iˤel5 >wut}CQVl*^MN6?\mq~(KG{r¨8qЉ~c:`0;IENDB`socnetv-1.9/src/images/socnetv.icns0000644000175000017500000055236112410526714017707 0ustar dimitrisdimitrisicnsTOC ic08ic09ic075pic08PNG  IHDR\rf pHYs  @IDATxg+UAP ]D]-$5-1Dhbb7EFI5vXر" JS,lww^{<;N9s̙y{%=@y=@y=@y=@y=@y=@y=W_ %VPCaeeΡCAQaBu\aPeE(( aAP^PTuC( wUHXܨ85nɚfq b6ǽ*4-ʯЦ/CTRýCeBuM/M G˂o}5~:fBi(,, ϟJwv(^Y; apơlBP]Bk.]k(o}Lwz/n[zt{gjD(x1)yx _DB{R^!\;,y""EmĎ J,yRz5ҺrIaތ犼`]@0@ś~:%ĕCiYI8et@ `^֯70|rӡ>R[~eXhg{֟׷z=XhƅZwyɋtK8Pȹ@]z(C{%9|+ӔʊB6;;h 3}7#ɕ,֊P>g5`^U_0S ~jU^{P:ժVr'}2T3z_s-UCom\[{ Xsɖ_:?ٔ/5;]BZՁ{ 1E{jйda VjzQYB/XIxvp XѴ&OZDr5ɼy ηaauu(P{;TP[o3mGk׷mV0n|7=Y<'/=SX|mmq!Wjtv+ F3א· >Ty |Gk ocX^"7sN({|Rfxc͘neټ"T61 eP0ݓ J_.2띧5ؤũ=e@xXNmI')lTWw  UV!U+OzLox_3˚:\j@.^&)O d{қT\m!Օa.E6$4+:5 <130µw>h Ҷ= xqۊז/ zPp0q!{,(x?"J6<ӯB0yQ/Nob^{sE>f\#K@~4SPq҆$@<1 * ( F $Tgkm~P=s(+9u_~1_<쩍 zg!ݦGgAx]}Ww{xoտ\,z Ұ~?S˶!|W ڇˋCu\ie0%i]{[/+G <!AM>$n{axcs7->lJ6&~lFhQOmc@whhߥ_WšKй˺5:8<ΰoEAm&lk'|h)6?Mol@wylM:Me#:|r]q Mr!AC[9><7%{</y@(cu`(c<6/O qe&vI%2kқ߇lo W iCY ͟m\?ml8@uicH]?䘼=mucs)_u{oFq> 6?O meԱYٴ@7χA;`|ۘ>GEy{l~67 ;777i`+8(7+yt\+xM_1R /xAc #|f? A_>6荬.^_/Z# eqc @H] (-o=)>*J^V:Q#hg]@"ŇKbu/opW_}ZEbj Bf.H`U/~ Br}-^ k::1Wr@Qa˔Uxדy.T#^[ 877Zlbg78yHlƯ 6j,q۸yF.' }h򔡤V h^(~!xӃېcƇU\[OK"=<n:L}*,]ætЍgZazqyx]A 96bk4mjJx,S8uFec%z^ΕPΙzеWkqiཱུ{ t[͓}1y#m+)l ņb`!iϣXU3L +oJ)~ybFcY;jqaasB^ A[%.÷?N٢KT`IrCśIGm~Թ)zhcÂ6/5!HͅR@ЃF$AP?*N:)O<+ 춸k#o.ki|2ZC u^L$GS|Jc?TO{yBFjc~)mb'|)7Ņ$,Kby;}SOBȱ[N5#\ðj ϔ\l(\9؜; =d&éuk6ԦLCw|%X:{IlI+ DěsFrqiEƶq_ǩ֊41mۉC~I!'^N<%ecqazsII;9bP8P5hPC^M5Ep g'OV/wahoZ7|M}xl6{ 6#OO 8 FSQ2ڂMIʛ/#_@nX,8ϋxY[2jщp\t.˜;y YWtlm'K*,v1}e-cm:>sa]fvl g]o>k8^>g`vnOZK%⹧fjj̗4d X--ӧ>^mCWK= dAnݺeag,*O?I /G7U@wG'eG$-O{OՉ.:8>:^UId2QdH wq sbLuJ_h[x<yGe&MS: N(vjpQoVkl~as4 9mmcBk~;9p빡pYIٱ}zA:p꛸y)CoJ.$lsąb'?V~ PQ&"xRxN eFUTWG J5L!o$fIiK3x vi(t[_v),8|x{D}JZǓ0|՛ GEMՑ8M bLߧ~L:[ZTẎk%oh98HGs6d]}b]gr\&{]|Sߣ<'-†fҟ-)6wPf>SVAоX(BȌ5#8hz|q +y6mvza0z']8ts,v_R6C,dR^7#n7vWʰqvunz9QA0sc<"g}#u[qMA8C׷X:Kl,>u8EcYl*65u)8VzA/A=w 7)N+W)}A*NI]%lž^a= epm;x\1ffCj ?[m+Z&Uμ7ek0X5X;^o'R(Ć-UԈp)NESd|)^63q KCЛ&cotJ;U}(Tv[QBwZQ+\LIV:*bǵVТya6"GA+]EnYzS;ݷ1kO?q?[Gryc`E-Pa}N9ȓ]dcwgƲ>ecyIA=?yxl\i;Syz#qX_*kcq}لo%oDS!P99Ub G#5Mm:;79͒ OGs|"SmwN>]_Mk 0}<~ʳ!љf lZ898Cě?|8zƈDZ|!@aaMoXs/&x/αyևOcR_;ϵ*IukA)ɁGiVv[dFhJ֦DS簇_;9^_9wP,DC)n ii*|Iy^uw3SQ{b6o)q۸wkφGtV2]T/io曅r0DU6%?'O Kʋ +Z]D =i|-}K=W梃 @X)<yp[v.|Gcr%C"YsU%Lk`6/H cYOqM H<'n_<-лH3穃Á}cӾ]#ť~~e3(T?=T}1|0UXߺ˜+8͉ P>z}G/Z+J5?x~]b(`,RC깶/]t[ս x(Pk>8;PlVHrl[*^Zد M$G:ϼ6nq8KmaO(%M elz- qUpx>{ɑ֨l԰9,ݦzYL Fns6iɊv{ 6_:\a?.Л21Y?lӢ xra_:.55T|PácPRw׃yI8s/y}xC/T`cL=&P6o}|ϭQwdlaRq! BCqhZ<*' 'e g tA墓X's$Y&$6>$:t]'|$\} $ c?_ ڞ6/ztzT .K4Z0.^ϡc+j\&Cqdp7"Կ+: o*FV\EOl #{<4J9F+3M J拝cTAlp&c_ķD~#b/vG9LlǪg>]a%?}!Tl1?/ '9 %qzKjF5mO! s;7+_E!=Ib}m/ /=MO ŠUI^~'1$6!b'OşbqT'ADS3MyM1Ru&6o J 6P=Fĕ‡=tI?R 1Z(<"E|0>0Ovi|Aan}ZUAC#- 7&ԒCwO΁ᾡ{Y'uiFfoEf *.}¿V= Tꛂ|ϰD\T=#Tkh(߳ jF^iVd "R֙GgT'k;r8UPQ O0'q@,^gH@<^(x|G ~s_eWʩ/Hmhw'6ѐ0_o`$%J#wŋb@8uy zty_ { ZBA2ÁzIjӦZsb9}~jii" ^:| Z<ʔѐѡq<^>m.ģB[]O?k_|+始lR6A[ʑuEGARu@)qx̝S*>/ppI{dRʨcm9<A' !OJ ~GI cCߎ0OJanǜeM5R<';\+.R0Z'ABCJ=ƠÂ>^ .NnuHߣzZ>Pԥg(UH)hMkq8 ?@)w ;+B"@AQ7 O@ӁN2N,M z ҇. 'f:YpG 6ATEM5cKln;NpphqR ;>g ?zWNozҏuI,*Ju9QRtƾO ?ZOo΋Ƃ,AA  G{+ = ƣ}XL@7Klt)|JiA;XOl/6z˴%&B'62l"v~zNq`_{ l'9ĘscM/M(zxYu0Vcem?{ 8嶃Xח t3lOT'~)m?qWAXXŏx`8 +(x|\2~1q/߈ AYQt'-U@vb[:{9Օ܏vΓff! Ax([+cSqM#X7 [<:Y_|*69}m<koY/5Gq{]%Bp,@@(M]@@O|ӎ;@wƢ?c}|d ʌUQJ/D/Xl?O܍gKW` c67>c3l,;r}UG|#NaO25r. ns*1~]縕!ܥg I܎Py=zN lŇ^oڼxPlsMX̝~Y&u iAwt *rR8UYQ$\8Ǥ=XyO+IQo(%۹(>Zui:;a;^!6B\/hOS06" +)X3C,\{&#.3^,Qy&U/)J.SǦ#(}zAUI  lAQSşg.w3tU\'^,6;%q9XF9NSOl-.:@¸sbv*&+_(1G EgcWclebC?=f,Lw9?\pHl 89|=5NWUX8OG ;õ@!0Ҩv#' -Hs۳,wœiME{G2Rty5}iwP*щOi繐R-W9qS\(xia`  |Xy\l`|6>]1X`+퀃SƁ[pp!O tf=[QƄ `!-, IyK; 6ωO@z7xUrFł1,SW_ t,6Q6i1%vx\}ҰayJr?uJDR["x<@LpXa f_AKv+ʩ5|M[_Dc}gN[6*JA Ϳu>Rp?D,`M~??u umFzP׉΂]03fjA ~a>$97Js/y3n*_/_(7v!><'6!}@ox{^(c9(X)?v?y.#r—!~L!X,C# vO ݁@jOƛ6o.l%b" >G.``cexP-AÀyeWdʩ>)8DGF&c;9 1Q0/量='e-z1ls16?M6ׄkڢ$oA{yp1nkEj{ 2 |Y`X,` 8j;AM{ MAP$L*v$m56žY/s9r 6󹂹B#A?| .8_o8^0&t`WlyfdZO]皴9Ƈ'8^92o=ŏ#t΀;e 0Ks(8 bQXx|Ap)@O2`X`#l6/ݐԒ>WσB0mRkڒ6e<>0^+n ^?c0Rp!ƙ)blm䐥ܛ{.3)/XAFƬ/'8/k8"N)u3<$e'{P㇜dA ^P d~O =de ,t8Tل Mag80Caeڻބ lF%n.t/ce\,@xo6݌AzfoO>R^@8^`^)ɾ`]w 9O{C=~y5]$X8NbDpOeqE&RڤtA`y_I?l%@I='h99-GD29P/~Һioaئ8O{vy(=2Q8Oۺ$p'q;[Js &4m#>oOh3tz0p?ek-wl<[rB! e9Ǔ z D ؾ[z, :y4`<)huJۙy2|>R0ϓTSRO-m n.-І9 z@Nd?6_EW^ZLH,&CPqȁF;E#+  rlt"lz3} <7'ذ=s\x}_sUOiT8=Wx,e0،\{e o 6VCwXGEY].c ܇`q\^uCP0XlK+z8',7U[h"#LbNK1?Zpc׋m#@/"x2 z b=c5]TxQ`7))؛M<7RƻM؆G[_GRZ.|xRdBlyeg bH ul?TA0:O i[K MuHZ6!utF |r>o~ƧKoELXܺl`z(3b\!8'/x*`цCy3|^'~'_N2>vuR(~+8e}N t VۉK <^XzgFAC`q7{7 "+&^h'A |X˨ga^m/hw#& dD?CMA5_qz-T7.E bוX;bLQ! 5M؊1a楩bDp|8B<*8"Nu\X|]x/ s_lb;6y"\`vGKA.sr>)ާzWn_|;-+)jwL#&C͋Okb#5k@_`- >Hv[_YcTuYGvhnr8/ ybk  wd%/SBӖȂ^'ˢ2R^`qJp^6e q`|N#̱1r`c&^7\_ | _ 5OuZќ~PG0&[<崷n8ͅgRڱΎḻWq^7b7uX-V"(iF`8,0yJ,ccxarȀ]Ɓ೭>7V`?v0?+yG%*? ږ7mGtcĺ?|oS݆:|g8`eN&ClLMC907֔h-9FyޔG:ދB(HoA,WQVff t](t2>fwPҟg=;w[o^YI}w|Oݤ<*8h(}&y:!ArTk6ևsan3AmE/4d&뢒 5W5 .,uaX${ ֋-/䕬4,A7w%c>F0 Ϸ\yBsnLttk0s|FTG[xBOn}r- ԴXׇϛr\A+n^|M=E_^oU^/Sp-!(xFp6ds}  w@hkI^bSᢇ`/iq[{x}X S8N6t`;v?.8l8 kLWK5J 0gR6 wគvD7yǣhT|"93[@IDATHHG"onFɴ%|WPQ}nVnHX) *Y#| ׎!A ;+P?<)bؠr .ч=VOp@CǢO<76>t;a\ OmmodRlŋb@_SFJ,{*qM&l;;qH[ *#w gm?e HLe>؀Pn~cw7a[ցM x_ؿʮl*J|N\Q9 1"d ets# tkOEdlvS/,:8+φ9A,pdT[pEw?ħ-0_ HgwI{bn(sܸ}3SO)c욹$|O=fo}O?k=k!K;&%0&7%⍾C[ .7ʉUb#A8c}>ʉ0@!c|831[GۑfW9E܆PL<|_\!8Dw ,\)mHm)8X"~d!Xa̗hl?l&y%lM zA˕Md/dc^<=_'+w ڜ%؜2lPe|vƿO*0B)1XG:>5Bl0ar,&tda89 FXxD Ic8a@WArq86 [ vm?D} myב2>{c)[+WC Aϼs%fia~vp}ow1)`c͎]ט 1䃟zM=bDG}%ىG=s<P!a2||#SÄb'9P&b=@#gx i>OPω~#*ZE`!\-;;Eהa306մu9 h`iaCB]ugrn >obo@6U~z{Q7VL )F t'+u5vb#!A_ G|/ >fuNJoXD g831X8 l~; ;4e;dtxTPA>Ga<=,ktPgR Vt(ꛗ|t@!cqp\N[:lW7 Gطq?B*õQNqRG:I+.6ڧ;mo tpxnUQېvCaIP\\J c1 0i&Ť?@02a^P/jډ8}Gy'2)ĉ8}3Xl#m)A@o|cl`C] 枖cUbiab@@8HqDj=Po IZSO;-fo.A|TIYqq%VC}Nl3?O_ۅ]Ba>͞T 19bM, F:p :wOLGvZ0b3/dzsN;ǽ "wX_ o80I9@c-dnM{񰑶2s@AUn2>ȅuf mdxOGYmy @k=MZ1&Z9r-*F\%n8n91hǓ=] ԥgre.Iݠj6S10lp׀{{ vWʆ )YPZp9Se>i_sMX\+6BGOIMOg()aͽ'|PO!}&}ӾYWa۝Oi_-^cea{Fu_q5'̖c¹ n+O, Ux.* U+4PLCQIYm&`!3tT|q eiOBA^͂ю w~>Iۢ[,* YZ\*6@q!ɳ/'@_߭v'r`<=2@ :~(+tSZ w6 W O*վ{BeVʞڗ[gو'8Hvq:t_^Pzz_ *%1*]GŠ  tKCjFJ(EqAl^+!,wО"WU"K9Bбq?.K* .E%xp?e7ӔCPL]q;] ang_ 2>A56-. KT& #華w!caL=f*Φ=|Ku\f#$ *T[Եw<`XطC{f滾4J..%w\=딍w3&*Zպ.֩ QKMaQZV['r(GhG7^(C(\ǰn6c t)C,e}Do1Vֿ=$B i+i旾DЁ*ɯ?Ï*z/lL^YǡFj ,˯?~6t|5L/F)Co~H\ F )_ċuUǨqC':Vm+zF! I5ZWS&q|`{?]];yPWߓ PMG Q CA¾;7\)9B/cuP(qP XC3 5)=Ñov m"YG赤cLJ>RDzTFmqݛ:K0:la O1MO]\[wn<p8)X2Jj{{b6*AX{|F{ʼM\Wʷj.kP^JAO6|A¥%aCJOas7G) 8wk(XOH@`q+BR~ ?R2),N^RX8P<.y x_tk 1Wu A\X9"Acڇ u}5v[R7fY'%nC)s)_'y}2]u0~ħ߲J>e@u!Ckc/D,^k%/s'|pZFt\8ɩiQC)MSW:c|:q\E0]p^vgYTlV3K}"!ݸ .ITuN.c إ"T380 C>WNչ4O'(,S)5]N˶)㉀>le\sih"y%D]IʝhHЕ4#׳%1W-/v:~o8A>/6* qZsOےsv9뵻UV7:6-x@~/gqd*[:NX+)3t/mf8dA'6IZ)H|Mb}qN<_,x% 6)sќ;{Gg^2`C[KЙ^h[TF|'v y@k9yA,'vk #vW6t1jJqj}Oovީ#+BeaC17\I'E,Ѯ'v$g Umf--!Z_2 ˗%F0q e%pMUqJ($'eyG#}|MclOFê?2~S,ސ]Mgx$( 㑨6'O2e.[<}ƋmfxRmD>riב㐶ڻҖ> s [U+l׵kƗd~A>Xôwsla oGShʖ~mq˜Ҿ1G?C 1\vKP]&!VY7KUK68px}SFݞ'=lgm{b(?[c<;)g94w glAiMi?S\IUE0Ntn^N>'‡,@ƐPwI Cek~2v0M}QZC \ם6ǩ?Vwn,[JY.J ׾Mk^ 4֫ZyGvFty6F?-?}!-{<۵}B5Fn&R>5C,N񺣃MJZx,gIyE@1^ A(} #N Uo?,ן */O S <'s_%em،Cq} pXee7T&ߋ#EO}XY` "sy.*yaCa9Bp!ਿZzK$NHUËl- I|ꪈ9Hӱ^kirޝ2)J~#nc/ q_7;i{G.[[ᢻ3$w*n/\b .Wm}8-F[=[) S_"2jaaWݞ-C+sےMGŃA,}ÀEZv\3 \OxhKEGpWݻa$8_cӞ]$A9cv)Jk$4-Jxm]-PѤ&K:E ~=^Cz.8],> UMe;DjYܬ nfœ9^Pז;[`YomQc_AܹX>Jnp1˜E5hiCxͮY_8ˎ'o{7:5-WuvJuWuvib*@{9 ~(tN=r٤nK"NoFc>wHWsuZ2.'z4-<~`q! ?k4q*^S[{zp1 :ӓS' F,6%w!'`X/9~ƙme֕:D {OSݗ{l4fqٝ?8yftдi?fouKj0->(qgjs]7Z;qZ;Ƶ y.Cf \WʟDlHM۩1cc24LmSrCೝ}JeڿnĻTz -+ 5e|_PkQwS؄,yϻ 퉵qź>lv5t#k e_5X:|Ft /9`;&9պaYQkօ/=ͼPjTpq(clLy}aT [߸̕R#^/ Bv\*ao`i h/UsuI\[[A`Ǎg,@J`'%_C+/kLGޮ)w5ﷰ 6T. bni٬'c=@zTox|&庸68hcd7XxmL `M`\1gN i$tMuyT~| 3eÉ+oj6;j=B]5ޢfؘԉ xt:'c&wNoīwp?姙>씺#uxjena>| A~;huoWkMֻ˃zM|0|\wPdiHiiIvÝEndG0r.k8 ~J%1E[Tz\ hڕuY0=XF?K$p* c=E,p8'D顠/syR\nShoJt&.RX\nxvPCI7 0·xT:ވ?#9K9u}Gj^;{7/+JgG/7Uύ6yL'FI|#_,rۅ]R;,/f+Pz7bې6mt$(.~ 7l!Ȼp߾1x~e%u^2xmNɓfz k̎;o~uws]/˼I\ 7)&tIpG1d+~,q+{cFQ)y-3nu hHꄛn7]ۧmM-{KuzT/J¸E[hiG[?6lcΐo/n b*$ס$uA1IJdRh f)i"-[RY0(֢=@ĺ;WH1.>ܿg}޵s\çBԵe_}"% }= :%צ5Ύ_;J :ncЙaptV8272:1.t,s CzF`Tq|NjĢ}>g0yC.᛼li{YIsvM_bMGSvs7ÿs}@?`>`wQws(wσ8lkܔǫ1ls<>fHy}F8t< a x :0v>:ykwl( ]<ׅ4NgWw0 koAlOm:)rSu:pᕼyh~M6Ц% ֘i!1o-ipLr(~򎪯e .wua!φ` Gp?JyH[J1Iכ*L$XGg%cu'5Xuؚ`'''wkqCXatMc,Ap1W^hPwɱƃnڶ4 ,mO_Tgy{WNm\M߀kY%4Hn.Z6 ?ŚEwX_g}4|ǃ]#ep]]u~`y^ǃkhjv::b<,GL8Fm{4xx<X `:P1ǸzQ,|ym`WA0΀[JlKj0au~E>l9;3j>jQ|_4?딋J1/S˝o1F1΍D X881.| x\C1C5:lq~Dy6h2'D)nN7p ڑ-7$0(ba`~=HN {\2]2J׻R5HiȢ횚sX-vJnݼ!ysk}soN:ӆo}o ΰ$^{z0Np'|c`B)S@"۠͑F:tX vl F*? ##,98㜏\nVU m_?eopcmOI[]i|ux( Y;C[X9<@kk6jӺp8ls!:qaZM"y> n)&v\4/b޶|p+w_wa\E032ˠ1ՓpaiqLT1όVyōlctw}aU,1oHqm\`p߅g@ywwx![cZuDY}Cփ-aG8NCE:مpC+ +w7u7)m)% Y,-ipϻ8zC_P*y^nuk^Mm:_iG㖗Klq~ay䄽t8~E>pt>aO>qkCބ@swfW "[Ի*D\hpXV8uh_AMz/mp4JQBmy;X W>m\|GA.ي,\хQN{-n0`sp<.Fq! }y>eL-ru?!A~R:>ZjGkX |XlSwoAe[6mǗI䫟*i@ ;+X}>p}b"/@n48o&Y`W q<~Ƃ}uKCM%(ns!6>=V1ba[nxpS˵+sNݮ-hS!|C"KX27v/o"p\X7W"@B@Ul8LkS\3ob9x?>qG{Rq`36(q I1 ݦhKR;|;|9_qS鋓EPA? ` [Q_iO@]sNm\lc nA}*3K[nYP= \KA%x7uDyޅ {>D5e3iV쫸\q7O~.8AlF1ʟSE`OaGDQ\_EF܏6Ll t{Z# |O^7Mukhs ڬ/s?F\\l\GnrwmBy7Jl0.FqF\uh3hxU"Os|w,u~ mP6Hp',WO Ջz RKNj9P;»esK"fcݎu6SFyLxPgMßai56tֽPH: nڦDZw蟶yGk0~k8h[؛!AӋkuCm{/]\{REy@^,$.mEibGz0un`876h x9:*⣤r#hk (9r-Y/ MPoASb\(>KJn߁ ziH,V(] ׎HOqcXDj;ڐ!8nft }8=xG*5./ [q6x8*y NSsp.-0.@Otu.z NrsWv4/`}ܕD{Kz@)g¹`iS0AEY:<}nZ7D ~>W (Z5$;ncyc[cF 8|x;X3S}mu'?87i^Nuaur :\`Gps=a ak \u1 7ͭ`\:u_;#p0 F{]R {q(6 m11 nچ~SQn*Lxɏ%f`;u苅EGXr_[_kTBkc'2NLu&1*Ҝp1: |Ņ; хr5]`l= FQG榨^¦&d= VhR n6sqPzկ-  ?Pc۰+B \rIQ~cvND>$8ˀCZp^ _%Or|OpFl.6w}>`9ud$3m Ma k]8ǡ_,_`x `С {>)N;̇ibAur,v:XBw<.60]t36ľq8^s.J>;qfyCLP7}\?3q8oqK\Gj;Ny_۸&MNwX2uA ]iҊWG;Snr:M@ۍy ѿ'Ă|QcL \.j;+0ꍇa\ >VY  #4;ghu入k>6l'n ~ dN how87! n)`e2| _}avMէn2|;B̋lNS?X}REf/p`۷vpIHhsC>.PJY3y@' w8\;S -ށ<` l: J~y:cJbkӯ@mLbN|S>k9Qvz--JuՕ~3P ǃ}/B9!DYءaP)ߤ5Kr)m6$Jcsr_5s船9W`woڦP:E$ߠ#b tn8O{/dpl!iN( IP]y `kb -S2p~z&Y:x_O@;Sc>TM0 |_ЦƤ1_0+p|qL7)lyi Ck#1}ۦeaksrqh0 0;'b[#WBGGh ,u? >,b8&duH:ƈ6ϓI"`ͻX7 ` ?7"GR }t,s\ \@M+n 'nQPnGm߱<$L˷]-?Irj(֛7|u.tx(B/v7 ~k ( QVI?#|@5Tn7@ ^;*na `0@~+mUHіDR]o?N>rzލ*%V">l;|͹(=Q.Aq,hmSmPכMpڦߴէ r8_˱G`>hDyT_Ą̉CVߜ VP5t˰8aي4\v`7Q,ZúBGMu]:@qL>T aR[$}vS y#8lo[e\ .AhAaF67z1C&9|j`}p~r}t )|vm֛~,%Eq!?E]s`뫍76Aa*pt !0ԣ<ba5PQap)|1ЎxޕbsYq.hP⸊s|QЄ7ur$Wq=~j v9GQ 1h~T`U]K:h!g@Q㹩kpc]qʱO c@}AM?9^E:مut ;.0aCPWX7(S}`Acn1$V}^Sz+ 'suz.4}rXUAc@^7[l'|kh#8W ?McM}7^@ c%fHy\ gi{@\l@dGƉ`Es!=ޡc{97-RuĦ֦rbGYJE`Vsi9C_(_Rm .&F%IDAT^Wog _CfPņSu8qF Pyx}V{AĒci6n-ZJE^q!d+@ٟ2.⦉ͯ| ˭ .uʃa%fSz (_n( ] p hE0种ö{@SĻMiHuȱ |7v_;dwd?la>3/AC?(WYІAilۘJ>` PWAƶuqB\X%8m3ýB{guc)4y== AQ @^nr};`Mdq_cp`=$pc4EޠeMiHR;m,s(W|0tx7ڱ# Z~Ұ<(s ¥0BbsM: b>攗µ N~!cA- q^n8}qp8ohw1F+PwW;#`pz0( %Ĺ :'✶V+i۟A/nxiu} .^|FU},RЯ1"oEGeqQrEHhu^nDhF)uJB|x `;fpL84_E- uֶ oySSq}v8݃waϋ/SDZ JԹWg1տDJU  `a N{hc%6^UϰmCCz.?1G_?7dpft^3{uނ߁tpnio.mR?+@Ჸ6%Ѕ۠:iaddwqRq13ܴkzDkO{77KpL71@+>k,JN$a\FXo;D0v&ܢKC|B~S'n5Ja~^ f O5ܛu^o ߀硢ЯJo};'i?q$<u-)ўHn0/PTN7gyP\WgXu`p]XH-vAn<>Dm H1P.A>²hswyH8j[7>҆Gq:8WEžX.1X]ᳱ/4:ua6;*4߱[5>} %uAksKS `sqp)xm*y0F &VyuԀ=9`;"<y]F0XRe:= m!kμm"^~n]c̣zQ; VA>I !*H;[kіB}:<WD]\V~tr,@;*π]@(Lܠwb2c0 q \4(ȃ)|nkvƂ& S= vc,m}d9>V ).$[}zwa[եcƚEj8Neǹ8>R-lK5W-JmUC]kX4n?[joan߾Cj}TձKa%fkL[7- e@Dpzxg}BxԀ/`0>S7e9\ccyukl567QU<.1+u~c|]`8 n}RAnF;˝uCNSͪ?@0b?Tcԭ8{+NSX']Θtà1驁/[m-J }RZFDgaIM]y.d9UiϷJtc;V?iGS:+N=_<mm]b2V](b<}-r+% ñkp`\ kС1{TGKr; }l'. G0<,~ }쫎\r{h7wiN]i3{~4A3SMԎ%ռ5U=(rT(]2y^kSKz+M͗6ZcT&>i.Ϝy]B vE}SYR/m7ٲT989e8飣ŷ<0P3y`-oOp>F.S:=oNVX7XܕpxY?l6kb='G[7>OglcO'y˞4oQſK덩_Om]봮̪FvN4Rn,TB#DZ+Nj>?aZ󯥡ݦ39]{rygr894Sw#:b5Wkuq[q<񺼌NTIyv:c#xv,Jαܬa? ~ AY/rrYH"2F?S1ubM\ _aKPq EuJ؟L{\z&Nܙ6^h?3g Os]5MygÄzZϴK0ƣOwJ}8¡V$Yvga]5馩ci)Ks( 0 o+޵r _ѽ;nTb<źl^Enm3m pЙ{:6(_v0O1vg~0< n|_̆axD;W3U,t.+ 1ڐ-4.a5QʻCLRSIkWI/^pYc^3,hP':7͹p!=rNK]/o^6N!{~*K,̣o;͛x5MCذ8|\Tnfк -`&(GC@S@[|"305=7[C ZQ6x!bI)Ыnm%Hl o4Z/ne#lS}-毁>U:Aik-9s+c䢟GۂOsc" ]|o>/ T,TcɸiCҌ $ZO[tU>PuguHx7юKkO;ڣFIwAYc^ 7`qz~Axsa##_NFڇ[)̍L|%ҨkQJ%,ݒpan%&=hZ,w0#6p>5uڿrtǵi67ſ;%y_d/mPQCE[NS  <%QwU3tx:chu_ I;~0S_W\wMx.ӎkoV|,Ӣ)$Ye)vjf?{go k3Z=&ptV紐Uk;YyxX%"{5AŃ:pCIh5]<7 B LSf?c7:BGX:mΡ?L2q/KckD4oe!·)އL}ia4EG}Opo,#S7aShJwNoY]j1)}>6 Җz7MxTgr\fLuQm㻙-<+l%=^1{wbB\7E8y〲|C,r7w'SmvU5®V=F@XsPSPKhk srg·@?W=4/ (q8XԻ3sZyJE:7 xC׀c׶yg>~l ʌ\3df~a]Aje3h^P;3վ5o/XlZ'B՗S:fayw}n3?w1 kg+"ﵛKnꊍcǵw;9u< !?RΡ68^+IRj{c;tPa#!t]UYYֽ~DCQ))_;mJjsb}),f.䉶[ZO YV|o,QR-5B{cp?hpq_7fܠILGvn!ں!8_mfZӆ2^J)I!#;[: a.tNaiA\ncm)*$t׶2p<#CQ@0ac VZ[~hVj~U7u&E.z*µƏ؈yzl}u EuwnpKc{F{S BBmBg\׵+?y}rvGYOp|v9~sK]Zҹsڒψզ;|6MSgNn؍O.;.WBZJUV;=m"bA5;P>» [7G lSqb,v "YtJ8Ӣ,p>2zp9 <8aR~g-ZS.}z%v?Zx}'H B:Ĭ5'8~ԵaBbDmO&GZ`cFޠ |o#7PݴʖА}4v7АD72*2e{b;| {0Dxܨ߀h?u; Q[qg[ٯ[PSOWhr?[oE2 jz*6^hlkHcYztqt(n Ż;|H4ƹ.ݐFឥܖu m|צ(.lw;O mĺhGȳws@OW?aDjk4o9,5nb֯3~Asz#rm@hբIUϏM]n[3=iluvSuaQ^3{d:SG'uޱAL_g1jMu(Cap!:J'^_RD>y4g9i2 vG+URu/~$3᩶mƮ^9]Aܮ//V+.b)w~+AH_(x" B[1ɡ˧iO[6-ڶ6&8 n4Ǯd}΅;1xHm+i~}A*W7YuE=ٶ#ڎs;BVJONV:vB!^ï:xfY:^~]w~n k÷OUI՛oN)|P~Y6So nrT\O>[`LUgS?įk'r̳;~ !o\~ 7U4DZBbS!ޓ(Ѧ[n5m9CڊE2 @M":cMH,P067 ovk[],R[S]WSz>?N ϫ0VC@80 =niS}p\np[Ŷxfˍ ~6 6%NxT + "ow}lYNJ>2ӄexhJcoAr ͏3ڊ4mvkta 3 m;0?Ni |;N=|u٩CMu‡[]O X8-JO[b'O9݌O;Y};|lVk5 3M=R!W7p(+/77L_ :rCY9r5hla$p7wc: ;] Y}tbiTn3թqc鹕91]椫hjO9ç _fxqBE*X_r÷IGT{7&>ķ>ء.}\5_HlWp>zbFb8H@=ڨ3ĆàX[69<e7Ҝ>:ϋ_,{E<-\Q0OZқ^KsJEG@$mōx /3UԮ&`0kk*o,f CG+ʝ@+m*wd)~eCa4{Ee>)t⭾k n7@+=P96`{6 o:|t{doyӊM*} s L]tho >\Zݗ f` qy`S~P}=|)_ < >?$ۛ?g?5pkj|i-!@CTt~F?aU\jpȋ]XJy@hXV-MM|7l_|hnQTug|1}/?оa TQR-eׯRC~%4Zr xr|`Uoݿiɩy8\O݋@X`=ߏݿxz\x-2~]Z?y7_ڂot/t-"Km[ERxΩw,Fji@֛ͬ3@ 3tg9t~oo H7.!_PЖWo!/i9GƣIs]7ӷBWX^ 䫿ީI7 fVUz{Ntg0-{-e~1GiK,>CS{RhZb*['pjkR94d\Y$࿸^7 +iYn~#r,[q V9Q*hXoLjᨅ&RRͻ2>_2So|&XMTy+>nD# @hj@w<0i Ӡ|iߧZi+-7i4k|ATݦ{mg,/={hwa@õpcf[gZ5/6bMk--,+oaZ%I|^kGM}L; 5ၸP!=еѷ5jTuGLWOv(n.~=:okHy oZxnu'-K驏nPk&-r6ʬjf,7zn='u{}ź~Ԥ1mWa,@~3=i O4`VVzT?&7{*+z-z9tqpؼv3X5N+:j&u !|WM8='ፍ;<"Co:~5 f3d[4eYgMF#EDԠ_kѽڶsg&h*/gCD&Oi9=z<<`elȁ䁼_E9Nҵ#PoMVЍq@Dx7===жKg͋* D2$=pb,"z=msi>[NQ;hφ!6#t'kL==<'{=@Nd_s@Y-3C@PǁҦR@1HB2'[kH-}ͺ&܎Ums_4kG@t=O=~kR`~ש9ͺfӞQ,@OOXY@ 2v>s[Y5jYO=='nߺ kU%}C P+[܃;78OW8# 'V\6h{jgxok1=g+;lA҈\6]8#G`wynm/dm+p@'sV&3w,ɖK+REؗ}XW>`K14XӣٝZ:/Yr,@i^Ij󱟍?$ߠX>@A(FD- l,7z`G0axrmv&z -z&X@Pk٢֨[,f;x$ l7u~ߪ;+V~G==<wSce!O˄]sy ŵm16&OY u%|&|A[7٫YbkcۘpeOy@@zlZ,*z`z \wi]F{֕ Y{ǘҺ֞`f_lm5eii‚o[ȽpH{~1ce8i94蠜1;w5evQXoj2{fAQ-j/뵿h8~'voI@.| 9np({Am6־# M6آbFd+c ڛ o[hbr@x Ny1k@Y:wGf2Z=P^k[f=mv$?f>  v낹vҠ_ %ue.v}8Ml)!GN)zO&} h㶤ٞ69jݮR22 bTMꙫqBc?]6V~'mNWasîaY4.mZaVr^ u{4R!C_ms53 -n( ' }HJF吻IZL8R@@7<T7$eM8> Q-ƕ4Q%>c&02V/PƞˆF5Pke~q=RݓO bU[lBolրQOiTyx7 SKck&ZaCdެ"oNxO9cxP&AZy#؉#I==P@x {8dEVtYa>Y‰t  Lt!kmim]51ϱbGˀ)Ȏ^}137يMl=[EJmWܶvJg+pHlR n{aC]({|A<ÑGDteE== aׅiAXE3?O`N-;N 4# { G`4!`xpC(p ʸI{Zu_ ZBD545 n.W[feB/ fɮ0J|Z&)z z`#EDt:q9LItӆ:9m a/yO}z's@e}u(j/s9mUSZ-Kh־1خ n%ÄNLZ]BAv'@>B.xّGDdx5R@@u!O˄C%{8-1ѻ(0p2_-\,L}cb ;  ֱu7ӼVh=|.HFZ mC`{_q}yI>z KLHx  z }8^&2V aӼ;iI<}JS*zp9dqPy8Zg:=헄 O|e[7 L L( 4N{Q',( HL(,[TFLxhoHL(̋!GN҆:H)pqG>rȫYw,=[À8X8_x@S{Vא@ͽmw|R@pv  k&*f < q: u?J$tC9̓ G-6+?z|W, y2H1,#|D&0!`gk|ʊځ({`&o2EU]H9g%IV]QXlLF LdV-)IdےGpy:>DLhǶ A e{ސ2iBA@=]j QC=e[K^Ī!-vPwlL`"\/<#x^;%ria+x;ɄC ͒tayQ8='! <@g~ȑ}'y:8%D l_*_`uwpvY)]r)UҲ:Jy%'a(@] =n~a;{gB Ll+VJdӸLe0-H=Xn8ˏg`,w<"vml)HkS*7yVu O:*-w]u,O`帑-sw (@`{_V LO W m ua=.9eO?V>dtNS.qL^,S?p;ՄMs|D\2w9nz`t)?E~Sb;Smiy^fwb=3Hg%'}:--e5YmR'r?iev:%v̔6X*?kb9VtQ%hBʟWc{HSfZSC*,y|Gf=NՏׁS_帴6F넥Hw9v8p5oXmm+I%;MaYQˤeP{UV*_5iH됸H8^xr&qJV=i$H(nP!¬>mk_2w[]ۡD~V'  lN L2N+j62E4%iTq^g[mh^O>+]}uxjV_Ҡ@s8(ork%bGZcIdN}z'~ofY^?>iO'ViU.dPn~g4\s}C.|.~dikޱT;C}];kN[SJSxl*Jy+f?օoH̤.\6:ဘ\*+Omh#{5$$>(iq`IL(,exwd tIaax8y!'[LVtN R \x+ݖ Md u C#u "M<$ui{°>VʾC@ϤdHL& P2!8g1_ Lv^U%Qzt Iy٥PuӖy[Bi[ u6~`#obk5b+uN׬ac*'7+|oG:[9y`4,nxFCͷo?uҍz[lUefg]Gr`9VgZfOyB wMށatC^M4<_t>#ē' L*²ćq ,[5R>m}eA-!wCX3Tx1P#BjU|u:gp'SY$ {H]\W͆j߹psmv ߑO>N\g^kG7}mPջIO+zB>۶$'6kE'_6%uO,YFzGۯj9=`nPC=4hM߮.4Bj1ڋܕħyK]G\H`3TPL]U*2º8(&}BU6Ī?,%:_#tdC]l;]8]`` +rXr%s]78!C r xnbq{PXjrIV hivs;KUS6/ VZ{c(;oo SfG&}z aUOm qrqa5X8؊zacl3KYstg:pWqfotK]宸ǥi" 9+`_)k}!ꑫQkRW#Kawyy3#uErX,!pBƏz3qN\'<)Hvb#y+t:O<6ԧzxc?f]]lz%JCxD< =GT=J_,!lސѶ`R\spDE}YݏjLJp<WP- 7Ϸ|v)rg5N[{f{عaz:ޭ70iX]2>_I+@<zalStWν:/iK.r=.yg|6Jxʲ=m/nH]!?&(NK5 ڀ> {[:a-|VNuDp5#tpp}H{ ҆-l <`nhOӸU x oJS! 11D -gO E%ڀΓÞIV>8*t!g`|k3mO%ۘPW<A!9'Ba]HBN;> |sHK #֧`' \ 3g#GuZتC쪍.yXҾ~ywC6JGg7 knx bF/ gh˵h lHg/\w*rrYw iN\61Kit{K~îOsOb>\ak7V>,p'Ka8Lm&-p_/C }ʞ)s&;LAWBDDB;\NsfJ%ۿPs0Vk?h;J;g j?[rfX k0zrG?zO'l~-Nvr.zcW[.!D.fޝ=uEklIիZ I&R߿mݮwޕ  TUt- (!׫7ծ3O$ y9ADYH烝$rd a}YiЅUl{CGUNtA|Wui1r<cAY2@#J|G<+{4ߡ d{r((f`CxЦ֝Moe1hSZGldj sӱJ^]jL*~d2zml1`qجjr:]w[vx\-?i" mJ.i7v K 7&>H܁( ֣#(:?S.C`P LĊܝweSSX>¾RT`C<+Pr VFNS ?0nP߉?WJ .t^+*!]qL=M_- yhrӹ_QoMC8A}[]=@POoj=]g.ſ _^{M5 ȝ5˿z#.akT#! QxtGN2uZAN; jrsNG-abYQB!|7L BOٜbㅗ L(V?'juOSaPGۖ | O$RgU SQ; aW L8_d:H9/Jta|^CHƇqYe.rx~2 6RxzlvT8M &_r~U_kվc6cMr#5oV[{=c.]$-6cfkߝ eZ!yt. b0 pAYm,ۼ;x%fߕx>w=-6{;Ѕa{PC]()^kl_muQVlx2^˚NH)a['|.A'窱tas&2:}O|8yX͑:;_ Ă2hL>#ǯ:<&Bs^M$N!N5~+v gy8iK"sZ~:[{UI{ejе69+u҂}o&'I.üaOrʃ.' ^$vt]^CNYԙ\|Q8M)؉L4'u.{vΠ8Sng nĄTR}:Q>yCn 62VD9I]9ezxd| (i6JTLܾ0j0ۂOV LAlE ]Oc&RIRVr0LulOT- cLy=+5c_=M߶R7o qk ++N\7W6t&|Ίnge҄,[WuYi>Jzm%mIU. d]u]JiӿՈΏ ; "}9OO˩o'DN0@[#Nvv"gu8;sP{ϠF`W IOzک~}#xa=(S{L/yXv0!JGNt¾B)M>1* vu>YuoCq`~w[NvDؽ8p|kx_իA5ar 'b(@oT;C}Aa[¶RXzSLp@6zi6' v!'p94o‰\a0VO.,(H/_ºQxyK2i< OU\Wi|rG{et=f=J8m~ hl[p k[\۷ի+m_p%T0yFrQÇ2%\$U]O>Y;}%XnбM'BP]Cy|xǖa;j0>?`Pr!S12>C]-G›kn*yY]t7)S4ngx [%.lXysaˡt\Ze:d;4B `[ ԟ̲.פFdnjqGW9Pys(VDO^@IDAT -uܯv ( g"8d'"I˞VQ;ѿ ;uOHXepCX  >Pԋ ۄOC 3` YyvV!/tuN As&3YpilOiuprr~Aub;v9s8O=O5ީrmet^еߺV(r)-pG٬C;й8 #z(,,y>0Q\#գ㸮 nI.ǾQᝅstKk$ t!'.谉G©0v&/Jae56Gr]9%s[z}F9y0.q"/o]#灗%rKjqwFI*֭ļ̅$O? Wo軔MBeaP/M٧v>*T0Ԗ8z%313Knn֕֍6F|%e <<+O<#!]t %0.l۪`BNvƮQv .)'6leYJ}3K|޼)wsIz|JhO2pá}:ݎ6omRawb!!"{Zs:i<68<·`C֯Kn7z#LMgv7۵c{5[8r~[~R굒 g=f{CgF͐yOv6[ik.'|sԢN; ]Aѝ@p2 `qyʤK)vpcpE`EOj +}VZ/=ʫ(t貈tE8EX$Xv8^!ϜSuvg %|o|2V2vB[üe$^&p aE'!69 i$rȱс>9~2e?r޽@{E ӺJ]z5U7WjV5=PH]zO.ȒkS_E?V`]$-2h6B=` A8B /PVTD㔖 WCWy󲜻,(EK{| >»CUvX< o)LcV(_&@<*uw)٭xʦ. InH!t/F nȷL,p<97r+|I`2 0Shr\Siew etKi0&|-z3;u ۺYoicYzNN4M-9˜WTMq2G̲vgech]0|駯nϓs/5>e2d0C8I{tvXm:EOs9yw9{'uEy8=틅 ظBmF^fxׇ\\A C =6*[`wQ!BgW"'{8o?-aϋ.ԇqqpt o L:]~vs |=Haf.7I8#0xBW i CُQC|^|ՠmۭZ#Ƨkبx&ޖZ3&vfj lnup9ߜ/b~͌Hѡ舣CyaA{GbB/= O JO#qNڴ+ m&<6eO]G^dV?*#p-xZۃ2m,OzB?nqÞE»&0TְIۦ-yĵm joM:ŊΛmo?boM9e5ja7֏i|8v ]8-IvH+iZy,Ztt ܿt:RG/X/Й{&r]ec$dV@ lw2BurYp^NV8y~O_,$DzEHYL.73.(}~d&;'3}dvs#d, PQ'+a&cYuIPX YxfG~8a@]{Jԃu3V@`geOG o-sv*"e@{)MᓭvVOWfSI k<'XQ_^9Ӽi7<0Ugڢ/лni ̬tOPhxV'2FqkZ2@vuMөp~x+,a1ӑ0a]+( {t^͂opqx<;^#!R >Lʊ1t"|^:ށ캐#Cvb&1 ,\*KoĊn;}E]`wlǴN p<9Vd(g38pn@'Nnۆ4Ĥ{) _ \7!ypc7lVң*'!텖 ?:nta9A߲Oc{k0V'ufNaZr=x67Z^-Zlu!nxndY͛cE=9;Rl8_.[`4m%W&1;g_|l{/ZtWkغ2%TdPv#;CMu0Kp-IƠp@JXIE~NӽFzV18 a:ZxJXC Ea|]a| V=!p Om8^uǑcJJU3z%g;<)ԅ2°xץ0%B6cׄw |ȵ=I+ \_C .s&uA'O^¡ù|Wh#nj g5fq@ 5dd=1{k vڵqv^⁉&7JA'ݝTu_f=pYWm>4PW?tݦ?:adpBs]e::x:':7u; #Uy/)66ҡ\-@;kG.59$I ?*x%SJ/]M8&5Πy[犪+¿-¿ Z\(|_$hiӅG.9e,~'Q:%VwEe q.;'/Nx$E?DAv?fxZv]~:i[{fvF5V5H2q%d"=1Xwchdv>Ѷ^IC<9=CV 30p)*{{ w!eQ@;h:Dž(~,+Hh!,)+n;ݍ/yu{[)\a^7 ? L3PV}OgO8OKq:?)spNes;> `ҷP~y99*Ƴ?-L`›#y{ik5(*!p={9f4юVNǧ#qOKI YK SIn䀃 6{ju_S|o/Vټ'ő=Ry#$9퉌/~)<#x$&8^?<]"D=މ;'-=A6Dy-z!oOPF <+1E |HPT;j حkZ|&==/)B4 W.N.n8&74 jfvhC! bSBkM&Y[i#7KVW^ QY ta@ιB9]&|K"Ӳv,cYe(Bnq;[ ɪaNJv4բ6m|j?r[akkM*חKĿ{?${\P, @O-v]}'XчًrMrmsƅTRyХi;m1EB9kU_teu.6LXqppp@} V%ae/)29BA NΫw Dt7,7sY`57M>!|J`iBq{U?|@>-`3I rN9-L-'vECq%E$gΉcÖ7 \ aHrp0V8D8w_$0`k퐘ݑ Yi\Dr}q`Vap`ZмV W'\ Bq농q9۲e&Ժ D\hZ7Pth>.|+>rgp۳)k$?-5͘;t@_+\N@DsapvVվyUP{B "ҥXHGcCł**փĂH(k "z~/IvB +p3;ؾ*駶W7kƫ3sZ3/m!}2w{xRg<= AG#&?̿3<[nƹTGqp(~Yn Y ̭TكaJz¶r 탗+7m2|ǧ@YVϞ_&`IQЍ'ģ'4cX]J׋㇐<Ԏ4ըOy"F1 ~]O^xzN#m6*ƛv\T?8+Il 똨c0w1~!x Zb`.p{M8ԩ>V}ěƓ4'~89v't˞?Xz9scQU_kw@s͒Jd%LaRGceRM#$ƞ6*9}ھA KY|(ك9[%_l<3uDC4mkD_$Qp\V;FzRloݰFFy%L/w[v|8[·!BnӬqxu||>>B/_~bOI}ծ-V]ē7m=zƿ(r)?{>9fK5/5nj\CkS Z 0( VVx -[?8ܘS?jGab|XԯS: vOQ߶ IM?+a",ZLm6`O 1u$ՕZ_em{=Ao/e2Eƕx-Nc:~M1<0)}/@{mWF7cw-E`ax|, ^_:8vsmJ}UŰXIʩac|I)i1Y'13`*8KH6%|2+Gʜ19(jkr8/cKM* e| z h$T7MIXj_?}()/$Lx߻[aIěZu}c(/=5p.L+w_5^ɾ{Io##nxջ9:.Juc3f'O-g /CE< =sbަ'cP5st~\+²#뀿 E팩88vYqzsG m}?j_+ỤԼM}oʷr„^wԛzS'&mpel(l(ŷWज़nKISiSygzY;B0nz2;! j?rSQC'6{ PX <#mˁ2 /NE,_YoW)Gbi`:ݒSP5k%Oڜ[ Gb<զ0>+jG2͸vq^OǚkLJ 1ymp8Ja$mK5:7_Ï`}aQlTf\z#ɛx3txWo; >~QnlwPvj^Xx9= msGɃڑfqxL{ցk`ABE .VCiIX vt~j:~ mv]طԇ:Px3\]y#|p-fyL퉻'!yIz磰/S$uoG4׍lw]'@߯2^ogO,K~Ex!뢠X}6, |WLm?]/>4\[㞸|tۂe<| *KaunL+~<+A;Z΅k¶;2q&0p e jkt7`}rħ'oڥ6y8zp r>#RN~mvL _7^཰) Td>uZ3'~LFn ->3>RZ7y\_^N*OŒ>U2[ i*6XKӰ , }Fx ^)3)y?v|W~_&8C:MM!yFwO]Gh֭>hrH,Dʠ:v/?Z`HʬKlVX)~ `x#lp5G xuY_r+[r^;ׯyד vFp+G$Am2E0iZ:${2\mڌa8F`M=dä( |a7&@do^p3+)dzx iKC6<3U?E%Ґ>k;L0//mp(lkx1-\!p/w,u6ŶXDjcKxb3VH,KJ걩+5_ߤ۰;zڜ8Ηॹ)5 _^@{ {8JCbf%eٿAsڲOˁQL| g |VbnscݑPOےf4y-gcI8lwm<9q9kVlU6ReK/=mp'E D7k\It-:s=>Slc='au13?^6{Y||x\+kPIg49 |, {I= u_sbݮ-fZ|lPn٬Q(ix ]{kI+5km΁u;ny:|LA[>HRh|149,b@(^6WA xpa _HzڢO$Ncۛi1-}5>-r7Ȟ6A~^ U>:vh>1Ly#L`}p5u\P^1(zqO`?%4[ X|DwUW1; 8:_ˁCP|Eє'rv" <{ w ׹nys'>)Dl&p>EH .ow@CwPhZюĖaXxfl KGp=/B܄$7k5z /`/eԎ$-a8#j&f q-)k꧰?|61$u(SqŰ Xcp}'?PFM1z :dv =/:X m2s=`gb!ڑf'rC" %^v{))%X<<~~5s.̣ا6i]0yZ_x$ MV^Ƅ$6/I:\Kߵ/YQfLO]}qIc&*Ӿخh*L(k!_;QR̐ |ewݏĴ; pS s4n9#7E^^_P;aiX~o'LӖuD^/<^^`;mV G#)o44a<ЋHv (l@}y;?ˉGR}?.;Y^}f$ƺ6㎣kuMp-z߅UqtN\^lg|a~^à\y5uxnױ 4,AHV} Xz gǦs6ZiK͢>fA٘ -҃M6,~9]8|!܄o݈qC^ ڪOt_G{K6?N?Y:ROMpLn&2{3{BۤOmqC`9x$Xm;njmzBnZvnm:VIG 7u*Ƈ/<\t7q{1G!v_U<.Ǫݍ.r9xWyy|lό^d=ľ>B>0f롕C>nޤeLy8Zip;P' \IJVī굌讱lqikuk՝ag}iƫP\+}M>ܦ[2f^C펩_W ^ԯcڊ:źJVwπsp=njuE,\K՟hñ)%p3.;Ij.eꓺ~*ǃp|va"8? :[jݵ D؛>U^mt(5Oߦ'i{t`<86^^Tm8\8/Dx mddKh<0jGw~);}0l׳v_^i%e5peຝ^I=|iG>φ>f OGudc$T{"v7py=|kXfED݋g1x)y NUj+6z~k;O%0x< }( :v;{n'P y)ؖ!Dࡻ?x~ @Z>~kf<7k!yzP}Υ!s F0x:/~a|:^c}Cn7as^ԩ2^8jM֗]b;kgIZ(L%S׏e) t6VN:4.$MSR4vB5 KU#d%]!b&U7}8<tmURh!b^ o g݊iJ3ZMӿe/,wYCKc\{F|w^?w|&p<-˶9Ҍ״6gkkk>B^ |h~^JGv/z? =v1OنNK#l/ `JwcSQˬXEh{uC\ql0,bx 6}Sj_O:qpΧޖ@u<}`a¬a;nH6 -ϼ?7m 2uɧ_~AxI)׀P3_3s.|p,o%ډW}v"摏A<$e:~΋ޠC{x`S6| {̟-MҞ6k3/,X-C8V/ŇY,>{#!)D; ɸ:bK-Qvq2ιuBk8|P)~=&뉡e, : JK0g†Y;k?,}&FnF=}PhA{=xhW_=$^ ZT|Jߵym~w ^20ChT{-1b|||l!m?|| \\m;,KɾMݿyHy5_W@IDATGlzl^p}*7 ~+Uǫ7lEjGO gp- 8\ÖI_2 Hx<OԩƶOe-0?V1dS蒍QmSf(< L/ _{{Mډ{0>,_gہ_U'ifkߛ6Kd8 .ۃ]`Y^jxsL0Jꬡ)aK8:6%`còSyd'}Iֵe`݇W`WX@݇ҰyyA:[9ps/`h3M3]-l ;NN?"7}~| =|:OAƁkm]p3icVh[ h.Y^ᰂ&P6,x-M} +?G`Cͯxrjo=:R_<(? ~+~A}C6(b:xi0Vv>,Cߴu ]:B`;VMmy/ckp,(K}RL$)7cXLsN#·GO3뾺Ɓ{վלAby5q<, Gco]וXGJ⣙ךoϦp#elG 5n5&gTwC^ :K|n;McR/jGl;aGPr<~5xXɖkU˱>ˍ_'933aƬ`c X$hWRyplsjoGk8/xmv`KnW,ӆudg:0VwYyn#\;Z>N)M: |~SD팍`Qzu:P;ЖP{S,﹐;Ǔ~42u %Xn /` XSosoY j=iW#,׵Z}v*gzc(qC3GmC57`Z&IOw|59i=~kBx iݨ#5rc7ǃ湔amqԖ'}+voK<߀=%$/w:_I؍=׼bb%cZѾdLW,cmW}(S$N^0Xҏ66<݋fXE_*~+YŸR8ğ=E4g\Ʋ~>|vj_p M@9'Rۭ^7~O˾a8#2l׸5x7WW=pGRG x?/!Lk&KN\w; o]Pl Jxx;߮2^h@%~ZHp }.Qc{'kVtHmjW >|LWwXmI#KνaHO|,5)\ k Wk`EX ܏<~ c5IܵcC 8? ]GC#XװG *fu/%4#ٽ gps;qX<;y"mCJlHSe`lӝpmBmmB2Vmafm6^{X=RDG -|9f=DCU3 O(xW~=0>,߹R\wxW&m`,Oi'M'4~\oag'͓zE_!?kJpPyzΜ ]eux)BbGA{ͯ- 8*#!K;nIu57TߩeOk?zk_ƽ𮅯mjy_mMy78xwgb![G?\׮$nڴ;'؆nQ;bO?)ۜ2jZAa> -`zxd=\?T?-cS۟tPW׿&3L/m@9]u}QcZ$ug4N\@0e#%e[yct\C9WourP;2AsdaߜQ<2[ NYM[[Y,&}n SXVl_iF`lj\Ys"|sDRG6Sj!~ x^3X bZ}ԑzԶT=cz<?Q #mp xW1=$nTxHOjIi eXnp8G} }| 1P۶)Srp {v혯/z qQR iBmcc_lǡ N-is4۔xf{<+xpo^H;`y0jWq}i|CePƒu~#z3fVuy3M7~U5#U"s.xy>tn,#>cO},ȧq|Dx?yPЍWq\C -J}6Οos (icXm^8[$g(iW7id&%~c\ .= TIMP5&ߖ6fi_YˡoK3Jo'zQcCӜ̆ʜ1l٠ȍ4ۤ3ݸxѸi=^iL;<ŇRӺYwZCb(ߟ7<[׻zHZF*xѦ~9Sq@X阦_tnk/U-m?]5,a5> @1_?K೰.LIpfz2~-?c 8ea[p/ˉ?#~u^RBKI[>(t<&D_^|QssOL\ڞ)!ULlPf䐩|ƯUPuSŰL7W.4bZCډ:PjtJ<(!&LmO]>{o[|3Ω/>J-ϯ-_?!m-uZz@{n~>RuGxO-3maeXAd1Sx/^v>rQ[MPAtTb{}p(vw xo]<*Sݹ3>Sv¥#ƕk^b!*BRA~CoY  ;+p X>Yg|HꏏfJnlx3->p F :fڊlִ(i kF0rc_>GuB:ܐylkڶHx氏='i)wG ~+߃8Uyz>Qixց4(x{Zݲa6娏 |h {D߼C20x#~%^äY/ZN; #$3>g=qXTqmU?u4by5멻-=)Ωd L8 :935`8fkιxh:SKdmɇ2G`Z 4}OX} < :_Qn;aS8~ʰ ,ߋ/eica۸f8Yӵ%xv58^X d }Txߩص'u؉9߶({|n}=$e>~! ^jۖp;2,/J=8.}I I؍^R'6/uN Y Prnme1N~;'Kv%^͇!Aua~P\ww6ہy|d |y*uŸc|#yE=Kb`{x8VE>𿥰O>1uNOr<]z,]Ju8֙`Lk[ ,[_lg]C#0|`g6\U]`|lXu7pyp%l ™s'>}\<= &A=$1nvcCJOhOZlXˬu/\?gsv}DLZNOwX=1xQ?8~@3zڼ2eyo>|z1o> `̚l;zjнT~E )XCͩzw]_q~m^ An1TrSj7> S`'Xpx`U8Ɩx 룞xt|x[ 8vC!F[e8'RmZy+zSe:!Y, לx לm_)u*] iKK^<c(?(ĥc?~6͸i{[q&!qF"Lr]_M}ٖ CIK^ÔL efjPk], 7'AI]kbz!čCpww@,@96U;Yˋ>(8F]LFtx|k{X.#$-t#icܮkRǁz]3JƦ﯏ eKY SZ 8죏/2f8C /I%㣞6m*9U| _ ׂv==u%o2g{\Clc]fM:Fo\~/ )oBo w} σ_/ik4}OӃ k|(c8͉âڢirB77 nHS7c92>Oh^$U<t$ƺlMZسN_VhObx{m BD+ƵM~^/M5:^yTw72}' Xk;ΐX_M08^۽m>xp>u* v6~}maڭ޴״s^||' #5Ӗrc񰭉=*;e yƬC|>c3 a$c،7e GE;Y3Yȃs3ݹhnOn dS/x6y9'<;9P GOʱl׊~~G7qtC%a7;2VC8%C68>? q,0>b{mmIį͖yaMAgp#u)>vNS#HY i_Au Pfh-HV-JZsSz1~7ypʦMlq<Į,ՐrP;6ZJ08h˗ Xy#WCAu MS'EQWvcݿ^ ɟ˯&6^iۆc*Srߗm?>RG4beG#iKBD7tmci_zfOźc"8uSjNmJl΃ u"H;-;0C=oOOxիTi)`aaԱs68P^tg*c uӦ8vp,*ROؚa'4}(31.pXZ`z.Y1'xx(xoB{H<rWPjsW{iT{UkZM_Ms}z?+oGr'2ͳxp{GtC$n͓8jG7TGi9Df3t~ͯKe_!lkx+^|jGrKa_t5K@#q|UP \`!R4o2e&Ԯ ?b)*Ꚋ^86}y7Miƫʹ*۶؛a]W¢`뎟k״6ݰqsw=(!NԎŵ L ;EvfIm֮XaucpeRO Iߪ:·_._ NM v'^CdQ@ֹ>K]،;~xxi"˂muSzl`W8ˍ2bJjzV_>j+mh*SW{ǩO5}c7L{c mz-{ꈁC}8mp|q/ 8 !aXN{ }@my2pXV9Q_Jw 0xioKws8x7xH3שfgԳ2_YZ2*_r߶Vl*kb^ku }VM'[.\`'p?}ԗjkV̉ *,z:m.6莯_/FIz®>n$@q0EV8|WFk%ͰP)5 E]êosiobZk5ݴH/5mZ%\Gx58#@| -~ _BO}krZvkys rPQ~Eh!|O<]Qpgͬx6X  {/2\ۀc>׮Af>q늤 ?ˊW]ڍ[-E7b'`oA㟸6{MWWھ\¯y|| g-؎p:EWt@]Hӑmv͢u-ތگ%~ka"Ɓ<< IPI؍=7vC7m?<8lo-PQҗϸҴwSMjkXZԐO 8~}h;$ y v|88GM[x 3,~+gTlT] z5#bf/aҍW7׸se?mn0ϒuv}(Gyҝˇܣd>G \mDq|װښzF/Lm՝u_N8 {ǡJ ^ԭϵ|~0>@OkMbO<4F qaRȢV]xl).'Z.p'-i i63\Գ/O(+'z[Fة֦2;N={ }]D///.w" B+e-86Gcv֫AW>Juhi`_Y)j=Ֆ6P ^t(^2+0;G^ʋm /SuSx+5IO^φ_}z$ޕ{FnWad\g۟Ih{dzbي{j ӏAwsx؞vECNwsǓ6 G1Hp]( ]YYVӢ'oB7_^$a>R/tŸ͓ n[_bK֝@uC[u|ƕ:-_u%P[ƪ>V/u^agLw,fpnL9=X-?uN%e9)8')-qki~b~7FX&Zr|蹖-+})?(}PMdoگԯM3bԷϺz] <79Nרֺe'*O1~0- w K"m֖8؆Ԁi8EOX7P٬IKj%=㵄/\{+n(7D0# ӎЃoqS >>.ԉiϰg=>maU؟1gp;M5韲~r. )$LalD,O1-C<x$P{t/Wg_%<d.kx|o+wc#M? kz-_}"xO{e0AI~/Ej^jcY3rP;E {mjӫ-nPZF %ih@#Z,$_@nllLS,{>3bnNw?/-KBi،+ C|;8i ~G<^? 8\v1ke/i ~b=CX'4wT }z9^[Qk)ì~W[@h3T3#"Jԅ1LiڲYb7>.΄n߀=_dMHI [u+Gxhx!.5Ou/y2dSM^|'_) 8 QI[':lnI)isݸ5;6y#dPP:'%fc0\>bׂ32#~wQŹdLakf8%>6l mz-!_ӳtgs *xn<msL_pLS^ڈ#ڃ'^æn|(hp`#g,zc[Eش9,\O_'\%4Ry$nNmMm5X_y qIYc *ͼͶi{l cm?B_<=g{,'>5NKjf v쇇zUٽ+,#ZhCtCq~O1p'vbL^ av_+U,׹p Jmovf_>hφa)/aj:zz눶yX[[ ?&DDp \X}埶$$iyYaEXDyǀ9VRcfu윃hˬhįElmarf9wM_PO…p+ F:VדԎXu6g[BGw~-wglA9i䏞ڵ)ՖݔM k? ̓EǹR뎞5CU[smX:u14uMminJXXO$iKPiӵvyDC`}57ہ_^?6[=mY*0<3D=}5B5bgR4-ћe5qtG-~ ЅBW"7Yn ߌnhZkX*^9@샛>8<ؼW7;#5 ;= CyO4o8#8ه~!wNm F_,5ecQ?XX6 ȜԹ̘jk^!t_Wv z~\3Kz9\\k ԁ -RO@>"8?f%aWۍu״6ErĽj_[ e< J,t.Hڠ0e /*u{xxa^ AqOJ ޔ:v2O.m>^ K 6&ӧ)Eq%x.tm1~ ^G+4f?]v+v64Pnˬ}a:lX;MCGʜ.YK3z،sz+kye:\3|C}l@1[(-uViƛ'fE>fb6jmOW-ODb7X_xXєYô7|U<7J>(isy0*ua'{sh4mW :zyn8Қx>'x{Z0y-v5u$h:>x}$l_yb'< /ɋ{8Fy A͓fئxin377?@j>\N.TQj^ pA+~i2R7Ȧ$xƪW_u2ͼ5ߴ;{~ rn_ WBjZSl/ ^C{s =_,߯z^׃sIH%3s6 eeƪ̳ejs-\M`Ş=ڑ3 -׾_wNEF ^ O sb1n6ׁqrP$f9:25lƛ?vARm}?[fx /mM'P谹Gw5@Hane 1eEغPݬ炋\+pi͍q 67YۇߟحIjWO<5$_z̓h¶}𗍴5~?_6<8/@ےqD}o3Αmk UcAg%p{6c:EkP;8g/qY] AVr$47HdJnٿ)cC>8\ A^H]5TovJvc:sI86-^@Zxl,} ./A jW_SF%-a5yFs}'nή{B-ޑ "+MbzOP`^lXzқJ"<{X9gJ2Z]u3g4E]'m&!~ xT1q0w$ɧ]9=aW௄; >)>q$8h16#ɼHExzۂkZIP)B'///sS|3<:X<Y[Js.ݦp/qK¥k^u^ s޷_bR.үv䓸[I G}T}bO ~tx;ښf h[VY/MڥKNi, [o~_/N\A:Qp*'~?^nr7aә>e&QW=a5 ,s(r|cbR.AY`Ohl?9iWKW=.LbzeStWo\9| xo @A386qy?]/ 3\\9W\NO#qI1I85׍ιupئ7`յPK7nIltӟ-M\h 3m ].wW "9/mmJYq)F cT.uJܞТ?mCf.9 ŃzSpS :oFa8Gšb(0N*eZmp8dQ,<(җ@Jܞ~;IK}:z6@h%^:vKKu]+ב7`mn p-/Rt'ՏS`'$yWŶa6|m^q _ n1omׁs<rIH=P-$wݠAqoN%QUJ]GR_]pmAYe^kIcv0qMmd`v '3I7,>!>n{v3qqNj8]q+@txkItf8qi_6V+ -Ƹ>`}t1\osy/`; ؤ\[A}."YG|+֍ +V68b̻CŖNu^ a)5ismƱp#v]\ޕιs4;lPHn  `{Ÿu$xv{ڮi0Lo^gm(#֯4pSW?kyOZuʺ4HX`ĺ1}/\u9xKeѕt;8 jln״SE7o_O.qp)8yyXvXꇪNi5랰$m->aiﭰ ~+pi?a{x\A Gh0n$t| x)QUG}_̟Gm{_c4^09_M'D?@{Os@qDڍOԮ+2xUNv$vveɵaBWp ~' ʉ`mǪeَ&9OƠN^@IDATx߆nYI퇗1p}G8 'uMiURr3k9jRݔrȀL⏫ ;!.#@_]Eu՛WuqYX^I^ES'%Oүa\hP~[ꭧgNp2J7w1<Ե'27/y_I>i{4iھ64,ǁ=}8+c <(b}2>1\ k ut]D̯2vA]u|sz>6ؾp4ֵ|)ҏI[c"E0ORtw_xuRWهwrp JI_/i626I.o'-X.jD&Iv,J]u2'#E(_\̩^ilyR׌a'pUˏP׺ůMh_iSo7+7: OkqK&쓭ן -yY5ZXg믬ԾaR%AȒ3~?~J<&]+Rqa˺&<pm1Pkwы-͝J+ qw p=hga Xwu: 75j1p'qieFi}O5ĻDƽנ|{߱ lR+O|w3ȣ!dP _NvJLz!ljkd')pho&B`saIזeFpՙ^˖..Q+(]na[ Zs1E.TE]ןp܆y4'x}g=r#IU%-7c,nz~/-%V׹q<| `?;_Nc}\LR/d8\ v=//ɳp<76)!XrxxkӬ<o:ǰeFR` {߂c8tDoߤ)[`o?:]%FOeyr`@ƾO_$}Mxu7=Id| nzp1> n ɻ^~,>nqߧwm0 ` Wq!,IMupZ%O+/K#Y79`J 6I|eo>?aPJ\2le~OXk8 #̣b]9HL:6ZkYDp8#΍L[c"ؗ>m뀗u&yDӰm\%U}_{QW數vߴ3pw;`رrO$}|RτGڐr@k@K\o8jp5l uگڙE<2a T{]s!.l xk1>X|oE¸ ~1"c`.§†-?Nn)@LM?4k]".ag!i;?7er?n7e5\c22 g}>-I9W6E8w5 wKo K)K[O l) es!mx6 J .2C\mZgXe:EFk]Lo/׸qU=}),#f6/mK?IG᣹@}0ɴ6\>߄{@;' Zps΁[cOHH's*% xk14<(] }ۃQl{' `]L+2/m#5.[`nAS3:[#2nlOv:6K* 7QDZS_+X@OȪt]79?G}cO?5Ab}WK}m\|+'uI΄M[^ u7? XG/'%lU.o'vm+㒶d}_=x.g8otI@ʋ?qMĮE?mh m.0LC,3eOA#K\wr]d6Ĺ`.b&Lō‹&lPVIcupTKWx{t7 al/ula?̡Pky^2.wnN?zr^//r|8p cH.ݵtMY`I` [W;qo)!ax1pZo(釞vqeMS'[wP6ՑRg^g}+܏2HeJѯ$>f6?2Fc޶5K}~%=z ^bV4)7nlڹx0o9Ao^Ҭ'A\Y̡`Zإ?P7K)m2NKx{䤉M֮$/΂5;A^l}x5N:O\2Q!vh'e7l{$N]D ۲kpLxKnI픸k_ 0>O&f% ǃ ˶ eu4OGOnT2nږ& 1`p$(Nz/P ̦c1|?'UiJxɣp%oG I:SGnj6؃W!{^\R? fksً2T)|הkxPz/~\cvUni?{I+|K7~m׍?iU`@ ˤo:MP-^ݨb4I9(ֻӠDo 5_$i nRN 2?u¥rd1wZD^3j6N$ l ];lo⓾Oʴ.&$0 ^tqt"|#P7)oԵ'>w qC)4 p^2ct-N΂_ƀR/~] TП8^k t? c͞MW;}Vx3lπo^nw- ƒ%:Xf Ua66V\t/IwvQC$aevLooJ3Q#uҹqK]9`̊<.] ܃$zpͲ GUǙ[ b;[K[QuŬ ,`7 C7a0Dz/ @ԋm[JM  ^>[ډmvҌkڙWmtI-mGgۯ_0I|3l7-QnO']9_'/cp<0/皇O}Z~)3Υsr߀צl9"<LhԿS{<p2pdlG))'qE[),$%<K^aٿ}_MNy c2>ibGtz-`ҶSNDrx8 +@[KYμW ƹ#K7zmJ^^VٿDi; y[S,W)ҟ8١Ͷ9XHԍ路ӧ͸24M2-៾[Kpjk_t8zf m[z8`M*iNDO<,i Q#kl/^W[s ݕwp;v*xP%w{柴{ u Itڊk`%4v~l 7:~~Q{yv~Mh(gX2x&(=2΁E%a2X7"Ԯ|SJӦ D{16v~7/?)#.Zgq3vu#J?R*gGM|N3lYG"s]0޴3N{@),Z蛮99] %:]s* p*s`Sg(J9JlK(yy ߅CgM'݇>a+e<@ġŰu/|s7ЗXnS5q2瀗m?+g䑽FŤC5hI_n2.a˱W?޺ G퓿~hN.i?b4bTYtrO'Gap&I)\NN76.qW|.9N/h|_mT^iƓ$ҵ,7 7HeEp kZ lS~ b悛oqlsw!_ȨMOm\?9?ĹC\ĀsUwAD[ M8UǹViۿm7.4څ-ù)X^_ { 8SWSkgƼexrl ?='l(8]^ʵIʊf:S,= ~ j#vQ1MY_MnGLN*JYuo9(}i|F9׆?74` 'MޖD5O /#e`Ά`O=WnHZq%>4q(N2CFe|nFͿY`pO@\˲KKw:CTm_ Y!oz}"Ss%N\H>fm.}V+˦imn.Et[~Mv<Ʀ WLGjRK*`|9 䉷2Vן5LkyQ9Y)0miCJ̸ザV]O\㖹Xё":&gStn&>a]%POuNf_9B\#qqpt'lͲ\<p+lL .{ +OH<ˌb.ue{ b c#lܸh=`6rԋ%kYZ94Rrs#8qp祭6먮0fN9\.$M᦬bŖrw\8x` Ge/`Fnx: ۃ!2?_WVgNi% _ % ~Яt/S#Au\٩g:pN7ilSNϺp*l.X'qĸϤ; ѾL6`\^+z(U&1mHXݒJ3viuӲ+hie0EWI`Yqlՠźږ፭iv0uR:A>x(j_'{V~R ^#p= .{-mmKWt}peX'ÑYPqz.vP? [ \ϖ^.7{6iBhǴwlՅ_+@:LZu"՘1]UWwwյ`^5Z0?g[12g6cg28fi*v΂@}mv2>f}ʰ~'Ńpx3ЊXvNwA[S.OXw8z=bPڮ}&+Lu pust̶h} j2l $=777*x\$st' OH:'^t+J_KW[ӯm|2_6Ijm 􅅩s/Ͷ8uK\7(xLRϜKp 샴/.^R:4Y9ovko@ҥMPRی[-?ꚸB=nB5v<.t[8*η6\6RLd՘8UwXUU'Y]@7ʋ }R8f|5jKVcmOT8|s2I=ED3l~BD3ޅaMi.g[,|\`>ZuS,f X]%\%PszH_'T[6- |ݧ K,,qf4%k pwEւ`QhJW[{k*$p^wX园-=FueʵQU{îϰmƹfg{}_+ҧ*FW鼴~pnp5XkᑵZՅoc0jժ)+UE` 5MwwW5w<@}y eǨՕmŏ-;nH2/>P5sj)GEͫjMɇ`^9b1o~u!qa$?aJ*txclaa5CpN2wjԗap FQoMcxJ Ep+uGݚ`>rqA}dyLY΂m 8PLהt Խ,yqQ˅FatΣv!mo:r2\,];%x)[)/Iű˜_|pfP΅aci.Z݃՛up([Ծ}5l.?6{O*BG3}\gOX P0OS1&Uc~#`ݪx{j72]4i &B3.^xn|vjX\ujoy .SB)a!I&a4a~ѱB=o믞%yidq}4;'o;4- әp0LRσ2zi8/j.ȓ[,H68Q}x$ə6a[?.FےO 4p,l6IwI[愧aank}f^ ?uWɒyf޺o;k[.NJ1x`g#PI]q-yqR8~6mb]~(>a6{@/jqjq5~ ,$)Oe;t};Zަ׾ ~^D9WQknj^rZg;#';mx߰3:We cޯ꺢*c=y?^>a:`܂j [ %lS(''~iwkV>Z0k{jdh~>T}{_f̸yկW^Au:

V~؇6ڥoȮqՋsxZj̦UctO9VGyC.3c843q,U'm4&Mv{Y՟˿MC`^n/;e=/u8u\wFYg_z}֔z(P2e˕aE7~ְp<\eߚ ~h|b0unƹxKڟ XuT7$6*m[\#эv U]ms^W| |ԓ6XnxZOѰ/(.2jPbM1xo[1>{sxo g9Oa.A!~ϯ}^XG GO]7 6#suW'jqMϝM"4`Wf0c39:sg ^o|?3o_y)[WBPO!zxVf'a>71RupuG EL|~RLtT﻽+eҞ77R+q[IZP{8,p!ho-s0 L7 @X֟,hYD36/!m_;qވJ\ۥ֯+LOõVpdga| ^ `om}ҫ.a;p2oPǽC8"å[m/ʡ`nxȭ^ 1szQk^h;6l_汤s{7sav}'Mu}Ws@nׯbRa{0a06ɧ[ϸ;ypU׺x~w5՘8^A?$g=/jʌ<.qWV=]?u4 p;dfUk~;lN57἟.{aLGC"inߌ;ި,\@ͺio_v$3nnWqj< Xf;qpsڧX*M-/O-pַ~;?\&@ \ۛ<-?۹+v^ > l7Cְ]t`_g_6 I[8ṃS}\s<˷؅sn~9l]=g5s?jnueδi3\.y~)y oׯڜدYo ءC&]<.r#ݻ gϫ&u}f-iļ1AΫƯ q+W]\YgF:L]`5(!;Ag*nNa78.@=sk竵,xp3pr9_SG~fI|42}o;:Q~ꊇOxkc[5͆U+K31Яeh鸯^|*3<ҩv uCDqKs,˰2 t=\? B9˹̽aepWYb~u,/OKyh?zφ/oZ]n#x8u}#az7=堪ԛx~jVZdݫWdt>X]C'pi>z`_1s5~_bӃpRiO?!-y߽~^o[s~~|zwH IFteӾ_ auOc79-\sIp_| (p n7S/tgy槫4mE]z2*#-ۻAdx `Z[`6]َ鼔q']K3,2 *ǯwkྡn:yM @P&j_oيll#mKnv:V Xx*<e Lσ˰)x1o?U8w_zO۹Zmmci6*5δ&M8b~9T];8[ɨN{OAtS),*vs y=kN5K$J@x+$g2ojň'L~asTO7~cRWz,  }/N0rqH߅68`aR`?33_"&IV >} x31x,vobY"Yc_&p7,vr폲O:H*}ٮiqCi[Sfǩ[pMS`ڙT;vӅKvuphh=S׾7;9?}Ώ©2IEdb5fƎ+M~8?jq`y`iσu.ffUc) 81f_Di Ղ# =4[QW(Ϟ]U/4oj05h.щMLsxug0xq99ۉ& @:Yha?p#h!8 A8C=OyqQ~1*o=^ R˕Z%:Fxĸ0Iڼ92p8]/ ?{ɠuO5)p%8O;_VtOM)Ҷm6+o9p | b~A;e>cV_ZeUSZﰍyյ=g䗕x L{ GůՃv[wdH&]Es捩j=1vfgnpe8iJ57VZq qciw>Qx} VPo׏[cI}ݩ >tvº|+b?k ڍ7(oa2+7/Z9po#ڍw[kMfԥp2tS˱6(v[h_ig]/ꏬ|tB[T=' <);tMKHHY.e*֥ԭ /QD|G߃[Bs/.8VY;:*,m^7mLy/_ߞ0tc1 fcTuY|#Fy-u;ݗV\sz#~r> xo^%@N,_lEkI&Kß<,7)΀`C{*oپzaũTHyIlmv^ "ӽ q8l9;Ӯ1m^>)΋خS[4NYAq"ݘ`:x։;h]8|2 Cc0 6m=~ovox#Xe.KY;-3ۘ>˚q LR5oku7|~l6G#E|5W5 &ͫ~>Su]v pe>w+VQ=cYQ9*'oM9V[yA_zw8aI>+ /mjf_q.Kˋ:m';7r7^V>p̔2;~:g4O'3 欴kC;]?G@O_^7VYxsp#Zb`׬Ŷ{PӪ:m -Q)\L[vcVO!0H)8&g;2z#M\k1k˺cBׂ-&m_:Z@!ǰ*򞪋{ HVn4O&9W]>cЛ0ܴ$S&Z\E-aRvKPrWn|"z30z){~Vc'ӥ~.1YkxXk_\izz8)lɲl.8F I_f { UUz3_ Sq2$dZ <&b F2|,pzF7|Fm8₶,ژt*o"K0N1#t ʰ389_zrYz?mӫ, ?p&ΛRmnKd8$W`AoXF\Ux_tTG]I-na#xѶŽIǮjMr2%Beb6gi7 ŚԻS!̆_|^?#} h6Iw<GR3HlWB;Z\ܺ{Y@} րٗ۶y;P#8gMtۂ^IYtZa{gnmw߮DyZzg8ؕqi [޼Mv!v=3qQ;h0w3mLTo}tPg џ-bk2Z|U#ܑƏ_iأ׍N 7u[׼ c;Zĺn 1ߦb`p8TU9||MEu{$D'J=SN%CNˁރx%d>, [3[a`߹Q{;LIst {Kۺ^]y^?t|nHQu|.uZFkrEtM. c0:t[oLegWSĶ)>HzRϘÕ\z{'nt|7m櫋}ɏ{x؜4)nMMYMZצN[gLa鯬۰5Bl9p&%`]x)D~ yHa>^ܠ7ɚ{/ {yq^C)$p:q}wJ7>S{ɭ#y2p1L˰y?6p2/!?K{{~c{~=NR䓰N\]U֭&MiA^73ڧI5o Ix5oџ['_{~^{r9p^捵IEzo6ET uXR1^ 6?? d6=7ai)Dcީ>C]XޤIƯmei̭p-"םBBlg 2ӝ`_p0dl߄߾,!xP$w8^ Y4G:ave;3t}7ǂoˉ[]]_1+Z>lKؽ]&u9|4zv9PEL.z쩭22"M:Nm@1ts #lEr|(5/מD'I; C΋'f#Xt}շ6nt&6ťnSuyѻI2?]״t{^vb>ɫeNv7m=6N3t)5p-Թo41Q=:yj_N̓Yu{SFcFIlVwWch2v7el]?\3@1]׾ CVR/%T4MN+]9 JS~G"m:V%gӾZΰGC}ӥk*׀ZwbY(cr&I/my|n#E7@j~5smys~oy*n}EW?a^.ᔡ8JVo'M V| |_GtHįoiELjE,Uv2T M#[J_AhګOV7njb_-o72-ްHi<W⬃O'O. _m$8\ܬ)S72>r3+ͯ`naݽui(Rsqq%spy#ۨy} {t>?8[eYFM9}‹ZCbkֿl_+]za\?zm#!aF(N(j0x0߄^4mc[ms֜ Z\i=Ѧ,LLҵ弈?6nqJ'`.Ӕ69Ai^^L|i'P> q9Nw&eڦMw(@IDATnI]Oo}Ƭs;zkh.*MM%sO2uKn^F|%d$NnLñ1?:W(͸2'V.N_-NW+F~ܩ1|e-tWF_@%b?VDBuLtJܞ?Oڄ4Y(p)M۾ǯ^5ӌ;7.{67OwAxKگfU!Ltyn vL&rk2Oy|޼j.L cl)TN_J u < Uo㔸߼JqK[R~\oW7 ʃ~篛+î-|7nb_t:2xU,w{CڊwS)isz*OA7EIy׶MyeX.Ƕ;gl] 7r q7ಜA kIWEt{ ^$,읍G7|!>:h;乤6\Ƹc#p>O\ b^y.ze}xi]Zvοe>^_?7mtZDܹ%񗙪ԙκEz.oWGbc'WݳpLV?74cʔ#[8] nY39_ȸc֝Wͭ Ow?E8pyHމ.Wk?7%=ߌk:\zNezG\C3Kr5x&x9h]\{Q/ /](кE}|DKt%\cͺf>nlF6¯ 閮CwnR_fg]j 7n` ^&B*(t'5slZWeJ_]l6f|3m죏[ԗ=o nRuΨy?N;鬗<nuĿ|䅠XZ>;ǸJ3gK{ W>dԻt;amE/uIr h|`c}6'KT$W1e2˰mZč]o]te'ؚǺv\?guOУM9 mO;&l0(Y0 ꫼ NژY:T!ll]mɋ׬XMx4T{}.Ze0.IΨK}'b!kOǨ'[v.J1_%nOg+j-ثǴ~:2fǩpӼW|tEQ.]]קv2&*ãIs>|3O^NfC.k}s`PwR0@(AtCm MuoOoKYk[o?HE ٥13ΟWwj 6깡7j%_Oo{ϱt4hF\ᩜIeApzT]S遛./>6ͤNC?оU=tIK|w>%dA5mƤme6J;]O~^))u I3JIpqF& E5ʹOW}KoYHٶeQ▙r&hhK:s>J94[68\2_OG~כg"{sr^hk iJ]^.z]1?J)*[}6y5ڼ;{`#+[wXͷk۹!~Dgv5_fT8l 6WV-C@`pO oO+I`F?`33y[ b㾶Ӂi8.}b7o1WZA{ ո_M#,MIqW͟];çP'lp/@<_E]IpŠbNl=Ef4c2uc⬧u+/ʹa6f<:ŗmCAp~xkþ ~ډyq$7VB}6g7vH}- aqV΄ k_ϜoyK9~ C0A樗S ʣp#~dƕwݸ̀JܞJt-p#A|4x[OIUD|.y7< ?!sz(鷞POԾq/POŝp|7+Ebjէo:_pݽq'n!@#+olFJq1x޽utng1לZ-jj¼ա+έyjlÉvYoef:b`>rw\'uS wbmF~h p-\mWқqM`~.B/oՠM6]!XD,#Rc5{+߃2 nOAp=y6M~'g?O-`{)ۼmRi8׸f9C`rk/m!(|lkJ]'ף>>L8Vye^-:{\Wσ`_ ۵®%J)̷kutqQ^˹_kӔz(i{wMIm_׿E{T(߇=2VyjJkU+wV.ԝw6%b8ZZwY0p) ]鳺vbKjgT[q*9WHN\0:KO'}b#=fh|.}mEx!l'p$d̤M>xԥN_;Ytg86'!6z֯4ӕƻ .f|.LIo-.Mp4?ҍ)7g>Ä@}-!% F z/aV+g;>Nv mZYߐO[Û>灮q ˫GIդϨ>̟=/m͙H;_N0qջſ  l_5w6>ª/#+1}>$Vp1$]#N$'@NgLU<\*N+ N`+YΪj't){KKo!$QM)( R V( Ht"ws9d ̬gv_3CXޜ_zn/H|z[bZz^kk4J|Dvӯ%Y}i=+AiVWJ|yCh3NPՑz cS%SJ?&vk5`bWOHl`py9}ܟ'P_ԕ2 A]_UrYϘZk;zwc#eqv˴&cq\?>E_]>b0= =K@f1WBdf1#@ȥG6~ q|383O;3#=~E;Ƌck +wYn o7x'51 qr?Y|j_ewɗ t:\ ρfñ&oԙ!Y&({ԑKqs$cpc`>ee~|d`nիIM1VU-L6[<4MK_P;>^p6J{~ /-/4ݮI?}WӲ|֜3V ~1?\xXWVRo{^;GzjVW0zWJ:饞: C o=X=G_u!cg> _1b]?cp7A4LV㉇9<(vOGrsï=hƒ|R_//ԃêޒ5^4 ?+O~iC%ax1l"lCl zƁ~1]x]ҧJaWO ܠWrppݚ1xPn>8ֻ ׺SMsKBm &Hִ4s|gv`W²nDlA?)2ƅ%e_Tj"aZInA`?ǥ>;_~@'^_ r}?s6x :SڥcM]-yJ[7a48oABG3gҰ?p Wm{y('1>Zv} 5<טe9|qn1;mЂTxOoWnw >>?^l<9AOdAd~>)c8Cc?\K?wƲb:T1fg?i?w{_x. dsMy!8]k3x{(ruQ/.4/A7QY<@a 'o]u) W) kOZtC)/3s+RS%O&OкLYoڔvalez]7W螇5Wۖpw֡?S7$?cK<qh3̍iߘ1s~Q"-AoQteMwB%9Vo ԶwoHc6~B J7weo ?l[/^iWȿ,F0/! R[fy?`z2-an]^P.GTpaj_\), .ELjuCx`]}2uxlO0y _Cs[pd\%nR֥z0?o/Ԗu(o 8dN؎w}ۏ)>_J=1)/uitnu{{< ˂gyf>,O[<(uw S.ƕOZvS>uꧡ8<8fӰ+tN [YXByOǓ'ԝ A+; {?񨟺WN[ƠMU*X q+6yQyy.: kŧw&Wݝ$vjQCA6ΛkH6V}~,x)bPg_ 47!/A8#o'ct?ؘG8TҼ l_Ao~Zu ]sZv뵾?q/}˩` _~X&L-z]Jw7R:]ezR.y =67OKhHL>~*<"Yw\O D j!< 6u<rO&j`Y%az˰W׍{||ˠx> ;9`~4mqXۇU\*>%/CeZ^SL[}0}:} ^{GԎx*{N pBE҇''17ןw-/qp幸̍AkiV;"};m&ze'nDjX/xGt3xo?r㷚>K'gvx إ'/_||y, 4<C 7#y[r5/5U3:޾EcoBo8z{>Zj/bz d?'5_;0얒+ֽWem>ڇWCpxYq,̈́Fʴa<z u^/vǮȗqꃴ2NۼwQX`}( }WOE|d 'L\\:5Г"|V6뒼#pfP;6`X+}̃mYO:M{KYc) c=խIbh'EԑxOaf쯗!9E.h󙧔cKв |Xx:i. /iY)1vxcfh& #`EY_h6hܔYH]Ts&1᧗`0ōC< w½"yR_ڮdiIZ>(1Xn+ei, l ΃yE 9l'mZh8;`g uԖ[Lef\JW~X^\#aGbOR£X~}(uc2/S3]{_4R߱ڷ0vlnߎ \?,ol[4,ٌ`ڪ%,mU>!."qf}~aoRo21}/=zD=ЇÍQǘ<-=f~l,,R"|.RR_7t Ӟjae׹My۵\Mh&NU-z}T6Pۜ0 VEAo[ WDێ|ܒzPڵnٳÇ %``?Mx i]flk}55Z%w~NWx׻46? ů"K}F7҉]F+}ޞVci5Vfv踯'y n97Lv.wUf~]ê۫ >ƿ \iدHǖ0}(C2?l!|zzj䶒N{²z/_ {[82>k ,e1\  sg_ҟ, x+U>~ xAgݔke&+ψC;X09>tS4lG- {9+h@vC/tu*CGR7~&LZa\ݽT텈n<Ӯe<1ƠmN(=\L\JfʡNi<V}C(.j|7N;m[.y;i r4r㫑^Vni~#rlՒoa+p~_]] P:*IF]t?=>B뷜 p( IcHn[06+I-1 G4|t}|XM\(A)WeKD\3Np.XwzM+iW{ #㽠ORoo˚1ٶڻbCq|UŰsQσZ?qxc,?]SJԖ bu8~0a;p۰ZIP&oin\av[\`e`xZe'Ap oQ8=Or%3qCm/}6ؾyp߹oܮ>̝t{͗m,5r|+@G߾㒶wZ'2'I+4p>cZ/ IϱW%SebB6|ρ_¬pi֓/G=tï,p-I…9T7~w3Bz6Iz 'i+i+=~s )!oʧ2;r .¥C?diǰԉIl+T̹X'uE"dR7E N8eƆz]_s8 $8 T0픡}H6ump .Vt6N!Z'+):?Q^>t )/}yݟ`\7w(0=m'鋔u؟G~-?8հ(pJvli ; &d~ WKLV²v2n,R7 …y~$g+/A=zPǓ$Otey{? $<\_/U' gA'i۱*.jvҟR7-vRR6м->j%o)Zƕk :| |ӟ:Ú0$jzŪ,J G?'e|ד+JB u%i^}Gb}~n^RnRS["[KtP m;ueHr7I=a w?iY鰦N:o3|/#m;YLd,T)>`샾O9G}u⤸ӗv2=]Sԝ_S%p{\Chzvf_]ɣx7ԅ+8nQ^ɼ/e1Qe #W`fio,|AI]M~{k@ƪMA]ԖΖ=V=nGػb~ <pGNvRkrMq$xy=e~-I[-C-~l1\`~kK+}asz煑|] <j%cmY;8|?gf1"(u~wol8\^nG 3#mat.$7.B-n'jx ѩp I`u70πR&n w( w< ^}NէM)`z@tu$Db_C>8A/Sdu32LaxHʔi}=i^{Jۼu2 )OZ֗a'r^^אcV2m}`8|뗙@yol{n Ru|)$xqYƳ} ,t?c|+<d:D{zY~>RƨKř'ψJ&.Xb$/ $M,3 ?` 0ݱ2NKB*I?ƞP'/A0t+e*46#cdQ+IF'1Ξq-PSԵMgMmςq ]n r8HpqJʣD ״*QƕUx nV^~c`/> Eqg38~ nM`4xXW)nrӖ%OEuXN1 x'{%#°1@)ɺmRf^}=<(阦H:}42? Ӿ2bO2 eV\ ׂy8| ɭ>4?3oN/A9ueS]U$?l C`iRxn j+< [z>ke{D+і4MUP-}^BzږuؗѰxny<goڦZ)'xjsB`K]?0^QP慯q ^S%.ܼEie M ou w1yG| 8F/U YxDҶ6=aMcZ_F'%l&ƞ>x(jC+yP[أ'nHisӑvk)u(c6V&F&?fY&c0e2xlAfC7k5GmkO[u=eP?˖*Dvc{e7aIp_o*w'q:Xe[vc/QeUoYN[]ʲ%/gڇ@q[quadkb/7 hEd cpx)$b,.ӇzQ- =l׋_IM{qap(}{x=7MWy'}n/]Jlj%/˪m]9o3}0n^۹K>s*7p|)Ӯ}[,w7)b½pT.oŸwe>?3?wEZu;)9 SlS)m~<faf~hԵEJ'[ʕ>N(8l}KhwxXG{{5ѰFp9ű+%3^bX&R^[+M)ZlI8|zp8SqS[_.*m.|7 p 0.n \_6:@8YG4"i3zm+kme KݼŮ-}KZ=ozRP,E]q{ >Cfcpؖv)%k>y u9O,%au8?}ԋZ6F ROZi+> \m&jjtLcW` $P+btSד vF=y =_MQǁy=X ?Xp?feSWʱwY,ӣÔ} kzOĭMlFS?, [6)ڦρc8\(1Ⅶ_ݗ`ݟ˜f~B4} | ΁ g@@R7[bH?aHfAj]-<;`%/d )L];uh=6ھʆ}v#LY\ j%=K z~lz'naiX΃' x;6O۷a]b+DÓWl΋YU<dcrEOkw$?/a) :.b"Y^j̍iI=JƑrz<\0a7دSOeCzlB`?2~:`qvnaD7b>j%NY?sҭoo0{9 -R]_>|T(I'Ԯ$^&=K Pe#^'g'$qLvy6|g`6P,s#`5/J׈zHR$0so6$aҮ?]xlsi3aplq/ʿ!e+q hz\0zy@r̷!iVM=!ʺ$x'ݴzb3UdLϋq׋k/'sBLsе#UǤ4s')ׯ 73y }/ھy?P@=NU`=8l3aQ1D+IzyeY-yS&{ѽOaaPs+eQrӦxp5.1'6_nv/:z ӻ~/7Dkozp/`{xJBKp*|?y)egڮ̀p&>ډm m&z@l~&EAj[ π%.}˽+wUgI)iBp7=z]oxFӲ 7/|^GR/|ցR,7 @{ֱطgz=6'?U?J 6c[A Q': ux Hgdq#x.կU ϲ]`I:NuO=8n`Ec ůJw{:8Yp Gc#;=Ҳ|/x@mg;es)auOi{/RvSU5ckƖK1 Kt]}jOkh?p}/J2 5FO6J^VanXSr⾺懕< .jI|kmEOhoiI7M- #Vt|{pN_zuΒŨ!&\<4& .VO[(ä[6GO<_ W ŭ\[x0- K6 cOHR7W<fH[]lOko(`gbr2ͣX\~'1p}}I;>G:p&\Y;W;q]fʺ32in3tJۺp0'k=8G:0 H'ʅ6X ŋ훠׀|):AfIFAMz˰n/7cs#z*?g%𓳛s$خp, CvTø+zf#Cۺv9 bc]Ե6ǃiڦ$q6ScVmk788\ Ax> ^JrYW W-wN%Sa7ђ~߂mXZI3#`fCm1 H'dquJw@!̡>p'r7s%pSj[_@c{n /Q3@= Jod T7//8N5|Z}uWf60x ҿJ+ ?Xu[ -P[z/m p}&:Qb;B#ɺJUS_\u[⦫-ہ}(874C/בsA*-龰~j0׺؞g\Hp>|5/pk֮ضbh2%ݰ=o Ŷf'jKl.+*X~s~s oh7}b"dPp? mx pqV lo&;I|1Gru[8bK2B׍kȟQn}`}>NA“aiX~ >_w*؎HVآ'LRr5nԵ/IzhH'n[L\Λ2>]i|܌k EO>E==2oiOYC+3r#^ WkF:36uk+{3`SX/Dze^z>%x 3`X,ӓKVr{eY ϾI^C:8K=ǻ-lp*At@\YY艻`σ u_!eP+4Uy̗cxNW[]<<쫒2;aчu=@ iSn>f{}f` nUi]৚W 7j~~[|fa|}FI=YN[;1O$o0^ϗ<} o})&oviimʘe\]J&=*yJ{^׸b5-П^;{׽6oеp}/sb쾙(qϋ{ZbOJOw_,i0Ѱ1OلֆC9'bSK `3tJl׽7q3Un†OmcKW^9@x /evyBvP!``QJ6xt7bq:x9z=)׉/V~\D ]u%"| /6E G8d`e= DIۆ?*^mA?~%Xe_e?//P.׫6]"^/vZ)Qb?g2IGmI>[Vf}Svm/lV7G(^Uu@&NƀL܄JY~#@.|uIhztC0vKO,m7pC- 兑|+1ns煭!/_7imp \JѿՐǀ_qz(IOp< :SyYkOS3 yݹꃉbͅ},2.WwDo&_Ӂ{a0c&ڒ/c7|$ƣa~=ee Ӷy"r~6`}cµk ݽ Xf? Z X_m'}).8+lJgu˹Nl+u^B[<} b hwOE _|Y|؞>w~qd ѰBצ+a0\[$.P[C[IJsƏpzYfv c w`1)OHӀLt1+_w\YL:VME_ڣ1MK =p^16S%ƭ xݸ "x `ԝSKҞiD2-z?|<\ej+ {oΰx? n=$kݯ0l7y Nq?oK2z{OvOʸr}ȝ]² 3*.~7pP_ŵ ӝCz3! `>), \='lTi] ݾ seo%|>z @6Iuw{a<g:.x)L+cӮ^gv<`fb2Jl~%p/ئ -v]Ҿy[b)M#V)7<oaxF8mlw:O mQ/SG$$z<}Hq%ǦWRol_KmGbX\ƀ߲6τ_u$(ι%R[=4c櫗~ۼ}Rj;k]7fv 96p$bub^۱\@I@}&/P7gMxlnTm˃߼圸سPҳa,]Lp5X(=DSnR'[bސ[KNkʴSķ +:A(PiVO0yzgƠВE@yoNcR,_Q~Lp-C7G5?M\#`a!,K{XybO~v6vyS]h9?zzia81qÉQڢKX0;Dt#l ~_n){CJUBGZRl'6˨7~8+Svw=l炬2,m)ݸ}T2u{Wq˼)д_m9MZBLݤ;L}H,^Gj~%lwa dl{ћS`YFJI;mRtS:APqUHm2z= KAzƐuex.:7eҟȂ:IlC/Y`fH0/)7i;e<:HSh}r]? .>}bt*2q/篁]/{4xl_Q: bэz>*:7I a꽌'+-bEOa|x GA?67!lI-k^[FѦ8']XAڐ kL1΁(SP#S(z08< yAw%4A[ː*]vccvI𱦞|D[e=I*jz07|iIUE1/*7l]| /ۉ^)ڭ?+ bz1^P2٨ 1UR>a>u<՝Mp/B+h_$>|Ÿ́J;6iVƣaiht?u:HʡVRn2|^JuSUڵ%{3ˇބD(D{g(a> 6e,xҗя}Q'I'wn`6ܳqNŰ')]{"XpNȤz`ٓS%A" =L O(]!& ڢH[zcOXqڧK)p OzHһ"髕G/Kn50OPsc+!>gAI2N]~%jKRW%!GOZ:]KF^޼)Siݰ.^r O {}xwmJckLSǰб;خmcn>n >FA5qX6zPH1 ͣ$sH>ۀ9,qL7}?~H?z ԏUNU>uyi>t)u.%rpw#Gz!e-͑8IĞKǓfY۰_GA' {`w>4Cie*p+<t:0@ [\VneӓݴLn^걣v2iu{BE7z=}!Kqx!soXGee_;|-8x0nC 8~ %^)H~J&4v[0}Hzlg?bsп#$4O0c=PP^@ z E'CCY|;Yܠ^&N^@n^/Nv>$ny)%_OlO9쏇ȷzN} =O3ϡθs/6O[VdrNS??d<2!?7LYLrɗ7z؛<)c;p([w3ebofM^BKG*0ֆV0z3<̙ 1lQ&|]ǎˋu&.V}YF#6/av׃yhK?wxyMFT\檌Y/Bg`=P9w7 Zw#(X[.~\c[} X"q趕xl)mzSc\ٚ=(e]]̜%L3v8~_Fp8<'=igS%e۱ d[v>Ca0Wp*~D+hʹnlk  ZkZ}@Cd&*<ͪݜ7ßvgCo_˞l2붍2?A)x.@Soq*CFʾ/y =*m ߂aMrXΌ蓿/a3u=- ^}H}.˶Q|eMtDŽNm]&%W$^9d%{,nZ}^+!b pdx g'ƽl΀Syrl‚.m$i ?U8ු3 Αr,LdN&i-z7g6ɋ0~9| ևEc%nzx9Ҵ~1uji:`$xpW?F;$WbH?vCw3xp&p;hS΃a^xEmI;/p68ڃ,dF m+y>J e"O:֫xY5u`ia&!tsx= [m;̮wKҎ)7~n'@ e]}/,_%J,k=87Up0, kL@6iZ4[¤qf߂_5^2nvKN)7v]Ob4sx8 Ӛ1i<Ƃ%0=\S[;I]W wB2gD{W E=秦=`IM1~h?zʾ*C_zho 'N{-]ʘ?K)czA8z;O/uC};l8V/sE{o Dc+";bJ®X?׋ͬ\響758wk94p>z'5L%O?;CoR`A/Ojtj+'c@&\,f~ln:7dzPƉ6pJ< =,d$~մ^=}m+jXC -p8@_?^>p|(hS0 E9OKOs6i;K]q˼nvzki( nA_:/eD+fٕa 8Zp% ]Ӈ p L >xO{t)pPu7c-R7kkmX\CefG{JO?_ySI0 S\n\yy ^pp<% Ճm982 _}$ML'|G&(><\Z_]L_e<2ToǴ=do(^I_C]~f ^Fy%اN|NQ6o.=^ԖfK"0vA/P↓K:r] jXz;Vw5ZU2i7aW8Ɵk8#!r%ծxBÀL 8~28d) yg%ٞ KwP sBljL2ֱ-7Gxrv3|=їв񭡗12)WIҬCY_W/pd9gGovAy G;'D{ҢZwBv{um[{/e'/w80 ~ }l=Ώ6J=s|pk!۶_):$}'+U/} $ m>1YK<>xH&>Nfd$$R9o~Wj7vzlQ+ݵ{_C˺:ԕ.KE´E<Q <.myN 1l m76?m^wC⻾m?}h?*W~;]cބ~m$ R:rvYx9_5N?{&Oq77. x ,C(n|/yCP1'JtX}?LCv UJC$i-؆7%D_~ 7`{wTI%`YOUK׏--Y*U/{m\>DDJA^X9iׇ@[IRq,|]/ }jfKPJYy1 蓾1SaE׮^vbKڋr{u3C{x֓eS>%/jJ1ne憥;pS|6aC8yKLS}闂7bֵC7%tvb=03 y )_[]?Ep=:o|XE(eic恄wߝM=QgS$=i-n.vpSK/1M¯er ͚SڴO_KOH'!-cN.l ɋ";d-)'-(8ֹS5֭VW>kê`G >gaX̣<^>}\ o~8!i|#~^F8/g_!jlǴgVpu.)FÜ'.p"gOFҿӏޏUTGx 9S<Nj)[ ݈Υ_~ie>6CӁjQp{8^}P/7oM~(]-,؎R>x ʄ>d Ut 3g~uWoxw¹X^ɒp N^xb/v]W곂:| CSa-g}BmIi+uՖ9kj*oqiOԯ7js ٟmb(}axly p("E{. ;`DS7p:8JOcKnv> .75P},0X|# a K?$,aӦmNJy/BYJ,W4EUxCL-Ѧ$(tw0}?'dϷ1:Gn R7 NK7n̛v e("汽R[0 g9cl sw+G[픾ik3Kx x(cļ~,S ˇLUe.rmM>o76 ؞2Ҡ87dJ@_Δ<}pr 'v,ل2w2|ۆ ,@7y/* ԉg}ޔ!I^h7ZKSoGvJ7~m$2'u9G'fr||<}y#ɸź{`_?cxqhK}I^#㊏GA˚>޼\?Q9t9Qv7gtGwl3~|k/$N۷{9r%)AĴ59)fE@L1bΊ*DӪHr̐&}s5Tuzy{r>Vuy&"7;`9F+O_ơ21ơ~pQZ #:I"> \&ÁB0ͽs$Q;Nk7W(Sxq 4RĶ3Rjp?(GE:Jt?҃O{@'Lpv>^m.Kp*xiI-δ7nڢ2׃}cxx3YCI~~Yꉟgmqmt҆vm7İ]^a'^:jc@{}XmM0@q:Re#y߼,zAˊYqeFG8\p̀M@x>8utKno-y8=<,uXy aGJcA4y'l8 [O3 > [KáKO~ċF.,[ Bs۰k?-~NJ7:t׉/a큗޴uDc-vuvM}wp=< kŴ}p-؏{@oǂoZa8orY@]b~_V(֪S1P8Oo'k?V>L٣M4ʲ6}wخOb7sa pDM7Ax(" gΕG],1]7VneNx= , (n"7Ȯah^`s\ 7gG")CԍoZ7/b>č_pr]%~iWbi0 h n:1]y^DHCOiS;u`Z0n_ _[C}%>^XHkKcKWO|0= x}gF+|66mxiGaэm#eD7o^v׫c8/GJ4|) u|2=$ˊ#-ͳadoF.ֈy (qKWg<Į_IܖoOz^c47ߍ(;k7]jiOOן aPʾp<օCp D6E GBt+ohy O%eFJ=6(ɻ[)f|Js. [ a"ZT~Nl뜳?ˌ8/'#F7`\:_SbKYNOlzˊYSox< P)xxm>A*Yq[֖xuc2Mӎč_c<$uMeJ\"\ anr͍4ApG~߆.nֽp,7j/1dx1ڝAz OƳԡU|vۙ>*f%=Ge`*V([}[NxDl'hvB־}I=%y|9e!Zw8F?(z rQQC,ɢpC <N_;wPB~=fs#xF'^6u!tnY>)AmS/4 _˿<@3OtG±:؆Áqw8 .gCb[SJH[![^P΂`Omm/!嚏90}*]xǎڑ-Mq#?Ͼ 0My(.$?ϡe˦2,,)SB)\Ydtso> Ʈ'p OnJiL}t7[:*.LIa-;oR[!fVuhIw^wP{.7&l-mt EY'&Ƴ>,z.MC6۸Y!нkoGLme$r$_ڇM4ɣ6>ofQ*y:O:by2|z8@&8W_|E1܂KltqMY$=!|\nGQFK6u"}c?'^*~>dS<H<] &̱q|3D|eǼ#s=w9L> > >6ELdf{c8,nEf}r7[!79v!M7fY%ʐ60XnjZc*3;8v5@q#G*07xCW`?"e:mM/aMK ܔ;"[u\ [~8v/]p8c7Ndk}@yҿuUEqlneqy .huo>Ά,pp /`:tcmlDU_Re~N{ON<Ԏ4L- $m\LMW)Q1_tջM=_-s8 ~$X,X z{HYq0l>1 SL/ ;@4}ZeҸSnzlq-Q`>֥1v l8K$F/ӨG2&q0엗m_jX ~BP;PK5pA b@0ʚeA(]]s4ezoo3}s~}X  :6뙲s9[S72,2ő6״J) S-Ik+7SP62q⏮XAڒ6X8-PJk`<4-kc0}>l//鵽 ./o SGҾ Я4-B A< f`{O%hHXWW4-Lf^e襫ޔ0;o׳Γ}!w*RJ_62m3p3ˊ(\'F72χߺM""= BV^-XR^tt$$ϵ&dPSa+C7q ΍˓'7}:$/d!kگ'9G5WOL0pXnxvTeR˩#p ( [|NȷyoFzĦ_=]98OЍo?{I[2cSMSƋ-mnw+MpXa;P;W.'0_ez JiyѦI&ª@IDATz`q6eTd?az R.78717Hq!o_ԳWEwr/pp;§ UR?㧞CWg$4nX@bҥ UF?!}p,:Gs|K|c/TGpxm %`^G'7n]\lKg@1|GCpLi?8m6}>u~EQ2&Ӊy8]~u587O{ uXpDioTچaKWk;9~"GM?\-\^|^,#R82=Dˊ[Mv Wzv' [<,E1Mpyfٸy_KO097^b:tc 7֍LI赱n=utiξ fu!JȪ`2v| W܆nσ=1}'xX{'o-GC;zyrut^xˍ X<#'NT<nc~ۆSKYnlkۀc^Bd=ͯ N .J|NnyZ^mX\@ 7oqxr$iC)#x1L1.({\ EQ.^V)Y!wC&Y\u,TP0biQx:n'i+klSҡv՛'bMp/D3p`#M`П`9_4//nJ[֣gRؓ2~ij>?~ b<"}>-mBb<߃ jkl-M%-ã[R7\k:Wۧjw>\^\ڔ ao7Cof; /j7;:|C67m(-|h}ߘ@Ƥ~KYqH݅MM ގ_]q>|sSFpּa9n6jMM|ce)ԝ{t3O@_3p~V3N^xz7I~ּ݃7W7PÕ^ط-8>qxG1l曱@픯v5qKvX?<GEѵ=g}˂Hn:6 ]Ÿd^iep_V(㲨 (p:?^=.wQ$^ \3!U3AdvuI204xo@1lk8~^}xn `_{|rMbݸ[#Fl_o8eRol0WUX_/8^h[`x-q?W{/?Ò]wq ֍I媗n"j8ڗh."{ڱij0\Pʍ!z+dO7c˸Ocr ' eyi~7>HRIi[ǃi>9y=~( !b߿ց' 8CWed7J]D o/9МaICl egjy"7kYB R#^~Ű+xZv%z}@[~kh%im ΛnzꕐqA%83ŴƑYeu0ܼpoh.qƿuKit7;+qv_ SM4^k%;'ݧz!ý&$xH{rhLV$u-)y^%ZIY77@, x `|C2N10 n^ h% uź!`]Hz*ڣ׆7?Y΅}zl 87e6|oX  8x;ҬciWHzwn2mSʰ˸q n 3p ڝo9g;ukx8cr= O4phGHlr!8@&8/,T/*šJt7B_*<. ^Pki5fњˇFpsd)iK7:Į܄5_zW/ݴ ?@@KDa]8KC[Ƶ.a#R]0>7䛺(P˥d`Yڌ#XEm{s˱\> '|2sg v>{ bXzQp7A]%cyi m2NVS`)u=p`[_ p"~RAxĮn].-~˵M?trFʣC2~/.] EaF]8M %qcIvKI6s߭qʴn>2:dD[[bKus/a4nd>YLHn J˼,=<|||nWBDoih/l[ۮqʭd}ai3ZF>5˦~ .*x6xI(Xb\/TI3:\ >͚'jG~ѼLc- O`ߘ_}9o1wYk#zq,^uh_l}per*zOjkX<|[^J(Z1Z?Wsp8̀AN¦`]R_硯3 nXt]Mzsa{ wn{>Lt >|#D l ÷{:zfeEq> pGߋ{cSE<4tKJ['}_{{?v/2^p|i`_hMJܖ- (Lj,XlcٖtͰ {cعKuŲ8 t]ٯS-MùYlMM*D>a[$ay<7MmM)u-K%CD|v2oO6J n`ڕnVHcdl16p\fp)9xx~l 'x _mgYKmn96 7߈xlq4ħkUuv>e;IޗlxU~Vdat[h?OG=BoqoM7hWx\ʺp<ܸWai}0J8%)[-K@מ:vfFgڷp x': 'MÏ$'vEup5xi~)cj_mvH}PkI/ Wr-ǢE{Yp霄b/i;bErb-ix4]oˉKRs? n+(p3Ɖii{xJnS~7{7pd36kC!`G]7DKgx)ۧֆ0~71JtݵPX;ҵgCڱYɭkWֿrO@uw\ x&r!?|^!x=N0˲ ͱЦmKf}ց3E:%.0uak8Vw~c~e襫?iͯo/#$~ȥuHy)CY_*Ѳ.wrN!Y CC)ãmDx pz]zk\eJ{]nJ/K9F%Lpsm SGRn 7}`܍--ur6V^727םe3u-ݦݾ DGq,KX&ѧΟJq̆+qʳOz{vz{`nr<+wؗχÚF!Eہp.\/}<O/֧%n,6x\+b[}KVHqo=aMNRƉCu~bum:ܗH}BFEO-uS n,pњpF:g\`̣2=:M Up5π 莯s?Q;zl\8}\ +y`# H#ڂ#p#\8oki3\w,!^iWXR;o sW[JywSǨqQlA#z/w'Mkpxf(I~1ip!KQ&rp3ͣ G+Fep=|􃛾x~.Ӌ&魷-mno/gNPGJ{tv泬ă|q˼R7m}l/ p}>Dq䑃{,Lŵ8pN _Mnik|] zYπwAچ)+嚟`zlq눣ڿL{9\ 7띺G)譐l__cd,"d#K9eu* nRU VK98F;\5@u?MY1\">>xo}RsΦn|7 %UYqԟo'uiK`>na`]zFJ=ڿfm_᡿8O"_Eb`r#;ys8vRtLG> ηMhڴ)%Ww(0O /ۯ?㷾ڞ`|Û[Q|,DZhqjI~zϥM{ӯ/ˡ '|9fG5uQ KA? 㛅2}S7Ml^fm.p567׃񓦩i"?sS%'n|}d0ml) ӐmXI}اyX/Q;zt'D *~mSǦ_=T<|}["/O<_Z0 ez:;nf8NA{u%|<=g|5z(}y 8wzҧǥ%lIRtMag8/Gt!yv.m#Ǎq8 "XRF;\b S4 ] 7m6-/O=gaf7M%vu7e1raGE)K'隮isL7N7w_7ٯ?:TǴXb%SӉ/? këKDX<8}*| 7Hb/m ˼Mb///ڨW&H^IiiY?ǬcV@?=hrq $67HNl?A T-u:aO}ݰc~'R5M75m'  2!׿Tk8|Ra`/^hx J܌$ĺ+v/)ǂvU}a;GʺE7{k+9ƛ w6|˾<*1}gڲf=,q0qOV|p m!P?Z_-S͟.a#noߊz{Twlj-W teXE>./n:08<4똃,I+QM7`~5js|@,̩m\7 \ uZb6w06$o鷞瓢[q|jqO7m5%b=׆a=׀z1|rӧڔԳG ~߇'H&xt.jSW?pvz@[F8w^zz?/u^ax|>΁+-UJ.g8Nl7ضK_YnmG%u*׫6eGSkLU(q%ŴAy6|lKNe;|yXFc.eSXlM==iK~x'mOCƾ[I5puռ9IS߸eR@{\8|g^o:m[mҪSV6yr߂jWfn,z_ j`A`λܦ̀18E 3< i^yӷUtx)<[A\^t7F4r%P;zӖ0]p|/Q~@l#`xg ƳO,S)f%L,A񚳺=s^z^0T7UqMqFQFGed}E3fմ[?/ݿzI{qo}?Bd[cr gwM&q}Ă{_5՜gW?ܶںjQᵚ-=n^FoҸi7p(l Et_͞GՐoSj] ġj[{qQwxJ7iy]nol}㦾q˺:I]Ҿ>Ŷf%7p2l9΂HMX7,pn-y9Q;n:=y0ӣnSX95p+kp6%mO]ejwVmcT20qp~7W|v5&/&3a9X̰I ooZoV?wjEkWd5LW1w9V꺭iٴKb@gjkWÁճ&۪8ɅdM|82`eT q".y7Ωygx{jաC7i ~2<ܞ {\ypSS^0IՙJJ_ ww!eK?Ħ[>.#QK¼}^2W?} i{eU[^q[1Ch n:i1Tk4ƽv`~"-غck9?Rv᭐vs:|k#Py/֥,W/.zJ3SW? #d[x^op+)sϤa+ {dݸ-zWWY 3kK}_} q5xEՑ*3ژ[UMդoYҮ_y¬/ ?ìW-|!Mrڼ.no;^ۇ)s&u%^/2,wSrZwvWs+0'u5iY h繣,ܛ:! 7IӧztU6JQX0n lunuJ}S˸$H;TR^$tŏ^ ~瀗8 _ҾKPW)ˊ^Mص5%unwm7<^|2 p}8W~ur(kB>|.kRObI'd4{ݪ]XNA}s5h]}$\Z@~-zJb-0VsvɯKvYMeb_+׿V/,أ|[_m4uJC̞|Ur[5˫-"G]=3&$WfKj5v-٠\m/"2Rk~jdGRnZgie'Nwdusp^"57^ۭI|Kw4z+ug◶薩zoŰ)<>cK]qwhW[c@b~A)[8xMq#If9M{ꧽ+}Mٺ<??@Y!✵^ / Ι౰xi,pkr_Jٖe^a[M}ku6U/]iJu|Nj+.cEZoze|fU񉝪׬Z o '?~X!;{>] _ }tѸhVbOY0՝S[o^zٹ6ŤV݄S{qgB673u@tôd^t| ^^Ҭwt[pۭh]F -p\)t˺E'j'r[%9%iW\5۫-uM)ӵ,{<lW9ࡧ~^6SpriR2'֭12̖\P ˡw1ϿZWkrmώTS0:iʂ |9îi~ Kjb#o_ؖoM۷ί>Y+WYZ/M@)ԝ+n2fL8 gUvs#3m/E$tȬ%kUpXRrԍo o 7?[Jh3?Ѧ寮*ؖ(S!H?|;/ؿ_usŞ(m 3?% a~+9+؇{姯GW3]Sn5oCX\´N|\+w/}s˲?Oݓ/}O|l9eWSO~[y WS-DE^^;3깱7' *5M`i{hyk/967f7-OV~ E\R[/wVJYSmIˑ`'n^TKK)4S utKyy)Sr`h_t˫l菭㚠kƺ:MIڣy? GI],ߺ nΝo}4q1(e:\cۚʹϛZv-{d4_Kar.ܝrZ|'9.E6ÂG7u^fˋef>e]7j/,^nMM=eG:&c=}z$XioD'vf<}&z (v}Њ|SZn6Dʸѓ_?;gª b9_I*uԯ.>^ :,cr=Ï>kĪMvvXc 75&?f;28\jhTvNٸo""D!uxoML6$zR`S\>Pp _An n>70xP7|9dX .6]*J97}.xPx[d\ޔK{tRM,/"RKgDZ\7hy{mD%/ 71ޭCE=u*uÌs$<y%.iw$z p.M])uY`CO`͹7RI2D%i+):zq=?;IV>.ǁY/$&V|Ǫ~6,ҹu|i7\`}Y!to9z̔ooe?jLҚwAں·U 87l8 |%!k:e։FQ5O\ ۂدp%jNtjS=a.YoAEqx(eC+daX3^)sYr7ڭ /9S_OyxH3]qp?:i)tuk&8玃p/Ki˰ n3ҟt_{RM~.Km,~/vj84fx\M4 Lju &vp5w6=op7 rƻfh(?=y_^'3!ObI!?uFe*f#㷯$q%tX^SJ[6VIޥ K ZO^ڎXҫ#aq\?n4Dlþ3 ƠOE̻-6f^ o['}/b?sj0|: *C?jp*&r177_U[AkUX1vf50~^1 ?[/ُ͕Ie=wggCӪfQ9 \rܸoOJ-,4zZ7 7m9`38O2,zFq6LgV;k}fxhcZc`{+.A4ZG2-ҍ8=4Df$>?q^>:i;)H^M[7$^!p֟{1V۴6XG@`RҮne4Jzf{p\J{R 5Ww8rњMp|/kwe"4a2b+2*?%Wy_Y0d\jd28X -k'r_m5psr\6 x|ւMl(㶬 48\5|%xE|kZ[a^7>7jI b˽/z\v-/Ṝ/bގOOf%UnE[^brJWG/ꥤ`|l ȷ8ο䛴Y;ږT|y4Mk^=m'n^}uu> ǬVC0.K2+7I̕yM:|\+2 X^ut1(6]Ca0Ķ%=ԧ[y9NJlMg!mc^R;_aPoo4L$uLeX›6SַnHWV?NzſVRtmD m﹭11oaMZ}x{Z/b?qa&җQOz:9M7F|z|{w&TN@ˍǫR +z>5[VHPgCf/+#p:n%o%2䧛96 x 358q1 +wJ˞6ǖ43vzwyhŸ^,|Kbn`^OG|rzg'R-=n\71m7A1Poo[Z+4dҗ{ͥ/vAv?v?AL[?.ko\>u]8o|znznÕy|އTg^bhcoe{iAƶivG+7낒RGǧi+ _^즷bCi[OxCo֣쫅ib95tgym;M*I9Vy9`1F4lj˻*8_ fU-F6+DTc6loW)Mz$1yIb&B3|Cl;1ZS =uLYHohwj%d} @w"/VSٟKe=sV_6i^58-WEbN gՅ;F+B N辌ӿn?F7t+lV7T @>H3<$c!nBo[sAZHSF}gYa~:^_c-z+ߞuLĚ4_!Yl,46/@0|tB󨏻keUWX'XX4g+U&ֶISa^{HMF_[=y__^D_u;؊u̩Y}j';olaG3 7SAG=Tσ#P4DRҍn·7/)%YNqHtI6,nIº &`/Faj:RC$,n[5x9`ew.U2W5Ί&czonI̞Rv=Iuɡ3̭YnU2 YoJM+HFatE6bsZUS2Dj9蚺mirxُ a%j>{pk0_ainyZ,XFҡv{lM OXlM1N(J[ҵ_|,Oۿxnؾ#$/kkpL=ۃu3NVƛpz>i2V3?ŽU/g_9\\ Y|nv8bjclo_?mUv{;Q&:JQOJ5 ^k`Fңv5g{H2#)_t]Mi/pR]+nr˼jmC>Nu?23殒|Xab 〙^>OP/# gj4āUW|?0PhxǥB+r̥]V<KžŸ 2uaZwEnA{ҍ{ ½l-vǘ-|G##3U=,sDO<݄-úkήJ[C$ 2fQ8*lszm?짶Z؀ڠ`~(Bg 2e$̐!T{;Ul|SFYʫI3J^+кtu2r]VGOIyа__rf'_bUon*b08W=FzJTQ|UМ QiICi8:@˅RR?7J^OS3: J}DH\^~_zV*U\h}#mL~6*ִ `YQMuN@'Uڦ cKoX6 ^ܸ0|B;g~!i=WYZϪ:h2?v+"(P y[B t$rE+9JZɟ>k5P|P}P^Rd/4"?}RWeW1V~Za. QO@ړ4=״GZѡ_> B Jˢkxs4 5!9y.^Thʢ_ލL-e(atz[12ոdž1@ˤ 5oܟvu_~,E) x+ax+دSR@FRo/LeVwΊ' N^ =L׫ O1q@$k)t>Rz/e o -R/2t+s@vʤr+VGǥiEם`Q"d0 I:T!|Hh@93fW#wFҫb1F^Wb^ny;x?\2Uf\ KHjV-% ݻnFŀZZɢNstRiCeZ:\?esEߏJq'~HmܟBtH딯RnO0h=z:~O0֏庤ϥ<]?'We&)@CkM@ipH{ip9Lg A AV`MO@vXL`~ T4+c;Ō'#+Z0q e|r8la?ZX =l*}r^O%zZeCP͢~>ԥ\~?uJtUn0Pe&[c->&H**3@LܼEgOSn)4Iο\ :+jWC:λ(@yx"Dϻs?v9_cue 3Ity> %R40 >%2ЦU@F⹤U˭1x;>4Vli?Iې&k);)+&AHw)2o:+#) KmGZ9n0\5w ^z<Yj_12b'}sJBFJ1|׬^w%Vzv(Jۑ(z4kvu RpPN+At庤t:&8!Pzkq^Ys.Ƴ tIr _&ew> PڴÚS=6ϸs-9u3m?@r>vL鶇uLLβշ$n#9Yf$w6 zc9?27?(Qjrp9[dFIӮ褘LԆd& \_|Η)T0<N@"@Im>_GC4ŭ<@ C|%pBS:[pPF%?:t#8$+}6[K23|i2[) b`Bgg B\;6g?2Aqp@ hx0Q=WL3vw߫gLC!]g ]Wܟv#R> Hۀ>PSnz:j#{zg|P5zrJ?+uZ çRܥK~׻4>4k;*5 Ȱ*8}m iưkGqדJO-@*ϯ&4(V,=]TE7@?_=bc!w),o{텩\pe_.$QXVꪗ1N  k-ʢۓtK]Hǀ @ *Ư㐃d2R>FU {4cxK@{Lfk'OΎRC8XϤεS8#,$Xsm0h!=*O!``|_y5]LEx沆oQ3*Chw׬0%g-Oc?z>I+kZ|{ Ӯiʒw=z z.ǺNJP=;\ pQ=sM/ޱ9H!OW fpHpϯ#wW8(x| DzKNi<=c"/ihj-O6m?F=arǤ6QaLΪ6kvӆiL]0u醭'A2graXHe2e6I;MV3%Rqjs"&#jJDuoiuOnpFXϣív(\,s%)a7H]Q7JI/@&J6/t+s@:u5ckϟ>8~^z_Ja0hT›]_=2vEmOǏ= XiNGp!mX4qm~;f=eˎhA $?)ݬRgV|na {Ch?ظKX=p$ؖYg*m͡|<~E󼗟R.YңQr+\Vj,$H3(|mqtH]+kvW=Z=R`''/Ts:q $ŕRL8/vƯ8[R: F0p2*G[ΫL?VƗ?`L֦f>uOԏ\Cڦ"mT %gO*z*uMt֣y*7hyDU΃MwrՖ8ҾHxM&M i'AY̚\ڍ5Ksr*%yԨj1Ces6csWyg> | $#7}߀)HQ*MK7'hQ;+ܿD9WiX W.TPP)2ψXiδvL4_T:&S&kS2#`tT9%-F-^KE\Cnگc+ _Y@=9xi~ǂSW 뉦9n2< o=Rf O$ewu[hEF11PM!ȀW^2ꈁW93]ZkD5X hGDxyH%;0 T1kp+tOQ˘//vp8w#x[4kʠb!P_Fdxx0[X @J ]c@ ``&s4YQ5B153NJSXXx/HYn:'*)0W~_ro8=]?2(iW~ hQ~W)l/?}$}ORRtd 31#F0 @N?/H/K[EKJz<)ea:~ PJ+\Bp@y t^yx>oX ״h y 1 !`Cv G\ݟv05g$u7?ﮇR℆~r >qXNR*r|TA8p6+ցS3@OamMW` 8H8H#z0Rd.O RӁI4ӖR=xq ,~e@zd G~Ki hB .pK:  7z0zH"i~)` @)ہI+׿|hd Jq*gr5p Wi|r?|| hC˕_a": CĀ*zH0 ZҊiu2$5x?*'$ĀW2ei/#@} \ Hĵ0hx^ Uqn>/B50 dFNCFz%8mqZn;1y'0o|N޹oRDh딿DFʢ| N W4x_.i Uʐ` (^s&p7~kZ@C麶1O/~1 ,&ÑSm#m0ȅG4,Osu;$J%V.9b^ OOg+!vooF)/82^`rC`P= 2%C)P  RjZw>.~3+XaW-;ceʻy$85]Oj5(|Fljzcaaf+CsF[{}|볶fstBpTFk.XrFG_e`2dY@ 2ռ^h dXٰcPymj=9-vE6~֯UB1 @f ~櫄S;2ﱆgE bdL-$\ f;k.˶+o$/%e@)2]e`KV:d&~>{_"d/Fmf:?zm\F@'[ ߿󋞵I,2N)r/<@  34VЎ>7 ,xl;1SkoE'"g.)}0꓍3 \`{ԐA`?S-7ɾDZ|7}RQH0 0D|d tlj#HYLg~ORs5^-oN|viα|X-/7o>׸>ĀVϘo77Za8jǠ{X}2{'jyF7Ɏ 7 'cD_e@Ջ>}e4^˳nBeXЄ=UdW`!e@RwYaVTc3ٿ@~^$20@H,22+^t? s V+3CFi07uBL<~gwΤaM޿=#O'G T4aT̝m<3ˆ:lS͢ HlLю{@}b&x=t[d Đia}7Q`_0+tڳ/|C/`B`L P&#` -5 S+$_b \Ql%\>F hl` 8 p(ٍ`eY]5V<`3{H"@7'ݼ Tz{sa TQKm ^j10lV` ==#` ]dnL;5. ݰ˄D` 11*#`hlY1_~HygvSlK3{ؽz=zWq|0 2@)#q T 8FZQ_֓2&MO(F1aU~}($@Tf`9VKowu>s]?0`nhE6}l_ (h"G Џvju2P,QflC}f+[,;f.HFD>@}!-m%gmҡWc@dWuj|ifC)?e QH0 h|^c`z+n\gόbp<@vV>Tkfᑡfcʽ2|:=@2Y]e$N ִ[dbEm[7ezF42Gق@I}V=XV7 YQk*u@Ɖ\sL^f,hw7bC``2+82Y{&~hz,'|Ͱн-u*$sX܊/  jb f8zM- k}=H=k[qvˌݝZ׃9\Rͮ_;5njbdV/؍@0@ ׎dQm{l#h95)VxaP2G64z!@0P! P!" tgZv-v6 e;v؏fꁗC?,SSǶme/r* v]Z"0l6>iEгW'xOZ>ΊXf^ e~^l<8}24 Jb JzQ` .}a:s/#X|+NnwmYPczI1ky/CK^d ^*G Zho.7L W}=|m#ӕ]Jw;.knYޗkFVx Z9(e`"ˬZjn}k̾zw%Ka=z{دXIyΟ*!$*TFႁlryb;^6l4wMjϛM}l F5!P/_nM5[5 [_2^rmb` d:\([0 t׌3 (o5w$Nd6īPOϣxS*L~>`!$0iEY.00sձu>h?>IaŠ ?~G4X.o,=}5 lS>f=ym<@KG&"ְ`xO\@򤢜@7U 36qI?0A$>*6OV[Yx7`R1QYF2 ٷy/7ZmvxCvۖ{n]lE`,e_Blf&@u2@u>(u0pP <-z `fe(a pHYs  5IDATxU噀)4i )(TDQP-ƞhL6$5jb&h4Fc4&]b5+v f0g(s|ϹwIijj^cRoSj^ne?/_ m_RuMGE먁iR6Nϼ>kwXAW YjߐTDEϔ?U,OgSSB]O7&U Xض_Zp˩m|^Y*X6X(Ke_>4aR^w}+c9WF겉p)5:!vuzaYZe&ޓ|\VHgiMKa_h*/4R3`nyZV+U&* mt$`ا+~gYtezsHuz=ݩl- `xotkBZ8 !5J-Y9K+sd+ <=8&ȔYEy?T4OTnDl—V6pxTv堔x#'yz~a:-e X+eo(&U'v O e^iLA*?{^ITO<:ܲ2fu>r=΂{v{hHҐBj3Ez;/o P x#mϏG7F?Kv?ydv[F׆AZsK(Wxւ,X%BZf2`c :vyE2$!3ܤwcl8Z#NeeZx%xx6ʴ,!n `8 qOA@zMcn' ,q;6`Yx_V3to/Y>5els*Sڤxح4^vF_ms\ `oA[|\p  c`s.ZzisooMM}Cc~e}Ggs.$sch?>]r k*\!.^/۟2!}ڳXK ݺ[۶vrU5W׬wKOxV)oMPڃs9ׄXmo0ʟN k o|,UBWڿfe&nsD&VZkgb5(m*n|!f 8ꙵ]*l e|\ ]SD^q}>p4Ϲ}p0lo b[J}vGy&R*TT8Kxp3r\w<8K|AD|8~8T]_Hɛn5[AIs;08w-Gp| AquERjX7(M&Oź#\9a#tXFԶ͸tnmpnuj[e|T-y:*ƍIW46erhJl힤lτxp;"(":|"a,x- &InAƹr$f\i^55>4[QXͻ> 5PImR뤡?g;f5v`3ūҸ{+L\]bdbplͭ9`:koKJs\ ocq4yYnn+ >p^V[LxZJY& `0OgFLw iA֩5z o. C ܦn|(1)."rτ#_\ևL`>kKyԳ s\"q~cJIt SgZl K@!< h94Ց:z;2qs1)` 1}hkYSM5Us/Vިk3gg,}C 4jSuy4K5BU¿&˲^nEWO ˟+Uq=u9&mpXl07.-d9b~b(韽8b׈>U;銲ߵJ3nX".L]&wW442I'sK4b^T=g1I-cbTvݛ3NXq\ &Quܽ ΏyS::UH"br.V,7TJ^{+$:bP7mixP\*;sHW~^e<;mn.MO~׶h%ofȈ74|Cex1 O+=Vsv3O}\w`w5_m e9ۥ:sxg=&u'{I +:AIo|1<>S*S$H9Lm a(b>[mN+۵#cД@g=WAaPtA[׶;+Yg_ڪTL[y,߮霁z | xNp:8nרHwJן>8nw/􇚧z„vP6}YwDimvM;Al +vhKԊuuC;B@΄KK@N_ Gqٮdy?)O.>ݱsh&;P~^HH'vev)Om6>HeIzmM+ZmjQ]ΌTiWXtIu@єaP\D::K^$qm5R'6ɔp1DrT<D0aeb])yԮ[I^bTFt|{6veŊX#^w |\TWJsH ˁM{TOHP`P?$K *9( ebyےMwwʵ}AUn$tWLmqWN9@Gs`&\&5=Z>bR:= L'Gu%xGp(6(έ+C4cg : )3gk<q bby)fh&'ˊO>rs; Uj޺پ'XL1a+U(JsБq::.gF90UsH(&emw]h+Pw9t`&`Czr9.ڙ[v}hKf5j?Th_:L3[peu#SpBc5Dˢps( `6 ]_y7?S!5`L(f}qa}`Kx&A@/MJ%ک X0 緝m2s\0>p|UhC1GxȮ_mur0j164Σ#08pw] [6uv:s_EgF9N`.50 1_K0뚬,Wp7`p5)K8zc(1ۥnn!nVsH9<*9 9ch+][zuI~ i8G:oz+DFF('aNy:q D3a\=Y?e .` ";R &۾X$֣h Ƿ)pLy׆CXPHs&6x !7KeMJ46Ty#=Zg+/<וs,n: \ng@H$Z' c\b$la`\@P@{c5`׆חD]ʩhD$FީCK~:ѣq^`t2 SP"gş uM7( F^E1qCWRɺ+Aw&v>#8F`_orIkkN_ZY΋>3zZ@lhI`28filw1 ;o]2R]>Λ?o[&}n#gSb .ʾAmJ~+&Pڠm!U:}<(&W:`=P\ O0A˗б4V9~&6+:)8և}~w<ۀY/+~<)ve3͖8?SskDRiaΕkmuk71l K82_eS[trC~@| @B~@s4mn%>߄gAQI%` ]H๎qTJaV y^A*Q3j:nyɏ~Px`NE[_Riw$"\p}` /"q-ח:(kR#>(&eueoJ!{'`rh\8c8*ϕ [yy1@o>5q;S/p^GN}=^(>Kxw{E&rO|80'/)Fk5NbUEkA@= >09cuq+PWZ[ PWdGg:sXm7 Gv } u yǹ PP%g?{w we,< On|ry/up%hee6XCAMc$_\RCg~:  8R7;>qw(LE_Y>gԬԪàԺ!GEDd {ܪ RAPшn+e9 "2GW[>/]ڤצx-ڷ3xh< J--Q.iЯ#&xA+w+S5:wNn!}23Êbqo=r0> EDj;"Vws9ɥ:DCC?#mP&}<8b/Ķ#A8mb.3K]i؊_#UzCŘnyt)}S+~}9 țgLAIO** 'hJʩtdu}Q=9xh:~h: SWVHqDcڨkmq΅7oe>%/cK=One[x^ 8ϏF1ygl1ANzڦ};/7~L(/)~ӻ$>[/Ngub|\闂[e6h05Tؐph`8 ]@CÉ~uч"JZt]\\vQ zur9XZɏض2bRDR WClu'c˴`10F-bfXOLG~gBzdQ|":Iٟv?P/A8 h3j'@v:A⢯|LpNoHB$vư/=8֩@1\c 7 jx&8pQ)y]V#MICq{|eQo![=ϛA'^2ByiBS=FA%GQ^_vK4;߂)p>1eSNNQDucֻ.}t^_п;L; nw̃PW7HlQ*{.mڥHC/ufQo|2On4wq{NUI)0]l[jԌMY9j Qh@+ S'CqN}mc+N}k+ b W|t Đ֥B\3ICuRvKg7YiOlaUVm߽+-59 gf6l`eٮL0iu!k?Awxй&WL~$wYT ;GCX$y;ݝ^_9-ǹuToA ;V>5B:3oZl7%wRbQpaaĿI( UyG>rf檌  AiԻugpJQʀa(V{A7J}\D_=l76Yw6L+:Do¿aׯ$n|`5G0ie'ɤ^.RqAX;z~?Gh烋c۟OazF\*^Og5W^W3r)un\!gǀ*F~&}v c7hQgm8FnUӃk-K Kg 1DL_'9#k6u}o;3SУFWS(7?Kl|y&|Ǭ+TgNs#'s+$n_@_D y`6mP~](?w%,T?}gӞ8gZzi}RО 3tIOvӏ~;X&y. Nh_6EOZ*U6,=mPNJٕVB`{A ns|6!QUgpMW\[?:v+t}v>4j dFW km㣳Q|1ByzG{^ZOUꁥnk{(f z*>4--htƇ4j&p`d7\ a>0z N}A~ǩݏn tMc~tC:MV~ѱU1*๎֨*:m8tՁѕbՎ6(:}8s~FOʐa{r|y/v97LdRhA=4g:ck>z5_EöMSXGp\SL\ gǜP6 Nqw0V?gbPm=hZ#Ԕjje` RUꡭ;E6xM[`px>AGlq0q)B4fy>|qK$lLdo{&Vgp|S]46 @Cn:_UV*rd]έ7'`_ #ms.oA;!U$\H14 /StV#@xWYOOJ*wLu qŤV0uQգudxBwX3paZ:U7Tk N0t Б{C>Pֹc%V1x&EH,t 8휵FU&@l_kLX^] F58z{ @A]`6(ֽ;y"ȡ(9A dF| bKy;8 w{ 8֜Tt :DN]Dq<op7v$oE&hFWr<09W*|M+p ;MW4FmA7ErWjl./Ï<nŝ$&N]"[Jcq yc a4<V168mH Z$;x gڴBo >þn:QjG6o+׵JNGX:pAq>ǺLqk?ˑL蘁d#>#Žs)K1tf{}ɥ1IL$0k/8lgIϢ5je8x F<&W>x&:@(:_Z:Fz:/LGlvS\$9>Qk;>>HYf{>\4pn[Op(GCƝ)csbЬs\S_+yGNv19ʝGD۔t`& :8A ).R\ Co?%:^GPvSPH$R4F*޿mk@D@).Vb/;JlvXPL|ź#P!(vh"0[Ǡ-߅@ *>t`w `[hPA+6nN6a/xnW&ڠOA=ű b๒׭X@kchmxm &Ґ_-Oak y8k:~T7/H:V1w{1).PL$\PsE}A00̑u yQLQ0/y,| bpn/  a:zXnT@s8w,0ׂWtvlh N sjl-n ee$r~Ox.2(|*wv3MXms>Q[`}`"8Iu1yn:׾бB,_佀rp2 ?tDַ1 r,E%ޱ [7YoRwC1umv =/p> }a[G&SuJ>t833=t EOYۚ4N5:`H&2\:`Pu94 qty=AVGqb|F/$DŽlOA%+5u%AROAC]QQ] @d@T;b0K89JGԹ"8gQ6 ڤ.(6Dz:ZVGuU7vУcwAų&3@'*L4 Gp[ : ҹNѮv~K|W %Ru5YzQ!jEvL%ϖ2hN7\2~ y.'DXl`Dl[wǩKҹE8cT?ܵgp u1MZuR8[o{b|uui`VTm](;:[P4ܶuRoP\-Qwq HQ@O \:@4 Vdp?ɕmNnDzXV_U~8ߙԥx[_c;?Plˆ:GA`~Ap/}}fb]!aRI gZwj:8ל'b&qQ=N۫Qs@(Aݖ(;@p'(>PO{ݮKj[n&qkli _Sޒ\]c Jجcz 8tdSg):M }l4smt mrd6P|R;ۄ8Od9r~}]MrJ%^U*ҋ۳m~Yѩ5)͛=:sx*+]tCӂ7L##k,tO`#<$e< OAse ~5"0eEGքQ pӄ ݼy7x$}MjsoC-̄@Qwc[EۖzL,uIT/{.U/UM$ZҞ'(r_|gJyOx7' IxXFcXN׹6WUd Hߠ|:1 `nQ}w!RU 㪇mž'STm.Y]YZS_ Qzүy?wSae74^:9'Aج4H̫D9>(Cu d2m<WW`pcQ,nAuK>;z1[+dVWm!Fxb:q,vseՖo eGd@v`r}9< tW( l m ΰ>\c6ƧX#yiyhjK}n6Hm5f=p6~#zݤ.nوZ\̷FLMn!#:6/pr' [A7Е3op| ^wK78ޗ??~n`3l$8pͷf~fޞ//|l^.N, ]R~n 6-^ G|ۻ- ^]~!TW,U}jMT߮s4+Xב7cnV:UgSw(m?_-+L``;ܵYeRo ->e_Jְbj~'ӨtQSҘ~&7y}Ӆ左z}R?3ji(_Ϳr_?*<Ν*d_K>=uXu?oǪSFpT8W WT>+5bJ'[|k-?LA #V~/(f_G Feǩ|hǚxK]!Y VERH+R3ѻ2[ռ=WNܐ杺y*o_GۑP|W{R٫[늜oMofMizu}emUJ^=#uV*h>P W6Uud: XJ\t푞yt4UbYi.5SōﲁNs'eZႺ$ [*_z0-;: {kT&u{wHQ mh#_% 6cU;#}'jW====z#ӮlPIENDB`socnetv-1.9/src/images/import.png0000664000175000017500000000316212534331160017354 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  ~tIME+EI&iTXtCommentCreated with GIMP on a Mac_[IDATX͗\W?7Y&M4c iĨhPŠPH(@dQ4 &P?"Z(Pݠ6*AHZM6qlTv3~3;;] \fsn70YֹtV^Ž;_tifqxUuz*"D@DHE%ϪLD Dq\õfbFqPXm˨6"凷<]֚1 ȹf3\c@w0,yNT*|&W*uȧ\# fyh"<\D0!ƴZzV(Qv=2{s=y h ˀyߜ;O?7oBD2@2j+@{2 =$S._}yg˟+4Z-Jyb@T1| }%߽ZQ[ Y<480?DP,1z7&7.DkVd4lA,=xos[q0q6KIӘ4hٙgGeE;r5pphKoPQ6Ҕ4FDa?x3_'7ysF60rG!8\|r;ӼpwXqpSUGc]+5nU=Y՚%f9% cc|裻(8Rpظa}g G͂Hu ł_&zko{K333K'Դqrd`C;nxh\|)s^?x@rC~Xk24r,˸vZr :êJmQTۼW.\޽{wQP+3iF^g訞?={ =0+y,Mc={⪿3]V(JǏ?KAmye˖ "R|(w[_=5^cIENDB`socnetv-1.9/src/images/net3.png0000644000175000017500000000321312410526714016712 0ustar dimitrisdimitrisPNG  IHDR S4sBIT|d pHYsrntEXtSoftwarewww.inkscape.org<IDATHL?{^~^@A)bEi éE֦Zu&iԸe]ܖئYЭY6t?RgIZc"prgbjw}===UV{;/.2*))Q1u/7-vbi!U7W.\@k\ߴaɎuZ-Be5ΚҢTU()p_A,U2~/aj]85#)F"&Z^14◨H hkgpWܲ;āgQ1?ªJW$C*1y ˬ6aSD= Opq~+=+6{ǯduw^x&瞕wEWML]ml~V5>9GXI3f~*Vɐ~_D"/"ZpMqǰ0[f=!-=w}:5P/l^bTt\1@mjێ)Fާ큆g/^xDD8cc'`U:_l)b@q.ec>LŖǞ:l`m 1??+" D$'۟۶ |zzseK?Wp,8XV  IKA\4TS N[$C*36+UDqIdK$K&aIR% v%Ʒ-y)+rWd;F۞$N6=35rH/yڪ ! INigڠ-:1iC09F44Kvt1|/Մfywq 1 r\wN5^T &R{sS3݁oD>8Y705?IENDB`socnetv-1.9/src/images/walk.png0000644000175000017500000000163712410526714017007 0ustar dimitrisdimitrisPNG  IHDR szzbKGD.l pHYs  tIMEl&iTXtCommentCreated with GIMP on a Mac_[IDATXKA?9J!K HhAm-t@Jt?A$6qȠ@Xu:b|gdP2uvG8s x N:QHs_X;ڜDAWdf09Zi[y@*e-/*v'.gɉ"]x(hO-$NgeC S4ŷ8f+1 ]xd"h*]l:7uBI(hXA9Sml_bdVׁVfw]SPZzq*(rrtS4`Y{,)Zf},c53Lգh|~T:sTFryCh։d|kw2+TFWW!/ώ=9fSF5KDA'v+Zf H9A ^nF9KոT 1gR#tq6h+hd5E 'Ig$x7αdn]7LB]7\"`F gznཇ3j][i+OXs OHX0-̌]1c|CࠝZfJQu40uq>}MNQjtBqm|[p07nsoȌp zvɔ'l1?c ٭{HgIENDB`socnetv-1.9/src/images/nodein.png0000644000175000017500000000273212410526714017322 0ustar dimitrisdimitrisPNG  IHDR VόsBIT|d pHYsP)tEXtSoftwarewww.inkscape.org<WIDATHW{LW TD#k-P`T.꘢Y3bfn3fN:c\ M|cP/R @J[(gbZysw1=s,*pO'VL-)~F f'e{ןiЪ;2L +0Ѵa4`8#$x{&%u@80"Xw9Rq?^.! |0Fi+orlx)F\eC3Y@D.\R空LSS{ fk<,`ϫc4OuW.7`*Ŧ6֞՘*CҀd<7MLmým2$"YxLϩϕEF`4?$7<>.Xj͚91&J"Ѽ%¥sFxz:'1h^o| uukn13j\@@5c9mRvUnq@MӏT0?i{ZS~SD @Hf>=< D f^AW&O4/WH^wJ[1mA!̌Y};.6#ÇQ/$Ml65ab)hCz5ek3 ]#:&t Jn(AHnt|}}~@DkCE"MDLDGQXdhw%@B>lmVeh.hJ@`13cfdTИ\FEadH#w'n Meboe[N" P @*3 3[j|*#T*(ˑ^Yr"}8 6@xWh0xO6Uw0Tcgf&"ɭ[HbQV"JDDN 毒%?%<V@hi,oSu7XSMDo̅DD2ؾI"7fFAa͎u"Rrr䙦XҲPtu?p5q&'YoHEnyN f5-VF\f甞ೊxh?ƽf#(*g2A7"sﺆqGJi-_H,1nEb㝍>uKWW'gH/OQݒ~k3ゃ yUGG+R䳡Bkn7l.\`K~X0&wQؘ FΠ%>ϚϦ^ /z]{Hi-aӽJpՊS\+yV|ܬK{KjIENDB`socnetv-1.9/src/images/socnetv.ico0000644000175000017500000004005612410526714017516 0ustar dimitrisdimitris@> @(@| B*%s,`? ~-_4?24L>9$!{^x 5<-&o~Ny99vzlD@ %q|+bNDmWC 8XcN/]G"z$tw4Q,M3 $twOd   } ZX"y &qj#t}@3\[ 8] P9 Qc'k[5F/utttuv}zz{zrrutqwtlkkjhhgf*da +00 wS&ut3Ps.VM5D"nzw- @ { { < $t33.P- h _ya9 |  u5\yg$SV/Q& w&p  yy7o&   /a9} bE|u~ -Mwh  xhvZ({wt=Y  O Q  KGvs &  ?xv n}G  % us i ;Yhi_4 l ur@  {  6|9:~y. |}9usKwp& rh\Xz_s )vtDv iz z*    &w  rq]  zr    }f| !Nf]z )s\yoi,    =bmsNd* z_TqJ}/8  { Eu&|U]xD{lb K] O<    JN ZE`rYsKe6G%r|$u~'73 F~+  }   w|  2t1 El2G$u}%r|6G/Y(3T#w{R  Z35T   Z59\ |]wa,a/Y(sk| z    v   u )pu }  sr    |k s   u yy   o  v (knGs= y    z  r4yE(kn&ooKQ Ivtf*.e    e37_ pzTIH&pn! cWsR y {y N!Pp * Y_ z3qn"R9 JF  P@ HB)xE| Xd  Uf d` w |  hj"s `Y  Fwz >Z,sv7e&  8V5~y9m7 qlP B |V  Y$xG   'i  }s@ Dzv  p / q   #}Lti %q~B ,mAp  }$v iuO\yj }  ~*mys 4b  8zx4 i  ll~   cy`aZ~   * ly T= H) /8 %NGly } ,mxO S{g. yk.LMB3tQ  g! n'   ?` !s j3n*4"==++-yw:A  |   MuHwi$_V(h`evTqG s $nSoa  1X4KJIGLFGd;B<P421184>3-4l52///0V"zFEDBDFLOyGJPJOHIM[SWPrVTQQSUXI!|#y!n af?b v*|M "3- -Y99 }!x# Op ? %dY(me=30SP>3 Bt Q7Y: 0 7%/XW.\_<< 8~w&  27{vF hx ,G: (fg(hw/WT$t{B*5K7E 0W'??7=?w?socnetv-1.9/src/images/zoomout.png0000644000175000017500000000310112410526714017551 0ustar dimitrisdimitrisPNG  IHDR szz pHYs  tIME5 *bKGDIDATXŖ[PSWڷヽ3Nf-iǎcLGXQ%Ђ hQHTBJ0w~3%!QA6Tk'Di̚>'g}_'" .`U5kך XZ~V[/C jMVMFhFk-liUS5s*+KT2-W>goFUJJK /FaaD eUMOt'k3ҵ;!\h& A Q1ZJ9x=tg:}qߧgK` /Z崲ctƃT16aZ{7ȟ#J(V<+00Wq:y(F&'113YOVK0]CV>nFMC# U+S\*Si877ǃQIBMπ!!ɞwAppt`l|= m4|MJ\w?|KSAAՊw={¬.z]gЪ)ǏY0v`׮apΞ(RL11Gw߃QXT7֣:`d6H J~Ea ן䐐Ќ(ؗZӮHӹma9:V2 FcwVFppfbM8>:PHN2G"2R"xyVU~@{/ڎ)~[5YY ::ɩJ,T vq-=M-_UWpZ4d-1UֲY 3{00Q_“ _,, "AlcoW"7_,_= l 1S B{W"[?qN?g B *1 Pv!^#]BnOhyX bx g (\x@K?qB˙OD. vr {|px_Em96n,OiQ>aډo#j{X8WBq1-"%!}CZۍHKT:7ÿП֭с"k G/G IENDB`socnetv-1.9/src/images/diameter.png0000644000175000017500000000245612410526714017643 0ustar dimitrisdimitrisPNG  IHDR ꂣAbKGD.l pHYstIME ')"8`IDATHǵLUe?^*u MՔ FW/CLkU :Z `K?[f?mmM77Ɯe jіK@,Ep_~]9{p=,=y|}}ޣ0AII!1=OqkjIwvd\wCXl~UePUӃXM|4un'h裣a<D'tDj*Ǔqow3Av9R$ϧu$7o_QAcZST (k^k+ocd%M8n.Y@ISt {G7>+سjk4*~?.;TYY|DoEr…ш-q6"-wbbzx+joon3D#|LLUH?Z_Ӄ^Uő!ڤJ7]A@πLIq|9YYKB ^/??N@3'@\Tx*| (F=^iVc_*>i_'uIY+ :-V)ʷ"#@d3+̖L:'DlT-9N7T,9 v89v?^ Yخ!&^yUχjpqՠChfi_e?bs`_NIrr 8AqcRප Y+|^See5TV T@lȮ0j$Ϗ"/&E]#8pLuENܞEN*I'ܗ,5 a * $D#.]G4l6ZSJs3HEl{MC\f…l#c`$%KOOgԩ$tt'S<VD;cٿ_Roj*͛9\Y dgHPrsɡχ(/ۍ'd\.%%|߹(.qʔ0l~6;mg>!EE5(J-#}#`9O^/=OWQ%+Z."sxku34"9I`/pc拺::x D<ߥjj8ΰfu7wT(`de5#uqqMMzY䉉M|vӁpp BNlwu5vww+3F@"\b, jU^-G,=5wnK^^+UZSS $q.{>0 J2>|"vnv=!͇%.^ϞP!'_XxaGAbAͦݰ!br1>J!.Z]o5o2ӳ9:ݡ )11r^yZ&| ҥR.2"B*P)|JV03##ooDž`b[w[mq>[]011Sz^<}76~&I_SJG}_V\x띚ju7g,R%!dAˎK|8C#thbfݳgF5 ziO;cc2JM…bKOV[閎J15Y,.[2Ǜk`RiZMJ)|;@dL844tV\x<2lko?OR6&Z#  ir W'+?PB2?ςp0gb"0 mmG[&09y]Cq%Ωu;{uv){H`-MIALH}UUh!ت8Η66m* BeJp@7JŔu:PY*3ڏTw݄QB䓤e6S(CLHwRP4W>dvek42`<`FE!Ӕ,i egF D$_D"Zn |J^@=N=X1jtDkPfZZ'MJńmjmEYs ?aXlq%ecrbP(5!?|hb/ӺdIR%ۥRf665RJa 4:=:Pzxo|w47U%0!$z5_nˋabG^ߴ$xaXA LK˜q1"ƙ3{vԌm\>`-xtNBeUꎅӆANwPjUܹsvQqܶXj7(kIENDB`socnetv-1.9/src/images/selectall.png0000664000175000017500000000172112527557765020040 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  tIME ) 0tEXtCommentCreated with GIMPW9IDATXOHq?k[ҠV .zRT%K-$RHF-[FWii+Y,6m]L<~7GdJ.@?tqV/%;q7 ) ?X@Gp7dbƋd"A0 }zֺ)d#-.dǎ$ ih2a8 ҅r xpz3dSqߏYYIK/THR ݣhB0PR)޻px.J%ܵ> bKQ <͗ ys57scq2##d'#m6)4r%xqTl<?d=meo]Kr|K+II^#Rc1FDi$oIմuw3m覉mms(6H?Xj!ΓioFG[Jq Z[yo(!1uu{iB(DΝ4Y<|ȽNƤPF"НB8#h/R]^βN>ݼYdC6:45їL(( @93b*-_NUo/Swu1Y֕q_z9XVWE+-gFv紨yUwVg }F4m5 mE6nDK~>hIENDB`socnetv-1.9/src/images/view.png0000644000175000017500000001032012410526714017010 0ustar dimitrisdimitrisPNG  IHDR szzsRGBbKGD pHYs  tIME+ *(tEXtCommentCreated with GIMPW+IDATX  wp5-_IENDB`socnetv-1.9/src/images/texteditor.png0000664000175000017500000000367712534331160020250 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  ~tIME".t`&iTXtCommentCreated with GIMP on a Mac_[IDATX՗kl3;;;޵`cRAQTZEpQK Q HX% !D4TEJ $)L(JHӐIc{5fי#^667OhΞsW7w'MZG͝җ- ޅ޽{B{zwձxW&uP֬g͚>5<9\FD9$PP PjD͑Mv~{*27KEEZ1Nm(Zqںc( :yB񂱈cQCEXnu/[L8 Bߴ?D!!)@SxN/qX!># 9%U+P}zCiOC ciOyYGNx1,S66/9nڳGw]GJ V( T Q,K'X|r>kޮtk8 ņCP8_>bV34K2Wżu,cx/%>e&Y}hK}2h*f3\;u ,FvYחޏ܄ ]ؤc_.$P(~\:C`M y3xZcE&Is`x Y@k`44mPAP>\+\B&n`&b<@p 0m|bN33ٲm'JПٱ 5Vf~HBRұEnlm,}ChKl[%_ax~BHdHayK2"Es]8vMF6^"^m{e`a,kw gw _U{!⸰os'~ÑߣϞnXA)**BӴXl\?nCeу/q\R_7*g%xF߭/8̻.`px7# d4vm4'nײ=,/e1fQJ8ܼ;ϑ)ZozK#ot1cy GEEՈTd2I*b$PJ^/'6n |k((^/HD8lA`~@&9@7oqA.uѰ8Xru|>Rp]T;n5o֌o??pr_D oa HɗWV1nUTVVR^^DQZZ[)))fĉC444,u76n|ؾ^oȦ^D>>&PV^y%477r}ukv~Q)\o_#"N%ɈH}˾&/n3O@Rٌ+J6x<.NhM6mĵGum^?Doo1mFa? A)K"ضM2$1~$Z[[xn<ȣ41 X,F6t]pREp\&NSVaƟ56nkcԕdYISĶmm[qX/"⺮R)xS:{;KVqcTFN|OwgwOqCB0eʔRlKo:{ς6/M{AdIENDB`socnetv-1.9/src/images/new.png0000644000175000017500000000152412410526714016635 0ustar dimitrisdimitrisPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATX՗Na5{q\\0&bXX ѝRHECey{QJ!Iޜ3MN"SsՐn==d 12<S(CfIkR׾TUNK޷’=Oޣqnl4kZS YoK|mu5e<7ɺx&oFh?B~Ql% wIENDB`socnetv-1.9/src/images/distance.png0000644000175000017500000000252012410526714017633 0ustar dimitrisdimitrisPNG  IHDR  hsBIT|d pHYs{tEXtSoftwarewww.inkscape.org<IDATHylU? ԃ4XJ*)Aj ( &b"$I4hPbJ(B)]B(A=~ղIfތ*hLSXȺT2|>.]z/恡HUոU9SVF=0&֙8 ~ #Ct^|v;LLH"x /vafCgv|YS)jG;qĪU*s07 4y͑+oY6aSuq^{U=(!lRU?| دw"d[3Jg#Γg~!G;Ǐ?p\;zח.=Xz V|*Yxۖ- Ql`0`X6n|g  $"Hܤ ǎ5Q`0hZ'ͩN찞&"E<v/"CY,! X,A@ 0 HYE G/e$pQ@n 3MVwt\>#odq!-99ҋ ׮].OǕ+KK`CV9:PHDnX,\$._Vа]u~SX8m8i/?"hUժx:a2K/ĸ([ |BsxzPHl %icX| lI"g۹ADT`%s|NBՊ֚66j>~Vz <8BDd-/9rصӧ?I~^%[L3zQSVN ͳ˪z0~V9"JI!;ղd݇s \Wz5|>|^/>^/ӤW϶mE5y>j O%%i@x}SSW;@hq:I$3k n/޻\Q{>/o^DU9DDFe  Uceǚ5,+*"rUV! a'>v8"EL})?ee͆rQܬG+*b47oya0uuuFGŋz85@ݝ;鬩x(xs ʙ_1ADr̙m-#鱝2IENDB`socnetv-1.9/src/images/scalefree.png0000664000175000017500000000320212534331160017766 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  tIME_&iTXtCommentCreated with GIMP on a Mac_[IDATXý{PTe "R,ICq uKdl,8:c&#d]QK sRKc&Ig|ۆ z:>>ig{RHZn711" CV?R&_}Qѣ7Ƕ aG6S6n4>ݷd}Z̙3gRj~s+kk5w?U}!$1::8p p#QX8-"1 @pN?*n_ٗ~9֡zE=, H1 c2x`i+-sE"Za[DZ\rh\tqoi-]doKZD~*-CD$7S @pBȈ+[ӆT 6bNiyEK^pu@ dr4olvX]'V .*RX:@m6߉xן"Q1RSSZٻ än=Y.N`9;p8ezHPy"(+{ėOS69aT< tr zYXXRbfzVV4M'0F[0NX5m ]u~CN % <\n(جUiõ^."R oyXg;-1111Ҍk',nXKNg/fvH1PtI#D@&mRG0粂9+N)ߢ$i8r$ݼ5/we`eeU P@60E3 zp;WLcc~S)e~=ؑ--v̐ KIxuVˀo{ziHY8+\ =n[G_5*r xo_)~k`R*8ZˁMMkg%v8 V Ϸao7rs/E#0( tzw^{8wdY_ ðRC \.z}m9`Al[3Z͛? ޜt:AF4 ݫ̪M:,zXBcˀ@|@ʺ_  `n@o7ޒ/6\j'V \j.h|hl\a7j?j?W!<=c;@aiv`)=I1MSMgvK\3cT`uqۑDCIɣGinlT*VJӜ %@X.+6|8f6u՗wSzo%2?wٳUog. ֽX8ξKn7̝L&;3{GИ3: IENDB`socnetv-1.9/src/images/zoomin.png0000644000175000017500000000312612410526714017357 0ustar dimitrisdimitrisPNG  IHDR szz pHYs  tIME51EHbKGDIDATXŖmLSg7%˲}s2`-.FQcL]DV H( ٬ PP@,yK UA9zsS[$MNs9N󌫢Vb0X5&Y_o fklh10vg l(Q^;tUijwTVee&Ū9/,+JJQxMϝZPҎN4uލ4w=S]tF/ LQPT23zw;g7ϔM(**\[JU.@ĝSc1\'t"eͽ"L=j9Z)ᛰadpctôtw#rr󑑡ӹ.6ku OL`|z33:`:|Mo݆*W))yT[].ܛF#&qn#zzJeZP*SZS-W< jf"<܂9ósqhRNH gΜ'7uRt{?3BQOIJ^y0X`m'ES)z58NSwbjv;sAd=mqqcN&8i@k 2B\L TZ>2BFk+ <(5X{: H`rJr 9ozhM z@AD=O'S99%q>XXÇ|혚Fx!xID<1Р!SIݔ ;zGȺ:@j j~Jɲ? 0EM?~%̺ Ec^iQ[B2kmROwO2,A8߃;#dr4֣GH$6Q r.+CDDj?]ah9MEI'v(!6NJeǣŠ4 بe>Mﳱ}l˞=RP~biP( T]`jrsLdVkEh;ĵ0Ů_YF.$w%PDb.YGgޡ{RR,ecVw֡mûȞw.WIY3滏c _5v-H_“ⷁ@'`^J_{?db5u>[}Ӿ zr# qA$(Y ȱ5J?) 3|02 D;Tύ\[m?m TAD&8D2 ^Ǖ?i[i&|/4Ad`E) bћXx2 ^Q}mli2tQ^aڅΓo jXu F_fBͩ4a7@=;UYy?l8x%E8p _]"DĦ3Kp&IIENDB`socnetv-1.9/src/images/diamond.png0000664000175000017500000000036012527107060017454 0ustar dimitrisdimitrisPNG  IHDRVΎWbKGD pHYs+tIME 8 tEXtCommentCreated with GIMPWXIDAT8˽I @/^ڍ`[c| "g":i'@dya .rcS5lI-mt +R^Z7"| i .IENDB`socnetv-1.9/src/images/connect.png0000644000175000017500000000151312410526714017473 0ustar dimitrisdimitrisPNG  IHDR ꂣAsBIT|d pHYs-5tEXtSoftwarewww.inkscape.org<IDATH]HQ1#[8 ISë "C* $/ nnh H- )dӧ ?Pn8y㜣DTRʨΥN/_ v:v ^(n"xM o^==(*B*T?,Bh}Pk+ ݵ..JK 9lشfYYL&L33|'oiypk_66vb]Jj|{;pf`AQJ]3\[K_u5FrX#0=M pT8)"2R෈hmxeH\v%`@x ֤ӉP ^9YH88IY>R|` cteH&sGs} `1,])ڀ@RJ8ds%m,F] *v0QlةRJUTUVͺ:r 49w|;0y<{NH)ZTFGLtg'͵ZRzl"J⴦&zVbgM&RN匛dxgBFg6=B?}@7zUm&!hƴ49"~OJ Q2PΞ%#` QSCv;Jb"ǁE@ٻoHAT[q88vjvӧylܸAGv6xGJIXUUt; h8uFq8psT~)Q8_Cl,Y ݷXAUUt8n | UE11wOǛ_} 4CEXxT[̯ә(ׯӹlAtJJ [ >{Foc#ݹ]p"!Ĵ@>MIa^` ﷴP[ױ:f%]nkE'o2֧r2>kE33-^: tupd"5 3#"0?x?ήpVz:E^BǶH6=ʲ6*EI}?ݭp P ! V6U^MC08a#sQjGJ^ dl#q\c b )B~]K#Uu4 Lx<@#1ח99\￧9=/8ٳYsٰfw*]R S9bfRX0^rCZ~\Nͷok'NKKis1q4/[9mO3$&rݢEǎLp8,_&!D>Av#HQv20&RJvX+R$$d"(6,\d<9/v}D~ xb!02 ŋY |B懒"#"q<{22ŒPVo6̛{55ݼwRad"J1RWp0d'Zn)@dLpB44pytGNףlߏu:FN$~I_ =]fMHUWS(M_AhIENDB`socnetv-1.9/src/images/filter.png0000664000175000017500000000431612534331160017331 0ustar dimitrisdimitrisPNG  IHDR szzbKGD pHYs  ~tIME65w j&iTXtCommentCreated with GIMP on a Mac_[)IDATXýV[l۫w׷@JH$DFJ*mժjKy j+TVE}@jU$$zQ*( H&8$_ls,G:;ggg|gs'N{Ci""Wrjg`s5`)۶d8T_h+]m2;vﱦƏySOrRy빟B*%J4l"ئiiBRɈqV :=L&o{yںVDR)S +x[x!_(PXDTFVGZCр뺸q$ Itq0vdy hpW{BR!,J48؏ޞn׏nqfӥF]!|<B-@^GZET2h|?p)٩džv XE F[C3gf{n!eiuu֚lL")$VWWѨSRFP@nii<]^Xl.G HϿap~tf4fNL`9LOs#̤eY_y7@י=ngq`Cnݪ~LN?XSpf*nL? sgf~{*4) 9!8B'B@8[/ i:3s٫' Kt,>;/+npp(; @kk}pcM[O>åN{m8](,/] -C0ãi:}2s4ڑͽs{Oj5fYBp8x+ppj)%@ܬ}gP(=|jr4%  n4f2GȰSYP( q> 'Oy̩>2M}#G_]um\K5Ne^VG/\߶z$۶d5z{{?|]xvS ǰf~r˶0m[ gNLNhvio VE "xdGBѣ:[Lf491|hp`p]PJﰚ#/ͣ\Z@Z\.bV- 3<3 uQ|Zpvz ZjCc\% TR*0b` i> |Jj() 0"^F|~I:RD؍PJC*yE-J饄2OQ*T-c:Oh}t)XNcZ2~O>?_bt,>uj*B;Q|7}F>. Z   )H]z|bzO%Ii;v_P)(Ajr @.RX f, Y7vsS,c7oyEx7X.`mfWÃ/7kEq&T#:JWabPXȯ;(jxE^E.yblܻ';w]^bQ\*^_AhvXWP÷ .ʂ ʐ,ttoskoW3gyi&87L36/ӄκ6n'qB!Ϝ4]_Lo/ꮞA.^*.[W8"4>p='H$X$:D>& qZ^f,|H`PFe[y.Jh'[+࢙Dҏ+cO5x{M|TU_P~RRX~nPeooos4tfia!ZbrR٪s̮rW4PoC>Ѐ"SL"@DR)N>} _Ϡ /3c|;}}Ͼ͛!Hc3 Zngg|43Gfnx;W|%I'')((~f$ lY6MwGZUsC*薪pzv&x]E ,geQBk+ ]{WC>9}mԎTT=[764WXW2( %gmڐL#0h B|78}V(Uk BaclXf,(-_ ":-kK3yfn{5Rf=_/+Ҍ H|ټ_k_ZVuYV,VRp~yvS^ G[kwd닁 cJeO:䫸h@|o@"Wbbb67gg7] }- %XYYE#n+7!bԿum==]jc'F(QAڶj(c/g[X+._νVBQE 9 ^IENDB`socnetv-1.9/src/images/add.png0000644000175000017500000000406112410526714016573 0ustar dimitrisdimitrisPNG  IHDRĴl;sRGBbKGD pHYs ,tIME2)'IDAT8Y7*;a/ -"Є(L;?IMJ:(B5P R   R9, qL;4 P3!+$   @*- >)5(  "   .    8! bQc 2SO@    <'wSA&-++ cq+ !$M)  E1 -*!T# XF2%< ;Ⱦ&086F! ׻: #"!^Ƣո?cšžR x0IENDB`socnetv-1.9/src/images/symmetry.png0000644000175000017500000001032012410526714017727 0ustar dimitrisdimitrisPNG  IHDR szzsRGBbKGD pHYs  tIME +]F tEXtCommentCreated with GIMPW+IDATX  \ZI F&'  J G][\ZI F&'  J G][ @eIENDB`socnetv-1.9/src/images/remove-old.png0000644000175000017500000000406112410526714020114 0ustar dimitrisdimitrisPNG  IHDRĴl;sRGBbKGD pHYs ,tIME#`vIDAT8Y 6Q 6Q  6Q 6QcdgtijpU@Cg ҺCgBf˱ VmƮ*̷5P7n2׽IENDB`socnetv-1.9/src/images/nodes.png0000644000175000017500000000205212410526714017151 0ustar dimitrisdimitrisPNG  IHDR @sBIT|d pHYs"":tEXtSoftwarewww.inkscape.org<IDATHALw??(mjY'6NH MD<]Ne  Kh] ;xY!4m,&hh-bk)è@m/~>~/+AϔRN'X,XS)#|뙈hfͺ:XH$Hkhc~~ebh3"2UJ)#wB<~+3fL& Q³{$TyoΟ" M륷 x;rmkkLx 9--kA3'rqa+L~wq0}"x3jr݋{7ޗ`C}xNhvVÜ6GkjXؕi]cn*$y8LTfi/֍y28l2MShFS,!"1Aq!T@KwE/mL 9LOt.agG璯xd҅h5J]cܩ$`un3( 2A|ݑ4 [^p+O_qwM,yZ2h5o͏.|bСIl#t"A@#Ɓ` pdy?l:-@ P /-6_B!;~Ɋ,xOYeP6׹a{hٶ|$ h4U$ @%  ,xV}P˶_  vNޘ ;J kaQM|x x@aM,|HB4aD>),)ܫJ*:>9ݷf<'ڗ|688koe %9`ɒ3Kf%3Df/x&^iUXR`[^@J3].#x-xHUOu]2n gY&]UUP22l:w; ޝŹmne 䃢*$(`ޣ2\Jun,أ(HO{-0 'x^`_޼yy]Z{{$sA6/_'x(0R[Kco,;v {$3I+ Q]]T%U-u'a|@,HN\@_&QPut*Y4c=^LPeZQ 68k79NG7 R덝PqS5_í4i B`.4oړ cƂ;w?tJ:&ʩ^zy#dWʖ nM-~-E<+hǐw~ϮP,h~z3--1[=ZiVSNJ1X,b蜼}=#Y0LS-W mH IENDB`socnetv-1.9/src/images/color.png0000644000175000017500000000266512410526714017171 0ustar dimitrisdimitrisPNG  IHDRĴl; pHYs  gAMA|Q cHRMz%u0`:o_F+IDATxb?-0@ ,x.G,e۲ draf#31ġ#"92"9hADn>bH=uGgg 6?pC_fg% LLL(`BBB  @ ۶m޽ssf  @` &*|ԩ_7o\MM@syA @pA*Ι3?p 3իW/\𿭭IINX0222``pm;zΝ/_xӧO?~Ia h`, r-#%K cǟ??{ݻwϚ5yO>^^11j@ʪĵk?#Lap&%?ܞ=_#oҥg̘gϞ578;!+_q˗_z5إ  d@@YO : Yc\ΉūV_L ˁ̟?S !//o(`q{s6fd  ]pu[_i@WX 2e &#򿊊JP`YscΝ7zn[@.] ZP tDmm`qpd.@ ff0'ڵk3(#,[ l… 2t"0x@·l/Pq@`vc ʐr쮀2(,Zܹs SKAWUUE$@ 600l ɉ ""M6yd+a" RmllqT*JDLmJd ²9H͗åosLJs%-+_B5z*@Z4F@rJt*`ҜDpĶz :¦-I wP}lz7|:ZJ%p) ƀ4P]RKKR{HN׬h *wBPr~ĨըM`WD HIA$|{E!Fm\+@prY~9))rzXF*=eyTթqv{2"`4T{n` L]P LD.{Zte%:\p|y9D,ѲXDB+4qT2+ Q/^&ULc+KR?#"[@# @bg_afG=k|xZXXHlY7lomnY= 7x= KMEudRlNLc.8Oܜշ?lW#"b XU|cmg~8PSikS*IENDB`socnetv-1.9/src/images/save.png0000644000175000017500000000224312410526714017001 0ustar dimitrisdimitrisPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<5IDATX͏TE{n@FM4DHČL\.M\kXÎč΂htA\ =`PQ` ^` QVR{:ԩz~6>>]vN5ػ」;v<:̗7XSڠkg|p?0x.ory #ӟd}k@E@xp,#˲m00aP|#%"+Օs΃* ,I Iw:琷Ղv+m(r8 㯼IkFؽ{k놮ZVmQg5 ,# /#&|#!Я'aa%% 4M:ólc V_)Fara-р: TIO4/!g iXvj#wPE x:Tx3x$wh6h߱R^DM_ 8ňKvRPZ0}@Ű&WU.2p}Ʉ-Jsf+7QW?תd~')Ü wl 55Y1{X0+WVU!ѐS,-޸Wj'WڣU<_o|w__sl}5ځ?MKFIENDB`socnetv-1.9/src/images/.directory0000600000175000017500000000010412526613416017330 0ustar dimitrisdimitris[Dolphin] PreviewsShown=true Timestamp=2015,5,19,13,57,18 Version=3 socnetv-1.9/src/images/saveas.png0000644000175000017500000000216112410526714017324 0ustar dimitrisdimitrisPNG  IHDRĴl;bKGD pHYs  ~tIME.*'#IDATxՓk]Us97KiK+Z:+ gӂ8с R,RŖU֛>ҤI{9g~8HL &koq>d< js}=u飹Ǐ8ܝ~q7vՑW5ogN;Nց>~H\'x$#M*Xk1b׎׸)Q] vʋ? /wi5}wĕcys<0k/ d_^'1@ӳxu҃77MPRN&4kd[?JudZ v/1;tF,Fa02yh7 # ;좹Ph ]HDrs:>̆ޕfq .nzc4 u F,{/>AmpWT{`v^rm/KC忁-"VVr::uXxv4A Ը8-|-yRkUiq:<6vo\BpRk`6\ba}UŦ˝jmG6m `zMKhmpR &Z0"tGpS5l64 mFpȖ93q|1B!b*'XV?"K\#E@7!n5"Tih2@k-8BS/07FR @!lS=13Ac ;>1b)CDIP݅2۱"OƄ#_;^'Z Zɲf@ұ4O64@TD\>S̃,%La#Mch@=LD)M+| bq# w$Pݛb#Y|R ei{7r Ww3!ՑBG[k婢$[ja릦 9ˤV.E`p"8n[q1;mKEzfuI $_p {^^j]v6٦?}eO:nlaǶ6>,- Ēj؈/<&y("(+fcd.voD 冈pz>0#" ss2 -!2EzZ`p._Z;ZI:1<Z 5\NKDƿ`VJ[>ȏ4eN4\p4!yiqd}!D`SF>Sij~%·R ,+@lk} 5tk8k pXH!Q 3E@*a|:WElJDPJJ9{77Xo/NG%"-i jz. * ********************************************************************************/ #include "matrix.h" #define TINY 1.0e-20 #include //allows the use of RAND_MAX macro #include //needed for fabs, qFloor etc /** * @brief Matrix::Matrix * Default constructor - creates a Matrix of given size (default 0) * Use resize(int) to resize it * @param Actors */ Matrix::Matrix (int rowDim, int colDim) : m_rows (rowDim), m_cols(colDim) { row = new (nothrow) Row[ m_rows ]; Q_CHECK_PTR( row ); for (register int i=0;i 0){ qDebug() << "Matrix::clear() deleting old rows"; m_rows=0; m_cols=0; delete [] row; } } /** * @brief Matrix::resize * Resize this matrix to m x n * Called before every operation on new matrices. * Every Row object holds max_int=32762 * @param Actors */ void Matrix::resize (const int m, const int n) { qDebug() << "Matrix: resize() "; clear(); m_rows = m; m_cols = n; row = new (nothrow) Row [ m_rows ]; Q_CHECK_PTR( row ); qDebug() << "Matrix: resize() -- resizing each row"; for (register int i=0;i 99999) newFieldWidth = fieldWidth -5; else if ( actorNumber > 9999) newFieldWidth = fieldWidth -4; else if ( actorNumber > 999) newFieldWidth = fieldWidth -3; else if ( actorNumber > 99) newFieldWidth = fieldWidth -2; else if ( actorNumber > 9) newFieldWidth = fieldWidth -1; os << qSetFieldWidth(newFieldWidth) << right << QString("%1").arg(actorNumber) ; } os< 99999) newFieldWidth = fieldWidth -6; else if ( r > 9999) newFieldWidth = fieldWidth -5; else if ( r > 999) newFieldWidth = fieldWidth -4; else if ( r > 99) newFieldWidth = fieldWidth -3; else if ( r > 9) newFieldWidth = fieldWidth -2 ; os << qSetFieldWidth(newFieldWidth) << QString("-") ; } os << qSetFieldWidth(1) << QString("-"); os.setPadChar(' '); os< 99999) newFieldWidth = fieldWidth -5; else if ( actorNumber > 9999) newFieldWidth = fieldWidth -4; else if ( actorNumber > 999) newFieldWidth = fieldWidth -3; else if ( actorNumber > 99) newFieldWidth = fieldWidth -2; else if ( actorNumber > 9) newFieldWidth = fieldWidth -1; else newFieldWidth = fieldWidth; os << qSetFieldWidth(newFieldWidth) << right << QString("%1 |").arg(actorNumber) ; for (register int c = 0; c < m.cols(); ++c) { element = m(r,c) ; newFieldWidth = fieldWidth; if ( element == RAND_MAX ) newFieldWidth = fieldWidth; else if ( element > 9999) newFieldWidth = fieldWidth -5; else if ( element > 9999) newFieldWidth = fieldWidth -4; else if ( element > 999) newFieldWidth = fieldWidth -3; else if ( element > 99) newFieldWidth = fieldWidth -2; else if ( element > 9) newFieldWidth = fieldWidth -1; else if ( (element - floor (element) ) != 0 ) { if ( element *10 == qFloor(10* element) ) newFieldWidth = fieldWidth-1; else if (element *100 == qFloor(100* element) ) newFieldWidth = fieldWidth-1; else if (element *1000 == qFloor(1000* element) ) newFieldWidth = fieldWidth-2; else newFieldWidth = fieldWidth-2; } else if (element < 1.0 ) { if ( element *10 == qFloor(10* element) ) newFieldWidth = fieldWidth-1; else if (element *100 == qFloor(100* element) ) newFieldWidth = fieldWidth-1; else if (element *1000 == qFloor(1000* element) ) newFieldWidth = fieldWidth-2; else newFieldWidth = fieldWidth-2; } else newFieldWidth = fieldWidth; if ( element == -1 || element == RAND_MAX) // we print infinity symbol instead of -1 (distances matrix). os << qSetFieldWidth(newFieldWidth) << right << QString("\xE2\x88\x9E"); else os << qSetFieldWidth(newFieldWidth) << right << element; } os << '\n'; } return os; } void Matrix::findMinMaxValues (float & maxVal, float &minVal){ maxVal=0; minVal=RAND_MAX; for (register int r = 0; r < rows(); ++r) { for (register int c = 0; c < cols(); ++c) { if ( item(r,c) > maxVal) maxVal = item(r,c) ; if ( item(r,c) < minVal){ minVal= item(r,c) ; } } } } // makes this square matrix the identity square matrix I void Matrix::identityMatrix(int dim) { qDebug() << "Matrix: identityMatrix() -- deleting old rows"; clear(); m_rows=dim; m_cols=dim; row = new (nothrow) Row [m_rows]; Q_CHECK_PTR( row ); qDebug() << "Matrix: resize() -- resizing each row"; for (int i=0;i=erased) { setItem( i, j, item(i,j+1) ) ; qDebug() << "case 2"; } if (i>=erased && j=erased && j>=erased) { setItem( i, j, item(i+1,j+1) ) ; qDebug() <<"case 4"; } if (i>=m_rows || j>=m_rows) { setItem( i, j, 0) ; qDebug() <<"case 5 (border)"; } qDebug() << "Matrix: new value (" << i << ", " << j << ")="<< item(i, j) ; } } for (register int i=0;i j && symmetry) { if (a.item(i,k)!=0 && b.item(j,k)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(j,k)); } else{ setItem(i,j, item(i,j)+a.item(i,k)*b.item(k,j)); } } qDebug() << "Matrix::product() - ("<< i+1 << ","<< j+1 << ") = " << item(i,j); } return *this; } //takes two ( N x N ) matrices (symmetric) and outputs an upper triangular matrix Matrix& Matrix::productSym( Matrix &a, Matrix & b) { for (register int i=0;i=j) continue; for (register int k=0;k j ) { if (a.item(i,k)!=0 && b.item(j,k)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(j,k)); } else //k <= j && ik ) { if (a.item(k,i)!=0 && b.item(k,j)!=0) setItem(i,j, item(i,j)+a.item(k,i)*b.item(k,j)); } else { if (a.item(i,k)!=0 && b.item(k,j)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(k,j)); } } return *this; } Matrix& Matrix::pow (int power, bool symmetry) { Matrix t=*this; for (register int k=1; k qFabs ( m_pivot ) ) { qDebug() << " A("<< i+1 << ","<< j+1 << ") = " << temp_pivot << " absolutely larger than current pivot "<< m_pivot << ". Marking new pivot line: " << i+1; m_pivotLine=i; m_pivot = temp_pivot ; } } if ( m_pivotLine != -1 ) { A.swapRows(m_pivotLine,j); swapRows(m_pivotLine,j); } qDebug()<<" multiplyRow() "<< j+1 << " by value " << 1/m_pivot ; for ( register int k=0; k< rows(); k++) { A.setItem ( j, k, (1/m_pivot) * A.item (j, k) ); setItem ( j, k, (1/m_pivot) * item (j, k) ); qDebug()<<" A.item("<< j+1 << ","<< k+1 << ") = " << A.item(j,k); qDebug()<<" item("<< j+1 << ","<< k+1 << ") = " << item(j,k); } qDebug() << "eliminate variables FromRowsBelow()" << j+1 ; for ( register int i=0; i< rows(); i++) { qDebug()<<" Eliminating item("<< i+1 << ","<< j+1 << ") = " << A.item(i,j) << " while at column j="<(1,n); float *vv; // vv stores the implicit scaling of each row vv=new (nothrow) float [n]; Q_CHECK_PTR( vv ); // QTextStream stream(stdout); // stream << "a = LU = " << a ; d=1.0; // No row interchanges yet. qDebug () << "Matrix::ludcmp() - loop over row to get scaling info" ; for (i=0;i big) big=temp; } if (big == 0) // No nonzero largest element. { qDebug() << "Matrix::ludcmp() - Singular matrix in routine ludcmp"; return false; } vv[i]=1.0/big; // Save the scaling. qDebug () << "Matrix::ludcmp() - big element in row i " << i+1 << " is "<< big << " row scaling vv[i] " << vv[i]; } qDebug () << "Matrix::ludcmp() - Start Crout's LOOP over columns"; for (j=0;j big) { // Is the figure of merit for the pivot better than the best so far? big=temp; imax=i; qDebug () << "Matrix::ludcmp() - found new largest pivot at row " << imax+1 << " big " << temp; } } qDebug () << "Matrix::ludcmp() - check for row interchange "; if (j != imax) // Do we need to interchange rows? { qDebug () << "Matrix::ludcmp() - interchanging rows " << imax+1 << " and " << j+1; for ( k=0; k=0;i--) { // Now we do the backsubstitution, equation (2.3.7). sum=b[i]; qDebug() << "Matrix::lubksb() backsubstitution: "<< "i " << i << " b[i] " << b[i] << "sum " << sum ; for ( j=i+1;j. * ********************************************************************************/ #include "datasetselectdialog.h" #include #include DataSetSelectDialog::DataSetSelectDialog (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); datasets_list << "Krackhardt: High-tech managers (advice), 24 actors" << "Krackhardt: High-tech managers (friendship), 24 actors" << "Krackhardt: High-tech managers (Reports To), 24 actors" << "Padgett: Florentine Families (marital relationship), 16 actors" << "Padgett: Florentine Families (business relationship), 16 actors" << "Zachary: Karate Club (simple ties), 34 actors" << "Zachary: Karate Club (weighted ties), 34 actors" << "Bernard: Killworth Fraternity (multirelational), 58 actors" << "Galaskiewicz: CEOs and clubs (affiliation data)" << "Freeman's EIES networks (multirelational, 32 actors)" << "Freeman: EIES network, at time-1, 48 actors" << "Freeman: EIES network, at time-2, 48 actors" << "Freeman: EIES network, number of messages, 48 actors" << "Freeman: The 34 possible graphs with N=5 (as multirelational), 5 actors" << "Mexican Power Network in the 1940s (list format)" << "Knocke: Bureacracies Information Exchange Network, 10 actors" <<"Stephenson and Zelen (1989): Network of 40 AIDS patiens (sex relationship)" << "Stephenson and Zelen (1989): Information Centrality test dataset, 5 actors" <<"Wasserman and Faust: star, circle and line graphs of 7 actors (multirelational)" << "Wasserman and Faust: Countries Trade (basic manufactured goods), 24 actors"; datasets_filenames << "Krackhardt_High-tech_managers_Advice_relation.sm" << "Krackhardt_High-tech_managers_Friendship_relation.sm" << "Krackhardt_High-tech_managers_ReportsTo_relation.sm" << "Padgett_Florentine_Families_Marital_relation.net" << "Padgett_Florentine_Families_Business_relation.paj" << "Zachary_Karate_Club_Simple_Ties.sm" << "Zachary_Karate_Club_Weighted_Ties.sm" << "Bernard_Killworth_Fraternity.dl" << "Galaskiewicz_CEOs_and_clubs_affiliation_network_data.2sm" << "Freeman_EIES_networks_32actors.dl" << "Freeman_EIES_network_48actors_Acquaintanceship_at_time-1.dl" << "Freeman_EIES_network_48actors_Acquaintanceship_at_time-2.dl" << "Freeman_EIES_network_48actors_Messages.dl" << "Freeman_34_possible_graphs_with_N_5_multirelational.paj" << "Mexican_Power_Network_1940s.lst" << "Knocke_Bureacracies_Information_Exchange_Network.pajek" <<"Stephenson&Zelen_40_AIDS_patiens_sex_contact.paj" << "Stephenson&Zelen_5actors_6edges_IC_test_dataset.paj" <<"Wasserman_Faust_7actors_star_circle_line_graphs.paj" << "Wasserman_Faust_Countries_Trade_Data_Basic_Manufactured_Goods.pajek"; (ui.selectBox) -> insertItems( 1, datasets_list ); } void DataSetSelectDialog::gatherData(){ qDebug()<< "DataSetSelectDialog: gathering Data!..."; int index = (ui.selectBox) -> currentIndex(); QString dataset_name = datasets_filenames[index]; qDebug()<< "DataSetSelectDialog: user selected: " << dataset_name; emit userChoices( dataset_name ); } void DataSetSelectDialog::on_buttonBox_accepted() { this->gatherData(); this->accept(); } void DataSetSelectDialog::on_buttonBox_rejected() { this->reject(); } DataSetSelectDialog::~DataSetSelectDialog(){ datasets_list.clear(); datasets_filenames.clear(); } socnetv-1.9/src/vertex.h0000775000175000017500000002536712542300240015565 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt vertex.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef VERTEX_H #define VERTEX_H #include #include #include #include #include #include #include using namespace std; class QPointF; class Graph; typedef QHash H_IntToStr; typedef QList ilist; typedef QPair pair_f_b; typedef QPair rel_w_bool; typedef QHash < int, rel_w_bool > H_edges; typedef QHash H_StrToInt; class Vertex : public QObject{ Q_OBJECT public: Vertex(Graph* parent, const long int &name, const int &val, const int &relation, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape); Vertex(const long int &name); ~Vertex(); long int name() const { return m_name; } void setName (const long int &name) { m_name=name; } void setEnabled (const bool &flag ) { m_enabled=flag; } bool isEnabled () const { return m_enabled; } void changeRelation(int) ; void addEdgeTo (const long int &v2, const float &weight); void addEdgeFrom(const long int &v1, const float &weight); void changeOutEdgeWeight (long int target, float weight); void setOutEdgeEnabled (long int, bool); void removeEdgeTo (long int target); void removeEdgeFrom(long int source); QHash* returnEnabledOutEdges(); QHash* returnReciprocalEdges(); long int outEdges(); long int outEdgesConst() const ; long int inEdges(); long int inEdgesConst() const ; long int outDegree(); long int outDegreeConst(); long int inDegree(); long int inDegreeConst(); long int localDegree(); /* sets eccentricity */ void setEccentricity (float c){ m_Eccentricity=c;} float eccentricity() { return m_Eccentricity;} /* Returns true if there is a reciprocal link from this vertex */ bool isReciprocalLinked() { return m_reciprocalLinked;} void setReciprocalLinked(bool reciprocal) { m_reciprocalLinked=reciprocal;} /* Returns true if there is an outLink from this vertex */ bool isOutLinked() { return (outEdges() > 0) ? true:false;} /* Returns the weight of the link to vertex V, otherwise zero*/ float hasEdgeTo(long int V); /* Returns true if there is an outLink from this vertex */ bool isInLinked() { return (inEdges() > 0) ? true:false;} float hasEdgeFrom (long int v); bool isIsolated() { return !(isOutLinked() | isInLinked()) ; } void setIsolated(bool isolated) {m_isolated = isolated; } void filterEdgesByWeight(float m_threshold, bool overThreshold); // void filterEdgesByColor(float m_threshold, bool overThreshold); void filterEdgesByRelation(int relation, bool status); void setSize(const int &size ) { m_size=size; } int size() const { return m_size; } void setShape(const QString &shape) { m_shape=shape; } QString shape() const { return m_shape; } void setColor(const QString &color) { m_color=color; } QString color() const { return m_color; } QString colorToPajek(); void setNumberColor (const QString &color) { m_numberColor = color; } QString numberColor() const { return m_numberColor; } void setNumberSize (const int &size) { m_numberSize=size; } int numberSize() const { return m_numberSize; } void setLabel (const QString &label) { m_label=label; } QString label() const { return m_label; } void setLabelColor (const QString &labelColor) { m_labelColor=labelColor; } QString labelColor() const { return m_labelColor; } void setLabelSize(const int &newSize) { m_labelSize=newSize; } int labelSize() const { return m_labelSize; } void setX(const float &x) { m_x=x; } float x() const { return m_x; } void setY(const float &y) { m_y=y; } float y() const { return m_y; } QPointF pos () const { return QPointF ( x(), y() ); } //returns displacement vector QPointF & disp() { return m_disp; } void set_dispX (float x) { m_disp.rx() = x ; } void set_dispY (float y) { m_disp.ry() = y ; } //FIXME -- VERY SLOW? void setOutLinkColor(const long int &v2, const QString &color) { outLinkColors[v2]=color; } //FIXME: See MW line 1965 - FIXME MULTIGRAPH QString outLinkColor(const long int &v2) { if (outLinkColors.contains(v2)) return outLinkColors.value(v2); else return "black"; } void setDelta (float c){ m_delta=c;} /* Sets vertex pair dependancy */ float delta() { return m_delta;} /* Returns vertex pair dependancy */ void clearPs() ; void appendToPs(long int vertex ) ; ilist Ps(void); void setDC (float c){ m_DC=c;} /* Sets vertex Degree Centrality*/ void setSDC (float c ) { m_SDC=c;} /* Sets standard vertex Degree Centrality*/ float DC() { return m_DC;} /* Returns vertex Degree Centrality*/ float SDC() { return m_SDC;} /* Returns standard vertex Degree Centrality*/ void setCC (float c){ m_CC=c;} /* sets vertex Closeness Centrality*/ void setSCC (float c ) { m_SCC=c;} /* sets standard vertex Closeness Centrality*/ float CC() { return m_CC;} /* Returns vertex Closeness Centrality*/ float SCC() { return m_SCC; } /* Returns standard vertex Closeness Centrality*/ void setIRCC (float c){ m_IRCC=c;} /* sets vertex IRCC */ void setSIRCC (float c ) { m_SIRCC=c;} /* sets standard vertex IRCC */ float IRCC() { return m_IRCC;} /* Returns vertex IRCC */ float SIRCC() { return m_SIRCC; } /* Returns standard vertex IRCC*/ void setBC(float c){ m_BC=c;} /* sets s vertex Betweenness Centrality*/ void setSBC (float c ) { m_SBC=c;} /* sets standard vertex Betweenness Centrality*/ float BC() { return m_BC;} /* Returns vertex Betweenness Centrality*/ float SBC() { return m_SBC; } /* Returns standard vertex Betweenness Centrality*/ void setSC (float c){ m_SC=c;} /* sets vertex Stress Centrality*/ void setSSC (float c ) { m_SSC=c;} /* sets standard vertex Stress Centrality*/ float SC() { return m_SC;} /* Returns vertex Stress Centrality*/ float SSC() { return m_SSC; } /* Returns standard vertex Stress Centrality*/ void setEC(float dist) { m_EC=dist;} /* Sets max Geodesic Distance to all other vertices*/ void setSEC(float c) {m_SEC=c;} float EC() { return m_EC;} /* Returns max Geodesic Distance to all other vertices*/ float SEC() { return m_SEC;} void setPC (float c){ m_PC=c;} /* sets vertex Power Centrality*/ void setSPC (float c ) { m_SPC=c;} /* sets standard vertex Power Centrality*/ float PC() { return m_PC;} /* Returns vertex Power Centrality*/ float SPC() { return m_SPC; } /* Returns standard vertex Power Centrality*/ void setIC (float c){ m_IC=c;} /* sets vertex Information Centrality*/ void setSIC (float c ) { m_SIC=c;} /* sets standard vertex Information Centrality*/ float IC() { return m_IC;} /* Returns vertex Information Centrality*/ float SIC() { return m_SIC; } /* Returns standard vertex Information Centrality*/ void setDP (float c){ m_DP=c;} /* Sets vertex Degree Prestige */ void setSDP (float c ) { m_SDP=c;} /* Sets standard vertex Degree Prestige */ float DP() { return m_DP;} /* Returns vertex Degree Prestige */ float SDP() { return m_SDP;} /* Returns standard vertex Degree Prestige */ void setPRP (float c){ m_PRC=c;} /* sets vertex PageRank*/ void setSPRP (float c ) { m_SPRC=c;} /* sets standard vertex PageRank*/ float PRP() { return m_PRC;} /* Returns vertex PageRank */ float SPRP() { return m_SPRC; } /* Returns standard vertex PageRank*/ void setPP (float c){ m_PP=c;} /* sets vertex Proximity Prestige */ void setSPP (float c ) { m_SPP=c;} /* sets standard vertex Proximity Prestige */ float PP() { return m_PP;} /* Returns vertex Proximity Prestige */ float SPP() { return m_SPP; } /* Returns standard vertex Proximity Prestige */ float CLC() { return m_CLC; } void setCLC(float clucof) { m_CLC=clucof; m_hasCLC=true; } bool hasCLC() { return m_hasCLC; } int cliques (const int &size); bool addClique (const QString &clique, const int &size); void clearCliques() { m_cliques.clear(); } //hold all outbound and inboud edges of this vertex. H_edges m_outEdges, m_inEdges; signals: void setEdgeVisibility (int, int, int, bool); protected: private: Graph *parentGraph; ilist myPs; long int m_name, m_outEdgesCounter, m_inEdgesCounter, m_outDegree, m_inDegree, m_localDegree; float m_Eccentricity; int m_value, m_size, m_labelSize, m_numberSize, m_curRelation; H_StrToInt m_cliques; bool m_reciprocalLinked, m_enabled, m_hasCLC, m_isolated; QString m_color, m_numberColor, m_label, m_labelColor, m_shape; QPointF m_disp; //QString *outLinkColors; H_IntToStr outLinkColors; //FIXME vertex coords double m_x, m_y; float m_CLC; float m_delta, m_EC, m_SEC; float m_DC, m_SDC, m_DP, m_SDP, m_CC, m_SCC, m_BC, m_SBC, m_IRCC, m_SIRCC, m_SC, m_SSC; float m_PC, m_SPC, m_SIC, m_IC, m_SPRC, m_PRC; float m_PP, m_SPP; }; #endif socnetv-1.9/src/guide.h0000775000175000017500000000430712542300240015334 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt Guide.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef Guide_H #define Guide_H #include #include class GraphicsWidget; static const int TypeGuide = QGraphicsItem::UserType+6; class Guide : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: Guide(GraphicsWidget *, int, int, int ); Guide(GraphicsWidget *, int, int ); enum { Type = UserType + 6 }; int type() const { return Type; } void die(); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: GraphicsWidget *graphicsWidget; int m_x0, m_y0, m_radius, width; bool circle; }; #endif socnetv-1.9/src/previewform.cpp0000664000175000017500000001057612542300240017141 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt previewform.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net comment : code borrowed from Qt5 codecs example ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include #include "previewform.h" PreviewForm::PreviewForm(QWidget *parent) : QDialog(parent) { encodingComboBox = new QComboBox; encodingLabel = new QLabel(tr("&Encoding:")); encodingLabel->setBuddy(encodingComboBox); textEdit = new QTextEdit; textEdit->setToolTip(tr("In this area you can preview your file.\n") + (" Select the correct encoding from the menu.\n") + (" Mac and Linux users select UTF-8\n") + (" Windows users select Windows-1253 or UTF-8\n") + (" Windows users in Russia select KOI8-R\n")); textEdit->setLineWrapMode(QTextEdit::NoWrap); textEdit->setReadOnly(true); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(encodingComboBox, SIGNAL(activated(int)), this, SLOT(updateTextEdit())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QGridLayout *mainLayout = new QGridLayout; mainLayout->addWidget(encodingLabel, 0, 0); mainLayout->addWidget(encodingComboBox, 0, 1); mainLayout->addWidget(textEdit, 1, 0, 1, 2); mainLayout->addWidget(buttonBox, 2, 0, 1, 2); setLayout(mainLayout); setWindowTitle(tr("Preview file & Choose Encoding")); resize(600, 400); } void PreviewForm::setCodecList(const QList &list) { encodingComboBox->clear(); foreach (QTextCodec *codec, list) encodingComboBox->addItem(codec->name(), codec->mibEnum()); } void PreviewForm::setEncodedData(const QByteArray &data, const QString m_fileName, const int m_format) { fileName = m_fileName; format = m_format; encodedData = data; updateTextEdit(); } void PreviewForm::updateTextEdit() { int mib = encodingComboBox->itemData( encodingComboBox->currentIndex()).toInt(); QTextCodec *codec = QTextCodec::codecForMib(mib); qDebug () << " PreviewForm::updateTextEdit() " << codec->name(); QTextStream in(&encodedData); in.setAutoDetectUnicode(false); in.setCodec(codec); decodedStr = in.readAll(); textEdit->setPlainText(decodedStr); } void PreviewForm::accept() { int mib = encodingComboBox->itemData( encodingComboBox->currentIndex()).toInt(); QTextCodec *codec = QTextCodec::codecForMib(mib); qDebug () << " PreviewForm::accept() returning codec name " << codec->name(); emit userCodec(fileName, codec->name(), format); QDialog::accept(); } socnetv-1.9/src/texteditor.h0000775000175000017500000000514512542300240016433 0ustar dimitrisdimitris/**************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt texteditor.h ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com *****************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef TEXTEDITOR_H #define TEXTEDITOR_H #include class QAction; class QMenu; class QTextEdit; class TextEditor : public QMainWindow { Q_OBJECT public: TextEditor(const QString &fileName , QWidget *parent=0 ); protected: void closeEvent(QCloseEvent *event); private slots: void newFile(); void open(); bool save(); bool saveAs(); void about(); void documentWasModified(); private: void createActions(); void createMenus(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool maybeSave(); void loadFile(const QString &fileName); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); QString strippedName(const QString &fullFileName); QTextEdit *textEdit; QString curFile; QMenu *fileMenu; QMenu *editMenu; QMenu *helpMenu; QToolBar *fileToolBar; QToolBar *editToolBar; QAction *newAct; QAction *openAct; QAction *saveAct; QAction *saveAsAct; QAction *exitAct; QAction *cutAct; QAction *copyAct; QAction *pasteAct; QAction *aboutAct; QAction *aboutQtAct; }; #endif socnetv-1.9/src/application.qrc0000775000175000017500000000040112377612576017117 0ustar dimitrisdimitris editcopy.xpm editcut.xpm filenew.xpm fileopen.xpm editpaste.xpm filesave.xpm socnetv-1.9/src/mainwindow.h0000775000175000017500000004052212542300240016412 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt mainwindow.h - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.sourceforge.net ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #ifndef APP_H #define APP_H #include #include #include /** SocNetV specific includes*/ #include #include "graphicswidget.h" #include "graph.h" #include "filteredgesbyweightdialog.h" #include "webcrawlerdialog.h" #include "nodeeditdialog.h" #include "datasetselectdialog.h" static const QString VERSION="1.9"; /** * This Class is the base class. It sets up the main * window and provides a menubar, toolbar and statusbar. * For the main view, an instance of class GraphicsWidget is * created which creates a graphics widget. */ using namespace std; class QMenu; class QAction; class QCheckBox; class QProgressDialog; class Edge; class Node; class QPushButton; class QLCDNumber; class QSlider; class QComboBox; class QGroupBox; class QTabWidget; class QSpinBox; class PreviewForm; class RandErdosRenyiDialog; class RandSmallWorldDialog; class RandScaleFreeDialog; class MainWindow : public QMainWindow { Q_OBJECT public: GraphicsWidget *graphicsWidget; MainWindow(const QString &f); ~MainWindow(); void initActions(); void initMenuBar(); void initToolBar(); void initToolBox(); void initStatusBar(); void initNet(); void initView(); void setLastPath(QString filePath); QString getLastPath(); void createFortuneCookies(); void createTips(); bool showLabels(); bool showNumbersInsideNodes(); bool showNumbers(); // Main network file loader methods bool previewNetworkFile(QString , int ); bool loadNetworkFile ( const QString, const QString, const int ); int activeEdges(); int activeNodes(); void openContextMenu(const QPointF & mPos); void changeAllNodesSize(int size); QString initNodeColor; int clickedJimNumber; //it is public because we need to be visible from activegraph. void createProgressBar(); void destroyProgressBar(); public slots: //FILE MENU void slotCreateNew(); void slotChooseFile(); void slotImportGraphML(); void slotImportPajek(); void slotImportSM(); void slotImportDot(); void slotImportGML(); void slotImportDL(); void slotImportEdgeList(); void slotImportTwoModeSM(); void slotFileSave(); void slotFileSaveAs(); void slotFileClose(); void slotPrintView(); bool slotExportBMP(); bool slotExportPNG(); bool slotExportPDF(); void slotExportPajek(); void slotExportSM(); bool slotExportDL(); bool slotExportGW(); bool slotExportList(); //NETWORK MENU void slotOpenTextEditor(); void slotViewNetworkFile(); void findCodecs(); void userCodec(const QString, const QString, const int ); void slotViewAdjacencyMatrix(); void slotShowDataSetSelectDialog(); void slotRecreateDataSet(QString); void slotCreateRandomErdosRenyi(); void createRandomNetErdos( const int N, const QString model, const int edges, const float eprob, const QString mode, const bool diag) ; void slotCreateRegularRandomNetwork(); void slotCreateRandomGaussian(); void slotCreateRandomRingLattice(); void slotCreateRandomScaleFree(); void createScaleFreeNetwork (const int &nodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode); void slotCreateRandomSmallWorld(); void createSmallWorldNetwork (const int &nodes, const int °ree, const float &beta, const QString &mode, const bool &diag); void slotShowWebCrawlerDialog(); void slotWebCrawl(QString, int, int, bool, bool); void prevRelation(); void nextRelation(); void addRelation(); void addRelation(QString relationName); //EDIT MENU void slotSelectAll(); void slotSelectNone(); void slotFindNode(); void slotAddEdge(); void slotRemoveNode(); void slotNodeProperties( const QString, const int, const QString, const QColor, const QString); void slotRemoveEdge(); void slotChangeNodeProperties(); void slotChangeEdgeLabel(); void slotChangeEdgeColor(); void slotChangeEdgeWeight(); void slotFilterNodes(); void slotFilterIsolateNodes(bool checked); void slotShowFilterEdgesDialog(); void slotTransformNodes2Edges(); void slotSymmetrize(); // LAYOUT MENU void slotColorationStrongStructural(); void slotColorationRegular(); void slotLayoutRandom(); void slotLayoutCircularRandom(); void slotLayoutCircularByProminenceIndex(); void slotLayoutCircularByProminenceIndex(QString); void slotLayoutNodeSizesByProminenceIndex(QString); void slotLayoutLevelByProminenceIndex(); void slotLayoutLevelByProminenceIndex(QString); void slotLayoutGuides(int); void slotLayoutSpringEmbedder(bool); void slotLayoutFruchterman(); void layoutFruchterman(int); void slotLayoutNodeSizesByOutDegree(bool); void slotLayoutNodeSizesByInDegree(bool); //STATISTICS MENU void askAboutWeights(); void slotDistancesMatrix(); void slotGeodesicsMatrix(); void slotGraphDistance(); void slotAverageGraphDistance(); void slotDiameter(); void slotEccentricity(); void slotWalksOfGivenLength(); void slotTotalWalks(); void slotReachabilityMatrix(); void slotConnectedness(); void slotCliqueCensus(); void slotClusteringCoefficient(); void slotTriadCensus(); void slotCheckSymmetry(); void slotInvertAdjMatrix(); void slotCentralityDegree(); void slotCentralityCloseness(); void slotCentralityClosenessInfluenceRange(); void slotCentralityBetweenness(); void slotCentralityInformation(); void slotCentralityStress(); void slotCentralityPower(); void slotCentralityEccentricity(); void slotPrestigeDegree(); void slotPrestigePageRank(); void slotPrestigeProximity(); //OPTIONS MENU void slotDisplayNodeNumbers(bool toggle); void slotDisplayNodeLabels(bool toggle); void slotDisplayNumbersInsideNodes(bool toggle); void slotChangeAllNodesSize(); void slotChangeAllNodesShape(); void slotChangeNumbersSize(); void slotChangeLabelsSize(); void slotDrawEdgesThickAsWeights(); void slotDrawEdgesBezier(bool toggle); void slotDisplayEdgesWeightNumbers(bool toggle); void slotConsiderEdgeWeights(bool); void slotDisplayEdges(bool toggle); void slotDisplayEdgesArrows(bool toggle); void slotBackgroundColor (); void slotAllNodesColor(); void slotAllEdgesColor(); void slotAllNumbersColor(); void slotAllLabelsColor(); //VIEW MENU void slotAntialiasing(bool ); void slotShowProgressBar(bool toggle); void slotPrintDebug(bool toggle); void slotViewToolBar(bool toggle); void slotViewStatusBar(bool toggle); void slotBackgroundImage(bool toggle); //HELP MENU void slotTips(); void slotHelp(); void slotHelpAbout(); void slotAboutQt(); //PUBLICLY AVAILABLE SLOTS. CALLED FROM GRAPHICSVIEW void nodeInfoStatusBar(Node*); void edgeInfoStatusBar (Edge*); void openNodeContextMenu(); void openEdgeContextMenu() ; void windowInfoStatusBar(int, int); void graphChanged(); //Called by graphicswidget to update node coords in activeGraph void updateNodeCoords(int no, int x, int y); //Called when user pushes the New Node button on the MW void addNode(); //Called by graphicswidget when the user middle-clicks void addEdge (int v1, int v2, float weight); //Called by graphicswidget when the user double-clicks void addNodeWithMouse(int, QPointF); //Called by Graph on saving file. int is the network type saved. void networkSaved(int); //Called by Graph to display some message to the user void statusMessage(const QString); void showMessageToUser(const QString); //Called from Graph when a network file is loaded. void fileType(int, QString , int, int, bool); //Called from MW, when user highlights something in the toolbox Comboboxes void toolBoxAnalysisGeodesicsSelectChanged(int); void toolBoxAnalysisConnectivitySelectChanged(int); void toolBoxAnalysisProminenceSelectChanged(int); void toolBoxAnalysisClusterabilitySelectChanged(int); void toolBoxLayoutByIndexButtonPressed(); QList selectedNodes(); protected: void resizeEvent( QResizeEvent * ); void closeEvent( QCloseEvent* ce ); // void myMessageOutput(QtMsgType type, const char *msg); signals: void addRelationToGraph(QString); private: QGraphicsScene *scene; FilterEdgesByWeightDialog m_filterEdgesByWeightDialog; WebCrawlerDialog m_WebCrawlerDialog; NodeEditDialog *m_nodeEditDialog; RandErdosRenyiDialog *m_randErdosRenyiDialog; RandSmallWorldDialog *m_randSmallWorldDialog; RandScaleFreeDialog *m_randScaleFreeDialog; PreviewForm *previewForm; QList codecs; QString userSelectedCodecName; DataSetSelectDialog m_datasetSelectDialog; Graph activeGraph; QPrinter *printer; QToolBar *toolBar; QComboBox *zoomCombo, *changeRelationCombo; QTabWidget *toolBox; QProgressDialog *progressDialog; Node *clickedJim; Edge *clickedEdge; QMenu *importSubMenu, *exportSubMenu, *editMenu, *statMenu, *helpMenu; QMenu *optionsMenu, *colorOptionsMenu, *edgeOptionsMenu, *nodeOptionsMenu, *viewOptionsMenu; QMenu *editNodeMenu, *editEdgeMenu, *centrlMenu, *layoutMenu; QMenu *networkMenu, *randomNetworkMenu, *filterMenu; QMenu *randomLayoutMenu, *circleLayoutMenu, *levelLayoutMenu, *physicalLayoutMenu; QMenu *colorationMenu; QCheckBox *layoutEadesBx, *layoutFruchtermanBx, *layoutKamandaBx, *nodeSizesByOutDegreeBx,*nodeSizesByInDegreeBx, *layoutGuidesBx; QComboBox *toolBoxAnalysisGeodesicsSelect,*toolBoxAnalysisConnectivitySelect, *toolBoxAnalysisProminenceSelect, *toolBoxAnalysisClusterabilitySelect; QComboBox *toolBoxLayoutByIndexSelect, *toolBoxLayoutByIndexTypeSelect; QPushButton *addNodeBt, *addEdgeBt, *removeNodeBt, *removeEdgeBt, *toolBoxLayoutByIndexButton; QSpinBox *rotateSpinBox ; QAction *fileNew, *fileOpen, *fileSave, *fileSaveAs,*fileClose, *printNetwork,*fileQuit; QAction *exportBMP, *exportPNG, *exportPajek, *exportPDF, *exportDL, *exportGW, *exportSM, *exportList; QAction *importPajek,*importSM, *importList, *importDot , *importDL, *importTwoModeSM; QAction *viewNetworkFileAct, *openTextEditorAct, *viewSociomatrixAct, *recreateDataSetAct; QAction *createErdosRenyiRandomNetworkAct, *createGaussianRandomNetworkAct; QAction *createLatticeNetworkAct, *createScaleFreeRandomNetworkAct; QAction *createSmallWorldRandomNetworkAct, *createRegularRandomNetworkAct; QAction *displayNodeNumbersAct, *displayNodeLabelsAct, *displayNumbersInsideNodesAct; QAction *selectNoneAct, *selectAllAct; QAction *findNodeAct,*addNodeAct, *addEdgeAct, *removeNodeAct, *propertiesNodeAct, *removeEdgeAct; QAction *changeNumbersSizeAct; QAction *changeLabelsSizeAct, *changeAllNodesSizeAct, *changeAllNodesShapeAct; QAction *changeEdgeLabelAct, *changeEdgeColorAct, *changeEdgeWeightAct; QAction *filterNodesAct, *filterIsolateNodesAct, *filterEdgesAct, *transformNodes2EdgesAct, *symmetrizeAct; QAction *changeBackColorAct, *changeAllNodesColorAct, *changeAllEdgesColorAct, *changeAllNumbersColorAct, *changeAllLabelsColorAct; QAction *drawEdgesWeightsAct, *displayEdgesWeightNumbersAct, *displayEdgesAct; QAction *displayEdgesArrowsAct, *drawEdgesBezier,*considerEdgeWeightsAct; QAction *backgroundImageAct, *viewToolBar, *viewStatusBar, *helpAboutApp, *helpAboutQt, *helpApp, *tipsApp; QAction *antialiasingAct; QAction *webCrawlerAct; QAction *netDensity, *symmetryAct, *graphDistanceAct, *averGraphDistanceAct, *distanceMatrixAct, *geodesicsMatrixAct, *diameterAct, *eccentricityAct; QAction *walksAct,*totalWalksAct, *reachabilityMatrixAct, *connectednessAct; QAction *cliquesAct, *clusteringCoefAct, *triadCensusAct, *invertAdjMatrixAct; QAction *cDegreeAct, *cInDegreeAct, *cClosenessAct, *cInfluenceRangeClosenessAct, *cBetweennessAct, *cInformationAct, *cPageRankAct, *cStressAct, *cPowerAct, *cEccentAct, *cProximityPrestigeAct; QAction *randLayoutAct, *randCircleLayoutAct, *clearGuidesAct; QAction *layoutCircular_DC_Act, *layoutCircular_DP_Act, *layoutCircular_CC_Act, *layoutCircular_SC_Act, *layoutCircular_EC_Act, *layoutCircular_PC_Act, *layoutCircular_BC_Act, *layoutCircular_IC_Act, *layoutCircular_IRCC_Act,*layoutCircular_PRP_Act, *layoutCircular_PP_Act; QAction *layoutLevel_DC_Act, *layoutLevel_DP_Act, *layoutLevel_CC_Act, *layoutLevel_SC_Act, *layoutLevel_EC_Act, *layoutLevel_PC_Act, *layoutLevel_BC_Act, *layoutLevel_IC_Act, *layoutLevel_IRCC_Act,*layoutLevel_PRP_Act, *layoutLevel_PP_Act; QAction *strongColorationAct, *regularColorationAct, *showProgressBarAct, *printDebugAct; QAction *springLayoutAct, *FRLayoutAct; QAction *nodeSizesByOutDegreeAct, *nodeSizesByInDegreeAct; QAction *zoomInAct, *zoomOutAct ; QAction *nextRelationAct, *prevRelationAct, *addRelationAct; QString fileName, networkName, previous_fileName; QString dataDir, lastUsedDirPath; QStringList fileNameNoPath, fortuneCookie, rgbValues; QStringList tempFileNameNoPath, colorList, tips; int statusBarDuration, minDuration, progressCounter; int maxNodes; int initNodeSize, labelDistance, numberDistance,initNumberSize, initLabelSize; int fortuneCookiesCounter, tipsCounter; //QString VERSION; bool pajekFileLoaded, adjacencyFileLoaded, dotFileLoaded, graphMLFileLoaded; bool fileLoaded, checkSelectFileType; int fileFormat; bool networkModified; bool bezier, edgeClicked, nodeClicked, markedNodesExist, showProgressBar, firstTime; bool considerWeights, inverseWeights, askedAboutWeights; QString initFileCodec; QString initEdgeColor, initNumberColor, initNodeShape, initLabelColor; QColor initBackgroundColor; QPointF cursorPosGW; //Carries the position of the cursor in graphicsWidget coordinates QLCDNumber *inDegreeLCD, *outDegreeLCD , *selectedNodeLCD, *clucofLCD; QLCDNumber *nodesLCD, *edgesLCD, *densityLCD; QDateTime actualDateTime, actualDate, actualTime; QTime eTime; //used to time algorithms. }; #endif socnetv-1.9/src/nodelabel.cpp0000775000175000017500000000377612542300240016530 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 1.9 Written in Qt nodelabel.cpp - description ------------------- copyright : (C) 2005-2015 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * 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 3 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, see . * ********************************************************************************/ #include "nodelabel.h" #include "node.h" #include NodeLabel::NodeLabel( Node *jim , int size, QString labelText) :QGraphicsTextItem(0) { source=jim; jim -> addLabel(this); setParentItem(jim); //auto disables child items like this, when node is disabled. setPlainText( labelText ); setFont( QFont ("Times", size, QFont::Light, true) ); setZValue (253); } void NodeLabel::removeRefs(){ source->deleteLabel(); } NodeLabel::~NodeLabel(){ }