pax_global_header00006660000000000000000000000064137743634000014521gustar00rootroot0000000000000052 comment=c83642c4f06bc1b52b3fc6d515da6784069c93de app-2.8/000077500000000000000000000000001377436340000121525ustar00rootroot00000000000000app-2.8/.appveyor.yml000066400000000000000000000104231377436340000146200ustar00rootroot00000000000000version: 'continuous.{build}' branches: only: - master - develop except: - # Do not build tags that we create when we or travis upload to GitHub Releases - # does not work see https://github.com/appveyor/ci/issues/1493 - /^(?i:continuous)$/ - /continuous/ # # Do not build on tags (GitHub and BitBucket) # skip_tags: true # # Start builds on tags only (GitHub and BitBucket) # skip_non_tags: true # Including commits with particular message or from specific user only_commits: #message: /build/ # Start a new build if message contains 'build' author: dimitris.kalamaras@gmail.com # Start a new build for commit of user with email jack@company.com #---------------------------------# # environment configuration # #---------------------------------# # Build worker image (VM template) image: Visual Studio 2019 # clone directory clone_folder: c:\projects\socnetv install: - cmd: echo %APPVEYOR_REPO_COMMIT% - cmd: echo %APPVEYOR_REPO_TAG% - cmd: echo "Setting last commit short id" - cmd: git rev-parse --short HEAD > MYVER.txt - cmd: set /p LAST_COMMIT_SHORT= < MYVER.txt - cmd: echo %LAST_COMMIT_SHORT% - cmd: del MYVER.txt - echo %APPVEYOR_REPO_COMMIT:~0,7% - cmd: echo "Setting SocNetV current version" - cmd: set SOCNETV_VERSION=2.8 - cmd: echo %SOCNETV_VERSION% - cmd: echo "Setting build type" - cmd: set BUILD_TYPE=release - cmd: echo %BUILD_TYPE% - cmd: echo "Setting BUILDNAME" - if "%APPVEYOR_REPO_TAG%"=="true" set BUILDNAME=%APPVEYOR_REPO_TAG_NAME%-%LAST_COMMIT_SHORT% - if "%APPVEYOR_REPO_TAG%"=="true" if "%APPVEYOR_REPO_TAG_NAME%"=="continuous" set BUILDNAME=%SOCNETV_VERSION%-%LAST_COMMIT_SHORT% - if "%APPVEYOR_REPO_TAG%"=="false" set BUILDNAME=%SOCNETV_VERSION%-%LAST_COMMIT_SHORT% - cmd: echo %BUILDNAME% - cmd: echo "Setting Qt environment..." - cmd: echo "Check Qt folders (debug)..." - cmd: dir C:\Qt - cmd: dir C:\Qt\5.15 - cmd: dir C:\Qt\5.12 - cmd: dir C:\Qt\Tools #- set QTDIR=C:\Qt\5.15\mingw81_64 - set QTDIR=C:\Qt\5.12\mingw73_64 - cmd: echo "Check project folder..." - cmd: dir C:\projects\socnetv - cmd: echo "Installing Innosetup..." - choco install -y InnoSetup - cmd: echo "Setting paths..." - set PATH=%QTDIR%\bin;C:\Qt\Tools\mingw810_64\bin;%PATH%;"C:\Program Files (x86)\Inno Setup 6" build_script: - cmd: echo "Running qmake..." - qmake "CONFIG+=%BUILD_TYPE%" socnetv.pro - cmd: echo "BUILDING %BUILD_TYPE% starts here with mingw32..." - mingw32-make after_build: - cmd: echo "check folder contents" - cmd: dir C:\projects\socnetv - cmd: dir C:\projects\socnetv\%BUILD_TYPE% - cmd: echo "Executing windeployqt" - windeployqt %BUILD_TYPE%/socnetv.exe - cmd: echo "check folder contents again..." - cmd: dir C:\projects\socnetv\%BUILD_TYPE% - cmd: echo "Deleting *.o and *.cpp..." - cmd: del C:\projects\socnetv\%BUILD_TYPE%\*.cpp - cmd: del C:\projects\socnetv\%BUILD_TYPE%\*.o - cmd: echo "check folder contents again..." - cmd: dir C:\projects\socnetv\%BUILD_TYPE% - cmd: cp COPYING %BUILD_TYPE%/LICENSE.txt" - cmd: echo "Running Innosetup..." - cmd: echo "Available Innosetup languages:" - cmd: dir "C:\Program Files (x86)\Inno Setup 6\Languages" - iscc innosetup.iss - cmd: type Setup-Manifest.txt - cmd: dir SocNetV*.exe - cmd: echo "Renaming file" - cmd: rename SocNetV-*installer.exe SocNetV-%BUILDNAME%-windows-installer.exe - cmd: dir SocNetV*.exe - cmd: dir artifacts: - path: SocNetV-*-installer.exe # Deploy to GitHub Releases deploy: - provider: GitHub tag: continuous description: 'Continuous pre-releases' artifact: /SocNetV.*installer\.exe/ auth_token: secure: owdxko+lS3nNcxIsxL4OTdfZ5UPwudTp2vtk8TlErxd0/tqAfsPGmZ8BjN5sYTTk # your encrypted token from GitHub draft: false prerelease: true force_update: true # on: # branch: master # release from master branch only # APPVEYOR_REPO_TAG: false # deploy on tag push only # Notify developer on any change notifications: # Email - provider: Email to: - dimitris.kalamaras@gmail.com subject: '[socnetv/app] build {{status}}' # optional # message: "{{message}}, {{commitId}}, ..." # optional on_build_status_changed: true app-2.8/.travis.yml000066400000000000000000000032331377436340000142640ustar00rootroot00000000000000 branches: except: - # Do not build tags that we create when we upload to GitHub Releases - /^(?i:continuous)$/ language: cpp # os: # - linux # - osx # sudo: require # deprecated dist: xenial # compiler: # - clang # - gcc env: global: - SOCNETV_VERSION=2.8 # We use it to name packages on non-tagged commits # explicitly define our build # maybe add a linux/clang pair ? jobs: include: - os: linux compiler: gcc env: FAILURES=true - os: osx compiler: clang env: FAILURES=true exclude: - os: osx compiler: gcc allow_failures: - env: FAILURES=TRUE before_install: # Add repositories etc - chmod +x .travis_before_install.sh - ./.travis_before_install.sh install: - echo "Install any required packages and dependencies"; - chmod +x .travis_install.sh - ./.travis_install.sh script: # Run the build script - if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then chmod +x .travis_build_linux.sh ; fi - '[ "$TRAVIS_OS_NAME" != linux ] || ./.travis_build_linux.sh' - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then chmod +x .travis_build_macos.sh ; fi - '[ "$TRAVIS_OS_NAME" != osx ] || ./.travis_build_macos.sh' after_success: - ls -lsh #- curl --upload-file ./Social*.AppImage https://transfer.sh/SocNetV-git.$(git rev-parse --short HEAD)-x86_64.AppImage - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh - echo "Listing directory" - ls -lsh - if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then bash upload.sh *SocNetV*.AppImage*; fi - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then bash upload.sh *SocNetV*.zip*; bash upload.sh SocNetV*.pkg*; fi app-2.8/.travis_before_install.sh000066400000000000000000000011261377436340000171440ustar00rootroot00000000000000#!/bin/bash echo "******************************************" echo "Adding Qt5 repos and updating platform..." echo "******************************************" # Check current directory project_dir=$(pwd) echo "Project dir is: ${project_dir}" if [ "${TRAVIS_OS_NAME}" == "linux" ]; then # Install base Qt5 repos for linux sudo apt-get -qq update sudo add-apt-repository ppa:beineri/opt-qt-5.12.10-xenial -y sudo apt-get update -qq elif [ "${TRAVIS_OS_NAME}" == "osx" ]; then # Update brew, we will use it ltr to install Qt5 brew update else exit 1 fi echo "Done!" exit 0 app-2.8/.travis_build_linux.sh000066400000000000000000000045731377436340000165030ustar00rootroot00000000000000#!/bin/bash echo "*****************************" echo "Building SocNetV for linux..." echo "*****************************" # Check current directory project_dir=$(pwd) echo "Project dir is: ${project_dir}" echo "TRAVIS_TAG is: $TRAVIS_TAG" echo "TRAVIS_COMMIT is: $TRAVIS_COMMIT" echo "SOCNETV_VERSION is: $SOCNETV_VERSION" LAST_COMMIT_SHORT=$(git rev-parse --short HEAD) echo "LAST_COMMIT_SHORT is: $LAST_COMMIT_SHORT" # linuxdeployqt always uses the output of 'git rev-parse --short HEAD' as the version. # We can change this by exporting $VERSION environment variable if [ ! -z "$TRAVIS_TAG" ] ; then # If this is a tag, then version will be the tag, i.e. 2.6 or 2.6-beta export VERSION=${TRAVIS_TAG} else # If this is not a tag, the we want version to be like "2.6-beta-a0be9cd" export VERSION=${SOCNETV_VERSION}-${LAST_COMMIT_SHORT} fi if [ "${TRAVIS_OS_NAME}" == "linux" ]; then source /opt/qt512/bin/qt512-env.sh qmake # default: all go to /usr make -j4 echo "Building finished! " echo "Files in directory: " find . echo "Attempting make install in appdir: " make INSTALL_ROOT=appdir install; echo "Files in appdir: " find appdir/ echo "Coppying .desktop file to ./appdir: " cp appdir/usr/share/applications/socnetv.desktop ./appdir echo "Coppying socnetv.png in current dir: " cp appdir/usr/share/pixmaps/socnetv.png . echo "Checking contents of /opt/qtXX/plugins: " find /opt/qt512/plugins echo "Downloading linuxdeployqt tool: " wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" echo "Make executable the linuxdeployqt tool: " chmod a+x linuxdeployqt*.AppImage unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH # - ./linuxdeployqt*.AppImage /usr/share/applications/*.desktop -bundle-non-qt-libs # export VERSION=... # linuxdeployqt uses this for naming the file echo "Run the linuxdeployqt tool: " ./linuxdeployqt*.AppImage appdir/usr/share/applications/*.desktop -appimage -extra-plugins=iconengines,imageformats echo "Check what we have created..." find . -type f -name "*AppImage" elif [ "${TRAVIS_OS_NAME}" == "osx" ]; then # nothing here, go away... echo "Strange. I am running in macOS although I am a Linux build script :)" else exit 1 fi echo "Done!" # always return zero exit 0 app-2.8/.travis_build_macos.sh000066400000000000000000000056001377436340000164360ustar00rootroot00000000000000#!/bin/bash echo "*****************************" echo "Building SocNetV for macOS..." echo "*****************************" APP_NAME="SocNetV" # Check current directory project_dir=$(pwd) echo "Project dir is: ${project_dir}" echo "TRAVIS_TAG is: $TRAVIS_TAG" echo "TRAVIS_COMMIT is: $TRAVIS_COMMIT" echo "SOCNETV_VERSION is: $SOCNETV_VERSION" echo "TAG_NAME: ${TAG_NAME}" LAST_COMMIT_SHORT=$(git rev-parse --short HEAD) echo "LAST_COMMIT_SHORT is: $LAST_COMMIT_SHORT" # linuxdeployqt always uses the output of 'git rev-parse --short HEAD' as the version. # We can change this by exporting $VERSION environment variable if [ ! -z "$TRAVIS_TAG" ] ; then # If this is a tag, then version will be the tag, i.e. 2.5 or 2.5-beta VERSION=${TRAVIS_TAG} else # If this is not a tag, the we want version to be like "2.5-beta-a0be9cd" VERSION=${SOCNETV_VERSION}-${LAST_COMMIT_SHORT} fi # Output macOS version echo "macOS version is:" sw_vers # Install npm appdmg if you want to create custom dmg files with it # npm install -g appdmg # Build your app echo "*****************************" echo "Building ${APP_NAME} ..." echo "*****************************" cd ${project_dir} qmake -config release make -j4 echo "Finished building -- dir contents now:" find . # Package your app echo "*****************************" echo "Packaging ${APP_NAME} ..." echo "*****************************" echo "Entering project dir ${project_dir} ..." cd ${project_dir}/ # Verify dir pwd # Remove build directories that you don't want to deploy echo "Removing items we do not deploy from project dir ${project_dir}..." rm -rf moc rm -rf obj rm -rf qrc echo "Contents of ${APP_NAME}.app:" find ${APP_NAME}.app -type f echo "Calling macdeployqt to create dmg archive from ${APP_NAME}.app:" macdeployqt ${APP_NAME}.app -dmg echo "Finished macdeployqt -- ${APP_NAME}.app now has these files inside:" find ${APP_NAME}.app -type f echo "Check if ${APP_NAME}.dmg has been created:" find . -type f -name ${APP_NAME}.dmg echo "Rename dmg archive to ${APP_NAME}-${VERSION}.dmg ..." mv ${APP_NAME}.dmg "${APP_NAME}-${VERSION}.dmg" find . -type f -name *.dmg echo "Calling productbuild to create product archive .pkg from ${APP_NAME}.app:" productbuild --component ${APP_NAME}.app /Applications "${APP_NAME}-${VERSION}.pkg" # You can use the appdmg command line app to create your dmg file if # you want to use a custom background and icon arrangement. I'm still # working on this for my apps, myself. If you want to do this, you'll # remove the -dmg option above. # appdmg json-path ${APP_NAME}_${TRAVIS_TAG}.dmg # Copy other project files cp "${project_dir}/README.md" "README.md" cp "${project_dir}/COPYING" "LICENSE" echo "Packaging zip archive..." 7z a ${APP_NAME}-${VERSION}-macos.zip "${APP_NAME}-${VERSION}.dmg" "README.md" "LICENSE" echo "Check what we have created..." find . -type f -name "${APP_NAME}*" echo "Done!" exit 0 app-2.8/.travis_install.sh000066400000000000000000000015211377436340000156210ustar00rootroot00000000000000#!/bin/bash echo "******************" echo "Installing Qt5..." echo "******************" # Check current directory project_dir=$(pwd) echo "Project dir is: ${project_dir}" if [ "${TRAVIS_OS_NAME}" == "linux" ]; then sudo apt-get install mesa-common-dev # sudo apt install libglu1-mesa-dev freeglut3-dev sudo apt-get install build-essential libgl1-mesa-dev sudo apt-get -y install qt512base qt512charts-no-lgpl qt512svg # sudo apt-get install qt5-default qttools5-dev qttools5-dev-tools qtbase5-dev qtbase5-dev-tools qttranslations5-l10n libqt5svg5-dev source /opt/qt512/bin/qt512-env.sh elif [ "${TRAVIS_OS_NAME}" == "osx" ]; then # We install Qt5 via brew brew install qt p7zip brew link --force qt # Add Qt binaries to path PATH=/usr/local/opt/qt/bin/:${PATH} else exit 1 fi echo "Done!" exit 0 app-2.8/AUTHORS000066400000000000000000000005531377436340000132250ustar00rootroot00000000000000Developer: Dimitris Kalamaras See my blog at: https://dimitris.apeiro.gr Translators: - Greek: None - German: Daniel Pinto dos Santos Debian packagers: - Caitlin Matos - Serafeim Zanikolas (>0.43) - Alejandro Garrido Mota (<0.44) Gentoo packager: - Markos Chandras Arch packager: - Tom Tryfonidis app-2.8/COPYING000066400000000000000000001057701377436340000132170ustar00rootroot00000000000000 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 . app-2.8/INSTALL000066400000000000000000000067071377436340000132150ustar00rootroot00000000000000Installation ============ SocNetV is multi-platform, which means that it can be installed and run in every Operating System supported by the Qt toolkit. The project offers binaries and installers for the three major Operating Systems: Windows, MacOS and Linux. If there is no binary package for your OS, please download and compile the source code, as explained further below. Installers & Binaries You can download an installer or a binary package for your Operating System from the project's Downloads page: https://socnetv.org/downloads Follow the instructions below to install it in your system. Install in Windows To install SocNetV in Windows, download the latest version from the Downloads page, and double-click on the SocNetV installer executable. Click Next and Accept the License (GPL) to install the program. Afterwards you can find the application in Start > Programs. Install in MacOS To install SocNetV in Mac, download the latest SocNetV MacOS installer from the Downloads page, and double-click on the executable to start the installation. The application will be installed in your Applications. Please note that the first time you run SocNetV, you may need to double click on the SocNetV application icon holding down the META key. Install in Linux To run the latest version of SocNetV in Linux, download the latest Linux AppImage from the project's Downloads page. Make the file executable and double-click on it to run SocNetV. Please note that a version of SocNetV is available in the repositories of most Linux distributions. However that is not always the most recent and updated version. To install the latest and greatest SocNetV version, users of openSUSE, Fedora and Ubuntu/Debian are advised to add our own repositories to their systems. In Debian and Ubuntu, install SocNetV from our repos with these commands: sudo add-apt-repository ppa:dimitris-kalamaras/ppa sudo apt-get update sudo apt-get install socnetv In Fedora and openSUSE, choose and add the correct repository from here: http://download.opensuse.org/repositories/home:/oxy86/ Once you add the repo, install SocNetV using the command (Fedora): sudo yum install socnetv or (openSUSE): sudo zypper in socnetv Compile from source code To compile and install SocNetV from source you need the Qt5 toolkit development libraries. Qt is an open source C++ toolkit, for Windows, Linux and MacOS. Windows and MacOS users should download and install Qt from https://www.qt.io/developers Linux users need to install the following packages: openSUSE: libqt5-qtbase, libqt5-qtbase-devel, libQt5Charts5-devel, libqt5-qttools Fedora: qt5-qtbase,qt5-qtbase-devel, qt5-qtcharts-devel, qt5-qttools Debian: qt5-default, libqt5charts5-dev Once you have Qt5 installed, you are ready to compile SocNetV from source. Download the archive with the source code of the latest version from https://github.com/socnetv/app/releases/latest, i.e. SocNetV-2.x.tar.gz Then type in the following commands in order to decompress the SocNetV tarball and build it. Replace 2.X with the version you downloaded. 1) untar zxfv SocNetV-2.X.tar.gz (or use 7unzip in Windows/Mac) 2) cd socnetv-2.X 3) qmake 4) make 5) make install # only for linux users, to install socnetv in /usr/local/bin Probably you have already done the first 2 steps, so just type in 'qmake' or 'qmake-qt5'. When you finish compiling and installing, run the application typing: socnetv Questions ========= For any questions, email us at: info@socnetv.org app-2.8/NEWS000066400000000000000000000221021377436340000126460ustar00rootroot00000000000000Social Network Visualizer (SocNetV) SocNetV News -==========- Jan 2021 ======== - Version 2.8, with some bugfixes Dec 2020 ======= - Version 2.6 and 2.7 release with bugfixes and new features. Feb 2019 ======== - Version 2.5 released with new features and bugfixes: Prominence scores distribution in reports and in-app mini chart. Support for custom node icons (PNG, JPEG, SVG, etc). Edge dichotomization algorithm. High quality theme, inspired from Material Design, for uniform look'n'feel of SocNetV across all OSes. Support for (double) edge weights in all formats. Improved export to PDF and Image (with lots of new formats). Improved web crawler. Lattice network generator. Improved memory consumption and faster measure computations. Search and select multiple nodes by their numbers / labels / prominence scores. Many bug fixes. Feb 2018 ======= - Version 2.4 released with many new features: New Force-Directed Placement layout: Kamada-Kawai. New layout type by prominence score: Node colors. Less clutter in visualization due to reciprocated edges. These are now being drawn in a single line. Improved memory consumption during user interaction with large networks Improved web crawler with pattern include and exclude options Improved Statistics Panel. Performance options in Settings dialog Improved UCINET format support (fullmatrix two-mode and edgelist). New "Check for updates" procedure. Much improved stability. See Changelog for bugs closed. Jul 2017 ======== - Version 2.3 released with bugfixes and new features: Dyad and Actor/Ego reciprocity Zero-weighted edge support and zero-weighted edge color selection functionality in Settings Bug Closed: #28 Edges with values in [-1,0) are not visible #29 Settings: Negative edge colour preferences break positive edge colours Jan 2017 ======== - Version 2.2 released with major new features: Hierarchical Clustering Analysis (HCA) Pearson correlation coefficients Actor Similarities Tie profile dissimilarities Maximal clique census New network symmetrization methods: Strong Ties, Cocitation Multi-relational data read and write in GraphML GML format support Support for EdgeLists with labels Support for Pajek multirelational directed networks Adjacency matrix plotting Better reports (in HTML with JS) Improved performance and GUI Sep 2016 ======== - Version 2.1 released with a few new features but lots of bug fixes. This version brings a new algorithm for d-regular random network generation, and also a nice new dialog to control it. See ChangeLog for a complete log of new features and bugfixes. - Version 2.0 released with major code overhaul, new GUI layout and lots of bugfixes and improvements. The new version brings stability, great performance boost, and nice new features such as separate modes for graphs and digraphs, permanent settings/preferences functionality, edge labeling, recent files, keyboard shortcuts, etc. Also there are improvements in Force-Directed layouts, i.e. Fructherman-Reingold. See ChangeLog for a complete overview of the new features. - The SocNetV Manual is now build with Doxygen and it is available at http://socnetv.sf.net/documentation 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 ======== - Did some work on the webpages at http://socnetv.org. 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.org app-2.8/README.md000066400000000000000000000215301377436340000134320ustar00rootroot00000000000000[![version](https://img.shields.io/github/release/socnetv/app.svg?logo=c%2B%2B)](https://github.com/socnetv/app/releases) [![travis](https://img.shields.io/travis/socnetv/app.svg?branch=master&logo=travis)](https://travis-ci.org/socnetv/app) [![appveyor](https://img.shields.io/appveyor/ci/oxy86/app.svg?logo=appveyor)](https://ci.appveyor.com/project/oxy86/app) [![langs](https://img.shields.io/github/languages/top/socnetv/app.svg)](https://github.com/socnetv/app.git) [![downloads](https://img.shields.io/github/downloads/socnetv/app/total.svg?logo=github)](https://socnetv.org/downloads) [![license](https://img.shields.io/github/license/socnetv/app.svg)](https://github.com/socnetv/app/blob/master/COPYING) [![website](https://img.shields.io/website-up-down-green-red/https/socnetv.org.svg)](https://socnetv.org) ==================================== [![socnetv](/src/images/socnetv.png)](https://socnetv.org) SocNetV - Social Network Visualizer ==================================== # 1. Overview Social Network Visualizer (SocNetV) is a cross-platform, user-friendly free software application for social network analysis and visualization. With SocNetV you can: - Draw social networks with a few clicks on a virtual canvas, load your field data from a file in a supported format (GraphML, GraphViz, EdgeList, GML, Adjacency, Edgelist, Pajek, UCINET, etc.), automatically recreate famous data sets or crawl the internet to create a social network of connected webpages. - Edit actors and ties through point-and-click, analyse graph and social network properties, produce beautiful HTML reports and embed visualization layouts to the network. [![socnetv](https://socnetv.org/data/uploads/screenshots/25/socnetv-25-padget-power-centrality-size-distribution.png)](https://socnetv.org) # 2. Features - Standard graph-theoretic and network cohesion metrics, such as density, diameter, geodesics and distances, connectedness, eccentricity, clustering coefficient, walks, reciprocity etc. - Matrix routines: Adjacency plot, Laplacian matrix, Degree matrix, Cocitation, etc. - Advanced structural measures for social network analysis such as centrality and prestige indices (i.e. eigenvector and closeness centrality, betweenness centrality, information centrality, power centrality, proximity and pagerank prestige), - Community detection algorithms such as triad census, clique census, etc. - Structural equivalence analysis, using hierarchical clustering, actor similarities and tie profile dissimilarities, pearson coefficients, etc. - Random network creation, i.e. Erdos-Renyi, Watts-Strogatz, scale-free, lattice, etc. - One-click recreation of well-known social network datasets such as Padgett's Florentine families. - Layout algorithms based on either prominence indices (i.e. circular, level and nodal sizes by centrality score) or force-directed models (i.e. Kamada-Kawai, Fruchterman-Reingold, etc) for meaningful visualizations of your social network data. - 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. - Built-in web crawler allowing you to automatically create networks from links found in a given initial URL. - Comprehensive documentation, both online and while running the application, which explains each feature and algorithm of SocNetV in detail. - Binary packages and installers for Windows, Linux and MacOS. # 3. Availability & License Official Website: https://socnetv.org Email: info@socnetv.org Author: Dimitris V. Kalamaras Blog: https://dimitris.apeiro.gr SocNetV is a cross-platform application developed in C++ and Qt5, an open source software development platform published under the GPL. This means you can compile and run SocNetV on any Operating System supported by Qt. See available packages and installation instructions below. SocNetV is Free Software, distributed under the General Public Licence Version 3 (see the COPYING file for details). The documentation is also Free, licensed under the Free Documentation License (FDL). 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 experience any problems with SocNetV! See bug reporting below. # 4. Installation SocNetV is multi-platform, which means that it can be installed and run in every Operating System supported by the Qt toolkit. The project offers binaries and installers for the three major Operating Systems: Windows, MacOS and Linux. If there is no binary package for your OS, please download and compile the source code, as explained further below. ## a) Install a binary package or installer (Linux/MacOS/Windows) You can download an installer or a binary package for your Operating System from the project's Downloads page: https://socnetv.org/downloads Follow the instructions below to install it in your system. ### Install in Windows To install SocNetV in Windows, download the latest SocNetV Windows installer from the Downloads page, and double-click on the executable to start the installation. Click Next and Accept the License (GPL) to install the program. The program will be installed in the usual Windows Program Files directory and a new Start Menu shortcut will be created. Afterwards you can run the application from your Start menu. ### Install in MacOS To install SocNetV in Mac, download the latest SocNetV MacOS package from the Downloads page, and double-click on it. If the package is an installer, the installation will start immediately and the application will be installed automatically in your Applications. Otherwise, if the package is just an macOS image disk, then double-click on it to open and drag the SocNetV icon/executable to your Applications. Please note that the first time you run SocNetV, you may need to double click on the SocNetV application icon holding down the META key. ### Install in Linux To run the latest and greatest version of SocNetV in Linux, download the latest Linux AppImage from the project's Downloads page. Then, make the .appimage file executable and double-click on it to run SocNetV. That's it. :) Please note that a version of SocNetV is also available in the repositories of most Linux distributions. However that is not always the most recent version. We urge you to use the latest version available from our website instead. Users of openSUSE, Fedora and Ubuntu/Debian may also add our own repositories to their systems. In Debian and Ubuntu, add our repository and install SocNetV with these commands: ``` sudo add-apt-repository ppa:dimitris-kalamaras/ppa sudo apt-get update sudo apt-get install socnetv ``` In Fedora and openSUSE, choose and add the correct repository for your distro version from here: https://download.opensuse.org/repositories/home:/oxy86/ Once you add the repo, install SocNetV using the command (Fedora): sudo yum install socnetv or (openSUSE): ``` sudo zypper in socnetv ``` ## b) Compile from Source Code To compile and install SocNetV from source you need the Qt5 toolkit development libraries. Qt is an open source C++ toolkit, for Windows, Linux and MacOS. Windows and MacOS users should download and install Qt from https://www.qt.io/developers Linux users need to install the following packages: openSUSE: libqt5-qtbase, libqt5-qtbase-devel, libQt5Charts5-devel, libqt5-qttools Fedora: qt5-qtbase,qt5-qtbase-devel, qt5-qtcharts-devel, qt5-qttools Debian: qt5-default, libqt5charts5-dev Once you have Qt5 installed, you are ready to compile SocNetV from source. Download the archive with the source code of the latest version from https://github.com/socnetv/app/releases/latest, i.e. SocNetV-2.x.tar.gz Then type in the following commands in order to decompress the SocNetV tarball and build it. Replace 2.X with the version you downloaded. ``` untar zxfv SocNetV-2.X.tar.gz cd socnetv-2.X qmake make sudo make install # or su -c 'make install' ``` Probably you have already done the first 2 steps, so just type in 'qmake' or 'qmake-qt5'. When you finish compiling and installing, run the application typing: ``` socnetv ``` or go to Start Menu > Mathematics > SocNetV. # 5. Command Line Options SocNetV is primarily a GUI program. Nevertheless, some command line options are available. Type: ./socnetv filename.net to start socnetv with network named filename.net loaded. # 6. Usage & documentation To help you work with the application, there are tooltips and What's This help messages inside the application, when running SocNetV. To see the full documentation, press F1 to display the SocNetV Manual. The manual is also available online at the project's website. # 7. Bug reporting & contact If you have a bug report or a feature request, please file it in our github issue tracker: https://github.com/socnetv/app/issues To contact us directly, send an email to: info@socnetv.org app-2.8/TODO000066400000000000000000000007701377436340000126460ustar00rootroot00000000000000KNOWN ISSUES ============ 1: Negative weights break centralities 2: UCINET (DL) files in nodelist1 format (one-mode) are not supported. 3: GraphML files containing special HTML chars (i.e. & ) in labels are not readable. 4: (Wontfix) Pajek files missing opening/closing quotes ("") around node labels cannot be imported correctly (#73) TODO ==== To request a feature and/or see the TODO list of SocNetV, open a new report at our github Issues page: https://github.com/socnetv/app/issues app-2.8/changelog.gz000077500000000000000000000554231377436340000144570ustar00rootroot00000000000000‹òæñ_changelog½[vÛh–.ø®Q ]i2“¤DÝ­ªŠZ²$;”aÉ ÉgV¯ub$D!LL”Ìx:s8#è!ôú½qFÒ{{ïÿRáÈεªÂ"€ÿºï׿üåþoë/Ir_Žót–ÜdÍsY}I~Îëe:Ë˪¤Cè矻IBoþñA·ÎÓbš½/§§[ô¿Ÿ³ªÎË"Ùœ$ýäoi‘ìî÷’½Ý½áÖy9ÉŠtž&Ûéb±*²çU–VÛ[ýµÿÑRß,§ù׬NÒb’ÔM:ÊiiCc‡“Ó$Ù8Ù;Á$»á$ãò)Ÿ|kôp°£ÿŸƒÝdÏÉC–6ËŠÞ¼¨Òi2§·pÚ|¤I½\,ʪ©:õdb/Ô§ ½s6Hî³Y6nð~º*’æ1¯ñC/Y•ËdLç˜MV¥ôÞsÞ<Ò ÿú”Ê(IÚMfÙCÓÏòñ—¤¬’*Ÿ>º?éë&›×I'dƒ¤àÉ{I6™fu·‡ÏG]ší)ãaçÉh…ENób*?`FZGE/-ë WÂ_»I­k_Κ|1Ët|&Õr4"ÐÑûzÿ mu\•³ŒÝÒy#˪¦%I5ï´¤¯%ýƒ_¥Ÿ'=L.ËÖR•KzòœÏfIÑñÿ¥U=à•þƒÎoe“Œè£-•&ÑÕ§ÅJ^VÁ9ÈЃ—ÀåÀåm6Rp¾Áežy:Þ/mp¹­Êy^dŘ—\V4Å$¯›*-Ôé(’*ìü‹Ò³¼OZ5<¡[R瓌¤=Î=¡àø1y ÓZ÷ Xaˆ(–sºËº7KGÙ¬îÉœz´ žEùL»±Ö #Í+¦—`>î…Û †QÉuÚŒ³‰~›VYòHÐRàZ;ç˯L†3{ðó–ÕÜgµ­æ¼,âÃÉmZÐûMI,¹üš!\ž:ôlIÛ8ºÜ:«”Ñýäcwe´;uÛ=(™rBÊÉ =œg›‰†ã¹É'HkCTê!eÉ!™¤MÚ¯éö31òq•>ψz1ÃqàÀ[¹²‹ýLoË[*º8 ¨–„L)¿0UZ~ù\uh¯„ʳå$ÛÉ ü—aO¤rÛÉG_h ZG!„cü˜mÃ>{Y.Æ>¾1¼TBU•P ûìLÊ%‰<Ý€#òù’tCO¼dR»Wâicg“_Ó1ñçU/yÇÔòú}îó×ì È&¿=#~Dë}ÇO€|4,¿fiQ n¯šï›(Ñä| ]9˃,ð2Ébº1+ œð»˜-ë’è&§"û/¦88%;4c=f„Ëò1 @VÞbÙö$ …[Bi•ßцÞú1§“´ÿcúœæ$‰ˆž3c éGšÎ¬Ñ6ú•y ­Lš„…¤ÉdEk +g™³b ÿ”Ñ9²~E÷ÏÒƒý+B!¢b¯H m–BFLþaBŸ&¯êEE×öj€Á.YT“_Hí`az^A_¹Îd1’Xè$%ã­N¤[ˆˆþ&K“;imàvûœ®d¯€ ‚9=eM~•÷èû&óÏ¢ý|IRÇ|”Îdä`ºZóA>µ‡<Sn’wyÏ© Ù Ìl£,/OW§¶î¤&8Ÿ ÈêÂ&9Ë,ª“@O×”e…?|m{šJôzÆô¹í‚xÃ"Í+ÛîbðÙU‘79_Ž‹{¸Ü%ÉdcF%!òtÛ^pwâ|Ñ_”³Õ´,Ú`xÒƸœñ`të‹–î”<©D¶+×!úiÁ·KIJçu‰rIÄØ>ÉàÂ1;„þ¤I_ñ$«›|ªsuåzH¯KIN# ŸèÎp^"ÑAmÀ Hý°9Ÿ%ÍŒ€Ry› —ÝS„9ä‡NÁû“l‘™ÓÿÉÞYÊň,¹÷Edz¤ÿ=½Ýäb_LIJ#€ ÿG_3H‘HùœB,dØ"ö5!"**Z±±œ/@!UÆ l–¿aÃ_— ñ‘©@{xrDНcH'i©EÉZ̃Á$m–Có§Ì.Ì"¶ä:Ë‘u58 w€0!üYAA~NXLm]…ŒÝ‹ mÃè´/Y¥¾¤ª"Â=H>¢>ñ‚ÒxP>&!q<ß= ÍD˜Ç51hVçÜjòâ¡4; ûzÓ@-ùG5š.kT²Á¸ó9?@Q íׇóÆÊí» F[þ}ë%˜Ù¿„Ø´>c¦maL‹ jš÷—Zé¯Ê8°ÐÇ Í«y&”ÇA”´žlGxHðIìM rÛÄ ÅÛXÁüq¶=H’Xª_ÌÉ‚€Äìüü;NK ÒÐhJ,’pžTŸ'‘ØëP»hJ€¥æY áòîìZ Lxx¾À; !–)ÐK¹½Þîî®ú ÂUtbÉ#8Õqdjˆ¨ôºa»¹#¼Lç²ÄS‘ÕŽTkìþs jOD“-{ZVt`s¿¨¬xä{™¨'VâÍÁÀf…[˜§+1×,òhù$p¦´ÖÁ þº£‚¿jñ|áéSIú«Þ%ºZpŠ—¿HUž»C×Îj/HJÝ” 1oðÕ}µÀ4‘æ¢êˆƒ ®t^g„Ýþn¨QÕsÔŒÀ_€È°Ë Ô”ñþü¢6#JÝFvb2™œi•©(N‚tÍ"!í„ÅÔPI ƒ¢yMJc0^š»\ÈÒ¨H-Ï¡3'o|ê ¸:úÄ“µ@k³ºÞ7è?mÓ LLà•œ¨Ò·Yk!£¹ÁÍzŸ5 ¶.€ˆ;fXÞôº?ÃCªc¦5DÒ9‡_/iÒQ÷%¨Â'ÇíPEIØOjhÇ?ç´3ü“úNìf"Cž=¥ù j­ßŒ2ɶ âs¶h²ßºG* ¿%¬‡H$m Oì{Ñ_0äõÓɯ˺a#}7/ù´ù×yJ"Áu-¶X3˰ªÈ„P•ßšdu”Ž¿LÅ 1&Y¿‰4Y.&ü¦8Mõ7(j8%BDDGì$¸eçS¢äP>ˆÊ ⨞KÏ9QØI¾öaÄ&‹!ËÑ:¦ùŠÀ –¯ ÉE^ÓÉCónØÆjã•Dça¡cù ¼“/u #œ€˜—Á{œ§_DNYc(¡ðòÏ%áÑU ¸raÀ[®æé×pxBH$›fìŽuác?fã/£ò«˜¹ùt~usùQMí¦Ã’n´œÍè‡*ÿÊ<·Ï׿®† ŠÝÁ÷Ñ‹‰ÝF† QÆo­‚Áåw•Ù‚·!­°?*ÏXcîßÜa7ç¤ÇU9#GݦG©¸9ð¹~ËÒŽ[[rK êTP›Äü´PiQ¸Îg1f –NX&Æë<ƒ'V3Ë/K*  Ëª™Œº±Ç/“e°ÌlKb;w,£ÂÙ(Ð >ªè#_&8gå€åH2‰8x›Wu#âhΛ„{øãï™"‡Aô~¢N`úÇðôæN¸O!#¥°¬Í–óÂ)øü×àóžÿþæîôæœïvY»=$Ñx©°#Ö‹Äô…›ù+]‡ÈÛ¼!›Ÿm½¿eUIbPï×nBçÌgdÔ5Ú[! =_”М/*¼âhéÞ÷Û–D ÂEÁtX0 à:w!ýÏléaøU}WXÉÝ«:© ¢ D,!wØHb1a©¸t@0ëmÖ ˆŸ/ÙóNc… ÁÙl‘|¿á#·Û±{®Ä2#"d"b•±åï w1g¿«äŽÙ‹ô<'°p79p'8§’Ù'þ zÙÙ@Õ-¼ÅšÃe ö …HW~É2}†*ž“F#¤a¯Ùq8bS»gáÎè®BIu„‰µÜ ‘…rödÆ€s&°XÊf DHp¨ÃÖïÙð¿;Ø=M>W¬6½97à $¢–¡ÜœóüÍáÁiò Lw(ŽEìP”0ê 5]ñÓUš¢ÏEô€Dÿ»`x¼ý“Ó€aå´-æn™ñgµ±:TR(±+ØÊжÚÊïÀXÆ_‘ÜÁ®2\š,H,B‚_„dˆÉOˆfëÚ?M®ó;½ƒ)uç=,©ïŘNÒée>ÍŠ§ x˜±ÚÕåvZš~ÔÓ­aÜñºH‰iÌ>ÁÚŠlZ0a8ËA ôL&‰Ú;90*-8Ò¨=ÁzZ6€5ÚÆa¼—ט…ú·ÜRâlX]Q,| —D—~ÍÎ"æØÙ½`k-,ß÷Þ¦ÌçÒ6ílŽ$ü£fkk}ñ¡&~‚R—Ëjœí4¬)6B†bVcj-úÞw4R§Êàg¢µtb”ëð!9iƒWqöäDÒ( g`ŠÇ´ås™Ç%-¨„=ñG2*e=ñ{ìø¦Ã``õJæ)cgÍù½#‡ŸL”Ù;F”¸Ñˆ œÈ­°QF¾lQbŠí½Ñ 8Æ¥ùYæß¾grsF$Ze×I™Õ¸töÌLÍϼHÛ€Qk>óAßÑ1Þ3n;ž²ýSa ¸—Sf`#1ü~wDÈöq£ØeŸ89^?´»Á!LJð’œvL××î¹ |rV¤³U«¬K¬¤/ÁPÌì+œÄtL¤ÎÏôÃ>‹½ùîàõ)ˆ“¯`°ÑÓcDlò9 @*Î’õwš,å° ‰ÂÇÇmB¤BÅV¸Ê·ºlÛ• ¦|†n­ýVt¬ó¥È¥©…ý:Mèä ·jf6uF‘âÒì—ôÂoZ!u¦ #{Ã*Iâ¿TBLïÒ¦äYqãA0ã„/(\æ‹3ªÎQ¨3Ú†xJ¢å8wªjþ[X¼h ^Öm$­Ã‰o~Dlƒ¬µeUÿ Í 5*w¦“tSnh©ÆUÕɰ'^8LìðI9B®CÖMµ¤S®;õŠôMfxƒä=à3\o¿còY;y·çÅŠ7åcä*x€ð8£}É%8‡Çc6÷ç[pL­Iƒ SÀ˜VÁ­ÎDoY;¡0 1ªÒûá Di‹›Qw(œã~ú^âÿ%NÁ‰‘kõ]‚¿KDB¦v$8 t™„†ï‰’*aø>œÚ‚*Aüh}Ħ •_-@‰&[fX‡äûúáãõ{3XÈߢÊáY™c;éˆlæmE³ý7éc}·(]J6@çøìáåðÝû›³^òÛÚrülNaW©SFz;GêºO_*\° ¡@bÐ^*4ábOæ,aïMæcâ0,Vx¢Ôfɼ8àÄৃäl!q_³•‹/YBWIY…P U‡€Uþù±œ!²A¢åƒÓLY²¹9K:¼B—à‘ëf_]Ž=üªcóAm;œØ–ÏÝ5öZDÀþ3ZqØ­2g EZ$}»‰Œ$c|<ƒ¦mz< "?üçn@(|$HÄ’‡ +°…÷HH…‹“ë××n' ¿FZ;/sq¶o«ŠRª!·y*Ö/¯`ˆõÍl@ly$([,gês27(·qËÔ¡TœÔ3–PÃòJ%%d¨€£÷V xÆ såãäAÿgØÛí:¹™Ê%z껽×N áèØi ÿ1¾G+5‹ï‹»Õðµ:ŠäÛÓ4¬½áas”­Hoú#iM›¤wD;ÜvH"ï‚·¶j?NÃ!ÒétFTÉ&Õà± ´ ¤X “˜¬{ª­2"i8 âðš!´^cäº=â_|aMø,ýŠgž[¬Ó#¼2M:Ÿnß]Ÿ‰ëE$¹{'Œ$—ÿ\æt™Y Ä¶¹°c©^¸ÀÃ\ÓŒõ .°¶q–ê/Œã°¼Ê (ò¸$—ËñŒHaê5뫚à7ºÿñ8­‚±4J·¦F¨ÜÁŠUnâ~Ÿõvj¾ÉÐXã#×Tú&¸š°ªžÎm4÷©]>pøþçwf—ßZ õßl´é\þ|.àõ¬q¶á€èêñäµ8È DâýûÁíÒÈ>‚ÊEåubª¨:x[Æ`¢?“€«3é mùVä&û,ü@Ï\FBP» 0 ‚¾à¬§[ñx!˜ºØ…"ðgáüË–}û|”M0¤ 2lÊEª¨­Ð¬%že)'‰°=Y"m'‚å*‹ ¬xõF’`/Á“bN舂SØÁöt\¶0¨mã’4š\± A¦yÕge!¹=7û)Ý{{CPŸ9º1§Ó(9ÞÝBS§¹i¹;«Ü.¼FÆk•ƒ2åƒÃqZ3yTtÉS–%RòSœ2Ó¹f„t9¸BÑ{Éé|Ž_0ç9AK‘…Ó†×iG×ã„¥à՘ў…ó¤C@2‚1 Ýž³”Рð£YŠM`ÁY£¹;0^€[[žÕkïc=Ãés‡{¸‚žÒU9|ŸäÏsÛö¿­ˆCüÄN@OƒSuç|þ˜Võcö4ØptËZ—½¦åÂ5MÛR*tÕü+ÇÜÉ‹ðûú_3£ã<ŠYþO’¿iõ2>\·Â7UYüïÿù¿~̪QYÃm›@ ‘âIRóhL±ý¬+½þ4ÎeîsÌm £‹Ï©²§t¾°øœY)>:ZÕÃrÄŒl@Ú{5 üf9„£â,shYİÙ*ûQ£âúÉ»ØLà°-È,òÉ~–^ÄöÀÕ:'ñjýïGÀðQÿû”f7¯u€Ë –f¦Ì©~ñ"ÒÛ €Qøu¼¬`skãe­É9«æ!ÜŒH©xî} ̸ýáH$uá6ÛoüçÙ_ÎþÇG5'Þ|½±14/ƒ]Æ5Å¡šfñän4a9øç3Ú¬CkÇÉGZëà÷NB÷î Ö wÖ%¼#o=c‘,Xš( ]L©àMuÎ’EPäÄPD$±Ê¥»ÿêôès9¬ï9~<Ès9Ç Áþ®\¡¶¤Ð˜HoÉ*Ë'!ÅA†0–¤áÀ2‡äí pÒ½0• Ue 4C%"øl†‡ì™±©5ÅÌ}›ÏJÓdìê&š=[™±¼Ëi†,°¤ÝaÖ[Ë$1­vr+z2¤6q¶&b]·J׎nȪËq C—üÆÛï¦:ÀàËIî}»jz ^©ÀâV±·Ž(¤æÍE0H©5>1CÅÿ8_Ž8üùj\V½#ø7²…6²"Øí-2 ÉtYðexd¼)=ö8lDM­œÞØX K‚‡s^Š@ _[î•D<`káQ®Å­ƒ"óPŽdM²ÄÁ/¼tf|%9«Æõ:W +Y{€Fáò6+¸8<µ ðvÕüg#D¹™ ®®Sè'¾D‚Ì´ába>µráBô75*<Ã-ˆÍ¿Û^MA3S—u€3OiÍ–'—£¨¡Ñi 0ŸŒs¾,{Ç/Ê%qnÖ|ÄfÈâ$<8+ôC(Ooöf+üÛC/Ú>¶ªFg YEJ`lE^Ó]8zMÛ|©üêFá –XžB:ˆfÕ^­ƒ”f6ÒÊûH¬ï­ÜåÍJ’|)’VšГS⟊ø—#=¦sJ “Pxé¶¢à ¦omnEÝ/+.’ÊÜã»2#!粪YåÜü‚SkëDþ “¹i1o9ñèš´ Yi£»|4å¾¹ØÃÇìks9É™‹ÉmçÈ(& ©®Š,m°-ÌV›ŽÀ.ß}º2èe2UsjŒÊ™qB: µÌy WÄÁyCÒÊN®­ãò×;Ñ(] °|H¹Æ7@<¼¼Î{Ke ò3MmˆŒ•1™ìH¢»%^’½…pf» £²ç‚¬f|ŠÖ¨SY⇕ç³p*•Ý÷Â$ 9›•cO‹Z ,ƒybOàÀÓ5k±3kúý™)ˆÀX[´ERõý¹å&ïôœ_ª„³ôTÛÏ—…¨‚¼’Ðd§zõ¢\,&K¨Ô¦}<‚é«¥$D1×׎8åˆé>b¢9C)Hš÷²š,ï}Êñó9ë“‘Î9Úü ,&[ + ¶÷‘^¼òúª3—ôgÈ›f`›ÉæX+]‰„š‡æ#‹páv/E÷[ú„®œ²Y±çm, 9˜ÎŒIçÅH¨ÛnUÁ¿ˆ|SžŸ—õ)Ö‚tª”\.ê΄y‚‡ë _¥2Q„ &Ÿœú'™BàÌ©ó›lRºÄs?[yÈá@9DfWKÔšüU»’Q´'ѵB£‰5±éMŒ®ÿ‹™x/6èå˜Iï¤î‚d3®a¡[gÎ3‘ŸØPÇ–×LÅO3 ´'\ú±JÇ_ˆeMšSTé7¶T"PËÕ§ÌÓVÉ’´G°3’â\kš.Ã…3Zü#ÖõÝðèàðp÷@c¥¼iÝ9•vœ÷%¶÷K˜Õðàh÷ä®U!]úPÑUIœWÁR哚ÄuŽ®È ¾êG{¯_¿>2$ú‚±ÀÉË@8$(ùvïäðاç –h00Ió­•EËí&)ýšŠ cÑVìÀ68ÛÂ%Ù3-5‘MüÿœHÈ%½,ZÒ†9:Ü;”Ô6­ã…{cc¼h`®ÑñŽO^ï&ç.jчá¯Õ‘©4öB¦® c $¬·MòiÞå8º‰?ÌãƒäΆSºŠ,¸Vt–«Ÿí_Ȇ%qÑ,*îѬۑ|üåÕE¦ä› ÷ÍÄ~±€†¬[6´Ú]DÿI–\”S`qóµÑl7çñþÞQò^³ìÚȲ4”YF÷U»`jmÛÝâ¶D²cH™wUIÔøB‚ LðþÍŒîÎ5t,ɘnáq9гÂ,§šg‡èé2«w†ˆ¤ÝcJ˜¼Ë›½Y€áYI¹çýí°V³,Fb]V=qÝ|¯»%§&Lß”(CöÝa¿Ò±¿ªa*!cùвÈÈ':—XXu“£?€GKy+}þÌ‘KPÄ5ï}p ëa‘¦ZÅ~ܧ^–3Í«§jÝ8š’J¼¿w6"_©œf° J¤I‘Î0¢†c)ý!®'géí u>ŒÖIÍŽ¾ä>V(#š•Dò’k7,ŽÉ‡ÍVL×)ô•‰ž…blID‰$½•ºÌµE*73kí#IbýˆTÜñig·Ä«îÒâ‹×:;·w·â"R§®BÚ…9j’ÎÙ»‹îZ0¿Hìì«‡ÒØi©4é[©‘ÊU”j×l{¬xŠûtí›ÐÎÖŸ&+z½òì¼G´˜>åFA9OÈ_Zšð;æE">|;7‹·ð¶@µæXMÒ)a­¯P’Θo¬¼MËs5:h!‹nsâí…•Ó—S—â>i}ºÜV¦QÄù»×«Î&ƒ(Ã$—½ƒÃ£¡+VØ•\Êz 1³MMœÆ$e„Á%±Dâ8ËÉëàÒ<08îd°èïÓ¾Ý?î%gˆrvXNKà3tP!"«Ímï>z]Õíôdß@b"vGٛLJ»¾¨šá¼BH„U$ýèg4þ0A²¾Å[ŒZpDïÄšáNAÓ:ÍlÆã ÿÓ1÷Irî¸N ŒÌ]í[°Û=>Þ3æj“H^í¸ ÉnñÑe†^:ÎÉþÉ^ðãSëC"'g#E¼Zk Úî»@w ŸG™ˆ8 :ÈÅèiئih¾‡/pÜ‹]îÉðxW£šX^ËMÍlæ3Ér׊kZT'‘ýÙAíë!´…YCäªÈ¦«ä·¾DmÄìî%Z’ÁÅ*¹ô}wÛ\M(R¾ü»n £]’€DHFò”¸$fò¬"v–$)¤$àÂ^êÅÿ&ñšÇkXúk¿€UÍ–LÈèl#±bWÅŠáÞ±b’eœèŒ»ìIæ;6Å^m ƺNE‰Æ‚­GÚ¾d‘IuXeZ™-Vqʪ‘i^%+‹Ò'’#ÜÙ×l ¡È‹¬Rƒ9’Áµ !\@£ìékIPZQò &Ij^Pö/’‘UË”hþRJV{¸\µ ˜¶„£4›D¶D¥' _¡ä¶ãË (ÿfË „¹HÖ)K%µØE:òesÙ⇪UN´ÄÉÝh‡é¤çÜ~4¯³ŠŠ7<êÔj\ƒÿ„KIixû¢”'èp†PÛT)iæ\Ï’o†m½ d´˜´xi6ʼ-86:k ·•Ógo‰èj¡/1B‰ýÂ×3 ÊExK† º[I ÈMÌEÉ–c!^&˹¼‘¡o' ×°@6ÝžwHq†§pðÐ v¤pŠN„XÍeþ)IjÍ^Hû]r˜kÅ÷ß nHµöÆÁ_&¼ÅR鵑 À…lIÔû®‹Kêá±fóýq¶i£xˆÚð8y Ûå8µ¶6g_D_«–Ê/ â¾}Ï’M ãY9þ‚ ¦öðê£+ç—ªÓÈ2B€×#pûø­,çë{X.ú„ ¬íüÑ-` &Í&'uö>º qFêT*µl÷&‡·¡šè9nëð6º^‚l@¦¬W9kl@cl›Þ¥øÕÓpð:,¹4±tH²×Sƒ¦1©•@}¤á™ígÕŠÔ˜ººû^¦•r„G X–Ó<’R*ˆ6h‡2Ã;¿(Ÿ‰Ò/g3-j‡xÑ®'<¡µ´ ’¸‚ºlÀçkr'”B€3Ò]|ÿÊÎû¼X~µ»äÙ‹×)ƒÖˆ%‹.Ì‹§’M'¾pÞ—l5*9æ”ÈFÕWIÎ?Þ½ÿ+Ǥõ2To‡ÎŠy ->MÞA,žõ$gV´"HYë“Ù;üEO#Ò«®rÆø®ÄÑjTÒ(Ð$#†4•’Ísb)ó{.‘)F\—…ÊmÔ…"–u‡)&ˆeJزnû•Ä_Z'¨ym7åI¦³bÀ2k{"×tŽnÕøMkÇof‡kÊét&•QäûÂ`©+©¡?"Ø:W-áÎj ·Bœ"Ïòz•£mX²/ýžW^Ôu>T<`ÿ®ÚØË ʹ}ÚØ™×zÌÇ9Ê€‹¦Dtš²bذŸsM‹eûð£I½òkv¶¤Ž‰^ðȸ+ ‡d!_xåü0ŠÄuÇ‘jj!ÒKGÌÁ .Ds¦08H•¶ÖÑ@|Ut q–¦z³”òëÑÑÎýµâ5· ·IoO‘È3+v§¶ÅœõïÄÊ"ƹt•ého ¡êønüX–µ uÀÍ ’¿gnw+I «ìÔsEÞBåãm --¼ŒŸ…¢ú×$ÅBj9¨†ÊÃHÂ6Ÿ Éœtÿ{E?Žª ‰{{C ¨¶e^ÀNB»¼i~-÷k­—Œ‰@² $Ð[z,ª*]ÙÀ>Xlæ41EÈJNZ¤ìK…·¤b£KòóiM?0¼Hk¤+9÷Î F*‡•"µ¢BZÇ ƒQÉž«*&œø‘k’Dá$°(¡]8mÔ†š8¡î/0Å?F?iÅœ0ä+t‘´j\új\?‘™Ê¾QõL¬ =–.mŸEP– heÊ`mábE„0â{>Ò"}«°®µ/yŸâoÓ ©]i›ÑºZ ÂsŽËq&ÒìË~AB„J'6<äÊ9ÀsΤ\(ýëMTµ3~ë`œ~Ò³\$ŠHã¨ýQú·[»¬-Ç ÑꢃĘ+êÌqEµÌlÉßæÇZÏ&Òj“Ï(éê¢sãæø«Äê²`Ìx!ï…² WâM6×H»#1ôO1˜ÖÚþ÷?ñ­Müð©ždm9ø’ Ë¶`¾U`Tq¨!‘0Òð„ÿÖVu [®Y‰W–î–ê ¦š}u鋾ÄVäuTzVmÈ–n¹*ƒT @±ëR…€ZÔV¯ß œnm Z4ŒˆµÀ·~HÖ(òm±¶€H„þMBpYQÜË¢–'µ·!Ä”œu\ֆ܄ˆ?sá FÖ×*•A"¡{€O€½f¬5‘Ïwâç|¨k ÔŒ‰í¶N&±èèûÁø n•gÃÎd¡ßõô {q¡K×ÄWV`—¤¸â £XW!¿%:„)3f÷ð6¼ÚbCZ¥µ¿Úù@C@øË[þGÜ §ý"ßt<Jü¨'íh‚ gNÐU\tE¹\FÎTͺίìæSAÊÜÜ:±"醃ÁÚNQH#É̦Ëûlå¥&`pcMH¹Ý'Z©‰Ø»/uP*7“bä›úLùÏ9ך'’bxësʵ­ºpÐ Œ+ MÅ+/ÏjP‹IóØEX+ <â*/;æ¬PÖoY‡„»Œ<´ô:1 È©/¾ ©—#yQ´y63gJ3šKiU‘Ï3/ÆÐèLfAkñÖ-MQ>í‰Ù»ûQ@C©2|8óक़^&ö.›SÒÙñä$ÓÊ‚i• FµšsN'h¸dDíê<ûÀ8àgŸ?šºG‘Æ£óñ±±$§>­%‰•%Ã8®ö3Müª–-âJQv)0!É«õÚ»± ¥]ƒ•ƒ™Xb˰¦©ß60¸rV§ÊLb cÓLÔ³kâ´¥F×&W ëÉ]R¨æ¥(x=¼}aD$ÿ«eè²XMUN‹$ЗV"Z0Od0é9تrÂñ.ë™9-^\õ8›”_WS¾q*MÊñrî:V)2#ÙuZ Â‡¸ˆ’\E§(=g£Z2"`5GÍ]BŸ¡Ê#5ð‹9¦(D'@ÿ YZÔC育R=d› ü7i5ù­’ßèrDhùèØ²¤AI¦¸šË„=gÒ´ó€KºBªÍ,îJê^£jæVTàh×½'îÈ‚jòÊg&×uò¥Eóq.u&nNç®Ö‚°ßеZt;W³” ÿq‘N$g¸(Àk5¼F1*âš{hÔ:<ŒÜV܆n•e¿e¿ç¬¢Õ„cJ»LïåUÌh¦:†¬ä‹Òuæª2Jí o( ‰ õþ‹˜°æÎÝ…ø‘«ÀKd)䵸߇°q@HgCæºo9H‘µ‚0øö.-œDñQÌ¥ë ¿)éçR0Ö÷jÑmwnÐ=5¸kyôZ=íp_÷§î «¬'þØý£ÃÝýcWÔ¼– 梨Ђî±Ðv4èY«©Ù@‡'{A^}pæ¤úÕœý¡ÁAÂo¾îîúÀÎׇa y"ºÅh3DäÛg{{ºs.­^ñÍ¥Qä•ý]·"(’ëZJxcƒ¿s“Zž!ã_ŒaëP‡ÇÇ*×huDPÔ%>³È‡«î…Îi5Ñå2Y“Â2ê~µÃ·Ú b¢Û]•p¯FeïB„>1„Þ=܄Рѫo`rÄ„-‡Wò‡}i;8ÀÛ(ãØ•‰rÁìS¿)"ŠÙc¤ƒ RÆé,á‡Ñ1T™8èFÉ œ0¨£H¨ÝhSU&¾‹qÙŸg°?æ‹Ð[¹žtÏñÂ}Ä k †â(Ÿ3Jn-ôWûh,`Ë*+D¸Ö±çMZ¥£ÿçÿªóÿý?ÿ×ÙlÄöƒÎ›³®´RZ(MáUh' çR¡)¯çƒV `×à8ФÍwƒªHª,\Bhˆ/X!ik6]Ú)Š’C§Ð9&¯½fൕ‘Ìub0H’%ˆâ#E‹ƒ‘P õú¿äÉ&Içÿý¿“¿&úã$‹n²C7±œÿòkÒ™üò+GoH#R~YMʺ—«¼u•Qã p!uŠÞu×z<µºÒáXÓ‡:­é¦vóÀkµàÔ Ë&næ…e­A“Á É›¬*Êål–' ·èÜ‘êšváçퟵØv½z!Ž‘ˆïÂãF/¢¾sÞT³¿žuÕ#}oƒ’G÷ùCÃ/Df¬¶ÉÇ›ñ!Äm4W,ÚPÁK•8rmŸÑÚKל—ÚÚg+NÞ Â)Ëx²µe+AÈ{YÍ'‹¸±ž‹Oƒ…‰Ý´ªË´-B•¿roî 6dÒêûTCŸZŒ<]b|±¶š”ŒÏ¢ ð„h%5é烗‡Úz}OºC¯gÈrg¡VD)±t¾[o¢çh´œäËÀ«§J/‘P=–ëtÜ ÊͶ¡ÎX^›@€ræV,‘“IÞsâMØÛU:0²(5rñ¶4»QÚQ6NYmÛJ"{•ÊlD:ål×óôáû»u4Cl°å}FÆ(ÇFSöÍÔ€@­L»Ä5ÀpXŸ>¾íŸ°9ïBëä² é¬fêµ»ÔÉKEzÄ—¢h(ÙCYåó?CFç¬Tz— ¹bä °ÈxµÅ\QÇ]¢ÌÄí“CÃÌH¬öëÌ QûœÀ&!{Ö#%ÔŠJVè™ ÏVÖQOY¡ÖÜ ®>È\e÷¼ž8Oo^ï‡{n5[¨žj!Ù:oÀÀ½;,2LóñúH‚]+Ñ8W‘r®=»Ýl­ã®ìƒMÃExX—>ë˜Ð~Œ¦ÁŠ£Èk‡ZýáÞá~<§€úÊù±6MJÛÐðž²2£‰Ã5Eµ™C5ëv·¤SM ‰Ñ?þÀRüpuÒ¿s?r.>üS…~ƒ:{Iê0.©Ñu…úجp!6˨Œ´cë8ËD~Ùp*R šº!œŸ×ë1‡æ–ÈTq-ˆ©…ª4›¹á[THfSïï‹IÍzK†Ôr©Dî‰Êlóï=ùïòrìá‘7T (lX*]¦“‰£¡“Lë¦á…÷ßúF K "ÙéHe§ápƒì„BùDò~Wzj³šÏš´ù9YG4Ò­F6÷˜¡¹¿Z”,Û³Õ.j­´|a[?·&ɔĢ¢Åd ³‡ûVq¸_–*Of¥ùt÷^1"5CfWyÇ?Šõž?„õp¬=X?J;c4ìK7ôL¢¸ù¸_=’Ž÷*qµÄ$,•>î C´}ÜX€ÿÝ5ÇRíýH ÁįÓ*åS ã%v@ dV¡.ÕY-Ï}2–Â>¢‚䜉„éw).ìÓ•4çÝ ÿ³lJe„ó)ˆ¼ZÓV öGÉeÈ;CJ½øWŠ˜¯¬‹/Ö#5¢åx$Úc¢UÇånÚ˜9­½ ¶¡N¬¿ôŒÐÕÑ…ÀQŒ 3eå-‡±ã».E7[·c/™–ð3°Þ,KߣÁŇ{m]'"*ŸHªU‰6ð$Xwg¶’ÏY#ƒ¿ú™ V­ã`Ò©³Œ¥‘~ªgbB{¡(€åªDóÝpï`¸·ÿšòÔaZ˜~-&Á×û¯÷Ž¢íiŸy¸4ë-”ä{¤³<ˆ½Cú¢jÒ£ X^ÖtùÞfc;Ø}tTô f®ŒõÂ)%üϵÏÔEžœìíD$ÃÐ ×¼!"‡D?¼2õñ 2«qed"÷ÿ/¸‹´5·Ìø(^ôœûûûÛ>Â}ªM=/ÃÊ‹ü×/ÄõÒ®øré3ìÛN.0Y/6®#,ŒìOñ³É¦’-åŒìõµWˆ8ùRG-òÆ„+êíBH[Ŷ½FÊÆªö8öQؾæ@ËgA8œŠÅãj‰ˆ’ÇYœê"·’¨%ÊÅ·b-Dòâ/¤Ã­»F@ÿýañ!x'xô@°ŠÀáò%Ä ^WgÐÓšÆÿt™_¶l3l%Á$ãõ9t|©‹lãkhŸv>d)ܩҀN/1Ò¥!Qî.ÔWl ¸å ¶J':g…Eö&o,÷èЋ3c^¨8%^7š]\'øM@®öé:œƒE3i ¥]Ü»¼Çá`·kº‰€öÈG?UjáO¾Ä+yõ÷„H&¢g<™¸ €iÍÒF…úõâô¥}Àòoþ”OÌËD”`šhµÀ™wΩ¤L¸^B° ÖÆ^­ö{,`â™ñ|ªtŒ·w·RTÒŠÑàÔ‚¾Ü¼‹Vc)¥ ¶“-m5!§¨’Ôå9.ðêîü¼Û ö*¤À*.ÖØ²“BÔcãëGSž!É%áº*I_–¹>“ªãì_&XeŸ+ŽÃ ÇÚnóÉt¸á:gèö‚r×$v€lü–u×’˜ÝT£T T§¢§Î«ÝDSu¿„¢ËŠ þZå˜{Îy®¯£P& =ܬ³Ã* ÑEI"Þm+ºÄ·‚ JF:¯Êù9T,ewp¢}4½Uß8@²YĈl>w*––sŽFá*èoÀäúÔðéI © ¯ÐbyîÃV|ОÖ|™%˜äp÷ß@ÅÈãêq… qì qÈ(ð'뇇ÇÍg¦iʽö”æ;>ü77ÝVÐ…å…›}nÔ \z¿yx]¼.wkj[2 ý›ê*{‘ù Ó5,ž3ši}ßu[8‹ã°íV««RÙнŽÊºÒP¯£•$ÑAx¨T×HÑ V(›2âôϵ–2‡½@æÉÂ&}µÖ‰©ÞJ§…ÁM®û޼ƒ˜[·–õçɤ*µŸÅ›&¼²Ãõ¼2;~W ÜXºOÓÝ‹/z*Àù\ÕT}IÏö¦©ƒ€Òköœ&ŒOGc#s²ìOÌLš¼7‘ÙÖv0X×6öOŽŽOPHçÝÅy¤X䄿Ôï¿>< “맯‡ÇCu4]ŷʳÙm»ãZ%òáñpo÷Ä"$Êç¸W‚”x†:g—æá·¥è %X;‡ûd[Üþþ‘PX´+Êì Öº¬Ýý ÎA;FË|NÉȯ'G}aˆÞj²q³ôßìïí*Æ&÷w÷Ébò å¾r°ÎLÚj»2"¬¡¹/†ýììÙG‚„Dº0çÑ`œÒ¥ŸìY,õ¹¤:W@@Û‰˜ÄõÙ˜~QkÚôÓÅIJ_Ÿ¼îž:Ór°›œ]]ܳp4Qø½ 8<¦ö’Ce°øôsZ×H]—oSR÷O­ ¨°&WPT«‰Ò"Žm„P>лÃuXnŒÿŠUûNˆë–´ö¥pÛåšZ ×)¥öê mr\Ù†¥MéK6ÃnþóàDÚŸáùºïä%:Ci8Lö’ý×~²§ÿ:rÿ:Ñ Ę¥ÿX£~„þ{¯ f,èÕ Õ ÓV3¢H÷RôçKÆ"ÑPG‡û­6¼VwÅŒ#œfãЕzþ¤ßïï¿~ WâZÚY\#pV1+´NÁOVñÓ|ADï­,ˆ­çõëƒ:Ô·ú­²|ê1ð‹Ø7ì‡ÓÜÒ8ßÈP£&¤TThs'1º®¬±hÎîÞ&ŒRhQo¤ŸMfºÝ°uãCâ6ÚÑ ¾ßïŽi¤á1aÀÔÊ"r™¹ ?aÜúîpx°wt@º«„Ü…oH- ºSˆfú!-¡¹´Â²~“V>%YØݻ§ph É^6ý Wÿ@[Ú/U*ùY\Ä£>(ìþªÀ¿yâ3؆éâÞÒ·D>^ÕÉåÕå½7JuZÅízÉþž5j ¾;>ë±—ŸãßûDDN<¡úÖë{ìuŸbG|›ô¸ëÙrj•à"rvoí_!i­O½ÝŒeb 5„Ðÿä—â3ð»^»¼'¸‘×5Nõm}âÍU_ØÑÚjï¦ xVƒ 5¢™ò¨S†ó^GF7Îæ£cbWC©%Ë»kÍZ“ïD!c†™G_#&uµKB9xxxù÷¿j„»7Їáñ5 »¶×h-wv°BJ=åÜ€z}öµA0EU>»ñèƒä&KK"¨!ÓÕΑ²3aÝÓ–ÄfWp_ÏQèàpww7lÄïFEÌJ´¨´¬O+?Î’ëÁîîõ›äîìšÔ ‡Xš¡%ÿÉѵ¨³/ûýwʰP „CøhSA•¨05àÁ~ˆ2å¡Zß`»V„¾ËÅ˾¹¢]íÿñÇëK Hïó¯4¸2máæ‰ã ²ø!Gi±~‡$¿< ãr’›VäÞæø¹´šüò#éçtêÍã/o9¤³"°ÚvLlC}Ûβ\‚þ.#+è†ÝÝŠhËžÒ–áÚòCºXHз"ÎlñÚÜ3,ò󠇑Jû©ÔŘZÝ >ÓçÙ’;_`ƒÖD ûð òbÞõ£Š_ŠÛöÆ´í(J]ê<„v3ñ.õëäÎ?‹Ô©Ó‹òKF¿½YV ú>ò§ ]æÄÙgË…€€^ Ämn¶þ2Y¹fÃ{é¯GÊ5œ§æ¢lç'sm`º½­$¨ºì¯]¢à¸¸mÐãø£ÿ­Н¸)åzÜDÁ@ :qÜ…„¤èÇ2—®€:êŽÉy¯y‰°o{/.3X¢ôÕä„%¨Ë©dËvŒšÊæN ."¬'Z –å\“®Ý[1$|Q¾wXÇ »”Ÿ‰¶9Ø^^øÝIÂùÚ²`ºbM4>_xê¢kÔ’©²$ÿ ª—@IeÂG+íµì™=Lím³·1O°dÛír[܇愵`T‡6€È?{xݨ*É[`)¦Ø}#°NG¶ ¸1QåÆ!¤-<=iyãtEOìJõfœµ¯=ì©•"¶(Æÿ•8 íÉ'Ê Çº•>ãÆÜ®ø4v|;¨$vJÔcÆ"&]’„b¾;m»½äcÉ¥¶ð˜±^l-ÀR¬Ä£%ï&’J0í»wÁdú¡-ä<2Fºuœr76“™%Ñ›óÚÛËÒ/³Ud³ìèÑvµ£¡{âwï{ÏAÁ%Qv CMŽßêo3ÏÜCZaqÖžkFñÀ®ÛÅ»OW$FZñ{´#cÒŠâJçVÙ áÜA·µ+`{Úó³DÌ%A] %qUl«,$ŠtH¬Rù„k´ò¢ëα(âzX•‡'ð" KÐëê!t±m¤–p˜Ÿ­J@ÒœØrC¡£;¡c>–@{è&P°®Eï€Î:ÖßÖ.Lœ@±ÓG§…xu'M€qOÃ{Ü.¡õ׉ /;|û&»ð—ÍÉl<È¿‚ƒUˆƒ¶'§¢}+Vâ©Z‰Uÿ÷Ú£˜Qœð „€_Î9í§"²ùËÇŠ¸Ü/,¬ýò†ëü©¤Üל kòË»²œàäãDÓÓäŠÂNZY© jbšíDx8,=ïYˆ»Åmi0zÚ¢^åã¿'ƒÉœsËÆÜ3xàÕqÎÂ;ÝxÅkb…ªîxzÍ)¿bög± *ÎîNè`‘8)êX1-»=·ml€ž¤#0i€eL¶Ô¶ƒäž¼£tXcЇFÞ„A=GKOO­¹«XR{/*­"­‹WŠøA¯\ÍØÌ L½³žÇ®7Ÿ…ûÆ'ã§E•hñ†äUØH/í"p] ÿ9ŸÙiâ/_ÂúÕlÜ᯽¿ÐZX3 ƒ_X¡áxci­¾'qàÓŸ/ì›Ç‘šázÎÐI¼Lg;5ÚXòÁ« ë¾Ö†ZæþÓgq£»¦!š¬¾ñe!-{é—s‰Ò­9±áUÙáÅS°:‰ò%n¤z ͬÑR;F°K&„~/ª[}]Ÿƒ%ÉùrPƒˆzGÛq/9ßy^\D•8õ¯q«W÷c¸‘TA5¹vÕÇl û=çbÌf§·`Àïd1àQzùˆˆÝ€Ê4”©].2žm÷fî—ÚôZd*;e½fü%û…Ô’3ªtÌ$. Ü¿\~#ð/feì µK#q„~¥&×=èÚòŸuÁÙMjMt)¨è£ø;p£ f¡àvx£ :O­áx°K -‚?ƒ>ªiöL‹æ%†èK(+¯Ç$°E„áR-Í÷ó’ÄÛüë<]¸^3É?)"Y™Ï²­â‘;®Ò¾Öc!€qÍô&®_œ<ÌRµ–Ùj8å`KÚÝ¥ge[§É ìŒìð ch«µ×ô´%0X•¢âé sâæÖ:6†µwÑ6ñm6ZÃŃd…\ ø°· 4-å-ìu>w1ù©9ÔxëÂMƽ„R‰åuW˜”H]6?…í"b£t3?§ð‡BM̩țšÐ¢Æ‰ô½& ü9ÿÍL2Aå÷F"Rº}¿i»DÓqÚ&ŠIº’( Vh®ºÄ(hgÖ ¨ýÀçîà5ר– Ì=>ÑÝðD9"EPDú»F•~+ü²%kµ¨Sœeº¹¤@'ðÍýYBÏ=ÖŸÒ®—²¤_*”6:Z8Cí 1Ô/†áÃ=}¸§÷‡ûúp_îËCñkºÖݶ³,l-]YÌæém&Tþ{`nòÖŠ¾ù~¼Æß}=FÝã“!¡Çö%ús±D«ðBÞgí :Úëb»bÃŒV¾ˆ‚b»ÂíÁúG{»²hÅÞµ`–íÀ‚Qö_ ÷N“woÎÇ´c”ãjSndmÃýããÓ8GSÈ;ã’2|®ÑÄõå¢5ññë½=Z>pRÄi«ì²‹À´¨—°ˆúÚYœìîÆUÙsyÖwŸlV‹Òég§"Â3¦¡D†VȈÜÛ=Þ%šRú@MSÅ.ÅRÓ˜…â«Ú¡çïœpæÒ¡{òMþÜCàœqTÔSpcyF?ÿ{’¬mèðp—íX¢ì;0>»ªrêbáÊ%E·hëÁîþ1‡­M%>[¢_Чã‡ô¡ïk„²?*4Zˆ´‚µþï<ý¥”ÅðiÀ±$•u¥„iB“²JÍhçZÑ E÷VkøsÄ úbåÄYNÊþZ¥û!¨Ù ½aX–ÞßÝõAJþgvÔÚÂ,æ¢÷L} i²¬ÌÉ#mö˜(,V;\@;ó%óŠ ÿÍf9-–dŽw­æÓþ.SïÝ×!ñ¾G 7¦Fß$Û¢g>¹{.LKÂs¯HMQ„“Œƒs¢Ê€Ñe ©YŠ®|×Âx¹Ê6f‘ËÙDzãà‘ÕCäú'âù,׋š$0OÈûÁ×ÉÃîº;ѵ[Ç“¬é«ø~fTFóšx¤Ó­Â‘O†»¯ÖǃO„ ò­k•,˜×àeåÁ†1T2³nÇ´Áªc6 ªë°Òf®lKí#9*H–©“墧µ¸—tÿ><÷Y ÜŽyuQ4 ÇVh\EÇ«šAiá>aÍòÎ{5P×h–ñ'zpõÛy+oIBA©:Çs=­:lgΚ!+ÒY½úp‹óù„!çõ–d5¡ÚÆZMS–Ök-ÒiùOùˆ&Rdy4é—LÎwNBÎê¡,ò ³³¤AóžU`·?¾{óéêýEÆËøÊŸÈÒá8 ‰£Ø:ût'2Œ¦0r&ë YÙVDˆOêñ#’dÔèƒú”hÁßÿ¾µ‡#4&CÝ£ãuÚÍ7é‚q)I… Ý+W ÙgF¼ƒ®*ÊX;ȶÏ!€†"Ø å_»F'Åj¡µÉW­"qoæ^h·äÛª˜§<Ðr;¡9òŸòTìP?s›ï DÜWü†ÍŽŸ“a•¥¥mœ+ï`öI}Œ­Ö É -ÀjÑzÀuĬ¸«oÿ°ÅJ-K?ù˜MɺÄpŽœvå ¬£®;9ÇTgÀZàDâa¦LÕ8+‡shìp\³Q"„ޝA.úk°J£~E!¦ É=ˆ=Pž¦Ò¾›ìo<§Õ¿·¢¢;oÒßRމ»“ 1®ô¹¾À§hð×2×ú?5Yý¥)̸1qG{ÿ"•~Z–t!h+¼Øùg3‘wwºP²îÎfÕáÑL_³xý­ÆzŽÂÊ#p³Û@ÈN dÚ~ 4ÅæÕ±ÐG(×ÉÎ< Ž‚B8éŒ3j9NQ[(d½àˆú'˜HËR‰§+¹xŸtØsJÇ]L— Ü"Ï·µ«¨!‘juòöÓûë³wW‡3|ÕH£ƒÝµƒÝ_;X.ãŒJ͈ÛAQZP:q=n³JĬwUFÒ=ï4« »ß¾ŒW•˯^ä#Ϥ¦Y¼±´ùM<ú]:¢ÿÚPÅ€ï(n ì’лXVÂGºíoBÙe&9DÚw‡$™'×}€«-²ßr6.I·`râädñ«çÚ™k]™q}„ N }{!Æip€×h†ÙÊÉìzÌXnÔ9Âqyájÿ°:yA¯ŠÛ‹·í¾€´V‰ vmœÔá…©Í&J¯ ¸í—}¿)Tu¯ÞÞ¼“¶×·Ú2 Ì\ät}K[n…ÏüÉ~äsÂ…H5Ô÷š•ÓRŠÀ1ØÕ86£õ—Z¤-`FÒô"éXHóÁš‹GÂKˆ*úV›âšª­,Aÿ„rEöç’84î™Æ$\ÛØZ¬ùàµZ 7 ÛÙŒ“ñNóiIúM¹$Å*éÜ ®·ƒ?€PŸEX|‘¡²ùL»úDJÚµ¸¡»!F X”¹5fmC®‡Y–ù‰Ó°Kö”ô…òÙUûrUAn‚jàÿ–ý7÷ý“p6íx¨^ µ§œ“*ûS7œç¦„pFƒ¾y{oVðÁÂ*}HÃOMAtíêh9œûšƒøØH?¤OÄ^c‘Aâ[=Ñ$)RÆèVO"ZJî6+0ÿõí‹ì‡M_ Œ´¬¾ë«Ü’þ{""×_T¦õi?Ι˟#¸Idráy§aFV»–m^Áé·Sãkmî3ã(¡Aò^Âܽ"ëzìtæ$0³ØÆë2½ßçTj~vù:ûAT¢m¼¥ë™KOUþ¬Ý5´XÉ¥«D"µå¹ã2ìDLäÕeå*ÛYë¼¹j˜^8ÃÙ핽óÆøõM…^øèÞŸ_XyýÐZw,ÕâGó,Ôr\¦Ã¤íí_}ðqmœ3Ѫ¦F‚­Æ¤l U ÆpÊ#ìÌrË…“z_ŠÇ\­€Ó¸ˆÊöc±Ð2ú9ÖÄ:o‡»ÝPóêsМ„r¶¢˜Ý¿»½F$P»„.Ûsÿéþ–I ­øÔ€ŸÕl%(«éŽûŽD±îáwºS~]íØ¿ ‡ƒ]šÄ(ú3êAl¨ 0 ´³¬á'd[à|4“BEšÏÌ]˜ü{,»fÏk8~É¡ªË/Ï·£ÜŒµôa…Ó±ñÇÐÀuOB¥$ Üõ/+ ׄq®ŽlÓÈ?2€R7$9Wf\pý‰“Ž*2ø+5}(]AÖ½Û„ÕEÐÍ4h1½#;ºáÚÑ-›åœ“G¾y~ôü¿µ—¬†%Paû!Ì`²ùiI¨ÚqñdÖùh “<—ŤfB#ɼk¨t㚊|.UU(ð÷n$¢mÓùË䔾 èi$hA¬Isô»&øPÀ6p s½áÎÕ+Ò$%ªbïþóv|o]>€Ê©¨<†Æj¶, dGÁšüÁÍ|ö³T"•È Ú^E_ƒÿ"BõSó9ýÈqņߧÄpFösn4Óð•C¡WÀw˜×_á&Ã"q¢°è©%²Ò3*ÀÎxd¥Î S«\âaª—€&I§(í<›å‹z­×˜˜8*±º ³?5ÉÁ`?ùÉÂ*`Ö0_9ñ"Ü*]qÔv­pöÝ1T¶}·p»§Ýˆ9LvŒ”ƒ­ü€ÑU9R⡱¿\HPe^˜TðË€%´WXÍ¥µø£®Ë¾í.ãÅ%êNtº=AÞÛ²~÷™ÿ²âQ=í£˜E£Þt‚ .&Ä«å¾bDE¬ %é|m!å2ÿ’ý‚‰¤7_‚ øîöý>ÎL»ÉŒ%œÎ枯u€’ú²v¹b´‚á\Ÿ4ÐÆù5üÐN ŸqPÝ7‘è¼ûtÕÕÎtg°^Ý“èŸ&·6Ò,+ž¡5v§f>¹›»‘œ :,…êio‘þÎß‚­ÜrïM¥¾e•Ú—ªët¾3±Êt°È¦REI>ˆQ^%žÝYŸ}lt#”çf<= *Ç.âBϵÂϺfðœbê÷.PÚJx˜”H¶¨óbáP% B;˜ï Æ2*Є£“¡fšE+jÓ?¦—y Ìí¸\ý¾ù°ÂüÖ·'];Ìq”$ÔN‰½@ûÂX 'îPl\~ýLÃwù9yöŸ>’꟠¿EFÅ«¿Ÿ&g¨Xt£jf ŸŒÙBCusÙÀÚ)é­¯Œ¡ú(ÐAl†:àìIÔ8`·Ö†½œÍÍø1!!óñîkÓz×lº¿·ÆpŸ¯•ðWö¹óËðköC7ª÷Q±7ïöâì#±®˜­™ÏÜ~¼gí nÎNש~ïŠÛuÎ…Yàð·A1,˜˜íëÛLýó‹\ýÔäßXlG¾HÜ'jðMB6 s/$¢—5‹Nßf&‚“Ó%Ä×ÉæZ"·ì Ù=Y»Îís6sWO4©Ó¶NûßHZ—á¸3”|cË`¼+vŒÚ8qȲ™Íظçia’`Üb°Z«QcŸLäŽÜ•ZØùàë0Dô WTé×ÏÑ%:¥‘QĈ|üxy}ûQžë:Â:ŸÎáŠÆ11~Í/a­t¯‹ò¹#ÛSÀíŠ_Ÿ cî%CÄÅ×w¦ß7ù4ySAø#¹žÕ‚¾ÇñÑĪld}#lïU[÷YÜÌ+xÿ•e¢ûaÛù2Àõ)žŒ] CêË(ПàWëŸE cÿNmY«œô'bÂUàk¡Çþk¾»‡ëç{‘þ¦õë Q¸“Õä»7´ö®_‹£½ñ:²žk˜µJsë™ÓÓ ~Ä;> ããͲώéúuÖ°Ý“ãÐû‰ý(ùê³éß„°—tˆ³®^®Šf…ÈHõ¸J›1ƒ‘MÎ$¸ºFÒUjùÀG}T+8¹ƒ^gZÅxzËÔªLD0E{?| ÒžòߺêGs¡iÚ¿á1•’ã] ŸÃs>dPÆîÇ+ŸÙßµG”p86§Ëj§Äj:4_aûRS¹)ÃÒuí#ÔÚ¢–/ê+á“ôΙÚ½ƒ£rh ðÛ\!îà æx³¬W쉨Ë*$ E¨¨BˆVNrVKSñÍÌíœÄ´‡Wu }¢É@› n,Ÿ »wåbYÁ:r¨OÖš]<<ÜuÕUhw,7}‰¤‰§åâáÔ›§Mþ‹—Þbb;Ta•ƒ‚BÉû5FøÔ >7 t^!g®g&4› <ê3 íùQ§øIà€œ|û þ)1™— ÕEµ UcØ?¥âúš*$´ —žÛШ*ˆ)AA¡ß/¼øÓ’‹ð}ä>±‘À½{„upåÇQg_f ¡©hàK—µÒt-=)]ÂÂÞ³N€ÃuhI¶Ò+ÎVT¶¿Ó~dlú{y{÷á:¹½»üùêçûäçË»û«7÷šIxþáúöÃÍåÍÇûäìæìý?î¯î»ÉçË»ËäâîÃííå…0#ùˆIóqÒ+9N¶/Ÿª²N®Æ¬hþG¹|å«Ò¦Õ|WÜ`°ÝÝjËfÜ¢³Èq×ë­Ó4™#µGZ>:¹çÌgâ`Ô>`´ m®f«ípeõB€‰Ù_Þ¯3ó•½è¼ƒUÆy>„1’Ú|¥H-=î+S¥ÏE,Á€R^›…:‚ý1èÆÑI˜PÝe„˃–µâ!wB½£‚dˇ›†-õ­låîø(¼c¦ë÷Ç×À2òIéœY+½³•Jü!X¥Å: \’#ý¸#§“ÀOûC*ïܵð1 â§`È$¯]¢DAswU¥|,ê{+´¼zQV‹…>ÖhÙâYÓ"vN#_þæ].Ý‚–ø4“òHà 8Üx’ 'ý*›‚¦6è°$iG†—Ûá ‘Y~‘>åbɧ)Iù„ô÷Y£}¬’m.OB>•ù,ýS˽@K?»I†›—ŽTýåÈ6ÑKrþ73N7 á¶Fò÷ZDIâxïúIE]ÀD‘<7WQXƒkßrœVW°‘d‘b„z÷÷—w¤mçÉp¢Êv¢}faûùþ÷®t–v•¯ÌCj%ÏšäK–-`HÍñMÀQÚŽV~‚Ð-.+ÊÖ!hK»XnÁ {këÉßÇ~Ò!0’.…»Ý­ßµ>®_Î`+^J(ì«ø©å*z-B×±€x^ûÕo=eµ)ìŠÀxÔèI߯ã§tØS€F–k­5—¨åzh3È‹DK‚R6ªÒS«Гªµ¤”²ù‚« jç[ñí%áá9°Ö‘9ò†e ³Y÷_ß¶"m&4o‡9®\Ø`¥œh™×á#õ:¬ÀÝ{‹_èPî6\š´ÔŒS~ÅÐ+p¼%i·fjÝPÉŲ¥#Ù´d‡íäJúB7Z%"_âcž<.¿ bE'è™Ë%¤ØÕ'}¤½œÊI¥\GxÃ$é¸Û`Öõ»šØæÛ8J‘Uu>̨è¥Ý/˜åMm|‰‡|Œ1¦'–?}L²¯)o„‹ÛXjÌqb%b9WW&ÊÑwвEk ð ®qŒzêí8?4/¦óæßÙ·Í]‰š¸Å¦Ú®ÖLpl­Þ ü ®3Ñ-öYÛËȤm¢Þð%Dœž8¾%Ždi%YsªA½ñ-ñ’üzÆ@òÊÌ~årúñ+‹ô áO® û!ÍZ‰+Z} ®pZ5[hgýïßøþ)­õJµeì®Ã/vH÷õ½Ýjefõ|½éÖÓZæÚR³Ã™Ð„B]W SóÿÀ"ÿCWy9“ŒÖ¤C*L<LÃŒ³ÕØW9kdàèþré H0¿cóÙß.dýµR?‚Ð`I\Ï3¢˹6¹Â§H bf›JË˵€ƒÎv·Ý•æbͤäܼ?M®Ïn.îÎ~¼L^†ßo3÷}V“ ×L’#.œo‰ÔbmµPH½îq ÷¸4)0Ê¥H%°ŸùÖÑ;H¯mÛózÁA*í!„Vå ŠÞóN< ùºœ_4ÏØœ±ýaª–qŒBá¸Ô,ÌŠúG2Õ^㚃íÍ*ôWßQ*+9ÅõéJ²²¨¾^¨´¨Þ(sÑlÂU¹ ÇþkXß/™8ö©#õ\’D/âb ÏÐñ"]ÜŠ\3 ­‚8/Þ—ö™!¾*‰²ó¦3Ѿ+‹wŒ?ILññ…-“dY¯ ÑÇê'‡LÎõq¸‡TK‡y¼×¾TÞSUÒ¨:g´¼™’mðaëG1_îrLVǺÛk”o½±Á¦ëd ç ,Û>Ðè./WŸ­v5ÝàUJç×Õæ-{ßæ&/mÛŠô‹„A`°–e;šÃý¾â~œgyÚïnÅmµOÅ4óúBKWËI†U±Í‰Py€-„ Ù‘X+B8^xÖ=õBTÀø(†[›RÔLƒ¡ÓµýuÔ˜|Ð3ÄÏ$“)6Ù×…”xX%„Å+TÔåÓë,½˜“§¸4jc´(+9˜Gc~áæ„ÏÄÖ¤®Äˆu˜jú2Ï4¡ãu ã*WœI$xåjŸ?‰Z"-pÁ2:[ æ©Ý„ÿRqmÓP0‘•út³´xέ}üj—Ó³¬„!g©®·ƒg2q€ŠØ¸LâÅt\*C€ââÚæðð€š¿ÌA&›¡•{¢„°z¯ÉÙQ²$¿ÄÂ5jS% K;ÆTGüÎ})>uˆ‡š‡ ¥è$‚;ѶÚbÄ$$Ýò4(«K¶$ÃU©ÐKÂí×­ÿDÇ9 €ýapp-2.8/debian/000077500000000000000000000000001377436340000133745ustar00rootroot00000000000000app-2.8/debian/changelog000066400000000000000000000212661377436340000152550ustar00rootroot00000000000000socnetv (2.8-1) xenial; urgency=medium * New upstream v2.8 -- Dimitris V. Kalamaras Sun, 03 Jan 2021 17:44:00 +0200 socnetv (2.7-1) xenial; urgency=medium * New upstream v2.7 -- Dimitris V. Kalamaras Mon, 28 Dec 2020 20:44:00 +0200 socnetv (2.6-2) xenial; urgency=medium * New fixed upstream version 2.6. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Mon, 28 Dec 2020 18:44:00 +0200 socnetv (2.6-1) xenial; urgency=medium * New upstream version 2.6. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Mon, 28 Dec 2020 13:44:00 +0200 socnetv (2.5-3) xenial; urgency=medium * New upstream version 2.5. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Fri, 08 Mar 2019 11:59:14 +0200 socnetv (2.5-2) xenial; urgency=medium * Fixed bug in debian package creation -- Dimitris V. Kalamaras Wed, 20 Feb 2019 14:59:14 +0200 socnetv (2.5-1) xenial; urgency=medium * New upstream beta version 2.5-beta. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Tue, 19 Feb 2019 01:00:00 +0300 socnetv (2.4-2) xenial; urgency=medium * Fixed upstream version 2.4. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Wed, 28 Feb 2018 14:18:03 +0300 socnetv (2.4-1) xenial; urgency=medium * New upstream version 2.4. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Tue, 27 Feb 2018 21:18:03 +0300 socnetv (2.3-1) xenial; urgency=medium * New upstream version 2.3. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Wed, 05 Jul 2017 11:18:03 +0300 socnetv (2.2-1) xenial; urgency=medium * New upstream version 2.2. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Sat, 21 Jan 2017 22:13:03 +0300 socnetv (2.1-1) vivid; urgency=medium * New upstream version 2.1. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Wed, 28 Sep 2016 22:13:03 +0300 socnetv (2.0-4) vivid; urgency=medium * Version 2.0 patch for Ubuntu. -- Dimitris V. Kalamaras Tue, 13 Sep 2016 00:13:03 +0300 socnetv (2.0-3) trusty; urgency=medium * New upstream version 2.0. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Mon, 12 Sep 2016 22:13:03 +0300 socnetv (1.9-3) trusty; urgency=medium * New upstream version 1.9. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Tue, 23 Jun 2015 17:13:03 +0300 socnetv (1.8-3) trusty; urgency=medium * New upstream version. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Fri, 05 Jun 2015 16:13:03 +0300 socnetv (1.7-3) trusty; urgency=medium * New upstream version. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Wed, 20 May 2015 16:13:03 +0300 socnetv (1.6-3) trusty; urgency=medium * Build to fix upload in ubuntu archive -- Dimitris V. Kalamaras Mon, 11 May 2015 19:17:03 +0300 socnetv (1.6-1) trusty; urgency=medium * New upstream version. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Mon, 11 May 2015 14:12:55 +0300 socnetv (1.5-3) trusty; urgency=medium * New upstream version. See https://socnetv.org/ChangeLog -- Dimitris V. Kalamaras Fri, 10 Oct 2014 15:14:34 +0300 socnetv (1.4-3) trusty; urgency=medium * upped rev for launchpad to accept package -- Dimitris V. Kalamaras Mon, 01 Sep 2014 20:14:34 +0300 socnetv (1.4-1) trusty; urgency=medium * Sync to new upstream version -- Dimitris V. Kalamaras Sat, 30 Aug 2014 18:06:23 +0300 socnetv (1.3-1) trusty; urgency=medium * Sync to new upstream version 1.3 * Supports multirelational networks. * Memory and speed optimization -- Dimitris V. Kalamaras Wed, 27 Aug 2014 20:36:23 +0300 socnetv (1.2-1) trusty; urgency=medium * Update package to upstream version 1.2 which bring a lot of changes * A new "prominence indices" conceptualization based on Wasserman & Faust * SocNetV distinguishes between Centrality and Prestige indices. * Added 3 Prestige indices (Degree, Proximity and PageRank) * New reachability measures (Walks, Connectedness, and Reachability Matrix) * New Connectedness statistic: Checks whether the graph is connected. * New "distance" index: Eccentricity e * Revamped GUI. Toolbox now has two tabs: Controls and Statistics. * New level visualization layouts: By IR Closeness Centrality, Stress Centrality, Eccentricity Centrality, Power Centrality, Information Centrality, Degree Prestige, Proximity Prestige and Pagerank Prestige * New circular visualization layout: By Proximity Prestige * New automagically recreated dataset: Wasserman_Faust_Countries_Trade_Data_Basic_Manufactured_Goods * SocNetV Manual: Updated documentation (added new indices, corrected wording) and added the Manual in Mac OS X .dmg package. * Fixed Bugs: Closeness Centrality in disconnected graphs/digraphs (it did not drop isolates), wrong classes for PC indices, Stress Centrality (wrong maxindex and circular layout in digraphs), not displaying of edge weights, Matrix::product errors, Eccentricity Centrality (wasn't reported as the inverse e), Geodesic Distance (it reported two nodes as connected even if their distance was 0) * Dropped Graph Eccentricity as it is known as Eccentricity Centrality * fixed layout and signal/slots in datasetselectdialog.ui * fixed warnings during compile (i.e. unused fileContainsNodeColors etc in loadPajek() -- Dimitris V. Kalamaras Mon, 18 Aug 2014 19:34:41 +0300 socnetv (1.1-1) trusty; urgency=low * Update package to upstream version 1.1 * 1.0 was the first version in Qt5 * New Feature: PageRank calculation and layout * New reference item: SRS Documentation by Vagelis Motesnitsalis * Re-introduce GraphViz import menu option * Init "Spring Embedder" tab checkbox to none when user creates new network. * fixed weighted graphs inDegree and outDegree centralization calculation * fixed bug in createDistanceMatrix for Closeness centrality min and max index * fixed bugs in standard and group Graph and Closeness Centrality calculation * Changed default arrowSize and colors * Added dataset: Knocke_Bureacracies_Information_Exchange_Network in pajek format * nicer printout of adjacency matrix, distance and sigmas matrix * Changed minimum size of scene and graphicswidget to remove scrollbars * Updated manual for NumberOfGeodesicsMatrix option -- Dimitris V. Kalamaras Mon, 04 Aug 2014 00:29:24 +0300 socnetv (0.90-1ubuntu1) maverick; urgency=low * update to version 0.90 -- Dimitris V. Kalamaras Thu, 14 Oct 2010 00:46:19 +0300 socnetv (0.81-1ubuntu2) karmic; urgency=low * update to version 0.81 * Support to auto create known data sets -- Dimitris V. Kalamaras Thu, 28 Jan 2010 02:49:48 +0200 socnetv (0.80-1ubuntu1) karmic; urgency=low * update to version 0.80 * support for csv and list files * new statistics: triad census, clique counts -- Dimitris V. Kalamaras Fri, 08 Jan 2010 19:32:01 +0200 socnetv (0.70-1) jaunty; urgency=low * update to version 0.70 * first webcrawler implementation -- Dimitris V. Kalamaras Mon, 29 Jun 2009 01:04:04 +0300 socnetv (0.51-2) intrepid; urgency=low * Fixed critical bug in rules-paths making previous DEB package -- Dimitris V. Kalamaras Wed, 18 Feb 2009 23:43:28 +0200 socnetv (0.51-1) intrepid; urgency=low * Update to 0.51 -- Dimitris V. Kalamaras Wed, 18 Feb 2009 22:18:45 +0200 socnetv (0.50-1) intrepid; urgency=low * Update to 0.50 * Fixes -- Dimitris V. Kalamaras Sat, 14 Feb 2009 02:58:40 +0200 socnetv (0.49-2) intrepid; urgency=low * Changed Standards to 3.8.0 * Added missind clean Ubuntu environment dependancy. -- Dimitris V. Kalamaras Tue, 13 Jan 2009 17:39:46 +0200 socnetv (0.49-0ubuntu1) intrepid; urgency=low * Initial release -- Dimitris V. Kalamaras Tue, 13 Jan 2009 15:52:01 +0200 app-2.8/debian/compat000066400000000000000000000000021377436340000145720ustar00rootroot000000000000009 app-2.8/debian/control000066400000000000000000000026121377436340000150000ustar00rootroot00000000000000Source: socnetv Section: math Priority: optional Maintainer: Dimitris V. Kalamaras XSBC-Original-Maintainer: Dimitris V. Kalamaras Build-Depends: debhelper (>= 9), qtbase5-dev-tools, qtbase5-dev, libqt5charts5-dev, libqt5svg5-dev Standards-Version: 4.1.4 Homepage: https://socnetv.org Vcs-Git: https://github.com/socnetv/app.git Vcs-Browser: https://github.com/socnetv/app/tree/master/ Package: socnetv Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Social Networks Analysis and Visualization software 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 (GraphML, GraphViz, Adjacency, UCINET, Pajek, etc) and modify them to suit your needs. . The application can compute network properties, such as density, diameter and geodesic distances, as well as node-wise and network indices such as various types of Centrality and Prestige. Various layout algorithms (i.e. Spring-embedder, circular and in levels according to prominence indices) are supported for meaningful visualisations of your networks. Furthermore, it can create various random networks (Erdos-Renyi, Watts-Strogatz, ring lattice, etc) with a few clicks. . app-2.8/debian/copyright000066400000000000000000000021141377436340000153250ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: socnetv Source: https://socnetv.org Files: * Copyright: 2004-2019 Dimitris Kalamaras License: GPL-3 Files: debian/* Copyright: 2014-2019 Dimitris Kalamaras License: GPL-3 License: GPL-3 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 package 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 . . On Debian systems, the complete text of the GNU General Public License can be found in the file /usr/share/common-licenses/GPL-3. app-2.8/debian/dirs000066400000000000000000000001361377436340000142600ustar00rootroot00000000000000usr/bin usr/share/doc/socnetv/ usr/share/man/man1/ usr/share/pixmaps/ usr/share/applications/ app-2.8/debian/docs000066400000000000000000000000511377436340000142430ustar00rootroot00000000000000NEWS README.md TODO changelog.gz AUTHORS app-2.8/debian/install000066400000000000000000000002151377436340000147630ustar00rootroot00000000000000socnetv usr/bin/ src/images/socnetv.xpm usr/share/pixmaps/ src/images/socnetv.png usr/share/pixmaps/ socnetv.desktop usr/share/applications/ app-2.8/debian/manpages000066400000000000000000000000211377436340000151030ustar00rootroot00000000000000man/socnetv.1.gz app-2.8/debian/menu000066400000000000000000000002521377436340000142620ustar00rootroot00000000000000?package(socnetv): \ needs="X11" \ section="Applications/Science/Mathematics"\ title="socnetv" command="/usr/bin/socnetv" \ icon="/usr/share/pixmaps/socnetv.xpm" app-2.8/debian/rules000077500000000000000000000002671377436340000144610ustar00rootroot00000000000000#!/usr/bin/make -f %: QT_SELECT=5 dh $@ override_dh_auto_install: $(MAKE) INSTALL_ROOT=$$(pwd)/debian/socnetv install # $(MAKE) DESTDIR=$$(pwd)/debian/socnetv prefix=/usr install app-2.8/debian/source/000077500000000000000000000000001377436340000146745ustar00rootroot00000000000000app-2.8/debian/source/format000077500000000000000000000000141377436340000161050ustar00rootroot000000000000003.0 (quilt) app-2.8/debian/watch000066400000000000000000000002161377436340000144240ustar00rootroot00000000000000version=3 opts=filenamemangle=s/.+\/v?(\d[0-9.]+)\.tar\.gz/$1\.tar\.gz/ \ https://github.com/socnetv/app/releases .*/v?(\d[0-9.]+)\.tar\.gz app-2.8/innosetup.iss000066400000000000000000000071331377436340000147220ustar00rootroot00000000000000#define APPTITLE "Social Network Visualizer" #define APPSHORT "SocNetV" #define RELEASEFOLDER "release\" #define EXECUTABLE APPSHORT + ".exe" #define NUMERICVERSION GetVersionNumbersString(RELEASEFOLDER+EXECUTABLE) #define VERSION "2.8" #define URL "https://socnetv.org" #define COPYRIGHT "2005-2020 " + URL [Setup] AllowNoIcons=yes AppName={#APPTITLE} AppId={#APPSHORT} AppPublisher={#APPTITLE} AppPublisherURL={#URL} AppSupportURL={#URL} AppUpdatesURL={#URL} AppContact=info@socnetv.org AppVerName={#APPTITLE} {#VERSION} Compression=lzma/ultra DefaultDirName={pf}\{#APPSHORT} DefaultGroupName={#APPTITLE} DisableProgramGroupPage=true LicenseFile={#RELEASEFOLDER}LICENSE.txt InternalCompressLevel=ultra OutputBaseFilename={#APPSHORT}-{#VERSION}-installer OutputDir=. OutputManifestFile=Setup-Manifest.txt ShowLanguageDialog=no SolidCompression=yes VersionInfoProductName={#APPTITLE} VersionInfoCompany={#URL} VersionInfoCopyright=Copyright (C) {#COPYRIGHT} VersionInfoDescription={#APPTITLE} Setup VersionInfoTextVersion={#VERSION} VersionInfoVersion={#NUMERICVERSION} WizardImageFile=compiler:WizModernImage-IS.bmp WizardSmallImageFile=compiler:WizModernSmallImage-IS.bmp [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl" Name: "catalan"; MessagesFile: "compiler:Languages\Catalan.isl" Name: "corsican"; MessagesFile: "compiler:Languages\Corsican.isl" Name: "czech"; MessagesFile: "compiler:Languages\Czech.isl" Name: "danish"; MessagesFile: "compiler:Languages\Danish.isl" Name: "dutch"; MessagesFile: "compiler:Languages\Dutch.isl" Name: "finnish"; MessagesFile: "compiler:Languages\Finnish.isl" Name: "french"; MessagesFile: "compiler:Languages\French.isl" Name: "german"; MessagesFile: "compiler:Languages\German.isl" //Name: "greek"; MessagesFile: "compiler:Languages\Greek.isl" Name: "hebrew"; MessagesFile: "compiler:Languages\Hebrew.isl" //Name: "hungarian"; MessagesFile: "compiler:Languages\Hungarian.isl" Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl" Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl" Name: "norwegian"; MessagesFile: "compiler:Languages\Norwegian.isl" Name: "polish"; MessagesFile: "compiler:Languages\Polish.isl" Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl" Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl" //Name: "serbian"; MessagesFile: "compiler:Languages\SerbianCyrillic.isl" Name: "slovak"; MessagesFile: "compiler:Languages\Slovak.isl" Name: "slovenian"; MessagesFile: "compiler:Languages\Slovenian.isl" Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl" Name: "turkish"; MessagesFile: "compiler:Languages\Turkish.isl" Name: "ukrainian"; MessagesFile: "compiler:Languages\Ukrainian.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: {#RELEASEFOLDER}*; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs [Icons] Name: "{group}\{#APPTITLE}"; Filename: "{app}\{#EXECUTABLE}" Name: "{group}\{cm:UninstallProgram,{#APPTITLE}}"; Filename: "{uninstallexe}" Name: "{userdesktop}\{#APPTITLE}"; Filename: "{app}\{#EXECUTABLE}"; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#APPTITLE}"; Filename: "{app}\{#EXECUTABLE}"; Tasks: quicklaunchicon [Run] Filename: "{app}\{#EXECUTABLE}"; Description: "{cm:LaunchProgram,{#APPTITLE}}"; Flags: nowait postinstall skipifsilent app-2.8/man/000077500000000000000000000000001377436340000127255ustar00rootroot00000000000000app-2.8/man/socnetv.1.gz000066400000000000000000000055731377436340000151210ustar00rootroot00000000000000‹Bæñ_socnetv.1…X]oÛ8}÷¯ ,6de:»3Å X7qRcœ8ˆv‹} %Úæ„5$eÇÅþø=÷R’e§ƒyhjQäåý<÷\¥ËOÂÛ¬Ta'Þ‰‹{éÄ?¼ûùB\,lö ÂçÑéOñIK#°²·îE|Ö¾–F{å.éâ“xßO Qûþ›²ÌñOš~û(¤ŒB<Ÿ\|}˜?.¦‹Aú±;/¿ˆA:kmTŠ•Á,lôôÛè³øþþ6Ú)çµ-¿¿iÛlÚ*Sñ•7“ÅõÓôq9? þÔ.qÙ8`(´RdÎz?ªŒ këŠDÔØ3Z;­ÊÜ„¬*£3H ¼a«¢ÕžÃ»Æ!q‹]‹Ó‹} Ý¿è°Í­â`k‘Éò=Lš>Šßþ³ªÿ+~Ü8¹?w£ØÓ9)Öj/2h\!q¥ ¸“¤ì¤O„±2‡K•ÉE.ö;[Ð18Yh:à몲.¨œL(d—wNVÛûY"øÇgý-“|£fڬыqþ»ÌT™âÃoåïê%Ï×Ó‡É2*déPÀ+´7ì]å`€«JE÷ŸØENÊlYªŒ4Ú«U%7ʧGw &¹BfÁºèã •‡tgëÍVTwŒ°Ÿ™Úã¸.7اÖk,ãm"öÒ¼ø÷^¸î%¾vÖ½?推ŒÅ±™Dy!$P™·&âFmœRÝã5ØÓo„óiŒ Wgð%[(éÙ©T‘gÁu Û…H†» Â¹ 7”©¹†'Ä¥NU*–Ê¢œãm™±^‘kzçdMØ+U¾Y×e,,ŠJ½²{ÎÉŠ³¯ºè”Aæ;Y¾tZ “žé×¶(ê’öæfÆÒ¥ÙXX(ŽÂ…¨\âkŠ«ÑÔª{<óæâèDõG­wÒ LGD#ô£ o5ÔrÙÈgzÉÄ¢ 5´ê ”,cÄAŽõßÃd%'Ç3é­bOÔêrœ ƒÓ£3q¹õ£'Ut"¾Èü–Ø .%[©DpÊÊQ3êÍóREÌ@¹·P)î•1£—ÒîËód"8õ*}ý(…!üÝ‹[ƒÒ,)áÅZÂ\} a3Œ~¸V”€+<#+௧Éý§©˜i—Õp€]픉µos¨åÑw!èÐÏiŸAa¤`¦F¹vX ¾ieNdN¡¸Eô·ˆe!Kx¾²&g7 ¹”PW% Oà ö¼)6òÏ Ô&à~ç°:E„a Žß©ø`l[²k/O[è¶‚$UH§Vš}¦é(]+)£^;&$yþ½£€múŠ~¬µ #à3ÚOì^–Ò»'¨Gã Z„¥ªF‚ 4—wݙ۬Ñ%ý´5.ç^»Ñ;Uâ'Œ…žŸféiE£Ò·Ó;TŠÍê‘l2}eÑími(¥È–ý–ÊÉÕ%Å$#Iè5rR½Z5LTOM·‰®hS¯×mHE`‰ÔæÄº”X-³êÄœ _“v”™é²~e÷2›/Ðò"Õš_?ßO–c"[‚:ß¹9Ðv"á‰Ó-aþMÄó.qy{3&ÌÈvPO®`:ˆÊ/Ûª÷WW õK­Û\áõÔkº …ùÐh4gÞº¹|Œôðȉ&~Üh_<«Ò¾‚oèň–[PC?'^Ég?Ï‚ÌlÁ¿+Ðà±öT×’?JôàV>##eÕ^–ÌÀHʆ>q´Á_$"•ùï,(ó;ÑßÏ56AkìªdÜUÑêÉ('nÐÎ2k†$¬Ftÿ‰bׂFu¼1øKÂ豿狢ÌCnѰ°¦ûvç>n9ø†À=/·G÷KÎ*Š’1秎鯈œxº&BÐÌ]¥r›C$DZtÜŒzÄë±Ï«WŽ'u ¹q2M«­‰ÕÇu›ëõZ9utjYpªÜÊo›×%²FFŠÂo»¶›Ù5®`:~vÖ½).ÿJ´m›×r¸­"t‡·N;B-×@ (qVZ¬âVe/+ûª|»Âð,l`µ_.mz{¨yÓ•t~}˜yŸï‘ÎuuIRA0RBî»~øÆNŒ…ض‡”p ŠèÛB‰*²,f8^Ÿ‚÷'ýöœØB›qIÜYKE5Ûµ:´àGÉ@Ó=ÊÝÃŒ}y. Blï+ S*Fö3^sEÀ7"ãìߌ;i,çß·?‰ÆMãçå§ù!†xE~•F–ˆ_òf-}i×þµ)ˆkÃâi¸W ø¦rо„9 f£xnk¸SÖr§ÕwF¢8 u#‹Ôá0_â& Ib¾ÿf)ß4ŠÉöª²:P‚ÌV‘B- GãrÂ[Ñ€Ô {/È ú*…áp´j·‰ÑˆÒLpåPzÛ¿H¢”d4ÏÑÇ×óǯOÓ»OËÁ²ïÊ3'8 –oÑñîáYÜ€#x`»:ëÆ¹âW´æ·qèèæ¿…]‡=}#½¥@Å’mCµßïÓMYó˜×ÌþjS™8è!ßT¤«v|ç1Œ|§Ú.ÓZ= ïfüÝÃQá@ ÌLk2ãÙbÎeÂt`Gmýºàÿž—=Aapp-2.8/socnetv.appdata.xml000066400000000000000000000027221377436340000157710ustar00rootroot00000000000000 org.socnetv.socnetv.desktop CC0-1.0 GPL-2.0+ Social Network Visualizer Social Network Analysis and Visualization software application

Social Network Visualizer (SocNetV) is a cross-platform, user-friendly free software tool for social network analysis and visualization. With SocNetV you can draw social networks with a few clicks on a virtual canvas, load field data from a file in a supported format (GraphML, GraphViz, Adjacency, EdgeList, GML, Pajek, UCINET, etc) or crawl the internet to create a social network of connected webpages. You can edit actors and ties through point-and-click, analyse graph and social network properties, produce beautiful HTML reports and embed visualization layouts to the network.

socnetv.desktop https://socnetv.org https://github.com/socnetv/app/issues https://socnetv.org/data/uploads/screenshots/25/socnetv-25-padget-power-centrality-size-distribution.png Dimitris V. Kalamaras socnetv.desktop
app-2.8/socnetv.desktop000066400000000000000000000004631377436340000152310ustar00rootroot00000000000000[Desktop Entry] Name=SocNetV X-SuSE-translate=true GenericName=Social Network Visualizer Comment=Social Network Analysis and Visualization software Exec=socnetv Terminal=false Type=Application Icon=socnetv Categories=Education;Science;Math;Qt; Keywords=Network Analysis,Social Network Analysis,Visualizer; app-2.8/socnetv.pro000077500000000000000000000121021377436340000143540ustar00rootroot00000000000000lessThan(QT_VERSION, 5.0) { error("SocNetV requires at least Qt 5.0!") } TARGET = socnetv TEMPLATE = app CONFIG += qt thread warn_on release #CONFIG += qt thread warn_on debug LANGUAGE = C++ # support QT += xml QT += network QT += widgets QT += printsupport QT += charts QT += svg QT += testlib # testlib only needed to use QTest::qWait in Chart::getPixmap()... INCLUDEPATH += ./src FORMS += src/forms/dialogfilteredgesbyweight.ui \ src/forms/dialogsettings.ui \ src/forms/dialogsysteminfo.ui \ src/forms/dialogwebcrawler.ui \ src/forms/dialogdatasetselect.ui \ src/forms/dialograndsmallworld.ui \ src/forms/dialograndscalefree.ui \ src/forms/dialogranderdosrenyi.ui \ src/forms/dialograndregular.ui \ src/forms/dialograndlattice.ui \ src/forms/dialogsimilaritypearson.ui \ src/forms/dialogsimilaritymatches.ui \ src/forms/dialogdissimilarities.ui \ src/forms/dialogclusteringhierarchical.ui \ src/forms/dialognodeedit.ui \ src/forms/dialognodefind.ui \ src/forms/dialogedgedichotomization.ui \ src/forms/dialogexportpdf.ui \ src/forms/dialogexportimage.ui HEADERS += src/mainwindow.h \ src/texteditor.h \ src/graph.h \ src/graphvertex.h \ src/matrix.h \ src/parser.h \ src/webcrawler.h \ src/chart.h \ src/graphicswidget.h \ src/graphicsedge.h \ src/graphicsedgeweight.h \ src/graphicsedgelabel.h \ src/graphicsguide.h \ src/graphicsnode.h \ src/graphicsnodelabel.h \ src/graphicsnodenumber.h \ src/forms/dialogfilteredgesbyweight.h \ src/forms/dialogedgedichotomization.h \ src/forms/dialogwebcrawler.h \ src/forms/dialogdatasetselect.h \ src/forms/dialogpreviewfile.h \ src/forms/dialognodeedit.h \ src/forms/dialogranderdosrenyi.h \ src/forms/dialograndsmallworld.h \ src/forms/dialograndscalefree.h \ src/forms/dialograndregular.h \ src/forms/dialogsettings.h \ src/forms/dialogsimilaritypearson.h \ src/forms/dialogsimilaritymatches.h \ src/forms/dialogdissimilarities.h \ src/forms/dialogclusteringhierarchical.h \ src/forms/dialograndlattice.h \ src/forms/dialognodefind.h \ src/forms/dialogexportpdf.h \ src/forms/dialogexportimage.h \ src/forms/dialogsysteminfo.h \ src/global.h SOURCES += src/main.cpp \ src/mainwindow.cpp \ src/texteditor.cpp \ src/graph.cpp \ src/graphvertex.cpp \ src/matrix.cpp \ src/parser.cpp \ src/webcrawler.cpp \ src/chart.cpp \ src/graphicswidget.cpp \ src/graphicsedge.cpp \ src/graphicsedgeweight.cpp \ src/graphicsedgelabel.cpp \ src/graphicsguide.cpp \ src/graphicsnode.cpp \ src/graphicsnodelabel.cpp \ src/graphicsnodenumber.cpp \ src/forms/dialogfilteredgesbyweight.cpp \ src/forms/dialogedgedichotomization.cpp \ src/forms/dialogwebcrawler.cpp \ src/forms/dialogdatasetselect.cpp \ src/forms/dialogpreviewfile.cpp \ src/forms/dialognodeedit.cpp \ src/forms/dialogranderdosrenyi.cpp \ src/forms/dialograndsmallworld.cpp \ src/forms/dialograndregular.cpp \ src/forms/dialograndscalefree.cpp \ src/forms/dialogsettings.cpp \ src/forms/dialogsimilaritypearson.cpp \ src/forms/dialogsimilaritymatches.cpp \ src/forms/dialogdissimilarities.cpp \ src/forms/dialogclusteringhierarchical.cpp \ src/forms/dialograndlattice.cpp \ src/forms/dialognodefind.cpp \ src/forms/dialogexportpdf.cpp \ src/forms/dialogexportimage.cpp \ src/forms/dialogsysteminfo.cpp RESOURCES = src/src.qrc # This is Windows only win32 { RC_FILE = src/icon.rc TARGET = SocNetV target.path = release/ } # This is Linux/Unix only unix:!macx{ # isEmpty(PREFIX) { # PREFIX = /usr/local # } # # workaround for Debian/Ubuntu deb-helper # equals(PREFIX,"/usr/local") { # PREFIX = /usr/local # } else { # PREFIX = /usr #} # No matter what PREFIX the user enters when # executing qmake to create the Makefile, # we always set it to be /usr because # it simplifies the .travis CI and .deb creation # The user may still install to a different folder # with the command: # make INSTALL_ROOT= install; PREFIX = /usr target.path = $${PREFIX}/bin TARGET = socnetv pixmap.path = $${PREFIX}/share/pixmaps pixmap.files = src/images/socnetv.png # documentation.path = $${PREFIX}/share/doc/socnetv # documentation.files = manual desktop.path = $${PREFIX}/share/applications desktop.files = socnetv.desktop manpage.path = $${PREFIX}/share/man/man1 manpage.files = man/socnetv.1.gz appstream.path = $${PREFIX}/share/metainfo appstream.files = socnetv.appdata.xml translations.path = $${PREFIX}/share/socnetv translations.files = translations/socnetv_*.qm doc.path = $${PREFIX}/share/doc/socnetv doc.files = changelog.gz NEWS README.md TODO COPYING AUTHORS INSTALL INSTALLS += pixmap desktop manpage translations doc appstream } # This is MacOS only macx { ICON = src/images/socnetv.icns TARGET = SocNetV } INSTALLS += target TRANSLATIONS = translations/socnetv_es.ts \ translations/socnetv_de.ts app-2.8/socnetv.spec000066400000000000000000000253031377436340000145120ustar00rootroot00000000000000# spec file for package socnetv # # Copyright (c) 2019 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 2.8 %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-qtcharts-devel, qt5-qtsvg-devel, qt5-qttools, fedora-release, desktop-file-utils, mesa-libGL-devel %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-qtsvg-devel, libQt5Charts5-devel, libqt5-qttools, unzip, 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: https://socnetv.org/ Vendor: Dimitris V. Kalamaras Source0: https://github.com/%{name}/app/archive/master.zip 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(Qt5Charts) BuildRequires: pkgconfig(Qt5Svg) Provides: %{name} = %{version} Obsoletes: %{name} < %{version} BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot # #DESCRIPTION SECTION # %description SocNetV (Social Network Visualizer) is a flexible, user-friendly free software application for social network 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 computes graph theory metrics, such as density, diameter and distances (shortest paths) in directed and undirected, weighted or non weighted graphs. It also computes node and network centrality and prestige indices, such as closeness, betweeness, eigenvector, information, power centralities and pagerank prestige. Community detection and structural equivalence algorithms are included, such as triad census, clique census, hierarchical cluster analysis, actor similarity and tie profile dissimilarities. Various layout algorithms (i.e. Spring-embedder, circular and in levels according to centrality or prestige) are supported for meaningful visualisations of your networks. Furthermore, SocNetV generates random networks using various models such as Erdos-Renyi, Scale-Free, Small-World, d-regular etc. The application also includes a simple web crawler to create a social network of web pages, where edges are the links between them. Author: Dimitris V. Kalamaras # #PREPARATION SECTION # echo "### PREP SECTION ###" %prep echo "### SETUP folder app-master ###" %setup -q -n app-master ## because master.zip unpacks to app-master/ echo "### SHOWING FILES ###" find . echo "### CHANGING OWNERSHIP AND REMOVING FILES ###" chmod -R a-x+X COPYING changelog.gz INSTALL NEWS README.md TODO man src find . -type f -name '*~' -delete find . -type f -name '*.bak' -delete rm -f config.log config.status Makefile socnetv.spec socnetv.mak # We do not need this replace hack anymore # because by default our .pro uses /usr as PREFIX and # qmake adds a useful INSTALL_ROOT variable in front of PREFIX # so we can use this scheme directly in our make install below # sed -i -e 's/PREFIX = \/usr/PREFIX = ./g' socnetv.pro # #MAKE SECTION # echo "### MAKE SECTION ###" %build %{qmake} %__make # #INSTALL SECTION # echo "###### 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 echo "### CALLING MAKE INSTALL ###" make install INSTALL_ROOT="%buildroot" # %make_install # NOTE %make_install is a macro available starting rpm-4.10. It is equivalent to `make install DESTDIR="%{?buildroot}"`. # I left it out to use INSTALL_ROOT directly... # We do not need this anymore. # qmake's Makefile defines copying for us and make does the job... #echo "### CREATING DIRECTORIES ###" #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} echo "### CLEAN SECTION ###" %clean [ -d %{buildroot} -a "%{buildroot}" != "" ] && %__rm -rf %{buildroot} # #FILES SECTION # echo " ### FILES SECTION ###" %files %defattr(-,root,root) %{_bindir}/%{name} %{_datadir}/applications/%{name}.desktop %dir %{_datadir}/%{name}/ %dir %{_datadir}/%{name}/translations/ %{_datadir}/%{name}/translations/* %{_datadir}/pixmaps/%{name}.png %{_datadir}/metainfo/%{name}.appdata.xml %{_mandir}/man1/* %doc changelog.gz NEWS README.md TODO COPYING AUTHORS INSTALL # #CHANGELOG SECTION # %changelog * Sun Jan 03 2021 Dimitris Kalamaras - 2.8-1 - Upstream v2.8 * Mon Dec 28 2020 Dimitris Kalamaras - 2.7-1 - Sync with upstream v2.7 * Mon Dec 28 2020 Dimitris Kalamaras - 2.6-2 - Synced with fixed upstream development 2.6 version * Mon Dec 28 2020 Dimitris Kalamaras - 2.6-1 - Synced with upstream development 2.6 version * Fri Mar 8 2019 Dimitris Kalamaras - 2.5-3 - Synced with new 2.5 version from upstream * Wed Feb 20 2019 Dimitris Kalamaras - 2.5-2 - Synced with new beta2 version from upstream * Tue Feb 19 2019 Dimitris Kalamaras - 2.5-1 - Synced with new beta version from upstream * Wed Feb 28 2018 Dimitris Kalamaras - 2.4-2 - Synced with fixed table version from upstream * Tue Feb 27 2018 Dimitris Kalamaras - 2.4-1 - Synced with new stable version from upstream * Wed Jul 5 2017 Dimitris Kalamaras - 2.3-1 - Synced with new stable version from upstream * Sat Jan 21 2017 Dimitris Kalamaras - 2.2-1 - Synced with new stable version from upstream * Wed Sep 28 2016 Dimitris Kalamaras - 2.1-1 - Synced with new stable version from upstream. * Tue Sep 13 2016 Dimitris Kalamaras - 2.0-2 - Spec patch for Buildservice * Mon Sep 12 2016 Dimitris Kalamaras - 2.0-1 - Synced with new stable version from upstream. * 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 app-2.8/src/000077500000000000000000000000001377436340000127415ustar00rootroot00000000000000app-2.8/src/application.qrc000077500000000000000000000004011377436340000157510ustar00rootroot00000000000000 editcopy.xpm editcut.xpm filenew.xpm fileopen.xpm editpaste.xpm filesave.xpm app-2.8/src/chart.cpp000066400000000000000000000310671377436340000145550ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt chart.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "chart.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Chart::Chart(QWidget *parent) : QChartView (parent ), m_chart(new QChart) { qDebug() << "Chart(QWidget *parent) "; setChart(m_chart); m_chart->setAnimationOptions(QChart::SeriesAnimations); } //Chart::Chart(QChart *ch, QWidget *parent) : // QChartView (ch, parent ) //{ // qDebug() << "Chart(QChart *ch, QWidget *parent) "; //} Chart::~Chart(){ qDebug()<< "~Chart() - deleting pointers"; delete m_chart; //m_series->clear(); } /** * @brief Add QAbstractSeries series to chart * If no series are passed, a new QSplineSeries is created with 1 point at (0,0) * @param series */ void Chart::addSeries(QAbstractSeries *series) { qDebug() << "Chart::addSeries()" ; // m_series = series; if (series) { m_chart->addSeries(series); qDebug() << "Chart::addSeries() - added series with name"<< series->name() ; } else { // default: a trivial series with one point // We need this for resetToTrivial() // to be able to call createDefaultAxes() m_series = new QSplineSeries(); *m_series << QPointF(0,0); m_series->setName("trivial"); m_chart->addSeries(m_series); qDebug() << "Chart::addSeries() - trivial series with one point created."; } } /** * @brief Adds the data point p(qreal,qreal) to the series. * @param p */ void Chart::appendToSeries(const QPointF &p) { qDebug() <<"Chart::appendToSeries() " ; m_series->append(p); // *m_series<series().empty() ) { qDebug() <<"Chart::removeAllSeries() - series count:" << m_chart->series().size(); qDebug() <<"Chart::removeAllSeries() - 1st series name:" << m_chart->series().first()->name(); m_chart->removeAllSeries(); qDebug() <<"Chart::removeAllSeries() - series count:" << m_chart->series().size(); } } /** * @brief Creates default axes. Must be called AFTER loading a series to the chart */ void Chart::createDefaultAxes(){ qDebug() << "Chart::createDefaultAxes()" ; m_chart->createDefaultAxes(); } QList Chart::axes(Qt::Orientations orientation, QAbstractSeries *series) const { qDebug() << "Chart::axes()" ; if (series == Q_NULLPTR) { qDebug() << "Chart::axes() - no series defined" ; return m_chart->axes(orientation); } else { qDebug() << "Chart::axes() - a series was defined" ; return m_chart->axes(orientation, series); } } /** * @brief Removes all previously attached X,Y axes from the QChart */ void Chart::removeAllAxes(){ qDebug() << "Chart::removeAllAxes()"; if ( ! axes(Qt::Horizontal).isEmpty() ) { qDebug() << "Chart::removeAllAxes() - m_chart axes: "<< m_chart->axes(Qt::Horizontal).size(); foreach ( QAbstractAxis *axe, axes(Qt::Horizontal) ) { m_chart->removeAxis(axe); } } if ( ! axes(Qt::Vertical).isEmpty() ) { qDebug() << "Chart::removeAllAxes() - m_chart axes: "<< m_chart->axes(Qt::Vertical).size(); foreach ( QAbstractAxis *axe, axes(Qt::Vertical) ) { m_chart->removeAxis(axe); } } } /** * @brief Adds the axis axis to the chart and attaches it to the series series * as a bottom-aligned horizontal axis. * The chart takes ownership of both the axis and the series. * @param axis * @param series */ void Chart::setAxisX(QAbstractAxis *axis, QAbstractSeries *series) { qDebug()<<"Chart::setAxisX() - Adding axis to chart"; addAxis(axis, Qt::AlignBottom); qDebug()<<"Chart::setAxisX() - Attaching axis to series " << series->name(); series->attachAxis(axis); } /** * @brief Adds the axis axis to the chart and attaches it to the series series * as a left-aligned horizontal axis. * The chart takes ownership of both the axis and the series. * @param axis * @param series */ void Chart::setAxisY(QAbstractAxis *axis, QAbstractSeries *series) { qDebug()<<"Chart::setAxisY() - Adding axis to chart"; addAxis(axis, Qt::AlignLeft); qDebug()<<"Chart::setAxisY() - Attaching axis to series " << series->name(); series->attachAxis(axis); // THIS IS QT_DEPRECATED // DO NOT USE THIS // m_chart->setAxisY(axis, series); } /** * @brief Add Axis axis to the QChart. Does not delete previously attached axis * @param axis * @param alignment */ void Chart::addAxis(QAbstractAxis *axis, Qt::Alignment alignment) { qDebug()<< "Chart::addAxis()"; m_chart->addAxis(axis,alignment); // We could also check if m_series and do: // barSeries->attachAxis(axisY); } /** * @brief Set the range of the (first) horizontal axis * @param from * @param to */ void Chart::setAxisXRange(const QVariant &min, const QVariant &max){ qDebug()<< "Chart::setAxisXRange()"; m_chart->axes(Qt::Horizontal).first()->setRange(min, max); } /** * @brief Sets the minimum value shown on the horizontal axis. * @param from * @param to */ void Chart::setAxisXMin(const QVariant &min){ qDebug()<< "Chart::setAxisXMin()"; m_chart->axes(Qt::Horizontal).first()->setMin(min); } /** * @brief Set the range of the vertical axis * @param from * @param to */ void Chart::setAxisYRange(const QVariant &min, const QVariant &max){ qDebug()<< "Chart::setAxisYRange()"; m_chart->axes(Qt::Vertical).first()->setRange(min, max); } /** * @brief Sets the minimum value shown on the vertical axis. * @param from * @param to */ void Chart::setAxisYMin(const QVariant &min){ qDebug()<< "Chart::setAxisYMin()"; m_chart->axes(Qt::Vertical).first()->setMin(min); } void Chart::setAxisXLabelsAngle (const int &angle){ qDebug()<< "Chart::setAxisXLabelsAngle()"; m_chart->axes(Qt::Horizontal).first()->setLabelsAngle(angle); } /** * @brief Set the label font of the horizontal axis * @param font */ void Chart::setAxisXLabelFont(const QFont &font){ qDebug()<< "Chart::setAxisXLabelFont()"; m_chart->axes(Qt::Horizontal).first()->setLabelsFont(font); } /** * @brief Set the label font of the vertical axis * @param font */ void Chart::setAxisYLabelFont(const QFont &font){ qDebug()<< "Chart::setAxisYLabelFont()"; m_chart->axes(Qt::Vertical).first()->setLabelsFont(font); } /** * @brief Set the line pen of the horizontal axis * @param font */ void Chart::setAxisXLinePen(const QPen &pen){ qDebug()<< "Chart::setAxisXLinePen()"; m_chart->axes(Qt::Horizontal).first()->setLinePen(pen); } /** * @brief Set the line pen of the vertical axis * @param font */ void Chart::setAxisYLinePen(const QPen &pen){ qDebug()<< "Chart::setAxisYLinePen()"; m_chart->axes(Qt::Vertical).first()->setLinePen(pen); } /** * @brief Set the grid line pen of the horizontal axis * @param font */ void Chart::setAxisXGridLinePen(const QPen &pen){ qDebug()<< "Chart::setAxisXGridLinePen()"; m_chart->axes(Qt::Horizontal).first()->setGridLinePen(pen); } /** * @brief Set the grid line pen of the vertical axis * @param font */ void Chart::setAxisYGridLinePen(const QPen &pen){ qDebug()<< "Chart::setAxisYGridLinePen()"; m_chart->axes(Qt::Vertical).first()->setGridLinePen(pen); } /** * @brief Toggle the legend of the QChart * @param toggle */ void Chart::toggleLegend(const bool &toggle){ qDebug()<< "Chart::toggleLegend()"; if (toggle) { m_chart->legend()->show(); } else { m_chart->legend()->hide(); } } /** * @brief Sets the background brush of the QChart * If no brush defined, it uses a transparent brush. * @param brush */ void Chart::setChartBackgroundBrush(const QBrush & brush) { qDebug()<< "Chart::setChartBackgroundBrush()"; m_chart->setBackgroundBrush(brush); } /** * @brief Sets the background pen of the QChart * If no pen defined, it uses a transparent pen. * @param brush */ void Chart::setChartBackgroundPen(const QBrush & brush) { qDebug()<< "Chart::setChartBackgroundPen()"; m_chart->setBackgroundBrush(brush); } /** * @brief Set the theme of the QChart * @param theme */ void Chart::setTheme(QChart::ChartTheme theme) { qDebug()<< "Chart::setTheme()"; m_chart->setTheme(theme); } /** * @brief Set the theme for when our chart widget is small * @param chartHeight */ void Chart::setThemeSmallWidget(const int minWidth, const int minHeight) { qDebug()<< "Chart::setThemeSmallWidget()"; setTheme(); setBackgroundBrush(QBrush(Qt::transparent)); setChartBackgroundBrush(); setChartBackgroundPen(); toggleLegend(false); setRenderHint(QPainter::Antialiasing); setMinimumWidth(minWidth); setMaximumHeight(1.5*minHeight); setMinimumHeight(minHeight); setFrameShape(QFrame::NoFrame); } /** * @brief Set the margins of the QChart * @param margins */ void Chart::setMargins(const QMargins &margins){ qDebug()<< "Chart::setMargins()"; m_chart->setMargins(margins); } /** * @brief Set the title of the QChart * @param title */ void Chart::setTitle(const QString &title, const QFont &font){ qDebug() << "Chart::setTitle()" ; m_chart->setTitleFont(font); m_chart->setTitle(title); } /** * @brief Applies a simple theme to axes (default label fonts, line and grid line pen). * WARNING: Axes must be already attached to m_chart */ void Chart::setAxesThemeDefault() { qDebug()<< "Chart::setAxesThemeDefault()"; setAxisXLabelFont(); setAxisXLinePen(); setAxisXGridLinePen(); setAxisYLabelFont(); setAxisYLinePen(); setAxisYGridLinePen(); setMargins(QMargins()); } void Chart::resetToTrivial() { qDebug()<< "Chart::resetToTrivial()"; removeAllSeries(); addSeries(); createDefaultAxes(); axes(Qt::Horizontal).first()->setLabelsAngle(-90); setTitle("Chart", QFont("Times",8)); setAxisXRange(0,1); setAxisYRange(0,1); setAxesThemeDefault(); } QPixmap Chart::getPixmap() { //create a temporary widget, which will display the chart QWidget* w = new QWidget; w->resize(1024, 768); QVBoxLayout *vl; vl = new QVBoxLayout(w); vl->addWidget(this); //show the widget, resized so we can grab it with correct dimensions w->show(); // wait for the graph to be drawn otherwise we have size problems QTest::qWait(500); //save windows to a pixmap QPixmap pixmap = w->grab(); //hide the widget w->hide(); qDebug()<< "Chart::getPixmap() ends!"; w->deleteLater(); return pixmap; } app-2.8/src/chart.h000066400000000000000000000077231377436340000142240ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt chart.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 CHART_H #define CHART_H #include #include #include QT_BEGIN_NAMESPACE QT_END_NAMESPACE QT_CHARTS_BEGIN_NAMESPACE class QSplineSeries; class QChart; class QAbstractAxis; QT_CHARTS_END_NAMESPACE QT_CHARTS_USE_NAMESPACE class Chart : public QChartView { Q_OBJECT public: explicit Chart (QWidget *parent = Q_NULLPTR ); //explicit Chart ( QChart *ch = Q_NULLPTR, QWidget *parent = Q_NULLPTR ); ~Chart(); void setTitle(const QString &title = QString(), const QFont &font=QFont()); void addSeries(QAbstractSeries *series = Q_NULLPTR ); void appendToSeries (const QPointF &p); void removeAllSeries(); void createDefaultAxes(); QList axes(Qt::Orientations orientation = Qt::Horizontal|Qt::Vertical, QAbstractSeries *series = Q_NULLPTR) const; void removeAllAxes(); void addAxis(QAbstractAxis *axis, Qt::Alignment alignment); void setAxisX(QAbstractAxis *axis, QAbstractSeries *series = Q_NULLPTR); void setAxisY(QAbstractAxis *axis, QAbstractSeries *series = Q_NULLPTR); void setAxisXRange(const QVariant &min, const QVariant &max); void setAxisXMin(const QVariant &min); void setAxisYRange(const QVariant &min, const QVariant &max); void setAxisYMin(const QVariant &min); void setAxesThemeDefault (); void setAxisXLabelsAngle (const int &angle); void setAxisXLabelFont(const QFont &font=QFont("Helvetica", 6 )); void setAxisYLabelFont(const QFont &font=QFont("Helvetica", 6 )); void setAxisXLinePen(const QPen &pen = QPen(QColor("#d0d0d0"), 1,Qt::SolidLine) ); void setAxisYLinePen(const QPen &pen = QPen(QColor("#d0d0d0"), 1,Qt::SolidLine) ); void setAxisXGridLinePen(const QPen &pen = QPen(QColor("#e0e0e0"), 1,Qt::DotLine) ); void setAxisYGridLinePen(const QPen &pen = QPen(QColor("#e0e0e0"), 1,Qt::DotLine) ); void setTheme(QChart::ChartTheme theme=QChart::ChartThemeQt); void setThemeSmallWidget(const int minWidth, const int minHeight); void setChartBackgroundBrush(const QBrush & brush = QBrush(Qt::transparent)); void setChartBackgroundPen(const QBrush & brush = QBrush(Qt::transparent)); void setMargins(const QMargins &margins = QMargins()); void toggleLegend(const bool &toggle = false); void resetToTrivial(); QPixmap getPixmap(); private: QChart *m_chart; QSplineSeries *m_series; }; #endif // CHART_H app-2.8/src/forms/000077500000000000000000000000001377436340000140675ustar00rootroot00000000000000app-2.8/src/forms/dialogclusteringhierarchical.cpp000077500000000000000000000106231377436340000224760ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogclusteringhierarchical.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogclusteringhierarchical.h" #include #include DialogClusteringHierarchical::DialogClusteringHierarchical (QWidget *parent, QString preselectMatrix) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); matrixList << "Adjacency" << "Distances"; measureList << "None, use raw input matrix" << "Jaccard distance" << "Hamming distance" << "Euclidean distance" << "Manhattan distance"; linkageList << "Single-linkage (minimum)" << "Complete-linkage (maximum)" << "Average-linkage (UPGMA)"; variablesLocationList << "Rows" << "Columns" << "Both"; ui.variablesLocationSelect -> insertItems( 1, variablesLocationList ); ui.variablesLocationSelect -> setCurrentIndex(2); ui.matrixSelect -> insertItems( 1, matrixList ); if (preselectMatrix == "Distances") { ui.matrixSelect -> setCurrentIndex(1); } ui.metricSelect ->insertItems(1, measureList); ui.metricSelect -> setCurrentIndex(3); ui.linkageSelect -> insertItems( 1, linkageList ); ui.linkageSelect -> setCurrentIndex(2); ui.diagonalCheckBox -> setChecked(false); ui.diagramCheckBox ->setChecked(true); connect ( ui.matrixSelect, SIGNAL(highlighted(QString)), this, SLOT(matrixChanged(QString)) ); } void DialogClusteringHierarchical::matrixChanged(const QString &matrix) { qDebug()<< "DialogClusteringHierarchical::matrixChanged()" << matrix; } /** * @brief Gets user choices */ void DialogClusteringHierarchical::getUserChoices(){ qDebug()<< "DialogClusteringHierarchical::getUserChoices!..."; QString matrix = ui.matrixSelect ->currentText(); QString varLocation = ui.variablesLocationSelect ->currentText(); QString metric= (( ui.metricSelect ->isEnabled() ) ? ui.metricSelect ->currentText() : "-" ); QString linkage = ui.linkageSelect -> currentText(); bool diagonal = ui.diagonalCheckBox -> isChecked(); bool diagram = ui.diagramCheckBox -> isChecked(); qDebug()<< "DialogClusteringHierarchical: user selected: " << matrix << metric << linkage; emit userChoices( matrix, varLocation, metric, linkage,diagonal, diagram ); } void DialogClusteringHierarchical::on_buttonBox_accepted() { this->getUserChoices(); this->accept(); } void DialogClusteringHierarchical::on_buttonBox_rejected() { this->reject(); } DialogClusteringHierarchical::~DialogClusteringHierarchical(){ matrixList.clear(); measureList.clear(); linkageList.clear(); } app-2.8/src/forms/dialogclusteringhierarchical.h000077500000000000000000000047631377436340000221530ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogclusteringhierarchical.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGCLUSTERINGHIERARCHICAL_H #define DIALOGCLUSTERINGHIERARCHICAL_H #include #include "ui_dialogclusteringhierarchical.h" class DialogClusteringHierarchical: public QDialog { Q_OBJECT public: DialogClusteringHierarchical (QWidget *parent = Q_NULLPTR, QString preselectMatrix = ""); ~DialogClusteringHierarchical(); public slots: void getUserChoices(); signals: void userChoices(const QString &matrix, const QString &varLocation, const QString &similarityMeasure, const QString &linkageCriterion, const bool &diagonal, const bool &diagram); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); void matrixChanged(const QString &matrix); private: Ui::DialogClusteringHierarchical ui; QStringList matrixList, measureList, linkageList, variablesLocationList; }; #endif app-2.8/src/forms/dialogclusteringhierarchical.ui000066400000000000000000000253461377436340000223360ustar00rootroot00000000000000 DialogClusteringHierarchical true 0 0 800 500 0 0 700 500 800 600 Hierarchical Clustering 170 0 Variables in: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span>Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors. In this case, the input matrix is expanded by listing row vectors followed by column vectors. This is useful only when you have directed data.</p><p><br/></p></body></html> Enable to include matrix diagonal in calculations Print dendrogram (avoid in large nets) Qt::Vertical 20 40 0 0 400 200 <html><head/><body><p>Agglomerative hierarchical clustering builds a hierarchy of actor clusters, based on their tie/distance dissimilarity, starting with single elements and aggregating them into clusters.</p><p>It takes a matrix (adjacency or geodesic distances) and a distance metric between actors as input and constructs a pair-wise dissimilarity matrix. </p><p>Initially, each actor starts in its own cluster (Level 0). In each subsequent Level, the pair of clusters with minimum distance are merged into a larger cluster. Then, the distance between the new cluster and the old ones is computed, using the specified clustering method (i.e. single-linkage clustering). The process is repeated until all actors end up in the same cluster. </p><p>Select an input matrix, a distance/dissimilarity metric and a clustering method (criterion) for the hierarchical cluster analysis. </p></body></html> Qt::RichText true 170 0 Clustering method (criterion): 200 0 Qt::StrongFocus <html><head/><body><p>Supported linkage criteria for agglomerative hierarchical clustering: </p><p><span style=" font-weight:600;">Single-linkage (minimum)</span>: The distance between two clusters will be determined by a single element pair, namely those two elements (one in each cluster) that have the shortest distance between them. In each step, the clusters that have the shortest distance will be merged. </p><p><span style=" font-weight:600;">Complete-linkage (maximum)</span>: The distance between two clusters will be determined by any two elements (one in each cluster) that have the longest distance between them. </p><p><span style=" font-weight:600;">Average-linkage (UPGMA)</span>: The distance between two clusters A and B is equal to the average of distances between all pairs of elements in A and B. </p><p><br/></p></body></html> 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 170 0 Input matrix: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Adjacency:</span> Use the active network's adjacency matrix as input to hierarchical clustering.</p><p><span style=" font-weight:600;">Distances:</span> Use the active network's geodedic distances matrix as input. </p></body></html> 170 0 Distance/dissimilarity metric: 200 0 Qt::StrongFocus <html><head/><body><p>Supported distance metrics for hierarchical clustering:</p><p><span style=" font-weight:600;">Euclidean distance</span>: The square root of the sum of squared differences between tie/distance profiles.</p><p><span style=" font-weight:600;">Jaccard distance</span>: The Jaccard index J is the ratio of same ties/distances reported by each pair of actors to the total number of their ties. Does not count absent ties. The Jaccard distance is 1 - J</p><p><span style=" font-weight:600;">Hamming distance</span>: The number of ties/distances to other actors which differ between each pair of actors.</p><p><span style=" font-weight:600;">Manhattan distance</span>: The sum of absolute differences between tie/distance profiles.<br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 40 app-2.8/src/forms/dialogdatasetselect.cpp000077500000000000000000000130111377436340000205770ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogdatasetselect.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogdatasetselect.h" #include #include DialogDataSetSelect::DialogDataSetSelect (QWidget *parent) : QDialog (parent), ui(new Ui::DialogDataSetSelect) { ui->setupUi(this); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); datasets_list << "Krackhardt: High-tech managers (multirelational), 24 actors" << "Padgett: Florentine Families (marital and business relations), 16 actors" << "Zachary: Karate Club (binary & valued ties), 34 actors" << "Bernard: Killworth Fraternity (multirelational), 58 actors" << "Thurman: In the office: Networks and Coalitions, 15 actors" << "Stokman-Ziegler: Corporate Interlocks in Netherlands, 16 actors" << "Stokman-Ziegler: Corporate Interlocks in West Germany, 15 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)" << "Knoke: Bureaucracies Information & Money Exchange Network, 10 actors, 2 relationships" << "Stephenson and Zelen (1989): Network of 40 AIDS patients (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" << "Borgatti (1992): Campnet dataset, 18 actors" << "Petersen graph: A non-planar, undirected graph with 10 vertices and 15 edges" << "Herschel graph: The smallest nonhamiltonian polyhedral graph. 11 nodes, 18 edges"; datasets_filenames << "Krackhardt_High-tech_managers.paj" << "Padgett_Florentine_Families.paj" << "Zachary_Karate_Club.dl" << "Bernard_Killworth_Fraternity.dl" << "Thurman_Office_Networks_Coalitions.dl" << "Stokman_Ziegler_Corporate_Interlocks_Netherlands.dl" << "Stokman_Ziegler_Corporate_Interlocks_West_Germany.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" << "Knoke_Bureaucracies_Network.pajek" << "Stephenson&Zelen_40_AIDS_patients_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" << "Campnet.paj" << "Petersen_Graph.paj" << "Herschel_Graph.paj"; (ui->selectBox) -> insertItems( 1, datasets_list ); } void DialogDataSetSelect::getUserChoices(){ qDebug()<< "DialogDataSetSelect: gathering Data!..."; int index = (ui->selectBox) -> currentIndex(); QString dataset_name = datasets_filenames[index]; qDebug()<< "DialogDataSetSelect: user selected: " << dataset_name; emit userChoices( dataset_name ); } void DialogDataSetSelect::on_buttonBox_accepted() { this->getUserChoices(); this->accept(); } void DialogDataSetSelect::on_buttonBox_rejected() { this->reject(); } DialogDataSetSelect::~DialogDataSetSelect(){ datasets_list.clear(); datasets_filenames.clear(); } app-2.8/src/forms/dialogdatasetselect.h000077500000000000000000000041051377436340000202500ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogdatasetselect.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGDATASETSELECT_H #define DIALOGDATASETSELECT_H #include #include "ui_dialogdatasetselect.h" class DialogDataSetSelect: public QDialog { Q_OBJECT public: DialogDataSetSelect (QWidget *parent = Q_NULLPTR); ~DialogDataSetSelect(); public slots: void getUserChoices(); signals: void userChoices(QString); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogDataSetSelect *ui; QStringList datasets_list, datasets_filenames; }; #endif app-2.8/src/forms/dialogdatasetselect.ui000066400000000000000000000056701377436340000204430ustar00rootroot00000000000000 DialogDataSetSelect true 0 0 500 200 0 0 500 200 800 400 Famous SNA data sets 400 90 <html><head/><body><p>Automatically recreate and visualize known data sets of Social Network Analysis, such as Padgett's Florentine families, Zachary's Karate Club, Knoke's Bureaucracies, etc.</p><p>Select the data set you want to re-create from the list.</p></body></html> Qt::RichText true 300 0 Qt::StrongFocus <html><head/><body><p>Click to select a data set</p></body></html> Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok app-2.8/src/forms/dialogdissimilarities.cpp000077500000000000000000000060321377436340000211550ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogdissimilarities.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogdissimilarities.h" #include #include DialogDissimilarities::DialogDissimilarities (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); variablesLocationList << "Rows" << "Columns" << "Both"; metricList << tr("Euclidean distance") << tr("Manhattan distance") << tr("Hamming distance") << tr("Jaccard distance") << tr("Chebyshev distance"); (ui.variablesLocationSelect) -> insertItems( 1, variablesLocationList ); (ui.metricSelect) -> insertItems( 1, metricList ); (ui.diagonalCheckBox)->setChecked(false); } void DialogDissimilarities::getUserChoices(){ qDebug()<< "DialogDissimilarities: gathering Data!..."; QString varLocation = (ui.variablesLocationSelect) ->currentText(); QString metric = (ui.metricSelect)->currentText(); bool diagonal = (ui.diagonalCheckBox)->isChecked(); qDebug()<< "DialogDissimilarities: user selected: " << varLocation << metric; emit userChoices( metric, varLocation, diagonal ); } void DialogDissimilarities::on_buttonBox_accepted() { this->getUserChoices(); this->accept(); } void DialogDissimilarities::on_buttonBox_rejected() { this->reject(); } DialogDissimilarities::~DialogDissimilarities(){ metricList.clear(); variablesLocationList.clear(); } app-2.8/src/forms/dialogdissimilarities.h000077500000000000000000000042711377436340000206250ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogdissimilarities.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGDISSIMILARITIES_H #define DIALOGDISSIMILARITIES_H #include #include "ui_dialogdissimilarities.h" class DialogDissimilarities: public QDialog { Q_OBJECT public: DialogDissimilarities (QWidget *parent = Q_NULLPTR); ~DialogDissimilarities(); public slots: void getUserChoices(); signals: void userChoices(const QString &metric, const QString &varLocation, const bool &diagonal); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogDissimilarities ui; QStringList variablesLocationList, metricList; }; #endif app-2.8/src/forms/dialogdissimilarities.ui000066400000000000000000000163001377436340000210040ustar00rootroot00000000000000 DialogDissimilarities true 0 0 700 350 0 0 700 350 800 600 Tie profile dissimilarities 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Distance metric: 200 0 12 Qt::StrongFocus <html><head/><body><p>Select a matching method. </p><p><span style=" font-weight:600;">Exact Matches</span>: Examines pairs of actors for exact tie or distance matches (present or absent) to other actors and returns a proportion to their overall ties. </p><p><span style=" font-weight:600;">Positive Matches (Jaccard/Co-citation)</span>: Looks for same ties/distances reported by both actors. Returns the ratio to the total number of ties reported.</p><p><span style=" font-weight:600;">Hamming distance</span>: Reports the number of ties/distances to other actors which differ between each pair of actors.</p><p><span style=" font-weight:600;">Cosine similarity</span>: Computes the pair-wise similarity of actors as the dot product of their tie/distance vectors divided by the magnitude of these vectors. <br/></p></body></html> 0 0 400 150 <html><head/><body><p>Compute a <span style=" font-weight:600;">dissimilarities matrix</span>, where each element (i,j) is the pair-wise distance / dissimilarity of actors i and j tie profiles to all other actors, according to a selected metric. </p><p>Select a distance metric. For example, the &quot;Euclidean distance&quot; is the square root of the sum of the squared differences of tie values that actors i and j have to other actors. Hover over &quot;Distance Metric&quot; select box for more info on each metric.</p><p>Also, specify where the &quot;variables&quot; are. For instance, select Rows to measure the outbound ties between all pairs of actors. Select Both to measure both inbound and outbound ties. </p></body></html> Qt::RichText true Variables in: 200 0 12 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span> Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><br/></p></body></html> 12 Qt::Vertical 20 20 Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 15 app-2.8/src/forms/dialogedgedichotomization.cpp000077500000000000000000000044111377436340000220070ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogedgedichotomization.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogedgedichotomization.h" #include #include DialogEdgeDichotomization::DialogEdgeDichotomization (QWidget *parent) : QDialog (parent) { ui.setupUi(this); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); } void DialogEdgeDichotomization::getUserChoices(){ qDebug()<< "Dialog: gathering Data!..."; qreal my_threshold = ui.weightThreshold->value() ; qDebug()<< "DialogEdgeDichotomization::getUserChoices() - We will dichotomize edges according to threshold: " << my_threshold; qDebug()<< "Dialog: emitting userChoices" ; emit userChoices( my_threshold ); } app-2.8/src/forms/dialogedgedichotomization.h000077500000000000000000000037401377436340000214600ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogedgedichotomization.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGEDGEDICHOTOMIZATION_H #define DIALOGEDGEDICHOTOMIZATION_H #include #include "ui_dialogedgedichotomization.h" class DialogEdgeDichotomization : public QDialog { Q_OBJECT public: explicit DialogEdgeDichotomization (QWidget *parent = Q_NULLPTR); public slots: void getUserChoices (); signals: void userChoices( qreal threshold); private: Ui::DialogEdgeDichotomization ui; }; #endif app-2.8/src/forms/dialogedgedichotomization.ui000066400000000000000000000113711377436340000216420ustar00rootroot00000000000000 DialogEdgeDichotomization 0 0 500 200 500 200 Dichotomize Edges 0 65 Sans Serif 13 <html><head/><body><p>Enter a threshold value to dichotomize the edges of a valued network, in order to create a new binary relation. All ties with equal or higher values will be set to 1, and all lower will be removed. </p></body></html> Qt::RichText true 6 QLayout::SetDefaultConstraint 0 0 Sans Serif Weight Threshold Qt::Horizontal 40 20 60 0 80 100 Sans Serif 1 -100.000000000000000 Qt::Vertical 20 15 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Vertical 20 12 buttonBox accepted() DialogEdgeDichotomization accept() 248 254 157 274 buttonBox rejected() DialogEdgeDichotomization reject() 316 260 286 274 app-2.8/src/forms/dialogexportimage.cpp000066400000000000000000000161631377436340000203060ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogexportimage.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogexportimage.h" #include "ui_dialogexportimage.h" #include #include #include #include #include DialogExportImage::DialogExportImage(QWidget *parent) : QDialog(parent), ui(new Ui::DialogExportImage) { ui->setupUi(this); // Get supported Image formats QStringList imgFormats; QByteArray bytes; foreach (bytes, QImageWriter::supportedImageFormats()) { imgFormats << QString(bytes); } ui->formatSelect->addItems(imgFormats); // Connect dialog signals to slots connect ( ui->fileDirSelectButton, &QToolButton::clicked, this, &DialogExportImage::getFilename); connect ( ui->formatSelect, SIGNAL(currentIndexChanged (const QString &)), this, SLOT ( getFormat(const QString &)) ); connect ( ui->buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); // Prepare Quality slider and spin box, and connect them changeQualityRange(1,100,1); connect ( ui->qualitySlider, SIGNAL(valueChanged(int)), ui->qualitySpinBox,SLOT(setValue(int)) ); connect ( ui->qualitySpinBox, SIGNAL(valueChanged(int)), ui->qualitySlider,SLOT(setValue(int)) ); ui->qualitySlider->setValue(100); // Prepare Compression slider and spin box, and connect them changeCompressionRange(1,100,1); connect ( ui->compressionSlider, SIGNAL(valueChanged(int)), ui->compressionSpinBox,SLOT(setValue(int)) ); connect ( ui->compressionSpinBox, SIGNAL(valueChanged(int)), ui->compressionSlider,SLOT(setValue(int)) ); ui->compressionSlider->setValue(0); // Set default button // OK button is disabled until user has selected a filename. (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); // Set which widget will have focus ui->fileDirSelectButton->setFocus(Qt::OtherFocusReason); } /** * @brief DialogExportImage::~DialogExportImage */ DialogExportImage::~DialogExportImage() { delete ui; } /** * @brief Changes Compression widgets range and stepping * @param min * @param max * @param step */ void DialogExportImage::changeCompressionRange(const int &min, const int &max, const int &step) { ui->compressionSlider->setSingleStep(step); ui->compressionSlider->setTickInterval(step); ui->compressionSpinBox->setSingleStep(step); ui->compressionSpinBox->setRange(min,max); } /** * @brief Changes Quality widgets range and stepping * @param min * @param max * @param step */ void DialogExportImage::changeQualityRange(const int &min, const int &max, const int &step){ ui->qualitySlider->setSingleStep(step); ui->qualitySlider->setTickInterval(step); ui->qualitySpinBox->setSingleStep(step); ui->qualitySpinBox->setRange(min,max); } /** * @brief Gets the filename of the Image */ void DialogExportImage::getFilename(){ QString m_format = ui->formatSelect->currentText().toLower(); QString m_filter = m_format.toUpper() + " (*." + m_format + ")"; QString m_fileName = QFileDialog::getSaveFileName(this, tr("Save to image"), "", m_filter); if (!m_fileName.isEmpty() && QFileInfo(m_fileName).absoluteDir().exists() ) { if ( QFileInfo(m_fileName).suffix().isEmpty() ) { m_fileName.append("." +m_format); } else if ( QString::compare( QFileInfo(m_fileName).suffix() , m_format, Qt::CaseInsensitive ) ) { m_fileName.append("." +m_format); } ui->fileEdit->setText(m_fileName); ui->fileEdit->setGraphicsEffect(0); (ui->buttonBox)->button (QDialogButtonBox::Ok) -> setEnabled(true); (ui->buttonBox)->button (QDialogButtonBox::Ok) -> setDefault(true); } else { qDebug() << " empty or dir does not exist"; // TODO Error message on form. QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->fileEdit->setGraphicsEffect(effect); ui->fileDirSelectButton->setGraphicsEffect(effect); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); } } /** * @brief Gets the file format of the Image */ void DialogExportImage::getFormat(const QString &format){ QString m_format = format.toLower(); qDebug() << m_format; QString m_fileName = ui->fileEdit->text(); qDebug() << m_fileName; qDebug() << QFileInfo(m_fileName).suffix(); if ( QString::compare( QFileInfo(m_fileName).suffix() , m_format, Qt::CaseInsensitive ) ) { m_fileName = QFileInfo(m_fileName).absolutePath() + QDir::separator() + QFileInfo(m_fileName).completeBaseName().append("."+m_format); qDebug() << m_fileName; } ui->fileEdit->setText(m_fileName); } void DialogExportImage::getUserChoices(){ QByteArray m_format = ui->formatSelect->currentText().toLower().toUtf8(); QString m_fileName = ui->fileEdit->text(); int m_quality = ui->qualitySpinBox->value(); int m_compression = ui->compressionSpinBox->value(); qDebug()<< "user choices: " << m_fileName << m_format << m_quality << m_compression; emit userChoices(m_fileName, m_format, m_quality, m_compression); } app-2.8/src/forms/dialogexportimage.h000066400000000000000000000045521377436340000177520ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogexportimage.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGEXPORTIMAGE_H #define DIALOGEXPORTIMAGE_H #include namespace Ui { class DialogExportImage; } class DialogExportImage : public QDialog { Q_OBJECT public: explicit DialogExportImage(QWidget *parent = Q_NULLPTR); ~DialogExportImage(); void changeCompressionRange(const int &min, const int &max, const int &step); void changeQualityRange(const int &min, const int &max, const int &step); public slots: void getFilename(); void getFormat(const QString &format); void getUserChoices(); signals: void userChoices( const QString &filename, const QByteArray &format, const int &quality, const int &compression ); private: Ui::DialogExportImage *ui; }; #endif // DIALOGEXPORTIMAGE_H app-2.8/src/forms/dialogexportimage.ui000066400000000000000000000265041377436340000201410ustar00rootroot00000000000000 DialogExportImage 0 0 500 220 500 220 500 300 Export to Image Save to file: Qt::Horizontal 48 20 220 0 <html><head/><body><p><span style=" font-weight:600;">Image filename</span></p><p>The path and the filename of the resulting image. </p><p>Click the button on the right to select a new filename.</p><p><br/></p></body></html> 50 0 50 16777215 PointingHandCursor <html><head/><body><p><span style=" font-size:10pt; font-weight:600;">Image filename</span></p><p><span style=" font-size:10pt;">Click this button to select the path and the filename of the resulting image.<br/></span></p></body></html> <html><head/><body><p><span style=" font-weight:600;">PDF filename</span></p><p>Click this button to select the path and the filename of the PDF. </p><p><br/></p><p><br/></p></body></html> ... Format DejaVu Sans Qt::Horizontal 98 20 0 0 50 0 50 16777215 <!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: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;"><span style=" font-family:'Sans Serif'; font-size:10pt; font-weight:600;">Image format </span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">Select the format of the resulting image. </span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">SocNetV automatically supports all image formats supported currently by Qt in your computer platform. </span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">Click on this drop down to see all supported image formats and select a format for your image.</span></p></body></html> Quality Qt::Horizontal 40 20 200 0 Qt::Horizontal 50 0 50 16777215 100 100 Compression Qt::Horizontal 18 20 200 0 Qt::Horizontal 50 50 50 20 50 25 100 Qt::Vertical QSizePolicy::Minimum 20 29 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogExportImage accept() 248 254 157 274 buttonBox rejected() DialogExportImage reject() 316 260 286 274 app-2.8/src/forms/dialogexportpdf.cpp000066400000000000000000000137261377436340000177770ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogexportpdf.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogexportpdf.h" #include "graphicswidget.h" #include "ui_dialogexportpdf.h" #include #include #include #include DialogExportPDF::DialogExportPDF (QWidget *parent ) : QDialog (parent), ui(new Ui::DialogExportPDF) { ui->setupUi(this); m_fileName = ""; m_dpi = 75; m_printerMode = QPrinter::ScreenResolution; m_orientation = QPageLayout::Portrait; // Populate printer modes QStringList resList; resList << "Screen" << "Print"; ui->qualitySelect->addItems(resList); // Populate dpi (currently only 75dpi is supported) QStringList dpiList; dpiList << "75" << "300" << "600" << "1200"; ui->resolutionSelect->addItems(dpiList); ui->resolutionSelect->setDisabled(true); QStringList orientationList; orientationList << "Portrait" << "Landscape"; ui->orientationSelect->addItems(orientationList); // Connect dialog signals to slots connect (ui->fileDirSelectButton, &QToolButton::clicked, this, &DialogExportPDF::getFilename); connect(ui->qualitySelect, SIGNAL ( currentIndexChanged (const QString &)), this, SLOT(getPrinterMode(const QString &)) ); connect ( ui->buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); // Set Cancel as default button // The OK button disabled until user selects a file. (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); // Set which widget will have focus ui->fileDirSelectButton->setFocus(Qt::OtherFocusReason); } DialogExportPDF::~DialogExportPDF() { delete ui; } void DialogExportPDF::checkFilename(const QString &fileName){ m_fileName = fileName; if (!m_fileName.isEmpty() && QFileInfo(m_fileName).absoluteDir().exists() ) { if ( QFileInfo(m_fileName).suffix().isEmpty() ) { m_fileName.append(".pdf"); } else if ( QString::compare( QFileInfo(m_fileName).suffix() , "pdf", Qt::CaseInsensitive ) ) { qDebug() << "suffix() : " << QFileInfo(m_fileName).suffix(); m_fileName.append(".pdf"); } ui->fileEdit->setText(m_fileName); ui->fileEdit->setGraphicsEffect(0); (ui->buttonBox)->button (QDialogButtonBox::Ok) -> setEnabled(true); (ui->buttonBox)->button (QDialogButtonBox::Ok) -> setDefault(true); } else { qDebug() << " empty or dir does not exist"; // TODO Error message on form. QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->fileEdit->setGraphicsEffect(effect); ui->fileDirSelectButton->setGraphicsEffect(effect); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); } } /** * @brief Gets the filename of the PDF */ void DialogExportPDF::getFilename(){ QString fileName = QFileDialog::getSaveFileName(this, tr("Save to pdf"), "", tr("PDF (*.pdf)")); checkFilename(fileName); } /** * @brief Gets printer quality mode */ void DialogExportPDF::getPrinterMode(const QString &mode){ if (!mode.isEmpty() ) { // m_appSettings["canvasUpdateMode"] = mode; if ( mode == "screen" ){ m_printerMode = QPrinter::ScreenResolution; } else if ( mode == "print" ) { m_printerMode = QPrinter::PrinterResolution; } } } void DialogExportPDF::getUserChoices(){ qDebug()<< "Dialog: gathering Data!..."; // User might have entered the filename manually! if (m_fileName.isEmpty()) { getFilename(); } if ( ui->qualitySelect->currentText().contains("Screen")){ m_printerMode = QPrinter::ScreenResolution; } else { m_printerMode = QPrinter::PrinterResolution; } m_dpi = ui->resolutionSelect->currentText().toInt(); if ( ui->orientationSelect->currentText().contains("Portrait")) { m_orientation = QPageLayout::Portrait; } else { m_orientation = QPageLayout::Landscape; } qDebug()<< "Dialog: emitting userChoices" ; emit userChoices( m_fileName, m_orientation, m_dpi, m_printerMode ); } app-2.8/src/forms/dialogexportpdf.h000066400000000000000000000047601377436340000174420ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogexportpdf.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGEXPORTPDF_H #define DIALOGEXPORTPDF_H #include #include #include namespace Ui { class DialogExportPDF; } class DialogExportPDF : public QDialog { Q_OBJECT public: explicit DialogExportPDF (QWidget *parent = Q_NULLPTR ); ~DialogExportPDF(); public slots: void checkFilename(const QString &fileName = QString()); void getFilename(); void getPrinterMode(const QString &mode); void getUserChoices (); signals: void userChoices( QString &filename, const QPageLayout::Orientation &orientation, const int &dpi, const QPrinter::PrinterMode printerMode, const QPageSize &pageSize = QPageSize(QPageSize::A4)); private: QString m_fileName; int m_dpi; QPageLayout::Orientation m_orientation; QPrinter::PrinterMode m_printerMode; Ui::DialogExportPDF *ui; }; #endif // DIALOGEXPORTPDF_H app-2.8/src/forms/dialogexportpdf.ui000066400000000000000000000364571377436340000176400ustar00rootroot00000000000000 DialogExportPDF 0 0 500 265 500 220 700 500 Export to PDF Page Orientation DejaVu Sans Qt::Horizontal 98 20 0 0 170 0 <!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: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;"><span style=" font-family:'Sans Serif'; font-size:10pt; font-weight:600;">Page Orientation</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">Select the page orientation in the PDF: Portrait or landscape. </span></p></body></html> Save to file: Qt::Horizontal 48 20 220 0 <html><head/><body><p><span style=" font-weight:600;">PDF filename</span></p><p>The path and the filename of the PDF. </p><p>Click the button on the right to select a new filename.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">PDF filename</span></p><p>The path and the filename of the PDF. </p><p>Click the button on the right to select a new filename.</p><p><br/></p></body></html> PointingHandCursor <html><head/><body><p><span style=" font-size:10pt; font-weight:600;">PDF filename</span></p><p><span style=" font-size:10pt;">Click this button to select the path and the filename of the PDF. <br/></span></p></body></html> <html><head/><body><p><span style=" font-weight:600;">PDF filename</span></p><p>Click this button to select the path and the filename of the PDF. </p><p><br/></p><p><br/></p></body></html> ... Quality DejaVu Sans Qt::Horizontal 98 20 0 0 170 0 <!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: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;"><span style=" font-family:'Sans Serif'; font-size:10pt; font-weight:600;">PDF Quality</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">Select the quality of the PDF. </span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">The default value is Screen, which results in a PDF exactly like what you see on the application canvas.</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt; text-decoration: underline;">Screen</span></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; font-family:'Sans Serif'; font-size:10pt; font-weight:600;"><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-family:'Sans Serif'; font-size:10pt;">Sets the resolution of the print device to the screen resolution. This has the big advantage that the results obtained when painting on the printer will match more or less exactly the visible output on the screen. It is the easiest to use, as font metrics on the screen and on the printer are the same. This is the default value.</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt; text-decoration: underline;">Print</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">On Windows, sets the printer resolution to that defined for the printer in use. For PDF printing, sets the resolution of the PDF driver to 1200 dpi.</span></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; font-family:'Sans Serif'; font-size:10pt;"><br /></p></body></html> Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Resolution (DPI) DejaVu Sans Qt::Horizontal 98 20 0 0 170 0 <!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: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;"><span style=" font-family:'Sans Serif'; font-size:10pt; font-weight:600;">DPI</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">Select the PDF resolution in DPI.</span></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; font-family:'Sans Serif'; font-size:10pt;"><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-family:'Sans Serif'; font-size:10pt;">The default value is 75, to match screen resolution.</span></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; font-family:'Sans Serif'; font-size:10pt;"><br /></p></body></html> buttonBox accepted() DialogExportPDF accept() 248 254 157 274 buttonBox rejected() DialogExportPDF reject() 316 260 286 274 app-2.8/src/forms/dialogfilteredgesbyweight.cpp000077500000000000000000000050121377436340000220140ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogfilteredgesbyweight.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogfilteredgesbyweight.h" #include #include DialogFilterEdgesByWeight::DialogFilterEdgesByWeight (QWidget *parent) : QDialog (parent) { ui.setupUi(this); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.overThresholdBt)-> setChecked(true); } void DialogFilterEdgesByWeight::getUserChoices(){ 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 ); } app-2.8/src/forms/dialogfilteredgesbyweight.h000077500000000000000000000037351377436340000214730ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogfilteredgesbyweight.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGFILTEREDGESBYWEIGHT_H #define DIALOGFILTEREDGESBYWEIGHT_H #include #include "ui_dialogfilteredgesbyweight.h" class DialogFilterEdgesByWeight : public QDialog { Q_OBJECT public: explicit DialogFilterEdgesByWeight (QWidget *parent = Q_NULLPTR); public slots: void getUserChoices (); signals: void userChoices( qreal, bool); private: Ui::DialogFilterEdgesByWeight ui; }; #endif app-2.8/src/forms/dialogfilteredgesbyweight.ui000066400000000000000000000113371377436340000216530ustar00rootroot00000000000000 DialogFilterEdgesByWeight 0 0 600 220 600 220 Filter edges Sans Serif 13 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 Sans Serif 13 Weight Threshold 13 Qt::Horizontal 40 20 60 0 Sans Serif 13 1 -100.000000000000000 Sans Serif 13 Filter edges with weight over threshold Sans Serif 13 Filter edges with weight below threshold 13 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogFilterEdgesByWeight accept() 248 254 157 274 buttonBox rejected() DialogFilterEdgesByWeight reject() 316 260 286 274 app-2.8/src/forms/dialognodeedit.cpp000077500000000000000000000265441377436340000175640ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialognodeedit.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialognodeedit.h" #include "ui_dialognodeedit.h" #include "global.h" #include #include #include #include #include #include #include #include #include #include #include SOCNETV_USE_NAMESPACE DialogNodeEdit::DialogNodeEdit(QWidget *parent, const QStringList &nodeShapeList, const QStringList &iconPathList, const QString &label, const int &size, const QColor &color, const QString &shape, const QString &path) : QDialog(parent), m_shapeList(nodeShapeList), m_iconList(iconPathList), nodeLabel(label), nodeSize(size), nodeColor(color), nodeShape(shape), iconPath(path), ui(new Ui::DialogNodeEdit) { ui->setupUi(this); ui->labelEdit->setText(nodeLabel); ui->sizeSpin->setValue(nodeSize); ui->nodeShapeComboBox->addItems(m_shapeList); for (int i = 0; i < m_shapeList.size(); ++i) { ui->nodeShapeComboBox->setItemIcon(i, QIcon(m_iconList[i])); } ui->nodeIconSelectButton->setEnabled(false); ui->nodeIconSelectEdit->setEnabled(false); int index = -1; if ( (index = m_shapeList.indexOf(nodeShape)) != -1 ){ ui->nodeShapeComboBox->setCurrentIndex(index); if ( index == NodeShape::Custom ) { ui->nodeShapeComboBox->setCurrentIndex(NodeShape::Custom); ui->nodeIconSelectButton->setEnabled(true); ui->nodeIconSelectEdit->setEnabled(true); ui->nodeIconSelectEdit->setText (iconPath); if ( ! iconPath.isEmpty() ) { ui->nodeShapeComboBox->setItemIcon( NodeShape::Custom, QIcon(iconPath)); } else { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->nodeIconSelectButton->setGraphicsEffect(effect); ui->nodeIconSelectEdit->setGraphicsEffect(effect); (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } } } else { // default -- should never happen... ui->nodeShapeComboBox->setCurrentIndex(NodeShape::Circle); } pixmap = QPixmap(60,20) ; pixmap.fill(nodeColor); ui->colorButton->setIcon(QIcon(pixmap)); connect ( ui->buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui->labelEdit)->setFocus(); connect (ui->labelEdit, &QLineEdit::editingFinished, this, &DialogNodeEdit::checkErrors); connect (ui->colorButton, &QToolButton::clicked, this, &DialogNodeEdit::selectColor); connect (ui->nodeShapeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &DialogNodeEdit::getNodeShape); connect (ui->nodeIconSelectButton, &QToolButton::clicked, this, &DialogNodeEdit::getNodeIconFile); } /** * @brief DialogNodeEdit::getNodeShape * @param shape */ void DialogNodeEdit::getNodeShape(const int &nodeShapeIndex){ switch (nodeShapeIndex) { case NodeShape::Box: nodeShape = "box"; break; case NodeShape::Circle: nodeShape = "circle"; break; case NodeShape::Diamond: nodeShape = "diamond"; break; case NodeShape::Ellipse: nodeShape = "ellipse"; break; case NodeShape::Triangle: nodeShape = "triangle"; break; case NodeShape::Star: nodeShape = "star"; break; case NodeShape::Person: nodeShape = "person"; break; case NodeShape::PersonB: nodeShape = "person-b"; break; case NodeShape::Bugs: nodeShape = "bugs"; break; case NodeShape::Heart: nodeShape = "heart"; break; case NodeShape::Dice: nodeShape = "dice"; break; case NodeShape::Custom: nodeShape = "custom"; break; default: break; } qDebug()<< "DialogNodeEdit::getNodeShape() - new node shape " << nodeShape; if ( nodeShapeIndex == NodeShape::Custom ) { // enable textedit and file button and raise file dialog ui->nodeIconSelectButton->setEnabled(true); ui->nodeIconSelectEdit->setEnabled(true); ui->nodeIconSelectEdit->setText(iconPath); if (!iconPath.isEmpty()) { ui->nodeShapeComboBox->setItemIcon(NodeShape::Custom, QIcon(iconPath)); ui->nodeIconSelectButton->setGraphicsEffect(0); ui->nodeIconSelectEdit->setGraphicsEffect(0); } else { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->nodeIconSelectButton->setGraphicsEffect(effect); ui->nodeIconSelectEdit->setGraphicsEffect(effect); (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } } else { ui->nodeIconSelectButton->setEnabled(false); ui->nodeIconSelectEdit->setEnabled(false); ui->nodeIconSelectEdit->setText (""); iconPath = QString(); ui->nodeIconSelectButton->setGraphicsEffect(0); ui->nodeIconSelectEdit->setGraphicsEffect(0); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } } void DialogNodeEdit::getNodeIconFile(){ QString m_nodeIconFile = QFileDialog::getOpenFileName(this, tr("Select a new icon"), ui->nodeIconSelectEdit->text(), tr("Images (*.png *.jpg *.jpeg *.svg);;All (*.*)") ); if (!m_nodeIconFile.isEmpty()) { qDebug() << m_nodeIconFile; ui->nodeIconSelectEdit->setText(m_nodeIconFile); ui->nodeIconSelectButton->setGraphicsEffect(0); ui->nodeIconSelectEdit->setGraphicsEffect(0); ui->nodeShapeComboBox->setItemIcon(NodeShape::Custom, QIcon(m_nodeIconFile)); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } else { // user pressed Cancel ? // stop if ( ui->nodeIconSelectEdit->text().isEmpty() ) { (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } } } /** * @brief DialogNodeEdit::getUserChoices */ void DialogNodeEdit::getUserChoices(){ qDebug()<< " DialogNodeEdit::getUserChoices()" ; nodeLabel = ui->labelEdit->text(); nodeSize = ui->sizeSpin->value(); nodeValue = ui->valueEdit->text(); nodeShape = "circle"; int nodeShapeIndex = ui->nodeShapeComboBox->currentIndex(); switch (nodeShapeIndex) { case NodeShape::Box: nodeShape = "box"; break; case NodeShape::Circle: nodeShape = "circle"; break; case NodeShape::Diamond: nodeShape = "diamond"; break; case NodeShape::Ellipse: nodeShape = "ellipse"; break; case NodeShape::Triangle: nodeShape = "triangle"; break; case NodeShape::Star: nodeShape = "star"; break; case NodeShape::Person: nodeShape = "person"; iconPath = m_iconList [nodeShapeIndex]; break; case NodeShape::PersonB: nodeShape = "person-b"; iconPath = m_iconList [nodeShapeIndex]; break; case NodeShape::Bugs: nodeShape = "bugs"; iconPath = m_iconList [nodeShapeIndex]; break; case NodeShape::Heart: nodeShape = "heart"; iconPath = m_iconList [nodeShapeIndex]; break; case NodeShape::Dice: nodeShape = "dice"; iconPath = m_iconList [nodeShapeIndex]; break; case NodeShape::Custom: nodeShape = "custom"; iconPath = ui->nodeIconSelectEdit->text(); break; default: break; } emit userChoices(nodeLabel,nodeSize,nodeValue,nodeColor,nodeShape, iconPath); } /** * @brief DialogNodeEdit::checkErrors */ void DialogNodeEdit::checkErrors() { qDebug()<< " DialogNodeEdit::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); } //getUserChoices(); } void DialogNodeEdit::selectColor() { qDebug()<< " DialogNodeEdit::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"; } } DialogNodeEdit::~DialogNodeEdit() { delete ui; } app-2.8/src/forms/dialognodeedit.h000077500000000000000000000057051377436340000172250ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialognodeedit.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGNODEEDIT_H #define DIALOGNODEEDIT_H #include namespace Ui { class DialogNodeEdit; } class DialogNodeEdit : public QDialog { Q_OBJECT public: explicit DialogNodeEdit(QWidget *parent = Q_NULLPTR, const QStringList &nodeShapeList=QStringList(), const QStringList &iconPathList=QStringList(), const QString &label = "", const int &size = 8, const QColor &color= QColor("red"), const QString &shape = "circle", const QString &path=QString()); ~DialogNodeEdit(); public slots: void checkErrors (); void getNodeShape(const int &nodeShapeIndex); void getNodeIconFile(); void getUserChoices (); void selectColor(); signals: void userChoices( const QString &label, const int &size, const QString &value, const QColor &color, const QString &shape, const QString &iconPath=QString()); void nodeEditDialogError(QString); private: QStringList m_shapeList; QStringList m_iconList; QString nodeLabel; int nodeSize; QColor nodeColor; QString nodeShape; QString iconPath; QString nodeValue; QPixmap pixmap; Ui::DialogNodeEdit *ui; }; #endif app-2.8/src/forms/dialognodeedit.ui000066400000000000000000000342601377436340000174060ustar00rootroot00000000000000 DialogNodeEdit 0 0 500 350 500 350 800 600 Node Properties 13 Node label 13 Qt::Horizontal 20 20 200 0 13 <html><head/><body><p>Enter a node label. </p><p>If multiple nodes are selected, the label you define here will be set to all of them, along with a numerical suffix, i.e. if you enter &quot;Jim&quot;, then the selected actors will be labeled &quot;Jim1&quot;, &quot;Jim2&quot;, &quot;Jim3&quot; and so on. </p></body></html> 13 Node size 13 Qt::Horizontal 40 20 60 0 13 <html><head/><body><p>Set the size of the node. </p><p><br/></p><p>Default node size: 8</p></body></html> 8 false 13 Node value (disabled) 13 Qt::Horizontal 40 20 false 13 13 Node color (click the button to select) 13 Qt::Horizontal 40 20 60 25 13 ... 60 20 13 Node shape 13 Qt::Horizontal QSizePolicy::MinimumExpanding 40 20 200 0 300 16777215 13 <html><head/><body><p>Select a node shape. </p><p>If you select &quot;Icon&quot; then you must click on the custom icon button (below) to select the desired icon file from your filesystem. </p></body></html> 13 <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click the button on the right to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> Custom Icon 13 Qt::Horizontal 30 28 200 0 13 <html><head/><body><p><span style=" font-weight:600;">Custom Icon </span></p><p>This box only shows the path of the selected custom icon file for all network nodes. If it is empty, it means you have not selected any image yet.</p><p>Click the button on the right to select a new image to be used as custom icon in all the nodes of the network.</p><p>Valid icons are images: JPG, PNG, JPEG, SVG etc.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>This is the path of the default background image. </p><p>Click the button on the right to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> 0 0 60 0 13 <html><head/><body><p><span style=" font-weight:600;">Custom Icon </span></p><p>Click this button to select a new custom icon for all nodes of the network. </p><p>Valid icons are images: JPG, PNG, JPEG, SVG etc.</p><p>If you don't select an image file, you must select one of the default shapes from the Node Shape menu above. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click this button to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> ... 13 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogNodeEdit accept() 233 316 157 274 buttonBox rejected() DialogNodeEdit reject() 301 322 286 274 app-2.8/src/forms/dialognodefind.cpp000066400000000000000000000201711377436340000175420ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialognodefind.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialognodefind.h" #include "ui_dialognodefind.h" #include #include #include DialogNodeFind::DialogNodeFind(QWidget *parent, QStringList indexList) : QDialog(parent), ui(new Ui::DialogNodeFind) { ui->setupUi(this); ui->labelsRadioBtn->setAutoExclusive(true); ui->numbersRadioBtn->setAutoExclusive(true); ui->indexRadioBtn->setAutoExclusive(true); ui->numbersRadioBtn->setChecked(true); ui->indexCombo->insertItems(0, indexList); ui->indexLabel->setEnabled(false); ui->indexCombo->setEnabled(false); connect ( ui->labelsRadioBtn, SIGNAL(clicked(bool)), this, SLOT( checkErrors() ) ); connect ( ui->numbersRadioBtn, SIGNAL(clicked(bool)), this, SLOT( checkErrors() ) ); connect ( ui->indexRadioBtn, SIGNAL(clicked(bool)), this, SLOT( checkErrors() ) ); connect (ui->indexCombo, &QComboBox::currentTextChanged, this, &DialogNodeFind::getIndex); // connect ( ui->indexCombo, SIGNAL(currentTextChanged(QString)), this, SLOT( checkErrors(QString) ) ); connect ( ui->plainTextEdit,SIGNAL(textChanged()), this, SLOT(checkErrors()) ); connect ( ui->buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); ui->plainTextEdit->setFocus(); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); } DialogNodeFind::~DialogNodeFind() { tempListA.clear(); tempListB.clear(); delete ui; } void DialogNodeFind::setError(const bool &toggle) { if ( toggle ) { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->plainTextEdit->setGraphicsEffect(effect); ui->buttonBox -> button (QDialogButtonBox::Ok) -> setEnabled(false); } else { ui->plainTextEdit->setGraphicsEffect(0); ui->buttonBox -> button (QDialogButtonBox::Ok) -> setEnabled(true); } } /** * @brief Gets the selected index * @param indexStr */ void DialogNodeFind::getIndex(const QString &indexStr) { selectedIndex = ui->indexCombo->currentText(); qDebug() << "DialogNodeFind::getIndex() str"<plainTextEdit->toPlainText(); qDebug()<< "DialogNodeFind::checkErrors() - raw text entered:" << textEntered; if ( ui->numbersRadioBtn->isChecked() ) { ui->textEditLabel->setText("Search for these numbers (enter line by line or csv):"); searchType = "numbers"; ui->indexLabel->setEnabled(false); ui->indexCombo->setEnabled(false); } else if ( ui->labelsRadioBtn->isChecked() ) { ui->textEditLabel->setText("Search for these labels (enter line by line or csv):"); searchType = "labels"; ui->indexLabel->setEnabled(false); ui->indexCombo->setEnabled(false); } else if ( ui->indexRadioBtn->isChecked() ) { ui->textEditLabel->setText("Search for nodes with this index score (i.e. > 0.5)"); ui->indexLabel->setEnabled(true); ui->indexCombo->setEnabled(true); searchType = "score"; } qDebug()<< "DialogNodeFind::checkErrors() - search type:" << searchType; list.clear(); tempListA.clear(); tempListB.clear(); if ( textEntered.isEmpty() ) { setError(true); } else { setError(false); } if (! ui->indexRadioBtn->isChecked()) { // user wants to search by numbers or labels // user has to enter a CSV or line separated list of values if (textEntered.contains("\n") && textEntered.contains(",")) { // error you cannot enter both? // return; } // check if user entered multiple lines tempListA = textEntered.split("\n", QString::SkipEmptyParts); for (int i = 0; i < tempListA.size(); ++i) { // take every linefeed separated value str = tempListA.at(i).toLocal8Bit().constData(); qDebug()<< "DialogNodeFind::checkErrors() - line:" << i << "str:" << str; // check if user has entered comma tempListB = str.split(",", QString::SkipEmptyParts); for (int j = 0; j < tempListB.size(); ++j) { // take every comma separated value str = tempListB.at(j).toLocal8Bit().constData(); qDebug()<< "DialogNodeFind::checkErrors() - line:" << i << "element at pos:" << j << "is:" << str; if (ui->numbersRadioBtn->isChecked()) { // user wants to search by numbers // check if str contains a dash if (str.contains ("-")) { //str.split("-") } else { if (str.contains(QRegExp("\\D+"))) { qDebug()<< "DialogNodeFind::checkErrors() - error! not number" << str; setError(true); } else { qDebug()<< "DialogNodeFind::checkErrors() - adding number" << str; list << str; } } } else { // user wants to search by labels qDebug()<< "DialogNodeFind::checkErrors() - adding label" << str; list << str; } } } } else { // user wants to search nodes by their index score // user has to enter > or < and a threshold // and select the desired index. selectedIndex = ui->indexCombo->currentText(); // check if user entered multiple lines tempListA = textEntered.split("\n", QString::SkipEmptyParts); for (int i = 0; i < tempListA.size(); ++i) { // take every linefeed separated value str = tempListA.at(i).toLocal8Bit().constData(); if (str.contains (">") || str.contains ("<") || str.contains ("=")) { list << str; } else { qDebug()<< "DialogNodeFind::checkErrors() - error! search by index without > or <" ; setError(true); } } } } /** * @brief Gathers user input and emits userChoices signal */ void DialogNodeFind::getUserChoices() { qDebug()<< "DialogNodeFind::getUserChoices()" << list; qDebug()<< "DialogNodeFind::getUserChoices() type" << searchType; emit userChoices( list, searchType, selectedIndex ); } app-2.8/src/forms/dialognodefind.h000066400000000000000000000045241377436340000172130ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialognodefind.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGNODEFIND_H #define DIALOGNODEFIND_H #include namespace Ui { class DialogNodeFind; } class DialogNodeFind : public QDialog { Q_OBJECT public: explicit DialogNodeFind(QWidget *parent = Q_NULLPTR, QStringList indexList=QStringList()); ~DialogNodeFind(); public slots: void setError(const bool &toggle); void getIndex(const QString &indexStr); void checkErrors (); void getUserChoices (); signals: void userChoices( const QStringList &list, const QString &type, const QString &selectedIndex=QString()); private: Ui::DialogNodeFind *ui; QStringList list; QString searchType; QStringList tempListA; QStringList tempListB; QString str; QString selectedIndex; }; #endif // DIALOGNODEFIND_H app-2.8/src/forms/dialognodefind.ui000066400000000000000000000201661377436340000174010ustar00rootroot00000000000000 DialogNodeFind 0 0 600 410 600 410 800 600 Find nodes (and select them) 0 100 16777215 300 13 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:'DejaVu Sans'; font-size:10pt; 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;"><span style=" font-weight:600;">Find and select nodes (by numbers, labels or index score)</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To find nodes by their number, enter numbers either comma-separated or line by line or array <br /><span style=" font-style:italic;">i.e. 1,2,3 or 1-10) </span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To find nodes by their label enter words either comma separated or line by line <br /><span style=" font-style:italic;">i.e. jim,julia</span> </p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To find and select nodes by their index score, enter the desired score in the form: <br /><span style=" font-style:italic;">&gt; threshold</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">or </p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">&lt; threshold</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">and select the index in the menu below</p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> By Number(s) By Label(s) By Index Score Qt::Vertical 20 29 Search for these numbers (enter line by line or csv): Qt::RichText true 0 40 <html><head/><body><p>Select how to search nodes (by their number, by their label, or by index score) and enter below what you want to find. Matched nodes will be highlighted.</p></body></html> Qt::RichText true Qt::Vertical 20 30 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok false 50 0 Index false 150 0 Qt::Vertical 20 25 buttonBox accepted() DialogNodeFind accept() 248 254 157 274 buttonBox rejected() DialogNodeFind reject() 316 260 286 274 app-2.8/src/forms/dialogpreviewfile.cpp000077500000000000000000000105131377436340000202770ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogpreviewfile.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org 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 "dialogpreviewfile.h" DialogPreviewFile::DialogPreviewFile(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 DialogPreviewFile::setCodecList(const QList &list) { encodingComboBox->clear(); foreach (QTextCodec *codec, list) encodingComboBox->addItem(codec->name(), codec->mibEnum()); } void DialogPreviewFile::setEncodedData(const QByteArray &data, const QString m_fileName, const int m_format) { fileName = m_fileName; format = m_format; encodedData = data; updateTextEdit(); } void DialogPreviewFile::updateTextEdit() { int mib = encodingComboBox->itemData( encodingComboBox->currentIndex()).toInt(); QTextCodec *codec = QTextCodec::codecForMib(mib); qDebug () << " DialogPreviewFile::updateTextEdit() " << codec->name(); QTextStream in(&encodedData); in.setAutoDetectUnicode(false); in.setCodec(codec); decodedStr = in.readAll(); textEdit->setPlainText(decodedStr); } void DialogPreviewFile::accept() { int mib = encodingComboBox->itemData( encodingComboBox->currentIndex()).toInt(); QTextCodec *codec = QTextCodec::codecForMib(mib); qDebug () << " DialogPreviewFile::accept() returning codec name " << codec->name(); emit loadNetworkFileWithCodec(fileName, codec->name(), format); QDialog::accept(); } app-2.8/src/forms/dialogpreviewfile.h000077500000000000000000000046071377436340000177530ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogpreviewfile.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGPREVIEWFILE_H #define DIALOGPREVIEWFILE_H #include #include class QComboBox; class QDialogButtonBox; class QLabel; class QTextCodec; class QTextEdit; class DialogPreviewFile : public QDialog { Q_OBJECT public: explicit DialogPreviewFile(QWidget *parent = Q_NULLPTR); void setCodecList(const QList &list); void setEncodedData(const QByteArray &data, const QString, const int ); QString decodedString() const { return decodedStr; } signals: void loadNetworkFileWithCodec(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 app-2.8/src/forms/dialogranderdosrenyi.cpp000077500000000000000000000132301377436340000210050ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogranderdosrenyi.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogranderdosrenyi.h" DialogRandErdosRenyi::DialogRandErdosRenyi(QWidget *parent, const qreal eprob) : QDialog(parent) { qDebug() << "::DialogRandErdosRenyi() " ; ui.setupUi(this); nodes = 0; model = ""; edges = 0; ui.probDoubleSpinBox->setValue(eprob); mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandErdosRenyi::getUserChoices ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); connect (ui.gnpRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::checkErrors); connect (ui.gnmRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::checkErrors); ui.gnpRadioButton->setChecked(true); ui.probDoubleSpinBox->setEnabled(true); ui.edgesSpinBox-> setDisabled(true); ui.undirectedRadioButton->setChecked(true); ui.diagCheckBox ->setChecked(false); connect (ui.gnpRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::gnpModel ); connect (ui.gnmRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::gnmModel ); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &DialogRandErdosRenyi::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &DialogRandErdosRenyi::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &DialogRandErdosRenyi::setDiag); } void DialogRandErdosRenyi::gnpModel (){ ui.gnmRadioButton -> setChecked(false); ui.probDoubleSpinBox -> setEnabled(true); ui.edgesSpinBox-> setDisabled(true); } void DialogRandErdosRenyi::gnmModel (){ ui.gnpRadioButton -> setChecked(false); ui.probDoubleSpinBox -> setDisabled(true); ui.edgesSpinBox-> setEnabled(true); } void DialogRandErdosRenyi::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void DialogRandErdosRenyi::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void DialogRandErdosRenyi::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void DialogRandErdosRenyi::checkErrors() { qDebug()<< " DialogRandErdosRenyi::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); return; } else { ui.gnpRadioButton->setGraphicsEffect(0); ui.gnmRadioButton->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } } void DialogRandErdosRenyi::getUserChoices() { qDebug() << "DialogRandErdosRenyi::getUserChoices() " ; 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 " << ui.probDoubleSpinBox->value(); qDebug() << "edges " << edges; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, model, edges, ui.probDoubleSpinBox->value(), mode, diag); } app-2.8/src/forms/dialogranderdosrenyi.h000077500000000000000000000045701377436340000204610ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogranderdosrenyi.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGRANDERDOSRENYI_H #define DIALOGRANDERDOSRENYI_H #include #include "ui_dialogranderdosrenyi.h" class DialogRandErdosRenyi : public QDialog { Q_OBJECT public: explicit DialogRandErdosRenyi ( QWidget *parent = Q_NULLPTR, const qreal eprob = 0); public slots: void checkErrors(); void getUserChoices(); void gnmModel(); void gnpModel(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int nodes, const QString model, const int edges, const qreal eprob, const QString mode, const bool diag); private: QString model; QString mode; int nodes, edges; bool diag; Ui::DialogRandErdosRenyi ui; }; #endif app-2.8/src/forms/dialogranderdosrenyi.ui000066400000000000000000000471761377436340000206550ustar00rootroot00000000000000 DialogRandErdosRenyi 0 0 615 520 0 0 615 520 800 600 Erdős–Rényi network generator 0 0 540 85 16777215 96 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate random network according to Erdős–Rényi (ER) model. </p><p>In fact, there are two models: in <span style=" font-style:italic;">G(n,p)</span> edges are created with Bernoulli trials, while in <span style=" font-style:italic;">G(n,M) </span>a graph is randomly selected from all graphs with <span style=" font-style:italic;">n</span> nodes and <span style=" font-style:italic;">M</span> edges. Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 350 0 Sans Serif <html><head/><body><p>Nodes <span style=" font-style:italic;">n</span></p></body></html> 0 0 120 0 Sans Serif 0 9999 100 Qt::Horizontal 0 0 350 0 Sans Serif Model 120 0 Sans Serif <!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 Sans Serif <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 false Qt::Horizontal QLayout::SetMinimumSize 0 0 350 0 Sans Serif <html><head/><body><p>Edges <span style=" font-style:italic;">M </span><span style=" color:#7c7c7c;">for G(n,M) model only</span></p></body></html> 120 0 Sans Serif 10 9999 Qt::Horizontal 0 0 350 0 Sans Serif <html><head/><body><p>Edge Probability <span style=" color:#7c7c7c;">applicable only in G(n,p) model</span></p></body></html> 0 0 120 0 Sans Serif 0.010000000000000 1.000000000000000 0.010000000000000 0.100000000000000 Qt::Horizontal 0 0 350 0 Sans Serif Graph Mode 0 0 120 0 Sans Serif Undirected false false 0 0 120 0 Sans Serif Directed false false Qt::Horizontal 0 0 350 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 120 0 Sans Serif Yes, allow false Qt::Horizontal Sans Serif 50 false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandErdosRenyi accept() 257 373 157 274 buttonBox rejected() DialogRandErdosRenyi reject() 325 373 286 274 app-2.8/src/forms/dialograndlattice.cpp000077500000000000000000000066301377436340000202550ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialograndlattice.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialograndlattice.h" DialogRandLattice::DialogRandLattice(QWidget *parent) : QDialog(parent) { ui.setupUi(this); ui.circularCheckBox -> setText("false"); ui.nodesSpinBox->setEnabled(false); connect ( ui.circularCheckBox, &QCheckBox::toggled , this, &DialogRandLattice::circularChanged); connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandLattice::getUserChoices ); connect(ui.lengthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(lengthChanged(int))); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); } void DialogRandLattice::circularChanged(const bool &toggle) { if (toggle) { ui.circularCheckBox -> setText("true"); } else { ui.circularCheckBox -> setText("false"); } } void DialogRandLattice::lengthChanged(int l) { ui.nodesSpinBox->setValue(l*l); } void DialogRandLattice::getUserChoices() { qDebug() << "DialogRandSmallWorld::getUserChoices() " ; nodes = ui.nodesSpinBox->value(); length = ui.lengthSpinBox->value(); dimension = ui.dimSpinBox->value(); neighLength = ui.neiSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); circular = (ui.circularCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "length " << length; qDebug() << "dimension " << dimension; qDebug() << "neighLength" << neighLength; qDebug() << "mode " << mode; qDebug() << "diag " << circular; emit userChoices(nodes, length, dimension, neighLength, mode, circular); } app-2.8/src/forms/dialograndlattice.h000077500000000000000000000051071377436340000177200ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialograndlattice.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGRANDLATTICE_H #define DIALOGRANDLATTICE_H #include #include "ui_dialograndlattice.h" class DialogRandLattice : public QDialog { Q_OBJECT public: explicit DialogRandLattice(QWidget *parent = Q_NULLPTR); signals: void userChoices( const int &nodes, const int &length, const int &dimension, const int &neighLength, const QString &mode, const bool &diag); public slots: // void checkErrors(const int &i); void getUserChoices(); void circularChanged(const bool &toggle); void lengthChanged(int l); // void setModeDirected(); // void setModeUndirected(); // void setDiag(); // void modifyDegree(int value); private: Ui::DialogRandLattice ui; int nodes; int length; int dimension; int neighLength; QString mode; bool circular; }; #endif app-2.8/src/forms/dialograndlattice.ui000066400000000000000000000414041377436340000201030ustar00rootroot00000000000000 DialogRandLattice 0 0 650 420 0 0 650 420 800 600 Lattice network generator 0 0 450 70 16777215 75 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate a lattice network. You can select how many dimensions the lattice will have (i.e. d=2 for a two-dimensional lattice) and the length of each dimension (i.e. l=3 for a 3x3 lattice of 9 nodes). Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 380 0 Sans Serif Nodes 0 0 110 0 Sans Serif <html><head/><body><p><span style=" font-weight:600;">Lattice Nodes </span></p><p>The resulting lattice will have this amount of nodes . </p><p>This value changes automatically as you modify the <span style=" font-style:italic;">length l </span>of each dimension (below).</p></body></html> 4 9999 25 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Length <span style=" font-style:italic;">l </span>in each dimension </p></body></html> 110 0 Sans Serif <html><head/><body><p><span style=" font-weight:600;">Length</span></p><p>The size of the lattice in each dimension.</p></body></html> 2 9999 2 5 Qt::Horizontal 0 0 380 0 Sans Serif <html><head/><body><p>Dimension <span style=" font-style:italic;">d</span></p></body></html> 110 0 Sans Serif <html><head/><body><p><span style=" font-weight:600;">Dimension </span><span style=" font-weight:600; font-style:italic;">d</span></p><p>The dimension of the lattice.</p><p>I.e. enter 2 for a two-dimensional lattice.</p><p><br/></p></body></html> 2 3 1 2 Qt::Horizontal 0 0 380 0 Sans Serif <html><head/><body><p>Neighborhood <span style=" font-style:italic;">n</span></p></body></html> 110 0 Sans Serif <html><head/><body><p><span style=" font-weight:600;">Neighborhood </span><span style=" font-weight:600; font-style:italic;">n</span></p><p>The distance within which the neighbors on the lattice will be connected</p></body></html> 1 6 1 1 Qt::Horizontal 0 0 380 0 Sans Serif Graph Mode 0 0 110 0 Sans Serif Undirected true false 0 0 100 0 Sans Serif Directed false Qt::Horizontal 0 0 380 0 Sans Serif Circular 0 0 110 0 Sans Serif <html><head/><body><p>If checked, the lattice will be circular</p></body></html> false false Qt::Horizontal Sans Serif PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandLattice accept() 257 373 157 274 buttonBox rejected() DialogRandLattice reject() 325 373 286 274 app-2.8/src/forms/dialograndregular.cpp000077500000000000000000000120751377436340000202710ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialograndregular.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialograndregular.h" DialogRandRegular::DialogRandRegular(QWidget *parent) : QDialog(parent) { qDebug() << "::DialogRandRegular() " ; ui.setupUi(this); nodes = 100; degree = 2; mode = "undirected"; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandRegular::getUserChoices ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); ui.degreeSpinBox-> setEnabled(true); ui.undirectedRadioButton->setChecked(true); ui.diagCheckBox ->setChecked(false); ui.diagCheckBox -> setEnabled(false); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &DialogRandRegular::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &DialogRandRegular::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &DialogRandRegular::setDiag); ui.nodesSpinBox->setFocus(); ui.nodesSpinBox->setValue(nodes); ui.degreeSpinBox->setValue( degree ); connect(ui.nodesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(checkErrors(int))); connect(ui.degreeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(checkErrors(int))); } void DialogRandRegular::modifyDegree(int value) { ui.degreeSpinBox->setValue( qCeil ( qLn (value) )); ui.degreeSpinBox->setMaximum( value ); } void DialogRandRegular::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; ui.degreeLabel->setText("inDegree=outDegree d"); } void DialogRandRegular::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; ui.degreeLabel->setText("Degree d"); } void DialogRandRegular::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void DialogRandRegular::checkErrors(const int &i) { Q_UNUSED(i); qDebug()<< " DialogRandRegular::checkErrors()" ; if ( ( ui.degreeSpinBox->value() * ui.nodesSpinBox->value() ) % 2 !=0 || ( (double) ui.degreeSpinBox->value() / (double) ui.nodesSpinBox->value() ) >= 0.5 || ui.nodesSpinBox->value() < 6 ) { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui.degreeSpinBox->setGraphicsEffect(effect); ui.nodesSpinBox->setGraphicsEffect(effect); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } else { ui.degreeSpinBox->setGraphicsEffect(0); ui.nodesSpinBox->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } } void DialogRandRegular::getUserChoices() { qDebug() << "DialogRandRegular::getUserChoices() " ; nodes = ui.nodesSpinBox->value(); degree= ui.degreeSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "degree" << degree; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, degree, mode, diag); } app-2.8/src/forms/dialograndregular.h000077500000000000000000000045341377436340000177370ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialograndregular.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGRANDREGULAR_H #define DIALOGRANDREGULAR_H #include #include "ui_dialograndregular.h" class DialogRandRegular : public QDialog { Q_OBJECT public: explicit DialogRandRegular(QWidget *parent = Q_NULLPTR); public slots: void checkErrors(const int &i); void getUserChoices(); void setModeDirected(); void setModeUndirected(); void setDiag(); void modifyDegree(int value); signals: void userChoices( const int nodes, const int degree, const QString mode, const bool diag); private: QString mode; int nodes, degree; bool diag; Ui::DialogRandRegular ui; }; #endif app-2.8/src/forms/dialograndregular.ui000066400000000000000000000364051377436340000201240ustar00rootroot00000000000000 DialogRandRegular 0 0 600 440 0 0 600 440 800 600 d-Regular network generator 0 0 450 70 16777215 95 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate a <span style=" font-style:italic;">d-regular</span> random network of <span style=" font-style:italic;">n</span> nodes. This is a graph where each vertex has the same number of neighbors <span style=" font-style:italic;">d</span>.</p><p>This model produces undirect and directed provided that n &gt; 5, d/<span style=" font-style:italic;">n &lt; 0.5 </span>and <span style=" font-style:italic;">n*d </span>is even. Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 380 0 Sans Serif <html><head/><body><p>The number n of nodes in the new Regular Network.</p><p>Note: For a <span style=" font-style:italic;">d</span>-regular graph of <span style=" font-style:italic;">n</span> nodes, it is necessary that <span style=" font-style:italic;">n &gt;= d + 1 </span>and <span style=" font-style:italic;">n*d </span>is even</p></body></html> Nodes 0 0 110 0 Sans Serif <html><head/><body><p>Enter number n of nodes in the new Regular Network.</p><p>Constraints: </p><p><span style=" font-style:italic;">n &gt; 6 </span>and <span style=" font-style:italic;"/></p><p><span style=" font-style:italic;">d/n &gt; 0.5 </span>and</p><p><span style=" font-style:italic;">n*d </span>is even</p></body></html> 4 9999 100 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Degree <span style=" font-style:italic;">d</span></p></body></html> 110 0 Sans Serif <html><head/><body><p>Enter the degree <span style=" font-style:italic;">d</span> each new node will have.</p><p>Constraints: </p><p><span style=" font-style:italic;">d/n &gt; 0.5 </span>and</p><p><span style=" font-style:italic;">n*d </span>is even</p><p>In directed Graph Mode, this Degree <span style=" font-style:italic;">d</span> option corresponds to the outDegree and the inDegree, which will be both equal to <span style=" font-style:italic;">d</span> in the generated directed regular network. </p><p><br/></p></body></html> 2 9999 1 2 Qt::Horizontal 0 0 400 0 Sans Serif Graph Mode 0 0 110 0 Sans Serif <html><head/><body><p>Click &quot;undirected&quot; to generate an undirected <span style=" font-style:italic;">d</span>-regular network </p></body></html> Undirected true false 0 0 100 0 Sans Serif <html><head/><body><p>Click &quot;directed&quot; to generate a directed <span style=" font-style:italic;">d</span>-regular network. </p><p>In this case, the Degree <span style=" font-style:italic;">d</span> option above corresponds to the outDegree and the inDegree which will be both equal to <span style=" font-style:italic;">d</span> in the generated regular network. </p><p>For instance, if you select <span style=" font-style:italic;">d</span>=4 and <span style=" font-style:italic;">Graph Mode</span>=directed, then each new node will have outDegree=4 (four outbound edges) and inDegree=4 (four inbound edges). The inbound and outbound edges of each node will not necessarily be from / to the same nodes.</p></body></html> Directed false Qt::Horizontal 0 0 400 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 110 0 Sans Serif Check to allow loops (nodes linking to themselves) in the new network No loops false Qt::Horizontal Sans Serif PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandRegular accept() 257 373 157 274 buttonBox rejected() DialogRandRegular reject() 325 373 286 274 app-2.8/src/forms/dialograndscalefree.cpp000077500000000000000000000113041377436340000205530ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt randscalefreeddialog.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialograndscalefree.h" #include #include #include #include #include DialogRandScaleFree::DialogRandScaleFree(QWidget *parent) : QDialog(parent) { qDebug() << "DialogRandScaleFree::DialogRandScaleFree() " ; ui.setupUi(this); nodes = 0; initialNodes = 0; mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandScaleFree::getUserChoices ); 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, &DialogRandScaleFree::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &DialogRandScaleFree::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &DialogRandScaleFree::setDiag); } void DialogRandScaleFree::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void DialogRandScaleFree::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void DialogRandScaleFree::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void DialogRandScaleFree::checkErrors() { qDebug()<< " DialogRandSmallWorld::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); // } //getUserChoices(); } void DialogRandScaleFree::getUserChoices() { qDebug() << "DialogRandScaleFree::getUserChoices() " ; 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); } app-2.8/src/forms/dialograndscalefree.h000077500000000000000000000046171377436340000202310ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt randscalefreeddialog.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGRANDSCALEFREE_H #define DIALOGRANDSCALEFREE_H #include #include "ui_dialograndscalefree.h" class DialogRandScaleFree : public QDialog { Q_OBJECT public: explicit DialogRandScaleFree(QWidget *parent = Q_NULLPTR); public slots: void checkErrors(); void getUserChoices(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int &nodes, const int &power, const int &initialNodes, const int &edgesPerStep, const qreal &zeroAppeal, const QString &mode); private: QString mode; int nodes; // n int initialNodes; // m0 int edgesPerStep; //m int power; qreal zeroAppeal; // a bool diag; Ui::DialogRandScaleFree ui; }; #endif app-2.8/src/forms/dialograndscalefree.ui000066400000000000000000000453011377436340000204070ustar00rootroot00000000000000 DialogRandScaleFree 0 0 600 530 0 0 600 530 800 600 Scale-free random network generator 0 0 450 100 16777215 110 Sans Serif 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. </p><p>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 Qt::Horizontal 0 0 380 0 Sans Serif <html><head/><body><p>Nodes <span style=" font-style:italic;">n</span></p></body></html> 0 0 120 0 Sans Serif <html><head/><body><p>The amount of nodes in the resulting scale-free graph</p></body></html> 4 9999 100 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Power of preferential attachment <span style=" font-style:italic;">p</span></p></body></html> 120 0 Sans Serif <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 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Initial connected nodes <span style=" font-style:italic;">m</span><span style=" font-style:italic; vertical-align:sub;">0</span></p></body></html> 120 0 Sans Serif <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 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Edges to add in each step <span style=" font-style:italic;">m</span></p></body></html> 120 0 Sans Serif <html><head/><body><p>Tthe number of edges to add in each step</p></body></html> 1 9999 1 2 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Zero appeal <span style=" font-style:italic;">α</span></p></body></html> 120 0 Sans Serif <html><head/><body><p>The initial attractiveness of a node - useful for isolate nodes with d =0</p></body></html> 0 9999 1 1 Qt::Horizontal 0 0 400 0 Sans Serif Graph Mode 0 0 120 0 Sans Serif Undirected true false 0 0 120 0 Sans Serif Directed false Qt::Horizontal 0 0 440 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 120 0 Sans Serif Check to allow loops (nodes linking to themselves) in the new network No loops false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandScaleFree accept() 257 373 157 274 buttonBox rejected() DialogRandScaleFree reject() 325 373 286 274 app-2.8/src/forms/dialograndsmallworld.cpp000077500000000000000000000120261377436340000210040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialograndsmallworld.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialograndsmallworld.h" DialogRandSmallWorld::DialogRandSmallWorld(QWidget *parent) : QDialog(parent), ui(new Ui::DialogRandSmallWorld) { qDebug() << "DialogRandSmallWorld::DialogRandSmallWorld() " ; ui->setupUi(this); nodes = 100; degree = qCeil ( qLn (nodes) ); bprob = 0; mode = "undirected"; diag = false; connect ( ui->buttonBox, &QDialogButtonBox::accepted, this, &DialogRandSmallWorld::getUserChoices ); ui->buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); 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, &DialogRandSmallWorld::setModeUndirected ); connect ( ui->directedRadioButton,&QRadioButton::clicked, this, &DialogRandSmallWorld::setModeDirected ); connect ( ui->diagCheckBox,&QCheckBox::clicked, this, &DialogRandSmallWorld::setDiag); connect(ui->nodesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(modifyDegree(int))); ui->nodesSpinBox->setFocus(); ui->nodesSpinBox->setValue(nodes); ui->degreeSpinBox->setValue( degree ); } void DialogRandSmallWorld::modifyDegree(int value) { ui->degreeSpinBox->setValue( qCeil ( qLn (value) )); ui->degreeSpinBox->setMaximum( value ); } void DialogRandSmallWorld::setModeDirected (){ ui->directedRadioButton->setChecked(true) ; ui->undirectedRadioButton->setChecked(false) ; } void DialogRandSmallWorld::setModeUndirected (){ ui->directedRadioButton->setChecked(false) ; ui->undirectedRadioButton->setChecked(true) ; } void DialogRandSmallWorld::setDiag (){ if (ui->diagCheckBox -> isChecked()) ui->diagCheckBox->setText("Yes, allow"); else ui->diagCheckBox->setText("No, set zero"); } void DialogRandSmallWorld::checkErrors() { qDebug()<< " DialogRandSmallWorld::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); // } //getUserChoices(); } void DialogRandSmallWorld::getUserChoices() { qDebug() << "DialogRandSmallWorld::getUserChoices() " ; 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); } app-2.8/src/forms/dialograndsmallworld.h000077500000000000000000000044621377436340000204560ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialograndsmallworld.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGRANDSMALLWORLD_H #define DIALOGRANDSMALLWORLD_H #include #include "ui_dialograndsmallworld.h" class DialogRandSmallWorld : public QDialog { Q_OBJECT public: explicit DialogRandSmallWorld(QWidget *parent = Q_NULLPTR); public slots: void checkErrors(); void getUserChoices(); void setModeDirected(); void setModeUndirected(); void setDiag(); void modifyDegree(int value); signals: void userChoices( const int nodes, const int degree, const qreal prob, const QString mode, const bool diag); private: QString mode; int nodes, degree; qreal bprob; bool diag; Ui::DialogRandSmallWorld *ui; }; #endif app-2.8/src/forms/dialograndsmallworld.ui000066400000000000000000000342771377436340000206500ustar00rootroot00000000000000 DialogRandSmallWorld 0 0 600 420 0 0 600 400 800 600 Small-World network generator 0 0 450 70 16777215 75 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate random network according to the <span style=" font-style:italic;">Watts &amp; Strogatz</span> model.</p><p>This model produces graphs with small-world properties, including short average path lengths and high clustering. Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 380 0 Sans Serif Nodes 0 0 110 0 Sans Serif <html><head/><body><p>The resulting graph will have N nodes and N*d/2 edges</p></body></html> 4 9999 100 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Mean Degree <span style=" font-style:italic;">d</span></p></body></html> 110 0 Sans Serif <html><head/><body><p>This is the mean edge degree each new node will have</p></body></html> 2 9999 2 10 Qt::Horizontal 0 0 400 0 Sans Serif <html><head/><body><p>Rewiring Probability <span style=" font-style:italic;">β</span></p></body></html> 0 0 110 0 Sans Serif 0.010000000000000 1.000000000000000 0.010000000000000 0.300000000000000 Qt::Horizontal 0 0 400 0 Sans Serif Graph Mode 0 0 110 0 Sans Serif Undirected true false 0 0 100 0 Sans Serif Directed false Qt::Horizontal 0 0 400 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 110 0 Sans Serif Check to allow loops (nodes linking to themselves) in the new network No loops false Qt::Horizontal Sans Serif PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandSmallWorld accept() 257 373 157 274 buttonBox rejected() DialogRandSmallWorld reject() 325 373 286 274 app-2.8/src/forms/dialogsettings.cpp000077500000000000000000000774071377436340000176350ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsettings.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogsettings.h" #include "ui_dialogsettings.h" #include #include #include #include #include #include #include #include #include "global.h" SOCNETV_USE_NAMESPACE DialogSettings::DialogSettings(QMap &appSettings, const QStringList &nodeShapeList, const QStringList &iconPathList, QWidget *parent) : QDialog(parent), m_appSettings(appSettings), m_shapeList(nodeShapeList), m_iconList(iconPathList), ui(new Ui::DialogSettings) { ui->setupUi(this); // m_appSettings = appSettings; //only use if var passed by pointer //data export ui->dataDirEdit->setText( (m_appSettings)["dataDir"]); ui->printLogoChkBox->setChecked( (appSettings["printLogo"] == "true") ? true:false ); // reports ui->reportsRealNumberPrecisionSpin-> setValue(m_appSettings["initReportsRealNumberPrecision"].toInt(0, 10) ); ui->reportsLabelsLengthSpin-> setValue(m_appSettings["initReportsLabelsLength"].toInt(0, 10) ); QStringList chartTypesList; chartTypesList << "None" << "Lines" << "Area" << "Bars" ; ui->reportsChartTypeSelect->addItems(chartTypesList); switch (appSettings["initReportsChartType"].toInt()) { case ChartType::None: ui->reportsChartTypeSelect->setCurrentText( "None"); break; case ChartType::Spline: ui->reportsChartTypeSelect->setCurrentText( "Lines"); break; case ChartType::Area: ui->reportsChartTypeSelect->setCurrentText( "Area"); break; case ChartType::Bars: ui->reportsChartTypeSelect->setCurrentText( "Bars"); break; default: ui->reportsChartTypeSelect->setCurrentText( "Lines"); break; } qDebug() << "reportsChartTypeSelect" << ui->reportsChartTypeSelect->currentText(); //debugging ui->printDebugChkBox->setChecked( (appSettings["printDebug"] == "true") ? true:false ); ui->progressDialogChkBox->setChecked( (appSettings["showProgressBar"] == "true") ? true:false ); /** * Style options */ ui->stylesheetDefaultChkBox->setChecked(true); /** * window options */ ui->leftPanelChkBox->setChecked( ( appSettings["showLeftPanel"] == "true") ? true:false ); ui->rightPanelChkBox->setChecked( ( appSettings["showRightPanel"] == "true") ? true:false ); /** * canvas options */ m_bgColor = QColor (m_appSettings["initBackgroundColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_bgColor ); ui->bgColorButton->setIcon(QIcon(m_pixmap)); ui->bgImageSelectEdit->setText((m_appSettings)["initBackgroundImage"]); ui->canvasAntialiasingChkBox->setChecked( (appSettings["antialiasing"] == "true") ? true:false ); ui->canvasAntialiasingAutoAdjustChkBox->setChecked( (appSettings["canvasAntialiasingAutoAdjustment"] == "true") ? true:false ); ui->canvasSmoothPixmapTransformChkBox->setChecked( (appSettings["canvasSmoothPixmapTransform"] == "true") ? true:false ); ui->canvasSavePainterStateChkBox->setChecked( (appSettings["canvasPainterStateSave"] == "true") ? true:false ); ui->canvasCacheBackgroundChkBox->setChecked( (appSettings["canvasCacheBackground"] == "true") ? true:false ); ui->canvasEdgeHighlightingChkBox->setChecked( (appSettings["canvasEdgeHighlighting"] == "true") ? true:false ); QStringList optionsList; optionsList << "Full" << "Minimal" << "Smart" << "Bounding Rectangle" << "None"; ui->canvasUpdateModeSelect->addItems(optionsList); if ( appSettings["canvasUpdateMode"] == "Full" ) { ui->canvasUpdateModeSelect->setCurrentText( "Full"); } else if (appSettings["canvasUpdateMode"] == "Minimal" ) { ui->canvasUpdateModeSelect->setCurrentText("Minimal" ); } else if (appSettings["canvasUpdateMode"] == "Smart" ) { ui->canvasUpdateModeSelect->setCurrentText("Smart" ); } else if (appSettings["canvasUpdateMode"] == "Bounding Rectangle" ) { ui->canvasUpdateModeSelect->setCurrentText("Bounding Rectangle" ); } else if (appSettings["canvasUpdateMode"] == "None" ) { ui->canvasUpdateModeSelect->setCurrentText("None" ); } else { // ui->canvasUpdateModeSelect->setCurrentText("Minimal" ); } qDebug() << "canvasUpdateModeSelect" << appSettings["canvasUpdateMode"]; optionsList.clear(); optionsList << "BspTreeIndex" << "NoIndex" ; ui->canvasIndexMethodSelect->addItems(optionsList); if ( appSettings["canvasIndexMethod"] == "BspTreeIndex" ) { ui->canvasIndexMethodSelect->setCurrentText( "BspTreeIndex"); } else if (appSettings["canvasIndexMethod"] == "NoIndex" ) { ui->canvasIndexMethodSelect->setCurrentText("NoIndex" ); } else { // ui->canvasIndexMethodSelect->setCurrentText("BspTreeIndex" ); } qDebug() << "canvasIndexMethodSelect" << appSettings["canvasIndexMethod"]; /** * node options */ m_nodeColor = QColor (m_appSettings["initNodeColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_nodeColor ); ui->nodeColorBtn->setIcon(QIcon(m_pixmap)); ui->nodeShapeComboBox->addItems(m_shapeList); for (int i = 0; i < m_shapeList.size(); ++i) { ui->nodeShapeComboBox->setItemIcon(i, QIcon(m_iconList[i])); } ui->nodeIconSelectButton->setEnabled(false); ui->nodeIconSelectEdit->setEnabled(false); int index = -1; if ( (index = m_shapeList.indexOf(m_appSettings["initNodeShape"])) != -1 ){ ui->nodeShapeComboBox->setCurrentIndex(index); if ( index == NodeShape::Custom ) { ui->nodeShapeComboBox->setCurrentIndex(NodeShape::Custom); ui->nodeIconSelectButton->setEnabled(true); ui->nodeIconSelectEdit->setEnabled(true); ui->nodeIconSelectEdit->setText (m_appSettings["initNodeIconPath"]); if ( ! m_appSettings["initNodeIconPath"].isEmpty() ) { ui->nodeShapeComboBox->setItemIcon( NodeShape::Custom, QIcon(m_appSettings["initNodeIconPath"])); } else { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->nodeIconSelectButton->setGraphicsEffect(effect); ui->nodeIconSelectEdit->setGraphicsEffect(effect); (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } } } else { // default -- should never happen... ui->nodeShapeComboBox->setCurrentIndex(NodeShape::Circle); } ui->nodeSizeSpin->setValue( m_appSettings["initNodeSize"].toInt(0, 10) ); ui->nodeNumbersChkBox->setChecked( ( m_appSettings["initNodeNumbersVisibility"] == "true") ? true : false ); ui->nodeNumbersInsideChkBox->setChecked( (m_appSettings["initNodeNumbersInside"] == "true" ) ? true:false ); if (m_appSettings["initNodeNumbersInside"] == "true") { ui->nodeNumberDistanceSpin->setEnabled(false); ui->nodeNumberSizeSpin->setValue( 0 ); } m_nodeNumberColor = QColor (m_appSettings["initNodeNumberColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_nodeNumberColor ); ui->nodeNumberColorBtn->setIcon(QIcon(m_pixmap)); ui->nodeNumberSizeSpin->setValue( m_appSettings["initNodeNumberSize"].toInt(0, 10) ); ui->nodeNumberDistanceSpin->setValue( m_appSettings["initNodeNumberDistance"].toInt(0, 10) ); ui->nodeLabelsChkBox->setChecked( ( m_appSettings["initNodeLabelsVisibility"] == "true") ? true : false ); ui->nodeLabelSizeSpin->setValue( m_appSettings["initNodeLabelSize"].toInt(0, 10) ); m_nodeLabelColor = QColor (m_appSettings["initNodeLabelColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_nodeLabelColor ); ui->nodeLabelColorBtn->setIcon(QIcon(m_pixmap)); ui->nodeLabelDistanceSpin->setValue( m_appSettings["initNodeLabelDistance"].toInt(0, 10) ); /** * edge options */ ui->edgesChkBox-> setChecked( (m_appSettings["initEdgesVisibility"] == "true") ? true: false ); ui->edgeArrowsChkBox-> setChecked( (m_appSettings["initEdgeArrows"] == "true") ? true: false ); m_edgeColor = QColor (m_appSettings["initEdgeColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeColor ); ui->edgeColorBtn->setIcon(QIcon(m_pixmap)); m_edgeColorNegative = QColor (m_appSettings["initEdgeColorNegative"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeColorNegative ); ui->edgeColorNegativeBtn ->setIcon(QIcon(m_pixmap)); m_edgeColorZero = QColor (m_appSettings["initEdgeColorZero"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeColorZero); ui->edgeColorZeroBtn ->setIcon(QIcon(m_pixmap)); if (m_appSettings["initEdgeShape"] == "line") { ui->edgeShapeRadioStraightLine->setChecked(true); } else if (m_appSettings["initEdgeShape"] == "bezier") { ui->edgeShapeRadioBezier->setChecked(true); } else { ui->edgeShapeRadioStraightLine->setChecked(true); } ui->edgeOffsetFromNodeSpin->setValue( m_appSettings["initEdgeOffsetFromNode"].toInt(0, 10) ); ui->edgeWeightNumbersChkBox-> setChecked( (m_appSettings["initEdgeWeightNumbersVisibility"] == "true") ? true: false ); m_edgeWeightNumberColor = QColor (m_appSettings["initEdgeWeightNumberColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeWeightNumberColor ); ui->edgeWeightNumberColorBtn->setIcon(QIcon(m_pixmap)); ui->edgeWeightNumberSizeSpin->setValue( m_appSettings["initEdgeWeightNumberSize"].toInt(0, 10) ); ui->edgeLabelsChkBox-> setChecked( (m_appSettings["initEdgeLabelsVisibility"] == "true") ? true: false ); /** * dialog signals to slots */ connect (ui->dataDirSelectButton, &QToolButton::clicked, this, &DialogSettings::getDataDir); connect (ui->printDebugChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setDebugMsgs); connect(ui->reportsRealNumberPrecisionSpin, SIGNAL(valueChanged(int)), this, SLOT(getReportsRealNumberPrecision(int)) ); connect (ui->reportsLabelsLengthSpin, SIGNAL(valueChanged(int)), this, SLOT(getReportsLabelsLength(int))); connect(ui->reportsChartTypeSelect, SIGNAL ( currentIndexChanged (const int &)), this, SLOT(getReportsChartType(const int &)) ); connect (ui->printLogoChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setPrintLogo); connect( ui->stylesheetDefaultChkBox,&QCheckBox::clicked, this, &DialogSettings::setStyleSheetDefault); connect (ui->progressDialogChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setProgressDialog); connect (ui->showToolBarChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setToolBar); connect (ui->showStatusBarChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setStatusBar); connect (ui->leftPanelChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setLeftPanel); connect (ui->rightPanelChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setRightPanel); connect (ui->bgColorButton, &QToolButton::clicked, this, &DialogSettings::getCanvasBgColor); connect (ui->bgImageSelectButton, &QToolButton::clicked, this, &DialogSettings::getCanvasBgImage); connect (ui->canvasAntialiasingChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setCanvasAntialiasing); connect (ui->canvasAntialiasingAutoAdjustChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setCanvasAntialiasingAutoAdjust); connect (ui->canvasSmoothPixmapTransformChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setCanvasSmoothPixmapTransform); connect (ui->canvasSavePainterStateChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setCanvasSavePainterState); connect (ui->canvasCacheBackgroundChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setCanvasCacheBackground); connect (ui->canvasEdgeHighlightingChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setCanvasEdgeHighlighting); connect(ui->canvasUpdateModeSelect, SIGNAL ( currentIndexChanged (const QString &)), this, SLOT(getCanvasUpdateMode(const QString &)) ); connect(ui->canvasIndexMethodSelect, SIGNAL ( currentIndexChanged (const QString &)), this, SLOT(getCanvasIndexMethod(const QString &)) ); connect (ui->nodeShapeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &DialogSettings::getNodeShapeIndex); connect (ui->nodeIconSelectButton, &QToolButton::clicked, this, &DialogSettings::getNodeIconFile); connect(ui->nodeSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeSize(int)) ); connect (ui->nodeColorBtn, &QToolButton::clicked, this, &DialogSettings::getNodeColor); connect (ui->nodeNumbersChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getNodeNumbersVisibility); connect (ui->nodeNumbersInsideChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getNodeNumbersInside); connect (ui->nodeNumberColorBtn, &QToolButton::clicked, this, &DialogSettings::getNodeNumberColor); connect(ui->nodeNumberSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeNumberSize(int)) ); connect(ui->nodeNumberDistanceSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeNumberDistance(int)) ); connect (ui->nodeLabelsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getNodeLabelsVisibility); connect(ui->nodeLabelSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeLabelSize(int)) ); connect (ui->nodeLabelColorBtn, &QToolButton::clicked, this, &DialogSettings::getNodeLabelColor); connect(ui->nodeLabelDistanceSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeLabelDistance(int)) ); connect (ui->edgesChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgesVisibility); connect (ui->edgeArrowsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgeArrowsVisibility); connect (ui->edgeColorBtn, &QToolButton::clicked, this, &DialogSettings::getEdgeColor); connect (ui->edgeColorNegativeBtn, &QToolButton::clicked, this, &DialogSettings::getEdgeColorNegative); connect (ui->edgeColorZeroBtn, &QToolButton::clicked, this, &DialogSettings::getEdgeColorZero); connect (ui->edgeShapeRadioStraightLine, &QRadioButton::clicked, this, &DialogSettings::getEdgeShape); connect (ui->edgeShapeRadioBezier, &QRadioButton::clicked, this, &DialogSettings::getEdgeShape); connect(ui->edgeOffsetFromNodeSpin, SIGNAL(valueChanged(int)), this, SLOT(getEdgeOffsetFromNode(int)) ); connect (ui->edgeWeightNumbersChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgeWeightNumbersVisibility); connect (ui->edgeLabelsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgeLabelsVisibility); connect ( ui->buttonBox, &QDialogButtonBox::accepted, this, &DialogSettings::validateSettings ); } /** * @brief DialogSettings::validateSettings * Validates form data and signals saveSettings to MW */ void DialogSettings::validateSettings(){ emit saveSettings(); } void DialogSettings::getDataDir(){ QString m_dataDir = QFileDialog::getExistingDirectory(this, tr("Select a new data dir"), ui->dataDirEdit->text(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (!m_dataDir.isEmpty()) { if (!m_dataDir.endsWith( QDir::separator() )) { m_dataDir += QDir::separator(); } ui->dataDirEdit->setText(m_dataDir); m_appSettings["dataDir"]= m_dataDir; emit setReportsDataDir (m_dataDir); } } /** * @brief Get the real number precision * @param size */ void DialogSettings::getReportsRealNumberPrecision( const int &precision) { m_appSettings["initReportsRealNumberPrecision"]= QString::number(precision); emit setReportsRealNumberPrecision(precision); } /** * @brief Get the real number precision * @param size */ void DialogSettings::getReportsLabelsLength( const int &length) { m_appSettings["initReportsLabelsLength"]= QString::number(length); emit setReportsLabelLength(length); } /** * @brief Gets the chart type in reports */ void DialogSettings::getReportsChartType(const int &type){ //if (!type.isEmpty() ) { qDebug() << "DialogSettings::getReportsChartType() - type: " << type; m_appSettings["initReportsChartType"] = QString::number(type-1); emit setReportsChartType(type-1); //} } /** * @brief DialogSettings::getCanvasBgColor * Opens a QColorDialog for the user to select a new bg color */ void DialogSettings::getCanvasBgColor(){ m_bgColor = QColorDialog::getColor( m_bgColor, this, tr("Select a background color") ); if ( m_bgColor.isValid()) { m_pixmap.fill(m_bgColor); ui->bgColorButton->setIcon(QIcon(m_pixmap)); ui->bgImageSelectEdit->setText(""); m_appSettings["initBackgroundColor"] = m_bgColor.name(); m_appSettings["initBackgroundImage"] = ""; emit setCanvasBgColor(m_bgColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getCanvasBgImage */ void DialogSettings::getCanvasBgImage(){ QString m_bgImage = QFileDialog::getOpenFileName( this, tr("Select a background image "), (m_appSettings)["lastUsedDirPath"], tr("All (*);;PNG (*.png);;JPG (*.jpg)") ); if (!m_bgImage.isEmpty() ) { (m_appSettings)["initBackgroundImage"] = m_bgImage ; ui->bgImageSelectEdit->setText((m_appSettings)["initBackgroundImage"]); emit setCanvasBgImage(); } else { //user pressed Cancel } } /** * @brief Gets Canvas Update Mode */ void DialogSettings::getCanvasUpdateMode(const QString &mode){ if (!mode.isEmpty() ) { m_appSettings["canvasUpdateMode"] = mode; emit setCanvasUpdateMode(mode); } } /** * @brief Gets canvas Index Method */ void DialogSettings::getCanvasIndexMethod(const QString &method){ if (!method.isEmpty() ) { m_appSettings["canvasIndexMethod"] = method; emit setCanvasIndexMethod(method); } } /** * @brief DialogSettings::getNodeColor * * Opens a QColorDialog for the user to select a new node color */ void DialogSettings::getNodeColor(){ m_nodeColor = QColorDialog::getColor( m_nodeColor, this, tr("Select a color for Nodes") ); if ( m_nodeColor.isValid()) { m_pixmap.fill(m_nodeColor); ui->nodeColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initNodeColor"] = m_nodeColor.name(); emit setNodeColor(m_nodeColor); } else { // user pressed Cancel } } /** * @brief Gets the index of the selected shape in the ui::nodeShapeComboBox * If custom shape, it enables and sets the nodeIconSelectEdit/nodeIconSelectButton * Then it emits setNodeShape * @param shape */ void DialogSettings::getNodeShapeIndex(const int &shape){ m_appSettings["initNodeShape"] = m_shapeList[shape]; qDebug()<< "DialogSettings::getNodeShapeIndex() - " "new default shape" << m_shapeList[shape]; if ( shape == NodeShape::Custom ) { // enable textedit and file button and raise file dialog ui->nodeIconSelectButton->setEnabled(true); ui->nodeIconSelectEdit->setEnabled(true); ui->nodeIconSelectEdit->setText (m_appSettings["initNodeIconPath"]); if (!m_appSettings["initNodeIconPath"].isEmpty()) { emit setNodeShape(0, m_appSettings["initNodeShape"], m_appSettings["initNodeIconPath"]); } else { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->nodeIconSelectButton->setGraphicsEffect(effect); ui->nodeIconSelectEdit->setGraphicsEffect(effect); (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } } else { ui->nodeIconSelectButton->setEnabled(false); ui->nodeIconSelectEdit->setEnabled(false); ui->nodeIconSelectEdit->setText (""); ui->nodeIconSelectButton->setGraphicsEffect(0); ui->nodeIconSelectEdit->setGraphicsEffect(0); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); // emit signal // instead of empty iconPath string, we always emit iconPathList[shape] // this is to allow passing the path to built-in icons i.e. hearts. emit setNodeShape(0, m_appSettings["initNodeShape"], m_iconList[shape] ); } } void DialogSettings::getNodeIconFile(){ QString m_nodeIconFile = QFileDialog::getOpenFileName(this, tr("Select a new icon"), ui->nodeIconSelectEdit->text(), tr("Images (*.png *.jpg *.jpeg *.svg);;All (*.*)") ); if (!m_nodeIconFile.isEmpty()) { qDebug() << m_nodeIconFile; ui->nodeIconSelectEdit->setText(m_nodeIconFile); m_appSettings["initNodeIconPath"]= m_nodeIconFile; ui->nodeShapeComboBox->setItemIcon(NodeShape::Custom, QIcon(m_nodeIconFile)); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); emit setNodeShape(0, m_appSettings["initNodeShape"], m_appSettings["initNodeIconPath"]); } else { // user pressed Cancel ? // stop if ( ui->nodeIconSelectEdit->text().isEmpty() ) { (ui->buttonBox) -> button (QDialogButtonBox::Cancel) -> setDefault(true); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } } } /** * @brief DialogSettings::getNodeSize * @param size */ void DialogSettings::getNodeSize( int size) { m_appSettings["initNodeSize"]= QString::number(size); emit setNodeSize(size, false); } /** * @brief DialogSettings::getNodeNumbersVisibility * @param toggle */ void DialogSettings::getNodeNumbersVisibility (bool toggle){ m_appSettings["initNodeNumbersVisibility"]= (toggle) ? "true" : "false"; emit setNodeNumbersVisibility(toggle); } /** * @brief DialogSettings::getNodeNumbersInside * @param toggle */ void DialogSettings::getNodeNumbersInside(bool toggle) { m_appSettings["initNodeNumbersInside"]= (toggle) ? "true" : "false"; ui->nodeNumbersChkBox->setChecked(true); ui->nodeNumberDistanceSpin->setEnabled(!toggle); ui->nodeNumberSizeSpin->setValue( ( (toggle) ? 0 : 7) ); emit setNodeNumbersInside(toggle); } /** * @brief DialogSettings::getNodeNumberSize * @param size */ void DialogSettings::getNodeNumberSize( const int size) { m_appSettings["initNodeNumberSize"]= QString::number(size); emit setNodeNumberSize(0, size, false); } /** * @brief DialogSettings::getNodeNumberDistance * @param distance */ void DialogSettings::getNodeNumberDistance(const int distance) { m_appSettings["initNodeNumberDistance"]= QString::number(distance); emit setNodeNumberDistance(0, distance); } /** * @brief DialogSettings::getNodeNumberColor * * Opens a QColorDialog for the user to select a new node number color */ void DialogSettings::getNodeNumberColor(){ m_nodeNumberColor = QColorDialog::getColor( m_nodeNumberColor, this, tr("Select color for Node Numbers") ); if ( m_nodeNumberColor.isValid()) { m_pixmap.fill(m_nodeNumberColor); ui->nodeNumberColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initNodeNumberColor"] = m_nodeNumberColor.name(); emit setNodeNumberColor(0,m_nodeNumberColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getNodeLabelsVisibility * @param toggle */ void DialogSettings::getNodeLabelsVisibility (bool toggle){ m_appSettings["initNodeLabelsVisibility"]= (toggle) ? "true" : "false"; emit setNodeLabelsVisibility(toggle); } /** * @brief DialogSettings::getNodeLabelColor * * Opens a QColorDialog for the user to select a new node Label color */ void DialogSettings::getNodeLabelColor(){ m_nodeLabelColor = QColorDialog::getColor( m_nodeLabelColor, this, tr("Select color for Node Labels") ); if ( m_nodeLabelColor.isValid()) { m_pixmap.fill(m_nodeLabelColor); ui->nodeLabelColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initNodeLabelColor"] = m_nodeLabelColor.name(); emit setNodeLabelColor(m_nodeLabelColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getNodeLabelSize * @param size */ void DialogSettings::getNodeLabelSize( const int size) { m_appSettings["initNodeLabelSize"]= QString::number(size); emit setNodeLabelSize(0, size); } /** * @brief DialogSettings::getNodeLabelDistance * @param distance */ void DialogSettings::getNodeLabelDistance(const int distance) { m_appSettings["initNodeLabelDistance"]= QString::number(distance); emit setNodeLabelDistance(0, distance); } /** * @brief DialogSettings::getEdgesVisibility * @param toggle */ void DialogSettings::getEdgesVisibility (const bool &toggle){ m_appSettings["initEdgesVisibility"]= (toggle) ? "true" : "false"; emit setEdgesVisibility(toggle); } /** * @brief DialogSettings::getEdgeArrowsVisibility * @param toggle */ void DialogSettings::getEdgeArrowsVisibility(const bool &toggle){ m_appSettings["initEdgeArrows"]= (toggle) ? "true" : "false"; emit setEdgeArrowsVisibility(toggle); } /** * @brief DialogSettings::getEdgeColor * * Opens a QColorDialog for the user to select a new edge color */ void DialogSettings::getEdgeColor(){ m_edgeColor = QColorDialog::getColor( m_edgeColor, this, tr("Select color for Edges ") ); if ( m_edgeColor.isValid()) { m_pixmap.fill(m_edgeColor); ui->edgeColorBtn->setIcon(QIcon(m_pixmap)); m_appSettings["initEdgeColor"] = m_edgeColor.name(); emit setEdgeColor(m_edgeColor, RAND_MAX); } else { // user pressed Cancel } } /** * @brief DialogSettings::getEdgeColorNegative * * Opens a QColorDialog for the user to select a new negative edge color */ void DialogSettings::getEdgeColorNegative(){ m_edgeColorNegative = QColorDialog::getColor( m_edgeColorNegative, this, tr("Select color for negative Edges") ); if ( m_edgeColorNegative.isValid()) { m_pixmap.fill(m_edgeColorNegative); ui->edgeColorNegativeBtn->setIcon(QIcon(m_pixmap)); m_appSettings["initEdgeColorNegative"] = m_edgeColorNegative.name(); emit setEdgeColor(m_edgeColorNegative, -1); } else { // user pressed Cancel } } /** * @brief DialogSettings::getEdgeColorZero * * Opens a QColorDialog for the user to select a new zero edge color */ void DialogSettings::getEdgeColorZero(){ m_edgeColorZero = QColorDialog::getColor( m_edgeColorZero, this, tr("Select color for negative Edges") ); if ( m_edgeColorZero.isValid()) { m_pixmap.fill(m_edgeColorZero); ui->edgeColorZeroBtn->setIcon(QIcon(m_pixmap)); m_appSettings["initEdgeColorZero"] = m_edgeColorZero.name(); emit setEdgeColor(m_edgeColorZero, 0); } else { // user pressed Cancel } } /** * @brief DialogSettings::getEdgeShape */ void DialogSettings::getEdgeShape(){ if ( ui->edgeShapeRadioStraightLine->isChecked () ){ m_appSettings["initEdgeShape"] = "line"; } else if ( ui->edgeShapeRadioBezier->isChecked() ){ m_appSettings["initEdgeShape"] = "bezier"; } qDebug()<< "DialogSettings::getEdgeShape() - new default shape " << m_appSettings["initEdgeShape"]; emit setEdgeShape(m_appSettings["initEdgeShape"], 0); } /** * @brief Changes the edge offset from source and target nodes * @param size */ void DialogSettings::getEdgeOffsetFromNode( int offset) { qDebug()<< "DialogSettings::getEdgeOffsetFromNode() - new offset:" << offset; m_appSettings["initEdgeOffsetFromNode"]= QString::number(offset); emit setEdgeOffsetFromNode(offset); } /** * @brief DialogSettings::getEdgeWeightNumbersVisibility * @param toggle */ void DialogSettings::getEdgeWeightNumbersVisibility(const bool &toggle){ m_appSettings["initEdgeWeightNumbersVisibility"]= (toggle) ? "true" : "false"; emit setEdgeWeightNumbersVisibility(toggle); } /** * @brief DialogSettings::getEdgeLabelsVisibility * @param toggle */ void DialogSettings::getEdgeLabelsVisibility(const bool &toggle){ m_appSettings["initEdgeLabelsVisibility"]= (toggle) ? "true" : "false"; emit setEdgeLabelsVisibility(toggle); } DialogSettings::~DialogSettings() { delete ui; } app-2.8/src/forms/dialogsettings.h000077500000000000000000000127751377436340000172770ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsettings.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGSETTINGS_H #define DIALOGSETTINGS_H #include #include namespace Ui { class DialogSettings; } class DialogSettings : public QDialog { Q_OBJECT public: explicit DialogSettings(QMap &appSettings, const QStringList &nodeShapeList, const QStringList &iconPathList, QWidget *parent = Q_NULLPTR ); ~DialogSettings(); public slots: void getDataDir(); void getReportsRealNumberPrecision(const int &precision); void getReportsLabelsLength(const int &length); void getReportsChartType(const int &type); void getCanvasBgColor(); void getCanvasBgImage(); void getCanvasUpdateMode(const QString &text); void getCanvasIndexMethod(const QString &text); void validateSettings(); void getNodeColor(); void getNodeShapeIndex(const int &shape); void getNodeIconFile(); void getNodeSize(int); void getNodeNumbersVisibility (bool toggle); void getNodeNumbersInside(bool toggle); void getNodeNumberColor(); void getNodeNumberSize(const int); void getNodeNumberDistance(const int); void getNodeLabelsVisibility (bool toggle); void getNodeLabelColor(); void getNodeLabelSize(const int); void getNodeLabelDistance(const int); void getEdgesVisibility (const bool &toggle); void getEdgeArrowsVisibility (const bool &toggle); void getEdgeColor(); void getEdgeColorNegative(); void getEdgeColorZero(); void getEdgeShape(); void getEdgeOffsetFromNode( int offset); void getEdgeWeightNumbersVisibility(const bool &toggle); void getEdgeLabelsVisibility(const bool &toggle); signals: void setReportsDataDir (const QString &dir); void setReportsRealNumberPrecision(const int &precision); void setReportsLabelLength(const int &length); void setReportsChartType(const int &type); void setStyleSheetDefault(const bool &toggle); void setProgressDialog(bool); void setToolBar(bool); void setStatusBar(bool); void setPrintLogo(bool); void setDebugMsgs(bool); void setRightPanel(bool); void setLeftPanel(bool); void setCanvasBgColor(const QColor); void setCanvasBgImage(); void setCanvasAntialiasing(bool); void setCanvasAntialiasingAutoAdjust(bool ); void setCanvasSmoothPixmapTransform(bool); void setCanvasSavePainterState(bool); void setCanvasCacheBackground(bool); void setCanvasEdgeHighlighting(bool); void setCanvasUpdateMode(const QString &text); void setCanvasIndexMethod(const QString &text); void setNodeColor(QColor); void setNodeShape(const int &num, QString , QString nodeIconPath=QString()); void setNodeSize(int, const bool &); void setNodeNumbersVisibility(bool); void setNodeNumbersInside(bool); void setNodeNumberSize(const int v, const int &size, const bool prompt); void setNodeNumberDistance(const int v, const int &); void setNodeNumberColor(const int &v, const QColor); void setNodeLabelsVisibility(const bool &); void setNodeLabelColor(const QColor); void setNodeLabelSize(const int v, const int &); void setNodeLabelDistance(const int v, const int &); void setEdgesVisibility (const bool &toggle); void setEdgeArrowsVisibility (const bool &toggle); void setEdgeColor(const QColor, const int &); void setEdgeShape(const QString, const long int); void setEdgeOffsetFromNode(const int&offset, const int &v1=0, const int &v2=0); void setEdgeWeightNumbersVisibility(const bool &toggle); void setEdgeLabelsVisibility(const bool &toggle); void saveSettings(); private: QMap &m_appSettings ; QPixmap m_pixmap; //QString m_nodeShape; QColor m_bgColor, m_nodeColor, m_nodeNumberColor, m_nodeLabelColor; QColor m_edgeColor, m_edgeColorNegative,m_edgeColorZero, m_edgeWeightNumberColor; QStringList m_shapeList; QStringList m_iconList; Ui::DialogSettings *ui; }; #endif app-2.8/src/forms/dialogsettings.ui000066400000000000000000005232441377436340000174600ustar00rootroot00000000000000 DialogSettings 0 0 600 770 600 770 800 800 Settings & Preferences 495 700 Ubuntu 0 General 430 0 Ubuntu 12 Data Exporting Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>Click the small button on the far right corner to select a folder, where all SocNetV files and reports will be saved by default. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> Save folder Ubuntu 12 Qt::Horizontal 48 20 250 0 Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>This is the path of the folder where all SocNetV files and reports will be saved. </p><p>Click the button on the right to select a new folder. </p><p>If the folder does not exist, it wil be created.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>This is the path of the folder where all SocNetV files and reports will be saved. </p><p>Click the button on the right to select a new folder. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the folder does not exist, it wil be created.</p></body></html> 60 0 Ubuntu 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>Click this button to select a folder, where all SocNetV files and reports will be saved by default. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>Click this button to select a folder, where all SocNetV files and reports will be saved by default. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the folder does not exist, it wil be created.</p></body></html> ... Ubuntu 12 Qt::Vertical 470 26 470 118 Ubuntu 12 Reports 100 0 200 16777215 Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Label Length</span></p><p>Change the length of labels <span style=" font-style:italic;">in reports</span>. Use the spin box to the right to change the number of digits SocNetV should write when generating reports with labels, i.e. node labels in centrality reports.</p><p>The length cannot be a negative value. The default value is 10.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> Labels length Ubuntu 12 Qt::Horizontal 40 30 60 0 60 16777215 Ubuntu 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Label Length</span></p><p>Sets the length of labels <span style=" font-style:italic;">in reports</span>. This value describes the number of digits SocNetV should write when generating reports with labels, i.e. node labels in centrality reports.</p><p>The length cannot be a negative value. The default value is 8.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> 6 100 0 200 16777215 Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Real Number Precision</span></p><p>Change the precision of real numbers <span style=" font-style:italic;">in reports</span>. Use the spin box on the right to select a new precision namely the number of fraction digits SocNetV should write when generating reports with real numbers, i.e. centrality reports..</p><p>The precision cannot be a negative value. The default value is 6.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> Real number precision Ubuntu 12 Qt::Horizontal 40 30 60 0 60 16777215 Ubuntu 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Real Number Precision</span></p><p>Sets the precision of real numbers <span style=" font-style:italic;">in reports</span>. This value describes the number of fraction digits SocNetV should write when generating reports with real numbers, i.e. centrality reports..</p><p>The precision cannot be a negative value. The default value is 6.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> 8 Ubuntu 12 <html><head/><body><p><span style=" font-family:'Sans Serif'; font-size:9pt; font-weight:600;">Chart Type</span></p><p><span style=" font-family:'DejaVu Sans'; font-size:10pt;">This is the chart type that is used in reports, i.e. to plot the distribution of a prominence index, such as Degree Centrality.</span></p><p><span style=" font-family:'DejaVu Sans'; font-size:10pt;">Use the select box on the right to select a chart type.</span></p><p><span style=" font-family:'DejaVu Sans'; font-size:10pt;">SocNetV supports the following chart types:</span></p><p><span style=" font-family:'DejaVu Sans'; font-size:10pt;">- Spline charts, which present data as a series of data points connected by lines. </span></p><p><span style=" font-family:'DejaVu Sans'; font-size:10pt;">- Area charts, which present data as an area bound by two lines.</span></p><p><span style=" font-family:'DejaVu Sans'; font-size:10pt;">- Bar charts, which present data as vertical bars. </span></p></body></html> Chart type Ubuntu 12 Qt::Horizontal 98 20 0 0 170 0 Ubuntu 12 <!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: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;"><span style=" font-family:'Sans Serif'; font-size:9pt; font-weight:600;">Chart Type</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'DejaVu Sans'; font-size:10pt;">Select which kind of chart type will be used in reports, i.e. to plot the </span><span style=" font-family:'DejaVu Sans'; font-size:10pt;">distribution of a </span><span style=" font-family:'DejaVu Sans'; font-size:10pt;">prominence index, such as Degree Centrality.</span></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; font-family:'DejaVu Sans'; font-size:10pt;"><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-family:'DejaVu Sans'; font-size:10pt;">SocNetV supports the following chart types:</span></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-family:'DejaVu Sans'; font-size:10pt;">- Spline charts, which present data as a series of data points connected by lines. </span></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-family:'DejaVu Sans'; font-size:10pt;">- Area charts, which present data as an area bound by two lines.</span></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-family:'DejaVu Sans'; font-size:10pt;">- Bar charts, which present data as vertical bars. </span></p></body></html> Ubuntu 12 Qt::Vertical 388 23 470 0 Ubuntu 12 Image Exporting Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">SocNetV logo on exported images</span></p><p>Enable or disable the printing of a small SocNetV logo on exported PNG and BMP network images </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">SocNetV logo on exported images</span></p><p>Enable or disable the printing of a small SocNetV logo on exported PNG and BMP network images </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Print SocNetV logo on exported Images true Ubuntu 12 Qt::Vertical 17 15 470 0 Ubuntu 12 Debugging and Progressing Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Debug Messages</span></p><p>Enable or disable debug messages to strerr. </p><p>Once you enable this and press OK, you must close and run SocNetV again from the command line / terminal, in order to see the debug messages.</p><p>Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Debug Messages</span></p><p>Enables or disable debug messages to strerr. </p><p>Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Print debug messages to command line Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Progress Bars</span></p><p>Enable or disable the application Progress Bars.</p><p>Progress Bars may appear during time-cost operations. </p><p>Enabling Progress Bars has a significant cpu cost but lets you know about the progress of a given operation.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Progress Bars</span></p><p>Enable or disable the application Progress Bars.</p><p>Progress Bars may appear during time-cost operations. </p><p>Enabling Progress Bars has a significant cpu cost but lets you know about the progress of a given operation.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show progress dialog true Ubuntu 12 Qt::Vertical 17 20 0 0 470 0 Ubuntu 12 Application Style Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Debug Messages</span></p><p>Enable or disable debug messages to strerr. </p><p>Once you enable this and press OK, you must close and run SocNetV again from the command line / terminal, in order to see the debug messages.</p><p>Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Debug Messages</span></p><p>Enables or disable debug messages to strerr. </p><p>Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Use SocNetV Stylesheet Ubuntu 12 Qt::Vertical 20 20 470 110 Ubuntu 12 Window options 8 30 551 67 Ubuntu 12 Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Control panel</span></p><p>Enable or disable the Control panel (left panel)</p><p>The Control Panel is the widget at the left of the application window, where you can find essential functions and options for Editing, Analyzing and Visualizing your network data.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Control panel</span></p><p>Enable or disable the Control panel (left panel)</p><p>The Control Panel is the widget at the left of the application window, where you can find essential functions and options for Editing, Analyzing and Visualizing your network data.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show Control panel (left panel) true Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Toolbar</span></p><p>Enable or disable the application toolbar.</p><p>The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like from here...</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Toolbar</span></p><p>Enable or disable the application toolbar.</p><p>The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like from here...</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show toolbar true Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Statistics panel</span></p><p>Enable or disable the statistics panel (right panel)</p><p>The Statistics panel is the widget at the right of the application window, where you can see statistics about the whole network such as node and edge/arc count, density etc as well as statistics about the last clicked node (in-degree, out-degree, clustering coefficient etc).</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Statistics panel</span></p><p>Enable or disable the statistics panel (right panel)</p><p>The Statistics panel is the widget at the right of the application window, where you can see statistics about the whole network such as node and edge/arc count, density etc as well as statistics about the last clicked node (in-degree, out-degree, clustering coefficient etc).</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show Statistics panel (right panel) true Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Statusbar</span></p><p>Enable or disable the application statusbar.</p><p>The statusbar is the widget at the bottom of the window, where messages appear. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Statusbar</span></p><p>Enable or disable the application statusbar.</p><p>The statusbar is the widget at the bottom of the window, where messages appear. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show statusbar true Nodes MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Default node settings</span></p><p>Any change to these settings will apply to all existing nodes immediately.</p><p>Once you press OK, these settings will be saved and they will be used in all future SocNetV sessions.</p></body></html> Node settings Ubuntu 12 <html><head/><body><p><span style=" font-weight:600;">Node color</span></p><p>Click the colored button to select a new color for all nodes.</p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node color when creating new nodes (except when loading network files which declare specific node settings).</p></body></html> Node color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Node color</span></p><p>Click this button to select a new node color for all nodes.</p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node color when creating new nodes (except when loading network files which declare specific node settings).</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node color</span></p><p>Click to select a new default node color.</p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node color.</p></body></html> ... 60 20 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node shape</span></p><p>Click on any of the following shapes to select a new node shape for all nodes. </p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the default node shape will be saved and it will be used in all future SocNetV sessions when creating new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> Node shape MS Shell Dlg 2 12 Qt::Horizontal 40 20 200 0 MS Shell Dlg 2 12 <html><head/><body><p>Select a node shape. </p><p>If you select &quot;Icon&quot; then you must click on the custom icon button (below) to select the desired icon file from your filesystem. </p></body></html> MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click the button on the right to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> Custom Icon MS Shell Dlg 2 12 Qt::Horizontal 30 28 200 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Custom Icon </span></p><p>This box only shows the path of the selected custom icon file for all network nodes. If it is empty, it means you have not selected any image yet.</p><p>Click the button on the right to select a new image to be used as custom icon in all the nodes of the network.</p><p>Valid icons are images: JPG, PNG, JPEG, SVG etc.</p><p><br/></p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>This is the path of the default background image. </p><p>Click the button on the right to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> 0 0 60 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Custom Icon </span></p><p>Click this button to select a new custom icon for all nodes of the network. </p><p>Valid icons are images: JPG, PNG, JPEG, SVG etc.</p><p>If you don't select an image file, you must select one of the default shapes from the Node Shape menu above. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click this button to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> ... MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node size</span></p><p>Use the spin box to select a new size (in pixels) for all nodes. </p><p>Actual node size will vary according to selected shape. For instance, if the selected shape is circle and the selected size is 8, then the circle will have a diameter of 16 pixels. </p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node size when creating new nodes (except when loading network files which declare specific node settings).</p></body></html> Node size MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Node size</span></p><p>Select a new size for all nodes. Actual node size will vary according to selected shape. For instance, if the selected shape is circle and the size is 8, then the circle will have a diameter of 16 pixels. </p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node size.</p></body></html> 1 8 MS Shell Dlg 2 12 Node Number settings MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node number color</span></p><p>Change the color for all node numbers. Click the colored button on the right corner to select a new color.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number color when creating new nodes.</p></body></html> Number color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Node number color</span></p><p>Click to select a new color for all node numbers.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number color when creating new nodes.</p></body></html> ... 60 20 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Number distance from node</span></p><p>Change the distance of each number from the respective node. Use the spin box on the right to select a new distance (in pixels).</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default distance from node.</p></body></html> Number distance MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Number distance from nodes</span></p><p>Select a new distance (in pixels) of numbers from the respective nodes.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default distance from node.</p></body></html> 1 8 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node numbers</span></p><p>Enable or disable displaying node numbers.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node numbers</span></p><p>Enable or disable displaying node numbers.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display node numbers MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node number font size</span></p><p>Change the font size (in pixels) of all node numbers. Use the spin box on the right to select a new font size (in pixels).</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> Number font size MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Node number size</span></p><p>Select a new font size (in pixels) for node numbers. Set it to 0 to let the program automagically select a different font size according to the node size.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node number size</span></p><p>Select a new font size (in pixels) for node numbers. Set it to 0 to let the program automagically select a different font size according to the node size.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> 8 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node numbers inside shapes</span></p><p>Enable or disable displaying node numbers inside the node shapes</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node numbers inside shapes</span></p><p>Enable or disable displaying node numbers inside the node shapes</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display node numbers inside node shapes MS Shell Dlg 2 12 Node Label settings MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Node labels</span></p><p>Enable or disable displaying labels below the nodes.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node labels</span></p><p>Enable or disable displaying labels below the nodes.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display node labels Ubuntu 12 Label color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Node label color</span></p><p>Click to select a new color for node labels.</p><p>Any change to this setting will apply to all existing node labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node label color.</p></body></html> ... 60 20 MS Shell Dlg 2 12 Label font size MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Node label size</span></p><p>Select a new font size (in pixels) for all node labels.</p><p>Any change to this setting will apply to all existing node labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node label size.</p></body></html> 1 8 MS Shell Dlg 2 12 Label distance MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Label distance from node</span></p><p>Change the distance of labels from the respective nodes.</p><p>Any change to this setting will apply to all existing node labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default label distance from node.</p></body></html> 8 labelGroup numbersGroup nodesGroup Edges MS Shell Dlg 2 12 Edge settings 260 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Edges </span></p><p>Enable or disable displaying edges.</p><p>Any change will apply to all existing edges immediately.</p><p><br/></p></body></html> Qt::LeftToRight Display edges MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Edges </span></p><p>Enable or disable displaying arrows in directed edges. Useful If you work with directional social network data. You can disable it if your network is undirected.</p><p>Any change will apply to all existing edges immediately and saved for all future sessions (until you change it again).</p><p><br/></p></body></html> Qt::LeftToRight Display edge arrows MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> Edge shape MS Shell Dlg 2 12 Qt::Horizontal 40 20 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> Straight Lines false MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> Be&zier Curves MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click the colored button on the right to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click the colored button on the right to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> Default edge color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> ... 60 20 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click the colored button at the right corner to select a new color for negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;negative&quot; edges. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click the colored button at the right corner to select a new color for negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;negative&quot; edges. Until you change it again.</p></body></html> Negative valued edge color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click this button to select a new default edge color for all negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default color for &quot;negative&quot; edges. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click this button to select a new default edge color for all negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default color for &quot;negative&quot; edges. Until you change it again.</p></body></html> ... 60 20 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Zero edge color</span></p><p>Click the colored button at the right corner to select a new color for edges with zero weights.</p><p>ATTTENTION: Zero-valued edges are being omittted during network analysis computations. This setting is available for those that have data (i.e. weighted edge lists) with zero weights on some edges and they still need to draw those edges, despite the fact that they will not be considered in any SNA computation by SocNetV.</p><p>Any change to this setting will apply to all existing &quot;zero&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;zero&quot; edges. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Zero edge color</span></p><p>Click the colored button at the right corner to select a new color for edges with zero weights.</p><p>ATTTENTION: Zero-valued edges are being omittted during network analysis computations. This setting is available for those that have data (i.e. weighted edge lists) with zero weights on some edges and they still need to draw those edges, despite the fact that they will not be considered in any SNA computation by SocNetV.</p><p>Any change to this setting will apply to all existing &quot;zero&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;zero&quot; edges. Until you change it again.</p></body></html> Zero valued edge color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Zero edge color</span></p><p>Click this button to select a new color for edges with zero weights.</p><p>ATTTENTION: Zero-valued edges are being omittted during network analysis computations. This setting is available for those that have data (i.e. weighted edge lists) with zero weights on some edges and they still need to draw those edges, despite the fact that they will not be considered in any SNA computation by SocNetV.</p><p>Any change to this setting will apply to all existing &quot;zero&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;zero&quot; edges. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Zero edge color</span></p><p>Click this button to select a new color for edges with zero weights.</p><p>ATTTENTION: Zero-valued edges are being omittted during network analysis computations. This setting is available for those that have data (i.e. weighted edge lists) with zero weights on some edges and they still need to draw those edges, despite the fact that they will not be considered in any SNA computation by SocNetV.</p><p>Any change to this setting will apply to all existing &quot;zero&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;zero&quot; edges. Until you change it again.</p></body></html> ... 60 20 MS Shell Dlg 2 12 Edge offset from node MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Edge offset from node </span></p><p>Changes the offset of each edge from each source and target nodes. rs.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions when creating new edges.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge offset from node </span></p><p>Changes the offset of each edge from each source and target nodes. rs.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions when creating new edges.</p></body></html> 1 8 6 MS Shell Dlg 2 12 Weight number settings MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Edge weight numbers</span></p><p>Enable or disable edge weight numbers. When enabled, a number will be displayed along every edge indicating the edge weight.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default when creating new edges.</p></body></html> Qt::LeftToRight Display edge weight numbers MS Shell Dlg 2 12 Weight number color MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Edge weight number color</span></p><p>Click to select a new color for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number color when creating new edges.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge weight number color</span></p><p>Click to select a new color for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number color when creating new edges.</p></body></html> ... 60 20 MS Shell Dlg 2 12 Weight number font size MS Shell Dlg 2 12 Qt::Horizontal 40 20 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Edge weight number text size</span></p><p>Click to select a new text size (in pixels) for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number size when creating new edges.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge weight number text size</span></p><p>Click to select a new text size (in pixels) for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number size when creating new edges.</p></body></html> 1 8 MS Shell Dlg 2 12 Edge label settings MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Edge labels</span></p><p>Enable or disable displaying edge labels.</p><p>Any change will apply to all existing edges immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display edge labels MS Shell Dlg 2 12 Label color MS Shell Dlg 2 12 Qt::Horizontal 40 20 false 60 25 MS Shell Dlg 2 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Default edge label color</span></p><p>Click to select a new default color for edge labels.</p><p>Any change to this setting will apply to all existing edge labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge label color.</p></body></html> ... 60 20 MS Shell Dlg 2 12 Label font size MS Shell Dlg 2 12 Qt::Horizontal 40 20 false 60 0 MS Shell Dlg 2 12 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Default edge label size</span></p><p>Select the default font size for all edge labels.</p><p>Any change to this setting will apply to all existing edge labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge label size.</p></body></html> 8 Canvas MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Canvas Background</span></p><p>In this section, there are general settings for the canvas, such as the background color or image.</p><p><br/></p><p><br/></p><p><br/></p></body></html> Canvas Background MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Background color</span></p><p>Click the button on the right to select a new background color. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> Background color DejaVu Sans 12 Qt::Horizontal 40 20 0 0 60 0 DejaVu Sans 12 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Background color</span></p><p>Click this button to select a new background color. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background color</span></p><p>Click this button to select a new background color. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> ... 60 20 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click the button on the right to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> Background Image MS Shell Dlg 2 12 Qt::Horizontal 30 28 200 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>This is the path of the default background image. </p><p>Click the button on the right to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>This is the path of the default background image. </p><p>Click the button on the right to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> 0 0 60 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click this button to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click this button to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> ... MS Shell Dlg 2 12 Qt::Vertical 20 67 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Performance Settings</span></p><p>The following options influence the performance of the SocNetV canvas, inside which nodes and edges are being drawn. They affect the look of nodes and edges as well the precision and speed of the graphics engine.</p><p>Most of these settings have noticeable effects only in large networks. For instance, if you have a network with over 3000 actors and 10000 edges you might want to disable Antialiasing, Antialiasing Auto-adjustment and Smooth Pixmap Transformation while enabling Cache Background. </p><p>Also, if you need to visually interact with edges in a large network, you can experiment with the Update Mode setting to find which is suitable for your workload. The default setting &quot;Full&quot; means that the canvas is fully redrawn every time you make a small change.</p></body></html> Performance settings MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Anti-Aliasing</span></p><p>Enable or disable Anti-Aliasing. If enabled, the graphics engine will antialias edges of primitives if possible. </p><p>Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Anti-Aliasing</span></p><p>Enable or disable Anti-Aliasing.</p><p>Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> Qt::LeftToRight Antialiasing MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Antialiasing Auto-Adjustment</span></p><p>Enable or disable antialiasing auto-adjustment of exposed areas. Items that render antialiased lines on the boundaries of their bounding rectangle can end up rendering parts of the line outside. To prevent rendering artifacts, the graphics engine expands all exposed regions by 2 pixels in all directions. </p><p>If you disable this, SocNetV will no longer perform these adjustments, minimizing the areas that require redrawing, which improves performance. A common side effect is that items that do draw with antialiasing can leave painting traces behind on the scene as they are moved.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Antialiasing Auto-Adjustment</span></p><p>Enable or disable antialiasing auto-adjustment of exposed areas. Items that render antialiased lines on the boundaries of their bounding rectangle can end up rendering parts of the line outside. To prevent rendering artifacts, the graphics engine expands all exposed regions by 2 pixels in all directions. </p><p>If you disable this, SocNetV will no longer perform these adjustments, minimizing the areas that require redrawing, which improves performance. A common side effect is that items that do draw with antialiasing can leave painting traces behind on the scene as they are moved.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> Qt::LeftToRight Antialising Auto-Adjustment MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Smooth pixmap transformations</span></p><p>Enable or disable smooth pixmap transformations. </p><p>If enabled, the engine will use a smooth pixmap transformation algorithm (such as bilinear) rather than nearest neighbor.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Smooth pixmap transformations</span></p><p>Enable or disable smooth pixmap transformations. </p><p>If enabled, the engine will use a smooth pixmap transformation algorithm (such as bilinear) rather than nearest neighbor.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> Qt::LeftToRight Smooth Pixmap Transformation MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Save Painter State</span></p><p>When rendering, the graphics engine can protect (&quot;save&quot;) the painter state when rendering the background or foreground, and when rendering each item. This allows us to leave the painter in an altered state. </p><p>However, if the items consistently do restore the state, you should disable this option to prevent the application from doing the same.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Save Painter State</span></p><p>When rendering, the graphics engine can protect (&quot;save&quot;) the painter state when rendering the background or foreground, and when rendering each item. This allows us to leave the painter in an altered state. </p><p>However, if the items consistently do restore the state, you should disable this option to prevent the application from doing the same.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> Qt::LeftToRight Save Painter State MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Cache Background</span></p><p>Enable or disable caching of the canvas background. </p><p>The graphics engine can cache pre-rendered content in a pixmap, which is then drawn onto the viewport. The purpose of such caching is to speed up the total rendering time for areas that are slow to render (i.e. texture, gradient and alpha blended backgrounds). </p><p>If enabled, the canvas background is cached by allocating one pixmap with the full size of the viewport. The cache is invalidated every time the view is transformed. However, when scrolling, only partial invalidation is required. </p><p>If not enabled, nothing is cached and all painting is done directly onto the viewport.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. <br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Cache Background</span></p><p>Enable or disable caching of the canvas background. </p><p>The graphics engine can cache pre-rendered content in a pixmap, which is then drawn onto the viewport. The purpose of such caching is to speed up the total rendering time for areas that are slow to render (i.e. texture, gradient and alpha blended backgrounds). </p><p>If enabled, the canvas background is cached by allocating one pixmap with the full size of the viewport. The cache is invalidated every time the view is transformed. However, when scrolling, only partial invalidation is required. </p><p>If not enabled, nothing is cached and all painting is done directly onto the viewport.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. <br/></p></body></html> Qt::LeftToRight Cache Background MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Edge highlighting</span></p><p>Enable or disable highlighting of selected edges.</p><p>By default, SocNetV hughlights the edges you select with the mouse, as well as all edges connected to the selected node. Although a useful feature, this can slow down the application responsiveness when the network consists of thousand nodes and edges. </p><p>If disabled, selected edges will not be highlighted.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Cache Background</span></p><p>Enable or disable caching of the canvas background. </p><p>The graphics engine can cache pre-rendered content in a pixmap, which is then drawn onto the viewport. The purpose of such caching is to speed up the total rendering time for areas that are slow to render (i.e. texture, gradient and alpha blended backgrounds). </p><p>If enabled, the canvas background is cached by allocating one pixmap with the full size of the viewport. The cache is invalidated every time the view is transformed. However, when scrolling, only partial invalidation is required. </p><p>If not enabled, nothing is cached and all painting is done directly onto the viewport.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. <br/></p></body></html> Qt::LeftToRight Edge Highlighting MS Shell Dlg 2 12 Qt::Vertical 20 40 110 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-family:'Sans Serif'; font-size:9pt; font-weight:600;">Update Mode</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">Use the drop-down menu on the right to select the update mode when the canvas changes. Usually you do not need to modify this property, but there are some cases where doing so can improve rendering performance. </span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">The default value is Full, where the graphics engine will update the entire canvas when some of its contents change.</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Full</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">When any visible part of the canvas changes or is reexposed, the engine will update the entire canvas. This approach is fastest when the engine spends more time figuring out what to draw than it would spend drawing (e.g., when very many small items are repeatedly updated). This is the default setting as it is the fastest with network of thousands of edges.</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Minimal</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">The engine will determine the minimal region that requires a redraw, minimizing the time spent drawing by avoiding a redraw of areas that have not changed. Although this approach provides the best performance in general, if there are many small visible changes on the scene, the engine might end up spending more time finding the minimal approach than it will spend drawing.</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Bounding Rectangle </span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">The bounding rectangle of all changes in the canvas will be redrawn. This mode has the advantage that the engine searches only one region for changes, minimizing time spent determining what needs redrawing. The disadvantage is that areas that have not changed also need to be redrawn.</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">None</span></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">The engine will never update the canvas when something changes; This mode disables all item changes. Normally, you don't want to use this!</span></p></body></html> Update mode DejaVu Sans 12 Qt::Horizontal 98 20 0 0 185 0 DejaVu Sans 12 <!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: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;"><span style=" font-family:'Sans Serif'; font-size:9pt; font-weight:600;">Update Mode</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt;">Select how to update areas of the canvas that have been reexposed or changed. Usually you do not need to modify this property, but there are some cases where doing so can improve rendering performance. </span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt;">The default value is Full, where the graphics engine will update the entire canvas when some of its contents change.</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Full</span></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; font-family:'Sans Serif'; font-size:9pt; font-weight:600;"><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-family:'Sans Serif'; font-size:9pt;">When any visible part of the canvas changes or is reexposed, the engine will update the entire canvas. This approach is fastest when the engine spends more time figuring out what to draw than it would spend drawing (e.g., when very many small items are repeatedly updated). This is the default setting as it is the fastest with network of thousands of edges.</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Minimal</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt;">The engine will determine the minimal region that requires a redraw, minimizing the time spent drawing by avoiding a redraw of areas that have not changed. Although this approach provides the best performance in general, if there are many small visible changes on the scene, the engine might end up spending more time finding the minimal approach than it will spend drawing.</span></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; font-family:'Sans Serif'; font-size:9pt;"><br /></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Bounding Rectangle </span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt;">The bounding rectangle of all changes in the canvas will be redrawn. This mode has the advantage that the engine searches only one region for changes, minimizing time spent determining what needs redrawing. The disadvantage is that areas that have not changed also need to be redrawn.</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">None</span></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; font-family:'Sans Serif'; font-size:9pt;"><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-family:'Sans Serif'; font-size:9pt;">The engine will never update the canvas when something changes; This mode disables all item changes. Normally, you don't want to use this!</span></p></body></html> 110 0 MS Shell Dlg 2 12 <html><head/><body><p><span style=" font-weight:600;">Index Method</span></p><p>Use the drop-down menu on the right to select the indexing algorithm of the graphics engine for managing positional information about items on the canvas.</p><p><span style=" font-weight:600; font-style:italic;">BspTreeIndex</span><span style=" font-style:italic;">: </span>A Binary Space Partitioning tree is applied. All item location algorithms are of an order close to logarithmic complexity, by making use of binary search. Adding, moving and removing items is logarithmic. This approach is best for static scenes (i.e., scenes where most items do not move).</p><p><span style=" font-weight:600; font-style:italic;">NoIndex</span><span style=" font-style:italic;">: </span>No index is applied. Item location is of linear complexity, as all items on the scene are searched. Adding, moving and removing items, however, is done in constant time. This approach is ideal for dynamic scenes, where many items are added, moved or removed continuously.</p></body></html> Index Method DejaVu Sans 12 Qt::Horizontal 98 20 0 0 185 0 DejaVu Sans 12 <!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: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;"><span style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:600;">Index Method</span></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; font-family:'DejaVu Sans'; font-size:10pt;"><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-family:'DejaVu Sans'; font-size:10pt;">Select one of the indexing algorithms the graphics engine provides for managing positional information about items on the canvas.</span></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; font-family:'DejaVu Sans'; font-size:10pt;"><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-family:'DejaVu Sans'; font-size:10pt; font-weight:600; font-style:italic;">BspTreeIndex</span><span style=" font-family:'DejaVu Sans'; font-size:10pt; font-style:italic;">: </span><span style=" font-family:'DejaVu Sans'; font-size:10pt;">A Binary Space Partitioning tree is applied. All item location algorithms are of an order close to logarithmic complexity, by making use of binary search. Adding, moving and removing items is logarithmic. This approach is best for static scenes (i.e., scenes where most items do not move).</span></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-family:'DejaVu Sans'; font-size:10pt;"> </span></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-family:'DejaVu Sans'; font-size:10pt; font-weight:600; font-style:italic;">NoIndex</span><span style=" font-family:'DejaVu Sans'; font-size:10pt; font-style:italic;">: </span><span style=" font-family:'DejaVu Sans'; font-size:10pt;">No index is applied. Item location is of linear complexity, as all items on the scene are searched. Adding, moving and removing items, however, is done in constant time. This approach is ideal for dynamic scenes, where many items are added, moved or removed continuously.</span></p></body></html> Ubuntu 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogSettings accept() 227 281 157 274 buttonBox rejected() DialogSettings reject() 295 287 286 274 app-2.8/src/forms/dialogsimilaritymatches.cpp000077500000000000000000000063641377436340000215220ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsimilaritymatches.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogsimilaritymatches.h" #include #include DialogSimilarityMatches::DialogSimilarityMatches (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); matrixList << "Adjacency" << "Distances"; variablesLocationList << "Rows" << "Columns" << "Both"; measureList << "Simple / Exact matching" <<"Jaccard index" <<"Hamming distance" <<"Cosine similarity" <<"Euclidean distance"; ui.matrixSelect -> insertItems( 1, matrixList ); (ui.variablesLocationSelect) -> insertItems( 1, variablesLocationList ); (ui.measureSelect) -> insertItems( 1, measureList ); (ui.diagonalCheckBox)->setChecked(false); } void DialogSimilarityMatches::getUserChoices(){ qDebug()<< "DialogSimilarityMatches: gathering Data!..."; QString matrix = (ui.matrixSelect) ->currentText(); QString varLocation = (ui.variablesLocationSelect) ->currentText(); QString measure = (ui.measureSelect)->currentText(); bool diagonal = (ui.diagonalCheckBox)->isChecked(); qDebug()<< "DialogSimilarityMatches: user selected: " << matrix << varLocation << measure; emit userChoices( matrix, varLocation, measure, diagonal ); } void DialogSimilarityMatches::on_buttonBox_accepted() { this->getUserChoices(); this->accept(); } void DialogSimilarityMatches::on_buttonBox_rejected() { this->reject(); } DialogSimilarityMatches::~DialogSimilarityMatches(){ matrixList.clear(); variablesLocationList.clear(); } app-2.8/src/forms/dialogsimilaritymatches.h000077500000000000000000000044061377436340000211620ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsimilaritymatches.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGSIMILARITYMATCHES_H #define DIALOGSIMILARITYMATCHES_H #include #include "ui_dialogsimilaritymatches.h" class DialogSimilarityMatches: public QDialog { Q_OBJECT public: DialogSimilarityMatches (QWidget *parent = Q_NULLPTR); ~DialogSimilarityMatches(); public slots: void getUserChoices(); signals: void userChoices(const QString &matrix, const QString &varLocation, const QString &method, const bool &diagonal); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogSimilarityMatches ui; QStringList matrixList, variablesLocationList, measureList; }; #endif app-2.8/src/forms/dialogsimilaritymatches.ui000066400000000000000000000203061377436340000213420ustar00rootroot00000000000000 DialogSimilarityMatches true 0 0 700 390 0 0 700 390 800 600 Similarity: Matches Variables in: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span>Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors. In this case, the input matrix is expanded by listing row vectors followed by column vectors. This is useful only when you have directed data.</p><p><br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Matching measure: 200 0 Qt::StrongFocus <html><head/><body><p>Select a matching method. </p><p><span style=" font-weight:600;">Exact Matches</span>: Examines pairs of actors for exact tie or distance matches (present or absent) to other actors and returns a proportion to their overall ties. </p><p><span style=" font-weight:600;">Positive Matches (Jaccard/Co-citation)</span>: Looks for same ties/distances reported by both actors. Returns the ratio to the total number of ties reported.</p><p><span style=" font-weight:600;">Hamming distance</span>: Reports the number of ties/distances to other actors which differ between each pair of actors.</p><p><span style=" font-weight:600;">Cosine similarity</span>: Computes the pair-wise similarity of actors as the dot product of their tie/distance vectors divided by the magnitude of these vectors. <br/></p></body></html> 0 0 400 150 <html><head/><body><p>Compute a <span style=" font-weight:600;">similarity matrix</span>, where each element (i,j) is the pair-wise similarity score of actors i and j according to the selected &quot;matching&quot; method. For example, the &quot;Simple Matching&quot; method counts the number of times that actors i and j have the same tie / distance (present or absent) to other actors. </p><p>Select input matrix and where the &quot;variables&quot; are. For instance, select Adjacency matrix and Rows to correlate the outbound ties between all pairs of actors. Select Both to correlate the both inbound and outbound ties. Hover over &quot;Matching measure&quot; select box for more info on each method.</p></body></html> Qt::RichText true 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 12 Qt::Vertical 20 20 Input matrix: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Adjacency:</span> Use the active network's adjacency matrix as input to correlate ties between all pairs of actors. </p><p><span style=" font-weight:600;">Distances:</span> Use the active network's geodesic distances matrix to correlate distances between all pairs of actors.</p></body></html> Qt::Vertical 20 15 app-2.8/src/forms/dialogsimilaritypearson.cpp000077500000000000000000000056301377436340000215400ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsimilaritypearson.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "dialogsimilaritypearson.h" #include #include DialogSimilarityPearson::DialogSimilarityPearson (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); matrixList << "Adjacency" << "Distances"; variablesLocationList << "Rows" << "Columns" << "Both"; (ui.matrixSelect) -> insertItems( 1, matrixList ); (ui.variablesLocationSelect) -> insertItems( 1, variablesLocationList ); (ui.diagonalCheckBox)->setChecked(false); } void DialogSimilarityPearson::getUserChoices(){ qDebug()<< "DialogSimilarityPearson: gathering Data!..."; QString matrix = (ui.matrixSelect) ->currentText(); QString varLocation = (ui.variablesLocationSelect) ->currentText(); bool diagonal = (ui.diagonalCheckBox)->isChecked(); qDebug()<< "DialogSimilarityPearson: user selected: " << matrix << varLocation; emit userChoices( matrix, varLocation,diagonal ); } void DialogSimilarityPearson::on_buttonBox_accepted() { this->getUserChoices(); this->accept(); } void DialogSimilarityPearson::on_buttonBox_rejected() { this->reject(); } DialogSimilarityPearson::~DialogSimilarityPearson(){ matrixList.clear(); variablesLocationList.clear(); } app-2.8/src/forms/dialogsimilaritypearson.h000077500000000000000000000043151377436340000212040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsimilaritypearson.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGSIMILARITYPEARSON_H #define DIALOGSIMILARITYPEARSON_H #include #include "ui_dialogsimilaritypearson.h" class DialogSimilarityPearson: public QDialog { Q_OBJECT public: DialogSimilarityPearson (QWidget *parent = Q_NULLPTR); ~DialogSimilarityPearson(); public slots: void getUserChoices(); signals: void userChoices(const QString &matrix, const QString &varLocation, const bool &diagonal); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogSimilarityPearson ui; QStringList matrixList, variablesLocationList; }; #endif app-2.8/src/forms/dialogsimilaritypearson.ui000066400000000000000000000147761377436340000214030ustar00rootroot00000000000000 DialogSimilarityPearson true 0 0 700 350 0 0 700 350 800 600 Pearson Correlations Input matrix: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Adjacency:</span> Use the active network's adjacency matrix as input to correlate ties between all pairs of actors. </p><p><span style=" font-weight:600;">Distances:</span> Use the active network's geodesic distances matrix to correlate distances between all pairs of actors.</p></body></html> Variables in: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span>Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors. In this case, the input matrix is expanded by listing row vectors followed by column vectors. This is useful only when you have directed data.</p><p><br/></p></body></html> 12 Qt::Vertical 20 20 0 0 400 150 <html><head/><body><p>Compute <span style=" font-weight:600;">Pearson Product Moment Correlation Coefficients</span> (PPMCC) between the rows, the columns or both of a social network matrix (adjacency or distance matrix). The result is a <span style=" font-weight:600;">correlation matrix </span>containing the correlation coefficients between each variable (i.e. row) and the others. This might be useful if you want to check the pair-wise similarity of the actors, in terms of their ties. </p><p>Select input matrix and what &quot;variables&quot; to correlate. For instance, select Adjacency matrix and Rows to correlate the outbound ties between all pairs of actors. Select Both to correlate the both inbound and outbound ties.</p></body></html> Qt::RichText true Enable to include matrix diagonal in calculations Include input matrix diagonal 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Vertical 20 20 app-2.8/src/forms/dialogsysteminfo.cpp000077500000000000000000000074171377436340000201670ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsysteminfo.cpp - description ------------------- copyright : (C) 2005-2021 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 "dialogsysteminfo.h" #include #include #include #include #include DialogSystemInfo::DialogSystemInfo (QWidget *parent) : QDialog (parent), ui(new Ui::DialogSystemInfo) { ui->setupUi(this); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui->infoTextEdit)->setFocus(); QString information; information += "QT BUILD\n\n"; information += QSysInfo::buildAbi(); information += "\n\n"; information += "SOCNETV BUILD\n\n"; information += "DirPath: " + QApplication::applicationDirPath(); information += "\n\n"; information += "YOUR SYSTEM\n\n"; information += "OS: " + QSysInfo::prettyProductName() + " Kernel: " + QSysInfo::kernelType() + " " + QSysInfo::kernelVersion() + "\n"; information += "Architecture: " + QSysInfo::currentCpuArchitecture(); information += "\n\n"; information += "YOUR SCREEN \n\n"; information += "Geometry: \n"; information += QString::number(QApplication::primaryScreen()->geometry().x()); information += " x "; information += QString::number(QApplication::primaryScreen()->geometry().y()); information += "\n\n"; information += "Size: \n"; information += QString::number(QApplication::primaryScreen()->size().width()); information += " x "; information += QString::number(QApplication::primaryScreen()->size().height()); information += "\n\n"; information += "Available Size: \n"; information += QString::number(QApplication::primaryScreen()->availableSize().width()); information += " x "; information += QString::number(QApplication::primaryScreen()->availableSize().height()); information += "\n\n"; information += "Device Pixel Ratio (the scale factor applied by the OS/Windowing system): \n"; information += QString::number(QApplication::primaryScreen()->devicePixelRatio()); information += "\n\n"; information += "Logical DPI (i.e. 144 on Windows default 150% mode): \n"; information += QString::number(QApplication::primaryScreen()->logicalDotsPerInch()); ui->infoTextEdit->setText(information); // connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); } app-2.8/src/forms/dialogsysteminfo.h000077500000000000000000000036671377436340000176370ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogsysteminfo.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 DIALOGSYSTEMINFO_H #define DIALOGSYSTEMINFO_H #include #include "ui_dialogsysteminfo.h" class DialogSystemInfo: public QDialog { Q_OBJECT public: explicit DialogSystemInfo (QWidget *parent = Q_NULLPTR); private: Ui::DialogSystemInfo *ui; }; #endif app-2.8/src/forms/dialogsysteminfo.ui000066400000000000000000000064611377436340000200150ustar00rootroot00000000000000 DialogSystemInfo 0 0 700 400 0 0 700 400 800 800 System Information Liberation Sans 12 false QFrame::NoFrame <html><head/><body><p>Use the following to provide more meaningful information in your bug reports:</p></body></html> Qt::RichText true 0 310 12 true 300 0 Sans Serif 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogSystemInfo accept() 266 390 157 274 buttonBox rejected() DialogSystemInfo reject() 334 390 286 274 app-2.8/src/forms/dialogwebcrawler.cpp000077500000000000000000000341741377436340000201240ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogwebcrawler.cpp - description ------------------- copyright : (C) 2005-2021 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 "dialogwebcrawler.h" #include #include #include #include #include DialogWebCrawler::DialogWebCrawler(QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.seedUrlEdit)->setFocus(); ui.patternsIncludedTextEdit->setText("*"); ui.patternsExcludedTextEdit->setText(""); extLinksIncluded=false; socialLinks=false; extLinks=false; intLinks=true; childLinks=true; parentLinks=false; ui.intLinksCheckBox->setChecked (intLinks); ui.childLinksCheckBox->setChecked(childLinks); ui.parentLinksCheckBox->setChecked(parentLinks); ui.extLinksIncludedCheckBox->setChecked(extLinksIncluded); ui.extLinksCheckBox->setChecked (extLinks); ui.socialLinksCheckBox->setChecked(socialLinks); ui.selfLinksCheckBox->setChecked(false); ui.waitCheckBox ->setChecked(true); connect (ui.seedUrlEdit, &QLineEdit::textChanged, this, &DialogWebCrawler::checkErrors); connect (ui.maxUrlsToCrawlSpinBox, &QSpinBox::editingFinished, this, &DialogWebCrawler::checkErrors); connect (ui.maxLinksPerPageSpinBox, &QSpinBox::editingFinished, this, &DialogWebCrawler::checkErrors); connect (ui.patternsIncludedTextEdit, &QTextEdit::textChanged, this, &DialogWebCrawler::checkErrors); connect (ui.patternsExcludedTextEdit, &QTextEdit::textChanged, this, &DialogWebCrawler::checkErrors); connect (ui.intLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect (ui.childLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect (ui.parentLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect (ui.extLinksIncludedCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect (ui.extLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect (ui.socialLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(getUserChoices()) ); } /** * @brief Checks crawler form for user input errors */ void DialogWebCrawler::checkErrors(){ qDebug()<< "DialogWebCrawler::checkErrors..."; /* FLAGS */ bool urlError = false; bool patternsInError = false; bool patternsExError = false; bool checkboxesError = false; // CHECK URL seedUrl = (ui.seedUrlEdit)->text(); qDebug()<< "DialogWebCrawler::checkErrors() initial seed url " << seedUrl << " simplifying and lowering it"; seedUrl = seedUrl.simplified().toLower() ; QUrl newUrl(seedUrl); qDebug()<< "DialogWebCrawler::checkErrors() - QUrl " << newUrl.toString() << " scheme " << newUrl.scheme() << " host " << newUrl.host() << " path " << newUrl.path(); if ( newUrl.scheme().isEmpty() || ( newUrl.scheme() != "http" && newUrl.scheme() != "https" )) { qDebug()<< "DialogWebCrawler::checkErrors() URL scheme missing " << newUrl.scheme() << " setting the default scheme (http) "; newUrl.setUrl("//" + seedUrl); newUrl.setScheme("http"); qDebug() << newUrl; } if (newUrl.path().isEmpty() ) { qDebug()<< "DialogWebCrawler::checkErrors() - seed url is a domain without a path. " "Adding default path / to seed url"; newUrl.setPath("/"); } if (! newUrl.isValid() || newUrl.host() == "" || !newUrl.host().contains(".") ) { qDebug()<< "DialogWebCrawler::checkErrors() - seedUrl not valid."; QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui.seedUrlEdit->setGraphicsEffect(effect); (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); urlError = true; } else { if (!patternsInError && !patternsExError && !checkboxesError ) { ui.seedUrlEdit->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); urlError = false; seedUrl = newUrl.toString(); qDebug()<< "DialogWebCrawler::checkErrors() final seed url " << newUrl << " scheme " << newUrl.scheme() << " host " << newUrl.host() << " path " << newUrl.path(); } } // CHECK MAX LIMITS SPIN BOXES maxLinksPerPage = (ui.maxLinksPerPageSpinBox) -> value(); maxUrlsToCrawl = (ui.maxUrlsToCrawlSpinBox) -> value(); // CHECK CHECKBOXES (AT LEAST ONE SHOULD BE ENABLED) if ( !ui.extLinksIncludedCheckBox->isChecked() && !ui.intLinksCheckBox->isChecked() ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); checkboxesError = true; QGraphicsColorizeEffect *effect1 = new QGraphicsColorizeEffect; QGraphicsColorizeEffect *effect2 = new QGraphicsColorizeEffect; effect1->setColor(QColor("red")); effect2->setColor(QColor("red")); ui.extLinksIncludedCheckBox->setGraphicsEffect(effect1); ui.intLinksCheckBox->setGraphicsEffect(effect2); ui.selfLinksCheckBox->setEnabled(false); ui.parentLinksCheckBox->setEnabled(false); ui.childLinksCheckBox->setEnabled(false); ui.selfLinksCheckBox->setChecked(false), ui.parentLinksCheckBox->setChecked(false); ui.childLinksCheckBox->setChecked(false); ui.extLinksCheckBox->setEnabled(false); ui.extLinksCheckBox->setChecked(false); ui.socialLinksCheckBox->setChecked(false); ui.socialLinksCheckBox->setEnabled(false); } else { ui.extLinksIncludedCheckBox->setGraphicsEffect(0); ui.intLinksCheckBox->setGraphicsEffect(0); if (!patternsInError && !patternsExError && !urlError ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); checkboxesError = false; intLinks = ui.intLinksCheckBox->isChecked(); extLinksIncluded = ui.extLinksIncludedCheckBox->isChecked(); extLinks = ui.extLinksCheckBox->isChecked(); socialLinks = ui.socialLinksCheckBox->isChecked(); if (!intLinks) { ui.selfLinksCheckBox->setChecked(false), ui.parentLinksCheckBox->setChecked(false); ui.childLinksCheckBox->setChecked(false); ui.selfLinksCheckBox->setEnabled(false); ui.parentLinksCheckBox->setEnabled(false); ui.childLinksCheckBox->setEnabled(false); } else { ui.selfLinksCheckBox->setEnabled(true); ui.parentLinksCheckBox->setEnabled(true); ui.childLinksCheckBox->setEnabled(true); ui.selfLinksCheckBox->setCheckable(true); ui.parentLinksCheckBox->setCheckable(true); ui.childLinksCheckBox->setCheckable(true); } if (!extLinksIncluded) { ui.extLinksCheckBox->setEnabled(false); ui.extLinksCheckBox->setChecked(false); ui.socialLinksCheckBox->setChecked(false); ui.socialLinksCheckBox->setEnabled(false); } else { ui.extLinksCheckBox->setEnabled(true); ui.socialLinksCheckBox->setEnabled(true); ui.extLinksCheckBox->setCheckable(true); ui.socialLinksCheckBox->setCheckable(true); } if (extLinks) { ui.extLinksIncludedCheckBox->setChecked(true); } } } // CHECK URL PATTERNS TO INCLUDE TEXTEDIT qDebug()<< "DialogWebCrawler::checkErrors() - checking allowed patterns "; urlPatternsIncluded = parseTextEditInput (ui.patternsIncludedTextEdit->toHtml()); if (urlPatternsIncluded.size() == 0 ) { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui.patternsIncludedTextEdit->setGraphicsEffect(effect); (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); patternsInError = true; } else { if (urlPatternsIncluded.size() == 1 && urlPatternsIncluded.at(0) =="" ) { urlPatternsIncluded.clear(); qDebug() << "DialogWebCrawler::checkErrors() - return empty urlPatterns (ALL)"; } if ( !patternsExError && !urlError && !checkboxesError) { ui.patternsIncludedTextEdit->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); patternsInError = false; } } // CHECK URL PATTERNS TO EXCLUDE TEXTEDIT qDebug()<< "DialogWebCrawler::checkErrors() - checking disallowed patterns "; urlPatternsExcluded = parseTextEditInput (ui.patternsExcludedTextEdit->toHtml()); if (urlPatternsExcluded.size() == 1 ) { if (urlPatternsExcluded.at(0) == "*") { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui.patternsExcludedTextEdit->setGraphicsEffect(effect); (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); patternsExError = true; } } else { if ( !patternsInError && !urlError && !checkboxesError) { ui.patternsExcludedTextEdit->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); patternsExError = false; } } } /** * @brief Parses HTML-formatted input string and returns a list of all strings inside

...

* @param html * @return */ QStringList DialogWebCrawler::parseTextEditInput(const QString &html){ QStringList userInputParsed; if ( ! html.isEmpty() ) { QStringList userInput ; QString data; QString str; userInput = html.split(" at ::" << data.indexOf('>',0) << endl; str = data.mid ( data.indexOf('>',0) +1, data.indexOf("

",0) - (data.indexOf('>',0) +1) ); qDebug () << "str ::" << str ; str.remove("
"); str=str.simplified(); // remove wildcards, if there are any if (str.contains("*")) { str.remove("*"); } qDebug () << "str fin ::" << str; // urls and classes cannot contain spaces... if (str.contains(" ")) { userInputParsed.clear(); qDebug () << "urls cannot contain spaces... Break." ; break; } userInputParsed << str; } } else { userInputParsed.clear(); } qDebug () << "DialogWebCrawler::parseTextEditInput() - stringlist size" << userInputParsed.size()<< endl; return userInputParsed; } /** * @brief gathers data from web crawler form */ void DialogWebCrawler::getUserChoices(){ qDebug()<< "DialogWebCrawler::getUserChoices() - Emitting" << endl << " seedUrl: " << seedUrl << endl << " maxLinksPerPage " << maxLinksPerPage << endl << " totalUrlsToCrawl " << maxUrlsToCrawl << endl << " urlPatternsIncluded" << urlPatternsIncluded << endl << " urlPatternsExcluded" << urlPatternsExcluded << endl << " linkClasses" << linkClasses << endl; emit userChoices( seedUrl, urlPatternsIncluded, urlPatternsExcluded, linkClasses, maxUrlsToCrawl, maxLinksPerPage, intLinks, ui.childLinksCheckBox->isChecked(), ui.parentLinksCheckBox->isChecked(), ui.selfLinksCheckBox->isChecked(), ui.waitCheckBox ->isChecked(), ui.extLinksIncludedCheckBox->isChecked(), ui.extLinksCheckBox->isChecked(), ui.socialLinksCheckBox ->isChecked() ); } app-2.8/src/forms/dialogwebcrawler.h000077500000000000000000000060301377436340000175570ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt dialogwebcrawler.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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_dialogwebcrawler.h" class DialogWebCrawler: public QDialog { Q_OBJECT public: explicit DialogWebCrawler (QWidget *parent = Q_NULLPTR); public slots: void checkErrors (); void getUserChoices (); QStringList parseTextEditInput(const QString &html); signals: void userChoices( const QString &seedUrl, const QStringList &, const QStringList &, const QStringList &, const int &maxNodes, const int &maxLinks, const bool &intLinks, const bool &childLinks, const bool &parentLinks, const bool &selfLinks, const bool &extLinksIncluded, const bool &extLinksCrawl, const bool &socialLinks, const bool &delayedRequests ); void webCrawlerDialogError(QString); private: Ui::DialogWebCrawler ui; QString seedUrl ; int maxLinksPerPage, maxUrlsToCrawl; bool extLinks, intLinks; bool extLinksIncluded; bool childLinks, parentLinks; bool socialLinks; QStringList linkClasses; QStringList urlPatternsIncluded; QStringList urlPatternsExcluded; }; #endif app-2.8/src/forms/dialogwebcrawler.ui000066400000000000000000000513061377436340000177500ustar00rootroot00000000000000 DialogWebCrawler 0 0 600 656 0 0 600 650 800 800 Generate network from web links Liberation Sans URL patterns to onclude <html><head/><body><p><span style=" font-weight:600;">Allowed URL Patterns</span></p><p>Enter, in separate lines, one or more URL patterns to <span style=" font-weight:600;">include</span> while crawling. For example:</p><p>example.com/pattern/*</p><p>Do not enter spaces. Leave * to crawl all urls.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Crawl Internal Links </span></p><p>If enabled, the crawler will include and map <span style=" font-weight:600;">internal links, </span> that is pages from the same domain (or, in other words, from the same host ) as the URL being parsed each time.</p><p>If you do not want to crawl internal links, disable this option.</p><p> Please note that you MUST enable either this option or the &quot;Include external links&quot; option, for the crawler to work.</p><p>Default is to crawl internal links only. </p><p>You can further refine what kind of internal links to follow with the two options below: Child links and Parent links. </p></body></html> Crawl internal links true <html><head/><body><p><span style=" font-weight:600;">Crawl child links</span></p><p>If enabled, the crawler will map <span style=" font-weight:600;">child URLs</span></p><p>A URL is a <span style=" font-style:italic;">childUrl </span>of another URL if the two URLs share the same scheme and authority, and the latter URL's path is a parent of the path of <span style=" font-style:italic;">childUrl</span>. This applies only to internal URLs.</p><p>For instance, www.socnetv.org/docs/manual.html is a child URL of www.socnetv.org/docs/ </p><p>If you don't want to crawl child URLs, disable this option. </p></body></html> Child links false <html><head/><body><p><span style=" font-weight:600;">Crawl parent links</span></p><p>If enabled, the crawler will map <span style=" font-weight:600;">parent URLs</span>. </p><p>A URL is a <span style=" font-style:italic;">parent </span>of another URL if the two URLs share the same scheme and authority, and the former URL's path is a parent of the path of the latter URL. This applies to internal URLs.</p><p>For instance, the URL www.socnetv.org/docs/ is a parent URL of www.socnetv.org/docs/manual.html</p><p>If you don't want to crawl parent links, disable this option. </p></body></html> Parent links false URL patterns to exclude <html><head/><body><p><span style=" font-weight:600;">Excluded URL Patterns</span></p><p>Enter, in separate lines, one or more URL patterns to <span style=" font-weight:600;">exclude</span> while crawling. For example:</p><p>example.com/pattern/*</p><p>Do not enter spaces. Leave blank to crawl all urls.</p></body></html> 200 0 Liberation Sans <html><head/><body><p>Set the max links inside a page to be followed and crawled by SocNetV.</p><p>Set this to zero if you don't want to have this limit. In this case SocNetV will follow and crawl every link found in a page.</p></body></html> Max links in each page to follow PreferAntialias Qt::Horizontal 150 20 60 0 Liberation Sans <html><head/><body><p>Set the max links inside a page to be followed and crawled by SocNetV.</p><p>Set this to zero if you don't want to have this limit. In this case SocNetV will follow and crawl every link found in a page.</p></body></html> 9999 0 <html><head/><body><p><span style=" font-weight:600;">Links to social media domains</span></p><p>If enabled, the crawler will map (and possibly crawl) <span style=" font-weight:600;">links to social media websites</span>, such as twitter.com.</p><p>If disabled, the crawler will diregard any link to URLs in the following domains:</p><p>facebook.com<br/>twitter.com<br/>linkedin.com<br/>instagram.com<br/>pinterest.com<br/>telegram.org<br/>telegram.me<br/>youtube.com<br/>reddit.com<br/>plus.google.com</p><p><span style=" font-weight:600;">Note</span>: You can exclude more social media or define your custom social media exclusion list by typing domains in the &quot;URL patterns to exclude&quot; text edit above.</p></body></html> Links to social media false <html><head/><body><p>If enabled the application will draw a <span style=" font-weight:600;">self-link</span> when a page contains a link to itself. </p><p>Default is not to allow self-links.</p></body></html> Allow Self-Links false Qt::Vertical 20 40 <html><head/><body><p><span style=" font-weight:600;">Include external links</span></p><p>If enabled, the crawler will map <span style=" font-weight:600;">links to external domains</span>. </p><p>For instance, if this option is enabled and you start crawling www.supersyntages.gr/ where there is a link to a page of a different domain, i.e. www.aggelies247.com/news, then a node &quot;www.aggelies247.com/news&quot; will be added to the network. </p><p>If you don't want to crawl external links at all, just disable this option. </p><p>Please note that you <span style=" font-weight:600;">MUST </span>enable either this option or the &quot;Include internal links&quot; option, for the crawler to work.</p></body></html> Include external links false <html><head/><body><p><span style=" font-weight:600;">Crawl external links</span></p><p>If enabled, the crawler will <span style=" font-weight:600;">map external links AND crawl them for new links as well.</span></p><p>For instance, if you enable this option and start crawling the page at https://www.supersyntages.gr/ where there is a link to another domain, i.e. www.linuxinsider.gr, then the crawler will visit linuxinsider.gr too to find more links. </p><p>If you don't want to crawl external links, disable this option. </p></body></html> Crawl external links false <html><head/><body><p>Wait for a random number of milliseconds (<span style=" font-weight:600;">0-1000</span>) between network requests. </p><p><br/></p><p>Use of this option is recommended, as it lightens the server load by making the requests less frequent.</p><p><br/></p><p>By default this option is enabled.</p></body></html> Delay between requests true 0 0 450 130 Liberation Sans false QFrame::NoFrame <html><head/><body><p>Use the built-in web crawler to scan the HTML code of a given initial URL (i.e. a website) and map all internal or external links to other pages found there. </p><p>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><p>Enter the initial URL below and change crawling parameters if you like.</p></body></html> Qt::RichText true Liberation Sans Initial URL Qt::Horizontal 40 20 0 0 390 22 400 24 Liberation Sans <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> 150 0 Liberation Sans <html><head/><body><p>Set the total urls to be crawled. </p><p>This is the total nodes the result network will have. </p><p>Set value to 0, if you don't want any limits...</p></body></html> Max URLs to crawl PreferAntialias Qt::Horizontal 198 20 60 0 Liberation Sans <html><head/><body><p>Set the total URLs to be crawled. </p><p>This is the <span style=" font-weight:600;">maximum nodes</span> the result network will have. </p><p>Set value to 0, if you don't want any limits...</p></body></html> 1 2000 600 true 300 0 Sans Serif Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogWebCrawler accept() 257 324 157 274 buttonBox rejected() DialogWebCrawler reject() 325 324 286 274 app-2.8/src/forms/edgeeditdialog.ui000066400000000000000000000221011377436340000173540ustar00rootroot00000000000000 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 app-2.8/src/global.h000066400000000000000000000107621377436340000143600ustar00rootroot00000000000000#ifndef GLOBAL_H #define GLOBAL_H #define SOCNETV_NAMESPACE SocNetV #ifdef SOCNETV_NAMESPACE # define SOCNETV_BEGIN_NAMESPACE namespace SOCNETV_NAMESPACE { # define SOCNETV_END_NAMESPACE } # define SOCNETV_USE_NAMESPACE using namespace SOCNETV_NAMESPACE; #else # define SOCNETV_BEGIN_NAMESPACE # define SOCNETV_END_NAMESPACE # define SOCNETV_USE_NAMESPACE #endif SOCNETV_BEGIN_NAMESPACE #ifndef M_PI_3 #define M_PI_3 (1.04719755119659774615) #endif #ifndef M_PI #define M_PI (3.14159265358979323846) #endif #ifndef M_PI_X_2 #define M_PI_X_2 (6.28318530717958647692) #endif enum NodeShape{ Box, Circle, Diamond, Ellipse, Triangle, Star, Person, PersonB, Bugs, Heart, Dice, Custom }; enum FileType { NOT_SAVED = 0, // New network not saved yet or modified network GRAPHML = 1, // .GRAPHML .XML PAJEK = 2, // .PAJ .NET ADJACENCY = 3, // .ADJ .CSV .SM GRAPHVIZ = 4, // .DOT UCINET = 5, // .DL .DAT GML = 6, // .GML EDGELIST_WEIGHTED = 7, // .CSV, .TXT, .LIST, LST, WLST EDGELIST_SIMPLE = 8, // .CSV, .TXT, .LIST, LST TWOMODE = 9, // .2SM .AFF UNRECOGNIZED =-1 // UNRECOGNIZED FILE FORMAT }; enum EdgeType { Directed = 0, Reciprocated = 1, Undirected = 2 }; enum IndexType { DC = 1, CC = 2, IRCC = 3, BC = 4, SC = 5, EC = 6, PC = 7, IC = 8, EVC = 9, DP = 10, PRP = 11, PP = 12 }; enum ChartType { None = -1, Spline = 0, Area = 1, Bars = 2 }; static const int SUBGRAPH_CLIQUE = 1; static const int SUBGRAPH_STAR = 2; static const int SUBGRAPH_CYCLE = 3; static const int SUBGRAPH_LINE = 4; static const int MATRIX_ADJACENCY = 1; static const int MATRIX_DISTANCES = 2; static const int MATRIX_DEGREE = 3; static const int MATRIX_LAPLACIAN = 4; static const int MATRIX_ADJACENCY_INVERSE = 5; static const int MATRIX_GEODESICS = 6; static const int MATRIX_REACHABILITY = 7; static const int MATRIX_ADJACENCY_TRANSPOSE = 8; static const int MATRIX_COCITATION = 9; static const int MATRIX_DISTANCES_EUCLIDEAN = 12; static const int MATRIX_DISTANCES_MANHATTAN= 13; static const int MATRIX_DISTANCES_JACCARD= 14; static const int MATRIX_DISTANCES_HAMMING= 15; static const int MATRIX_DISTANCES_CHEBYSHEV= 16; struct ClickedEdge { int v1; int v2; int type; }; typedef QPair SelectedEdge; class MyEdge { public: int source; int target; double weight; int type; MyEdge() { source=0; target=0;weight=0;type=0;} MyEdge (const int &from, const int &to, const double &w =0, const int &type=0) : source(from), target(to), weight(w), type(type) { } // Copy constructor MyEdge (const MyEdge &edge) { source = edge.source; target = edge.target; weight = edge.weight; type = edge.type; } ~MyEdge(){} }; class GraphDistance { public: int target; int distance; GraphDistance(int t, int dist) : target(t), distance(dist) { } }; // implement a min-priority queue class GraphDistancesCompare { public: bool operator()(GraphDistance& t1, GraphDistance& 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 PairVF { public: qreal value; qreal frequency; PairVF(qreal v, qreal f) : value(v), frequency(f) { } }; // implement a min-priority queue class PairVFCompare { public: bool operator()(PairVF& v1, PairVF& v2) { return v1.value > v2.value; //minimum priority // Returns true if t1 is closer than t2 // else } }; SOCNETV_END_NAMESPACE Q_DECLARE_METATYPE(SOCNETV_NAMESPACE::MyEdge) #endif // GLOBAL_H app-2.8/src/graph.cpp000077500000000000000000030451731377436340000145660ustar00rootroot00000000000000/****************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graph.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : https://socnetv.org *******************************************************************************/ /******************************************************************************* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //allows the use of RAND_MAX macro #include //for BFS queue Q #include // for randomizeThings #include "chart.h" #include "graphicsnode.h" #include "graphicsedge.h" /** * @brief Graph::Graph * constructor */ Graph::Graph(GraphicsWidget *graphicsWidget) { qRegisterMetaType("MyEdge"); m_canvas = graphicsWidget; m_totalVertices=0; m_totalEdges=0; // We do init these two vars here, because they only get their values // on MW::resizeEvent which might happen after we have started creating // nodes. // For instance, this happens when we load a network from command line... canvasWidth = 700; canvasHeight = 600; order=true; //returns true if the indexes of the list is ordered. m_graphHasChanged=false; m_graphName=""; m_curRelation=0; m_fileFormat=FileType::NOT_SAVED; m_graphIsDirected=true; m_graphIsWeighted=false; m_graphIsConnected=true; // empty/null graph is considered connected m_graphIsSymmetric=true; m_graphDensity = -1; fileName =""; calculatedGraphReciprocity = false; calculatedGraphSymmetry = false; calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices=false; calculatedVerticesList = false; calculatedVerticesSet = false; calculatedAdjacencyMatrix=false; calculatedDistances=false; calculatedIsolates = false; calculatedDP=false; calculatedDC=false; calculatedIC=false; calculatedEVC=false; calculatedCentralities=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; m_reportsDataDir = ""; m_reportsRealPrecision = 6; m_reportsLabelLength = 8; m_reportsChartType = ChartType::Spline; m_vertexClicked = 0; m_clickedEdge.source=0; m_clickedEdge.target=0; file_parser = 0; wc_parser = 0; wc_spider = 0; m_graphFileFormatExportSupported<< FileType::GRAPHML << FileType::PAJEK << FileType::ADJACENCY; randomizeThings(); htmlHead = QString("" "" "" "" "" "" "" "" "" "" "" "" "").arg(VERSION); htmlHeadLight = QString("" "" "" "" "" "" "" "" "" "" "").arg(VERSION); htmlEnd = ""; // // Signals between activeGraph and graphicsWidget // connect( graphicsWidget, SIGNAL( resized(int, int)), this, SLOT( canvasSizeSet(int,int)) ) ; connect( graphicsWidget, SIGNAL( userDoubleClickNewNode(const QPointF &) ), this, SLOT( vertexCreateAtPos(const QPointF &) ) ) ; connect( graphicsWidget, &GraphicsWidget::userSelectedItems, this,&Graph::graphSelectionChanged); connect( this, &Graph::addGuideCircle, graphicsWidget,&GraphicsWidget::addGuideCircle ) ; connect( this, SIGNAL( addGuideHLine(const double&) ), graphicsWidget, SLOT( addGuideHLine(const double&) ) ) ; connect( this, SIGNAL( setNodePos(const int &, const qreal &, const qreal &) ), graphicsWidget, SLOT( moveNode(const int &, const qreal &, const qreal &) ) ) ; connect( this,&Graph::signalNodesFound, graphicsWidget, &GraphicsWidget::setNodesMarked ); connect( this, SIGNAL( signalDrawNode( const QPointF &, const int &, const int &, const QString &, const QString &, const QString &, const QString &, const int &, const int &, const QString &, const QString &, const int &, const int & ) ), graphicsWidget, SLOT( drawNode( const QPointF &, const int &, const int &, const QString &, const QString &, const QString &, const QString &, const int &, const int &, const QString &, const QString &, const int &, const int & ) ) ) ; connect( this,&Graph::signalRemoveNode, graphicsWidget, &GraphicsWidget::removeNode ); connect( this, SIGNAL( setVertexVisibility(int, bool) ), graphicsWidget, SLOT( setNodeVisibility (int , bool) ) ); connect( this, SIGNAL( setNodeSize(const int &, const int &) ), graphicsWidget, SLOT( setNodeSize (const int &, const int &) ) ); connect( this, SIGNAL( setNodeColor(const int &, const QString &)) , graphicsWidget, SLOT( setNodeColor(const int &, const QString &) ) ); connect( this, SIGNAL( setNodeShape(const int &,const QString&, const QString &)) , graphicsWidget, SLOT( setNodeShape(const int &, const QString&,const QString &) ) ); connect( this, SIGNAL( setNodeNumberColor(const int &, QString) ), graphicsWidget, SLOT( setNodeNumberColor (const int &, QString) ) ); connect( this, SIGNAL( setNodeNumberSize(const int &, const int &) ), graphicsWidget, SLOT( setNodeNumberSize (const int &, const int &) ) ); connect( this, SIGNAL( setNodeNumberDistance(const int &, const int &) ), graphicsWidget, SLOT( setNodeNumberDistance (const int &, const int &) ) ); connect( this, &Graph::setNodeLabel , graphicsWidget, &GraphicsWidget::setNodeLabel ); connect( this,&Graph::setNodeLabelColor, graphicsWidget, &GraphicsWidget::setNodeLabelColor ); connect( this, SIGNAL( setNodeLabelSize(const int &, const int &) ), graphicsWidget, SLOT( setNodeLabelSize (const int &, const int &) ) ); connect( this, SIGNAL( setNodeLabelDistance(const int &, const int &) ), graphicsWidget, SLOT( setNodeLabelDistance (const int &, const int &) ) ); connect( this, &Graph::signalRemoveEdge, graphicsWidget,&GraphicsWidget::removeEdge); connect (this, &Graph::signalDrawEdge, graphicsWidget,&GraphicsWidget::drawEdge); connect( this, SIGNAL( setEdgeWeight(const int &, const int &, const qreal &)), graphicsWidget, SLOT( setEdgeWeight(const int &, const int &, const qreal &) ) ); connect( this, SIGNAL( signalEdgeType(const int &, const int &, const int &)), graphicsWidget, SLOT( setEdgeDirectionType(const int &, const int &, const int &) ) ); connect( this, SIGNAL( setEdgeColor(const int &, const int &, const QString &)), graphicsWidget, SLOT( setEdgeColor(const int &, const int &, const QString &) ) ); connect( this, SIGNAL( setEdgeLabel(const int &, const int &, const QString &)), graphicsWidget, SLOT( setEdgeLabel(const int &, const int &, const QString &) ) ); connect( this, SIGNAL( setEdgeVisibility (int, int, int, bool) ), graphicsWidget, SLOT( setEdgeVisibility (int, int, int, bool) ) ); connect( graphicsWidget, &GraphicsWidget::userClickedNode, this, &Graph::vertexClickedSet ); connect( graphicsWidget, &GraphicsWidget::userClickedEdge, this, &Graph::edgeClickedSet ); connect( this, SIGNAL(signalRelationChangedToGW(int)), graphicsWidget, SLOT( relationSet(int)) ) ; } void Graph::initSignalSlots() { } /** * @brief Graph::~Graph */ Graph::~Graph() { qDebug()<<"Graph::~Graph() - Calling clear()"; clear("exit"); delete file_parser; } /** Clears all vertices */ void Graph::clear(const QString &reason) { qDebug()<< "Graph::clear() - Clearing graph... " "m_graph reports size "< 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; DM.clear(); } if ( SIGMA.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing SIGMA\n\n\n"; SIGMA.clear(); } if ( sumM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing sumM\n\n\n"; sumM.clear(); } if ( invAM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing invAM\n\n\n"; invAM.clear(); } if ( AM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing AM\n\n\n"; AM.clear(); } if ( invM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing invM\n\n\n"; invM.clear(); } if ( XM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing XM\n\n\n"; XM.clear(); } if ( XSM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing XSM\n\n\n"; XSM.clear(); } if ( XRM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing XRM\n\n\n"; XRM.clear(); } m_verticesList.clear(); m_verticesSet.clear(); m_verticesIsolatedList.clear(); m_vertexPairsNotConnected.clear(); m_vertexPairsUnilaterallyConnected.clear(); influenceDomains.clear(); influenceRanges.clear(); triadTypeFreqs.clear(); //clear relations relationsClear(); relationAdd(tr(("unnamed"))); m_fileFormat=FileType::NOT_SAVED; m_graphName=""; m_totalVertices=0; m_totalEdges=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; m_vertexClicked = 0; m_clickedEdge.source=0; m_clickedEdge.target=0; order=true; //returns true if the vpositions of the list is ordered. m_graphIsDirected=true; m_graphIsWeighted=false; m_graphIsConnected=true; // empty/null graph is considered connected. m_graphIsSymmetric=true; m_graphDensity = -1; m_graphDiameter=0; m_graphAverageDistance=0; m_graphSumDistance = 0; m_graphGeodesicsCount = 0; //non zero distances calculatedGraphReciprocity = false; calculatedGraphSymmetry = false; calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices=false; calculatedVerticesList = false; calculatedVerticesSet = false; calculatedAdjacencyMatrix=false; calculatedDistances=false; calculatedIsolates = false; calculatedCentralities=false; calculatedDP=false; calculatedDC=false; calculatedIC=false; calculatedEVC=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; m_graphHasChanged=false; m_graphHasVertexCustomIcons = false; qDebug()<< "Graph::clear() - Clearing ended. m_graph size" << m_graph.size() << "Asking parser and crawler threads to terminate"; graphLoadedTerminateParserThreads("clear"); webCrawlTerminateThreads("clear"); if ( reason != "exit") { qDebug()<< "Graph::clear() - Clearing end. Emitting graphSetModified()"; graphSetModified(m_graphHasChanged,true); } } /** * @brief Called on MW resizing to update node positions and canvasWidth and canvasHeight * @param w * @param h */ void Graph::canvasSizeSet(const int w, const int h){ qreal fX = (static_cast (w)) / canvasWidth; qreal fY = (static_cast (h)) / canvasHeight; qreal newX, newY; qDebug() << "Graph::canvasSizeSet() - new size (" << w << ", " << h<<")" << "adjusting node positions, if any."; VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ newX = (*it)->x() * fX ; newY = (*it)->y() * fY ; (*it)->setX( newX ) ; (*it)->setY( newY ); emit setNodePos((*it)->name(), newX , newY); graphSetModified(GraphChange::ChangedPositions,false); } canvasWidth = w; canvasHeight= h; statusMessage(tr("Canvas size: (%1, %2)px") .arg(QString::number(canvasWidth)) .arg(QString::number(canvasHeight)) ); qDebug() << "Graph::canvasSizeSet() - finished"; } /** * @brief Graph::canvasMaxRadius * @return */ double Graph::canvasMaxRadius () const { return ( canvasHeight < canvasWidth ) ? canvasHeight / 2.0 -30 : canvasWidth/2.0 - 30; } /** * @brief Graph::canvasMinDimension * @return */ qreal Graph::canvasMinDimension() const { return ( canvasHeight < canvasWidth ) ? canvasHeight-30 : canvasWidth-30; } /** * @brief Graph::canvasVisibleX * @param x * @return * Checks if x is visible inside the canvas usable area * and if not returns an adjusted x-coordinate */ double Graph::canvasVisibleX(const double &x) const { return qMin ( canvasWidth - 50.0 , qMax (50.0 , x ) ); } /** * @brief Graph::canvasVisibleY * @param y * @return * Checks if y is visible inside the canvas usable area * and if not returns an adjusted y-coordinate */ double Graph::canvasVisibleY(const double &y) const { return qMin ( canvasHeight - 50.0 , qMax (50.0 , y ) ); } /** * @brief Graph::canvasRandomX * @return * Returns a random x-coordinate adjusted to be visible * inside the canvas usable area */ double Graph::canvasRandomX() const { qreal randX = static_cast ( rand() % static_cast (canvasWidth) ); return qMin ( canvasWidth - 30.0 , qMax ( 30.0 , randX ) ); } /** * @brief Graph::canvasRandomY * @return * Returns a random y-coordinate adjusted to be visible * inside the canvas usable area */ double Graph::canvasRandomY() const { qreal randY = static_cast ( rand() % static_cast (canvasHeight) ); return qMin ( canvasHeight - 30.0 , qMax (30.0 , randY ) ); } /** * @brief Changes m_curRelation to relNum. * If relNum==RAND_MAX, changes to last added relation. * Then calls GraphVertex::relationSet() for all enabled vertices, to disable edges * of the old relation and enable edges of the new relation * Then, if notifyMW==TRUE, it signals signalRelationChangedToGW(int), * which disables/enables the on screen edges, and * Called from MW when the user selects a relation in the combo box. * Also called from Parser * @param relNum int * @param notifyMW bool */ void Graph::relationSet(int relNum, const bool notifyMW){ qDebug() << "++ Graph::relationSet(int) to relation " << relNum << " current relation is " << m_curRelation ; if (m_curRelation == relNum ) { qDebug() << "++ Graph::relationSet(int) - same relation - END"; return; } if ( relNum < 0) { qDebug() << "++ Graph::relationSet(int) - negative relation - END "; return; } else if (relNum==RAND_MAX) { relNum=relations() -1; } else if (relNum> relations() -1) { qDebug() << "++ Graph::relationSet(int) - not existing relation - END "; return; } VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ qDebug() << "++ Graph::relationSet(int) - changing relation of vertex" << (*it)->name() << "to" << relNum; if ( ! (*it)->isEnabled() ) continue; (*it)->relationSet(relNum); } m_curRelation = relNum; // Check if isWeighted so that multiple-relation networks are properly loaded. graphIsWeighted(); if (notifyMW) { //notify MW to change combo box relation name emit signalRelationChangedToMW(m_curRelation); //notify GW to disable/enable the on screen edges. emit signalRelationChangedToGW(m_curRelation); qDebug()<<"Graph::relationSet() - Calling graphSetModified()"; graphSetModified(GraphChange::ChangedEdges); } } /** * @brief Graph::slotEditRelationPrev * Decreases the rel number of editRelationChangeCombo * which signals to Graph::relationSet() */ void Graph::relationPrev(){ qDebug() << "Graph::relationPrev()"; int relNum=m_curRelation; if (m_curRelation>0){ --relNum; relationSet(relNum); //editFilterNodesIsolatesAct->setChecked(false); } } /** * @brief Graph::slotEditRelationNext * Increases the rel number of editRelationChangeCombo * which signals to Graph::relationSet() */ void Graph::relationNext(){ qDebug() << "Graph::relationNext()"; int relNum=m_curRelation; if ( relations() >0 && relNum < relations() ){ ++relNum; relationSet(relNum); //editFilterNodesIsolatesAct->setChecked(false); } } /** * @brief Graph::relationAdd * Adds a new relation named relName * Called by file parser to add a new relation * Also called from MW. * emits signalRelationAddToMW * @param relName */ void Graph::relationAdd(const QString &relName, const bool &changeRelation) { qDebug() << "Graph::relationAdd() - relation name" << relName; m_relationsList << relName; // add new relation to MW combo box emit signalRelationAddToMW(relName, false); if (changeRelation) relationSet(); } /** * @brief Returns current relation number * @return int */ int Graph::relationCurrent(){ return m_curRelation; } /** * @brief Returns current relation * @return string current relation name */ QString Graph::relationCurrentName() const{ qDebug() << "Graph::relationCurrentName() -"; return m_relationsList.value(m_curRelation); } /** * @brief Graph::relationCurrentRename * @param newName */ void Graph::relationCurrentRename(const QString &newName, const bool ¬ifyMW) { if (newName.isEmpty()) { qDebug()<< "Graph::relationCurrentRename() - m_curRelation" <0) return m_graph.back()->name(); else return 0; } /** * @brief Graph::vertexNumberMin * Returns the name of the first vertex. Used by slotRemoveNode of MW * @return int */ int Graph::vertexNumberMin() { if (m_totalVertices>0) return m_graph.front()->name(); else return 0; } /** * @brief Graph::vertexExists * Checks if there is a specific vertex in the graph. * Returns the vpos or -1 * Complexity: O(logN) for vpos retrieval * @param vertex number * @return vertex pos or -1 */ int Graph::vertexExists(const int &v1){ qDebug () << "Graph::vertexExists() - check for number v:" << v1 << " with vpos " << vpos[v1] << " named " << m_graph[ vpos[v1] ] ->name(); if ( vpos.contains(v1) ) { if ( m_graph[ vpos[v1] ] -> name() == v1 ) { return vpos[v1]; } else{ qDebug () << "Graph::vertexExists() - error in vpos for number v:" << v1; } } return -1; } /** * @brief Checks if there is a vertex with a specific label in the graph * Returns the vpos or -1 * Complexity: O(N) * @param label * @return vpos or -1 */ int Graph::vertexExists(const QString &label){ qDebug ()<<"Graph::vertexExists() - check for label:"<< label.toUtf8() ; VList::const_iterator it; int i=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it) ->label().contains( label, Qt::CaseInsensitive ) ) { // qDebug()<< "Graph: vertexExists() at pos %i" << i; return i; } i++; } return -1; } /** * @brief Finds vertices in strList by their number * @param QStringList * @return */ bool Graph::vertexFindByNumber (const QStringList &numList) { qDebug() << "Graph::vertexFindByNumber() - searchList:" << numList; QString vStr; QList foundList; QStringList notFound; int v=-1; bool intOk=false; bool searchResult = false; for (int i = 0; i < numList.size(); ++i) { vStr = numList.at(i); v = vStr.toInt(&intOk); if (intOk) { if ( vertexExists(v) != -1 ) { qDebug() << "Graph::vertexFindByNumber() - v" << v << "exists. Adding it to list"; foundList << v; } else { qDebug() << "Graph::vertexFindByNumber() - v" << v << "does not exist. Adding it to notFound list"; notFound << vStr; } } else { qDebug() << "cannot read" << vStr; } } if ( !foundList.isEmpty() ) { searchResult = true; emit signalNodesFound(foundList); } if ( !notFound.isEmpty() ){ //emit signalNodesNotFound(notFound); } return searchResult; } /** * @brief Finds vertices by their label * @param QStringList * @return */ bool Graph::vertexFindByLabel (const QStringList &labelList) { qDebug() << "Graph::vertexFindByLabel() - list:" << labelList; QString vLabel; QList foundList; int vFoundPos = -1; QStringList notFound; bool searchResult = false; for (int i = 0; i < labelList.size(); ++i) { vLabel = labelList.at(i); if ( ( vFoundPos = vertexExists(vLabel) ) != -1 ) { qDebug() << "Graph::vertexFindByNumber() - vertex with label" << vLabel << "exists. Adding it to list"; foundList << m_graph[ vFoundPos ]->name(); } else { qDebug() << "Graph::vertexFindByNumber() - vertex with label" << vLabel << "does not exist. Adding it to notFound list "; notFound << vLabel; } } if ( !foundList.isEmpty() ) { searchResult = true; emit signalNodesFound(foundList); } if ( !notFound.isEmpty() ){ //emit signalNodesNotFound(notFound); } return searchResult; } /** * @brief Finds vertices by their index score * @param QStringList * @return */ bool Graph::vertexFindByIndexScore(const int &index, const QStringList &thresholds) { qDebug()<<"Graph::vertexFindByIndexScore() - index"<< index << "threshold list" << thresholds; QList foundList; bool searchResult = false; VList::const_iterator it; QString thresholdStr=""; bool gtThan = false; bool gtEqual = false; bool lsThan = false; bool lsEqual = false; bool convertedOk=false; qreal threshold=0; qreal score=0; //FIXME bool dropIsolates=false; bool considerWeights = true; bool inverseWeights = true; switch (index) { case 0: { // do nothing break; } case IndexType::DC : { centralityDegree(true, dropIsolates); break; } case IndexType::IRCC : { centralityClosenessIR(); break; } case IndexType::IC : { centralityInformation(); break; } case IndexType::EVC : { centralityEigenvector(true, dropIsolates); break; } case IndexType::DP : { prestigeDegree(true, dropIsolates); break; } case IndexType::PRP : { prestigePageRank(); break; } case IndexType::PP : { prestigeProximity(considerWeights, inverseWeights); break; } default: graphDistancesGeodesic(true, considerWeights, inverseWeights, dropIsolates); break; } for (int i = 0; i < thresholds.size(); ++i) { thresholdStr = thresholds.at(i); thresholdStr=thresholdStr.simplified(); gtThan = false; gtEqual = false; lsThan = false; lsEqual = false; convertedOk=false; threshold=0; if (thresholdStr.startsWith(">=")) { gtEqual = true; thresholdStr.remove(">="); qDebug()<< "Graph::vertexFindByIndexScore() - thresholdStr starts with >="; } else if (thresholdStr.startsWith(">")) { gtThan = true; thresholdStr.remove(">"); qDebug()<< "Graph::vertexFindByIndexScore() - thresholdStr starts with > "; } else if (thresholdStr.startsWith("<=")) { lsEqual = true; thresholdStr.remove("<="); qDebug()<< "Graph::vertexFindByIndexScore() - thresholdStr starts with >="; } else if (thresholdStr.startsWith("<")) { lsThan = true; thresholdStr.remove("<"); qDebug()<< "Graph::vertexFindByIndexScore() - thresholdStr starts with < "; } else { qDebug()<< "Graph::vertexFindByIndexScore() - thresholdStr does not start with > or <"; continue; } threshold = thresholdStr.toDouble(&convertedOk); if (!convertedOk) { qDebug()<< "Graph::vertexFindByIndexScore() - " "cannot convert thresholdStr to float"; continue; } else { qDebug()<< "Graph::vertexFindByIndexScore() - " "threshold"<SDC(); break; } case IndexType::CC : { score=(*it)->SCC(); break; } case IndexType::IRCC : { score=(*it)->SIRCC(); break; } case IndexType::BC : { score=(*it)->SBC(); break; } case IndexType::SC : { score=(*it)->SSC(); break; } case IndexType::EC : { score=(*it)->SEC(); break; } case IndexType::PC : { score=(*it)->SPC(); break; } case IndexType::IC : { score=(*it)->SIC(); break; } case IndexType::EVC : { score=(*it)->SEVC(); break; } case IndexType::DP : { score=(*it)->SDP(); break; } case IndexType::PRP : { score=(*it)->SPRP(); break; } case IndexType::PP : { score=(*it)->SPP(); break; } } if (gtThan) { if ( score > threshold ) { qDebug() << "Graph::vertexFindByIndexScore() - vertex" << (*it)->name() << "matches. Adding it to foundList"; foundList << (*it)->name(); } } else { if ( score < threshold ) { qDebug() << "Graph::vertexFindByIndexScore() - vertex" << (*it)->name() << "matches. Adding it to foundList"; foundList << (*it)->name(); } } } } if ( !foundList.isEmpty() ) { searchResult = true; emit signalNodesFound(foundList); } return searchResult; } /** * @brief Removes the vertex v1 from the graph * First, it removes all edges to doomed from other vertices * Then it changes the vpos of all subsequent vertices inside m_graph * Finally, it removes the vertex. * @param int v1 */ void Graph::vertexRemove(const int &v1){ qDebug() << "Graph::vertexRemove() - v: " << m_graph[ vpos[v1] ]->name() << " vpos: " << vpos[v1] << " Removing all inbound and outbound edges "; int doomedPos=vpos[v1]; //Remove links to v1 from each other vertex VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( qAbs((*it)->hasEdgeTo(v1) ) > 0) { qDebug()<< "Graph::vertexRemove() - vertex " << (*it)->name() << " has outbound Edge to "<< v1 << ". Removing it."; (*it)->edgeRemoveTo(v1); } if ( qAbs((*it)->hasEdgeFrom(v1)) > 0 ) { qDebug()<< "Graph::vertexRemove() - vertex " << (*it)->name() << " has inbound Edge from "<< v1 << ". Removing it."; (*it)->edgeRemoveFrom(v1); } } qDebug()<< "Graph::vertexRemove() - Finished with vertices. " "Update the vpos which maps vertices inside m_graph " ; int prevIndex=doomedPos; qDebug()<< "Graph::vertexRemove() - Updating vpos of all subsequent vertices "; H_Int::const_iterator it1=vpos.cbegin(); while (it1 != vpos.cend()){ if ( it1.value() > doomedPos ) { prevIndex = it1.value(); qDebug() << "Graph::vertexRemove() - vertex " << it1.key() << " had prevIndex: " << prevIndex << " > doomedPos " << doomedPos << " Setting new vpos. vpos size was: "<< vpos.size(); vpos.insert( it1.key(), --prevIndex) ; qDebug() << "Graph::vertexRemove() - vertex " << it1.key() << " new vpos: " << vpos.value( it1.key(), -666) << " vpos size now: "<< vpos.size(); } else { qDebug() << "Graph::vertexRemove() " << it1.key() << " with vpos " << it1.value() << " < doomedPos. CONTINUE"; } ++it1; } //Now remove vertex Doomed from m_graph qDebug()<< "Graph::vertexRemove() - graph vertices=size="<< vertices() << "=" << m_graph.size() << " removing vertex at vpos " << doomedPos ; m_graph.removeAt( doomedPos ) ; m_totalVertices--; qDebug()<< "Graph::vertexRemove() - Now graph vertices=size="<< vertices() << "=" << m_graph.size() << " total edges now " << edgesEnabled(); order=false; if (vertexClicked()==v1) vertexClickedSet(0); graphSetModified(GraphChange::ChangedVertices); emit signalRemoveNode(v1); } /** * @brief Called from MainWindow to enable/disable isolate vertices (with no links) * For each isolate vertex in the Graph, emits the setVertexVisibility signal * @param toggle */ void Graph::vertexIsolatedAllToggle(const bool &toggle){ qDebug() << "Graph::vertexIsolatedAllToggle() - set all isolated to" << toggle; VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( !(*it)->isIsolated() ){ continue; } else { qDebug() << "Graph::vertexIsolatedAllToggle() - vertex" << (*it)->name() << "is isolated. Toggling it and emitting setVertexVisibility signal to GW..."; (*it)->setEnabled (toggle) ; graphSetModified(GraphChange::ChangedVertices); emit setVertexVisibility( (*it)-> name(), toggle ); } } } /** * @brief Checks if vertex is isolated * @param v1 * @return */ bool Graph::vertexIsolated(const int &v1) const{ if ( m_graph[ vpos[v1] ] -> isIsolated() ) { qDebug()<<"Graph::vertexIsolated() - vertex:"<< v1 << "isolated"; return true; } qDebug()<<"Graph::vertexIsolated() - vertex:"<< v1 << "not isolated"; return false; } /** * @brief Graph::vertexPosSet * Called from MW/GW when node moves to update its position * @param v1 * @param x * @param y */ void Graph::vertexPosSet(const int &v1, const int &x, const int &y){ m_graph[ vpos[v1] ]->setX( x ); m_graph[ vpos[v1] ]->setY( y ); graphSetModified(GraphChange::ChangedPositions,false); } /** * @brief Graph::vertexPos * @param v1 * @return */ QPointF Graph::vertexPos(const int &v1) const{ return m_graph[ vpos[v1] ]->pos(); } /** * @brief Called from GW::userClickedNode(int) to update clicked vertex number and * signal signalNodeClickedInfo(node info) to MW which shows node info on the * status bar. * @param v1 */ void Graph::vertexClickedSet(const int &v1) { qDebug()<<"Graph::vertexClickedSet() - " << v1; m_vertexClicked = v1; if (v1 == 0) { emit signalNodeClickedInfo(0); } else { edgeClickedSet(0,0); emit signalNodeClickedInfo( v1, vertexPos(v1), vertexLabel(v1), vertexDegreeIn(v1), vertexDegreeOut(v1), ( vertices() < 500 ) ? clusteringCoefficientLocal(v1): 0 ); } } /** * @brief Graph::vertexClicked * @return int */ int Graph::vertexClicked() const { return m_vertexClicked; } /** * @brief Graph::vertexSizeInit * Initialization function * @param size */ void Graph::vertexSizeInit (const int size) { initVertexSize=size; } /** * @brief Changes the size.of a vertex v or all vertices if v=0 * Called from MW (i.e. user changing node properties) * @param v * @param size */ void Graph::vertexSizeSet(const int &v, const int &size) { if (v) { qDebug()<< "Graph::vertexSizeSet() - for vertex" << v <<"new size" << size; m_graph[ vpos[v] ]->setSize(size); emit setNodeSize(v, size); } else { qDebug()<< "Graph::vertexSizeSet() - for all vertices, new size" << size; vertexSizeInit(size); VList::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); } } } graphSetModified(GraphChange::ChangedVerticesMetadata); } /** * @brief Returns the size of vertex v * @param v * @return int */ int Graph::vertexSize( const int &v ) const { return m_graph[ vpos[v] ]-> size(); } /** * @brief Sets the default vertex shape and iconPath * Called by MW::initApp() * @param shape */ void Graph::vertexShapeSetDefault(const QString shape, const QString &iconPath) { initVertexShape=shape; initVertexIconPath=iconPath; if ( ! iconPath.isEmpty() ) { m_graphHasVertexCustomIcons = true; } else { m_graphHasVertexCustomIcons = false; } } /** * @brief Changes the shape and iconPath of vertex v1, or all vertices if v1=-1 * @param v1 * @param shape * @param iconPath */ void Graph::vertexShapeSet(const int &v1, const QString &shape, const QString &iconPath){ if ( v1 == -1 ) { qDebug() << "Graph::vertexShapeSet() for all vertices" << "new shape:" << shape << "iconPath:" << iconPath; vertexShapeSetDefault(shape, iconPath); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { (*it)->setShape(shape, iconPath); emit setNodeShape((*it)->name(), shape, iconPath); } } } else { qDebug() << "Graph::vertexShapeSet() for vertex:" << v1 << "new shape:" << shape << "iconPath:" <setShape(shape, iconPath); if (shape=="custom") { m_graphHasVertexCustomIcons = true; } emit setNodeShape(v1, shape, iconPath); } graphSetModified(GraphChange::ChangedVerticesMetadata); } /** * @brief Returns the shape of this vertex * @param v1 * @return */ QString Graph::vertexShape(const int &v1){ return m_graph[ vpos[v1] ]->shape(); } /** * @brief Returns the IconPath of vertex v1 * @param v1 * @return */ QString Graph::vertexShapeIconPath(const int &v1) { return m_graph[ vpos[v1] ]->shapeIconPath(); } /** * @brief Changes the color of vertex v1 * @param v1 * @param color */ void Graph::vertexColorSet(const int &v1, const QString &color){ if (v1) { qDebug()<< "Graph::vertexColorSet() - vertex"<< v1 << "new color"<< color; m_graph[ vpos[v1] ]->setColor ( color ); emit setNodeColor ( m_graph[ vpos[v1] ]-> name(), color ); } else { qDebug()<< "Graph::vertexColorSet() - for all vertices, new color"<< color; vertexColorInit(color); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexColorSet() - for all, setting vertex" << (*it)->name() << " new color" << color; (*it)->setColor(color) ; emit setNodeColor ( (*it)-> name(), color ); } } } graphSetModified(GraphChange::ChangedVerticesMetadata); } /** * @brief Graph::vertexColor * @param v1 * @return */ QColor Graph::vertexColor(const int &v1) const { return QColor ( m_graph[ vpos[v1] ] -> color() ) ; } /** * @brief Graph::vertexColorInit * default vertex color initialization * @param color */ void Graph::vertexColorInit(const QString &color){ initVertexColor=color; } /** * @brief Changes the initial color of the vertex numbers * @param color */ void Graph::vertexNumberColorInit (const QString &color) { initVertexNumberColor = color; } /** * @brief Graph::vertexColorSet * Changes the color of vertex v1 * @param v1 * @param color */ void Graph::vertexNumberColorSet(const int &v1, const QString &color){ qDebug()<< "Graph::vertexNumberColorSet() - v1: "<< v1 << " color:"<< color; if ( v1 ) { m_graph[ vpos[v1] ]->setNumberColor ( color ); emit setNodeNumberColor ( m_graph[ vpos[v1] ]-> name(), color ); } else { qDebug()<< "Graph::vertexNumberColorSet() - changing color in all node numbers"; vertexNumberColorInit(color); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberColorSet() - vertex " << (*it)->name() << " new color " << color; (*it)->setNumberColor(color) ; emit setNodeNumberColor ( (*it)-> name(), color ); } } } graphSetModified(GraphChange::ChangedVerticesMetadata); } /** * @brief Changes the initial size of vertex numbers * @param size */ void Graph::vertexNumberSizeInit (const int &size) { initVertexNumberSize = size; } /** * @brief Changes the size of vertex v number * @param v * @param size */ void Graph::vertexNumberSizeSet(const int &v, const int &size) { if (v) { qDebug() << "Graph::vertexNumberSizeSet() - for vertex"<< v <<"new number size" << size; m_graph[ vpos[v] ]->setNumberSize (size); emit setNodeNumberSize ( m_graph[ vpos[v] ]->name(), size); } else { qDebug() << "Graph::vertexNumberSizeSet() - for all vertices, new number size" << size; vertexNumberSizeInit(size); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberSizeSet() - for all, setting vertex"<< (*it)->name() << " new number size " << size; (*it)->setNumberSize(size) ; emit setNodeNumberSize ( (*it)-> name(), size); } } } graphSetModified(GraphChange::ChangedMinorOptions); } /** * @brief Changes the initial distance of vertex numbers * @param distance */ void Graph::vertexNumberDistanceInit(const int &distance) { initVertexNumberDistance = distance; } /** * @brief Changes the distance.of vertex v number from the vertex * @param v * @param size */ void Graph::vertexNumberDistanceSet(const int &v, const int &newDistance) { if (v) { qDebug() << "Graph::vertexNumberDistanceSet() - for vertex" << v << "new number distance" << newDistance; m_graph[ vpos[v] ]->setNumberDistance (newDistance); emit setNodeNumberDistance(v, newDistance); } else { qDebug() << "Graph::vertexNumberDistanceSet() - for all vertices, " "new number distance" << newDistance; vertexNumberDistanceInit(newDistance); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberDistanceSet() - for all, setting vertex" << (*it)->name() << "new number distance" << newDistance; (*it)->setNumberDistance(newDistance) ; emit setNodeNumberDistance ( (*it)-> name(), newDistance); } } } graphSetModified(GraphChange::ChangedMinorOptions); } /** * @brief Changes the label of a vertex v1 * @param v1 * @param label */ void Graph::vertexLabelSet(const int &v1, const QString &label){ qDebug()<< "Graph::vertexLabelSet() - vertex "<< v1 << "vpos " << vpos[v1] << "new label"<< label; m_graph[ vpos[v1] ]->setLabel ( label); emit setNodeLabel ( m_graph[ vpos[v1] ]-> name(), label); graphSetModified(GraphChange::ChangedVerticesMetadata); } /** * @brief Returns the label of a vertex v1 * @param v1 * @return */ QString Graph::vertexLabel(const int &v) const{ return m_graph[ vpos[v] ]->label (); } /** * @brief Graph::vertexLabelSizeInit * Changes the default size of vertex labels * @param newSize */ void Graph::vertexLabelSizeInit(int newSize) { initVertexLabelSize = newSize; } /** * @brief Changes the label size of vertex v1 or all vertices if v1=0 * @param v1 * @param size */ void Graph::vertexLabelSizeSet(const int &v1, const int &labelSize) { if (v1) { qDebug()<< "Graph::vertexLabelSizeSet() - vertex"<< v1 << "new label size "<< labelSize; m_graph[ vpos[v1] ] -> setLabelSize ( labelSize ); emit setNodeLabelSize ( v1, labelSize); } else { qDebug() << "Graph::vertexLabelSizeSet() - for all vertices, new label size" << labelSize; vertexLabelSizeInit(labelSize); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelSizeSet() - for all, set vertex" << (*it)->name() << "new label size" << labelSize; (*it)->setLabelSize(labelSize) ; emit setNodeLabelSize ( (*it)-> name(), labelSize); } } } graphSetModified(GraphChange::ChangedMinorOptions); } /** * @brief Changes the label color of vertex v1 or all vertices if v1 = 0 * @param v1 * @param color */ void Graph::vertexLabelColorSet(const int &v1, const QString &color){ if (v1) { qDebug() << "Graph::vertexLabelColorSet() - for vertex" << v1 << "new label color" << color; m_graph[ vpos[v1] ]->setLabelColor(color); emit setNodeLabelColor(v1, color); } else { qDebug() << "Graph::vertexLabelColorSet() - for all vertices, " "new label color" << color; vertexLabelColorInit(color); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelColorSet() - for all, set vertex" << v1 << "new label color" << color; (*it)->setLabelColor(color); emit setNodeLabelColor( (*it)-> name(), color); } } } graphSetModified(GraphChange::ChangedMinorOptions); } /** * @brief Graph::vertexLabelColorInit * Changes the default vertex label color * @param color */ void Graph::vertexLabelColorInit(QString color){ initVertexLabelColor=color; } /** * @brief Changes the distance.of vertex v label from the vertex * @param v * @param size */ void Graph::vertexLabelDistanceSet(const int &v, const int &newDistance) { m_graph[ vpos[v] ]->setLabelDistance (newDistance); graphSetModified(GraphChange::ChangedMinorOptions); emit setNodeLabelDistance(v, newDistance); } /** * @brief Changes the distance.of all vertex labels from their vertices * @param size */ void Graph::vertexLabelDistanceAllSet(const int &newDistance) { qDebug() << "*** Graph::vertexLabelDistanceAllSet() " << " to " << newDistance; vertexLabelDistanceInit(newDistance); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelDistanceAllSet() vertex " << (*it)->name() << " new size " << newDistance; (*it)->setLabelDistance(newDistance) ; emit setNodeLabelDistance ( (*it)-> name(), newDistance); } } graphSetModified(GraphChange::ChangedMinorOptions); } /** * @brief Changes the default distance of vertex labels * @param distance */ void Graph::vertexLabelDistanceInit(const int &distance) { initVertexLabelDistance = distance; } /** * @brief Checks a) if edge exists and b) if the opposite edge exists * Calls edgeAdd to add the new edge to the Graph, * then emits drawEdge() which calls GW::drawEdge() to draw the new edge. * Called from homonymous signal of Parser class. * Also called from MW when user clicks on the "add link" button * Also called (via MW) from GW when user middle-clicks on two nodes. * @param v1 * @param v2 * @param weight * @param color * @param reciprocal * @param drawArrows * @param bezier */ void Graph::edgeCreate(const int &v1, const int &v2, const qreal &weight, const QString &color, const int &type, const bool &drawArrows, const bool &bezier, const QString &label, const bool &signalMW){ qDebug() <<"-- Graph::edgeCreate() - " << v1 << " -> " << v2 << " weight " << weight << " type " << type << " label " << label; // check whether there is already such an edge // (see #713617 - https://bugs.launchpad.net/socnetv/+bug/713617) if (!edgeExists(v1,v2)){ if ( type == EdgeType::Undirected ) { qDebug()<< "-- Graph::edgeCreate() - Creating UNDIRECTED edge." << "Emitting drawEdge signal to GW"; edgeAdd ( v1, v2, weight, type, label, ( (weight==0) ? "blue" : color ) ); m_canvas->drawEdge( v1, v2, weight, label, ( (weight==0) ? "blue" : color ), type, drawArrows, bezier, initEdgeWeightNumbers); // emit signalDrawEdge(v1, v2, weight, label, ( (weight==0) ? "blue" : color ), type, // drawArrows, bezier, initEdgeWeightNumbers); } else if ( edgeExists( v2, v1 ) ) { qDebug()<<"-- Graph::edgeCreate() - Creating RECIPROCAL edge." << "Emitting drawEdge to GW"; edgeAdd ( v1, v2, weight, EdgeType::Reciprocated , label, color); m_canvas->drawEdge( v1, v2, weight, label, color, EdgeType::Reciprocated, drawArrows, bezier, initEdgeWeightNumbers); // emit signalDrawEdge(v1, v2, weight, label, color, EdgeType::Reciprocated, // drawArrows, bezier, initEdgeWeightNumbers); m_graphIsDirected = true; } else { qDebug()<< "-- Graph::edgeCreate() - Creating directed edge. Opposite arc does not exist." << "Emitting drawEdge to GW..."; edgeAdd ( v1, v2, weight, EdgeType::Directed, label, ( (weight==0) ? "blue" : color ) ); m_canvas->drawEdge( v1, v2, weight, label, ( (weight==0) ? "blue" : color ), EdgeType::Directed, drawArrows, bezier, initEdgeWeightNumbers ); // emit signalDrawEdge(v1, v2, weight, label, ( (weight==0) ? "blue" : color ), EdgeType::Directed, // drawArrows, bezier, initEdgeWeightNumbers); m_graphIsDirected = true; m_graphIsSymmetric=false; } } else { qDebug() << "-- Graph::edgeCreate() - " << "Edge " << v1 << " -> " << v2 << " declared previously (exists) - nothing to do \n\n"; } // save the edge color so that new edges created when user clicks on the canvas // have the same color with those of the file loaded, initEdgeColor=color; graphSetModified(GraphChange::ChangedEdges, signalMW); } /** * @brief Called from WebCrawler when it finds an new link * Calls edgeCreate() method with initEdgeColor * @param source * @param target */ void Graph::edgeCreateWebCrawler (const int &source, const int &target){ qDebug()<< " Graph::edgeCreateWebCrawler() - from " << source << " to " << target ; qreal weight = 1.0; bool drawArrows=true; bool bezier=false; edgeCreate(source, target, weight, initEdgeColor, EdgeType::Directed, drawArrows, bezier); } /** * @brief Adds a directed edge from v1 to v2 * If type == EdgeType::Undirected then it also adds the directed edge v2 -> v1 * @param v1 * @param v2 * @param weight * @param label * @param color * @param type */ void Graph::edgeAdd (const int &v1, const int &v2, const qreal &weight, const int &type, const QString &label, const QString &color) { int source=vpos[v1]; int target=vpos[v2]; qDebug()<< "Graph: edgeAdd() - new edge from vertex "<< v1 << "["<< source << "] to vertex "<< v2 << "["<< target << "] of weight "<edgeAddTo(v2, weight, color, label ); m_graph [ target ]->edgeAddFrom(v1, weight); if ( weight != 1 && weight!=0) { graphSetWeighted(true); } if (type == EdgeType::Reciprocated ){ // make existing opposite edge reciprocal } else if (type == EdgeType::Undirected){ //create opposite edge m_graph [ target ]->edgeAddTo(v1, weight ); m_graph [ source ]->edgeAddFrom(v2, weight); } } /** * @brief Removes the directed arc v1 -> v2 * or, if the graph is undirected, the edge v1 <-> v2 * Emits signalRemoveEdge to GW to delete the graphics item. * @param v1 * @param v2 * @param removeOpposite if true also removes the opposite edge */ void Graph::edgeRemove (const int &v1, const int &v2, const bool &removeOpposite) { qDebug ()<< "Graph::edgeRemove() - edge" << v1 << "[" << vpos[v1] << "] --> " << v2 << " to be removed. RemoveOpposite:" <edgeRemoveTo(v2); m_graph [ vpos[v2] ]->edgeRemoveFrom(v1); if ( graphIsUndirected() || removeOpposite ) { // remove opposite edge too m_graph [ vpos[v2] ]->edgeRemoveTo(v1); m_graph [ vpos[v1] ]->edgeRemoveFrom(v2); m_graphIsSymmetric=true; } else { if ( edgeExists(v2,v1) !=0 ) { m_graphIsSymmetric=false; } } emit signalRemoveEdge(v1,v2, ( graphIsDirected() || removeOpposite )); graphSetModified(GraphChange::ChangedEdges); } /** * @brief Removes a SelectedEdge * @param selectedEdge * @param removeOpposite */ void Graph::edgeRemoveSelected (SelectedEdge &selectedEdge, const bool &removeOpposite){ qDebug()<< "Graph::edgeRemoveSelected()" << selectedEdge; edgeRemove( selectedEdge.first, selectedEdge.second, removeOpposite); } /** * @brief Removes all selected edges */ void Graph::edgeRemoveSelectedAll() { qDebug()<< "Graph::edgeRemoveSelectedAll()"; foreach (SelectedEdge edgeToRemove, graphSelectedEdges()) { qDebug() << "Graph::edgeRemoveSelectedAll() - About to remove" << edgeToRemove; edgeRemoveSelected( edgeToRemove, true ); } } /** * @brief Changes the canvas visibility of an edge * Called from * GraphVertex::edgeFilterByRelation * GraphVertex::edgeFilterByWeight * GraphVertex::setOutEdgeEnabled * GraphVertex::edgeFilterUnilateral * @param relation * @param source * @param target * @param visible */ void Graph::edgeVisibilitySet ( int relation, int source, int target, bool visible) { qDebug() << "Graph::edgeVisibilitySet() - source" << source << "target" << target << "relation"<< relation << "visible"<< visible << "emitting signal to GW"; emit setEdgeVisibility ( relation, source, target, visible); } /** * @brief Called from MW::DialogEdgeFilter to filter edges over or under * a specified weight (m_threshold). * For each vertex in the Graph, calls the homonymous method of GraphVertex class. * @param m_threshold * @param overThreshold */ void Graph::edgeFilterByWeight(qreal m_threshold, bool overThreshold){ if (overThreshold) qDebug() << "Graph: edgeFilterByWeight() over " << m_threshold ; else qDebug() << "Graph: edgeFilterByWeight() below "<< m_threshold ; VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ (*it)->edgeFilterByWeight ( m_threshold, overThreshold ); } graphSetModified(GraphChange::ChangedEdges); emit statusMessage(tr("Edges have been filtered.")); } /** * @brief Enables or disables all edges of a given relation * Calls the homonymous method of GraphVertex class. * @param relation */ void Graph::edgeFilterByRelation(int relation, bool status){ qDebug() << "Graph::edgeFilterByRelation() " ; VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; (*it)->edgeFilterByRelation ( relation, status ); } } /** * @brief Enables or disables unilateral edges in current relationship. * If toggle=true, all non-reciprocal edges are disabled, effectively making * the network symmetric. * @param toggle */ void Graph::edgeFilterUnilateral(const bool &toggle) { qDebug() << "Graph::edgeFilterUnilateral() " ; VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ (*it)->edgeFilterUnilateral ( toggle ); } graphSetModified(GraphChange::ChangedEdges); emit statusMessage(tr("Unilateral edges have been temporarily disabled.")); } /** * @brief Called from GW::edgeClicked() * which is emitted when the user clicks on an edge. * Parameters are the source and target node of the edge. * It emits signalEdgeClicked() to MW, which displays a relevant * message on the status bar. * @param v1 * @param v2 */ void Graph::edgeClickedSet(const int &v1, const int &v2, const bool &openMenu) { qDebug() << "Graph::edgeClickedSet() " << v1 << "->" << v2; m_clickedEdge.source=v1; m_clickedEdge.target=v2; // Clear status bar message if (m_clickedEdge.source == 0 && m_clickedEdge.target==0) { emit signalEdgeClicked(); } else { qreal weight = m_graph[ vpos[ m_clickedEdge.source] ]->hasEdgeTo(m_clickedEdge.target); qDebug() << "Graph::edgeClickedSet() - clicked edge weight:"<< weight; int type=EdgeType::Directed; // Check if the opposite tie exists. If yes, this is a reciprocated tie if ( edgeExists(m_clickedEdge.target,m_clickedEdge.source, false) ) { if ( !graphIsDirected() ) { type=EdgeType::Undirected; } else { type=EdgeType::Reciprocated; } } m_clickedEdge.type = type; m_clickedEdge.weight = weight; //emit signalEdgeClicked( m_clickedEdge.source ,m_clickedEdge.target, weight, type, openMenu); emit signalEdgeClicked( m_clickedEdge, openMenu); } } /** * @brief Returns clicked edge * @return */ MyEdge Graph::edgeClicked() { return m_clickedEdge; } /** * @brief Checks if there is an edge (arc) from v1 to v2 Complexity: O(logN) for vpos retrieval + O(1) for QList index retrieval + O(logN) for checking edge(v2) * @param v1 * @param v2 * @param reciprocated: if true, checks if the edge is reciprocated (v1<->v2) * @return zero if arc or reciprocated edge does not exist or non-zero if arc /reciprocated edge exists */ qreal Graph::edgeExists (const int &v1, const int &v2, const bool &checkReciprocal) { edgeWeightTemp = 0; edgeWeightTemp = m_graph[ vpos[v1] ]->hasEdgeTo(v2); qDebug() << "Graph::edgeExists() - " << v1 << "->" << v2 << "=" << edgeWeightTemp ; if (!checkReciprocal) { return edgeWeightTemp; } else { //check if edge is reciprocal if ( edgeWeightTemp!=0 ) { edgeReverseWeightTemp = m_graph[ vpos[v2] ]->hasEdgeTo(v1); qDebug() << "Graph::edgeExists() - and " << v2 << "->" << v1 << "=" << edgeWeightTemp ; if ( edgeWeightTemp == edgeReverseWeightTemp ){ return edgeWeightTemp; } } } return 0; } /** * @brief Returns TRUE if edge(v1, v2) is symmetric, i.e. (v1,v2) == (v2,v1). * @param v1 * @param v2 * @return */ bool Graph::edgeSymmetric(const int &v1, const int &v2){ qDebug() << "***Graph: edgeSymmetric()"; if ( ( edgeExists( v1, v2 , true) ) !=0 ) { return true; } else { return false; } } /** * @brief Returns the number |E| of graph - only the enabled edges * @return */ int Graph::edgesEnabled() { qDebug()<< "Graph::edgesEnabled() - checking if graph modified... "; int enabledEdges = (( graphIsUndirected() ) ? m_totalEdges / 2 : m_totalEdges); if ( calculatedEdges ) { qDebug()<< "Graph::edgesEnabled() - Graph unchanged, edges: " << enabledEdges; return enabledEdges; } m_totalEdges = 0; VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ m_totalEdges+=(*it)->outEdges(); } qDebug() << "Graph::edgesEnabled() - edges recounted: " << m_totalEdges; calculatedEdges = true; enabledEdges = (( graphIsUndirected() ) ? m_totalEdges / 2 : m_totalEdges); return enabledEdges; } /** * @brief Returns the number of outbound edges (arcs) from vertex v1 * @param v1 * @return */ int Graph::vertexEdgesOutbound(int v1) { qDebug("Graph: vertexEdgesOutbound()"); return m_graph[ vpos[v1] ]->outEdges(); } /** * @brief Returns the number of inbound edges (arcs) to vertex v1 * @param v1 * @return int */ int Graph::vertexEdgesInbound (int v1) { qDebug("Graph: vertexEdgesInbound()"); return m_graph[ vpos[v1] ]->inEdges(); } /** * @brief Changes the weight of an edge (arc) between v1 and v2 * @param v1 * @param v2 * @param weight */ void Graph::edgeWeightSet (const int &v1, const int &v2, const qreal &weight, const bool &undirected) { qDebug() << "Graph::edgeWeightSet() - " << v1 << "[" << vpos[v1] << "] ->" << v2 << "[" << vpos[v2] << "]" << " = " << weight; m_graph [ vpos[v1] ]->changeOutEdgeWeight(v2, weight); if (undirected) { qDebug() << "Graph::edgeWeightSet() - changing opposite edge weight too"; m_graph [ vpos[v2] ]->changeOutEdgeWeight(v1, weight); } emit setEdgeWeight(v1,v2, weight); graphSetModified(GraphChange::ChangedEdges); } /** * @brief Returns the weight of the edge v1 -> v2 * @param v1 * @param v2 * @return qreal */ qreal Graph::edgeWeight (const int &v1, const int &v2) const{ return m_graph[ vpos[v1] ]->hasEdgeTo(v2); } /** * @brief Changes the visibility of edge weight numbers * @param toggle */ void Graph::edgeWeightNumbersVisibilitySet (const bool &toggle) { initEdgeWeightNumbers = toggle; } /** * @brief Saves the default edge color * Used by random network creation methods * @param color */ void Graph::edgeColorInit(const QString &color){ initEdgeColor=color; } /** * @brief Changes the color of all enabled edges. * @param color * @return */ bool Graph::edgeColorAllSet(const QString &color, const int &threshold){ qDebug()<< "Graph::edgeColorAllSet() - new color: " << color; int target=0, source=0; edgeColorInit(color); QHash enabledOutEdges; QHash::const_iterator it1; VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ source = (*it)->name(); if ( ! (*it)->isEnabled() ) continue; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges.cbegin(); while ( it1!=enabledOutEdges.cend() ){ target = it1.key(); if (threshold == 0 ){ if ( it1.value() == threshold ) { qDebug() << " Graph::edgeColorAllSet() zero weight threshold " << threshold << " - edge " << source << "->" << target << " new color " << color; (*it)->setOutLinkColor(target, color); emit setEdgeColor(source, target, color); } } else if (threshold != 0 && threshold != RAND_MAX ) { if ( it1.value() <= threshold ) { qDebug() << " Graph::edgeColorAllSet() below weight threshold " << threshold << " - edge " << source << "->" << target << " new color " << color; (*it)->setOutLinkColor(target, color); emit setEdgeColor(source, target, color); } } else { qDebug() << " Graph::edgeColorAllSet() : " << source << "->" << target << " new color " << color; (*it)->setOutLinkColor(target, color); emit setEdgeColor(source, target, color); } ++it1; } } //delete enabledOutEdges; graphSetModified(GraphChange::ChangedEdgesMetadata); return true; } /** * @brief Changes the color of edge v1 -> v2 * @param v1 * @param v2 * @param color */ void Graph::edgeColorSet(const int &v1, const int &v2, const QString &color){ qDebug()<< "Graph::edgeColorSet() - "<< v1 << " -> "<< v2 <<" vpos ("<< vpos[v1]<< " -> "<setOutLinkColor(v2, color); emit setEdgeColor(v1, v2, color); if ( graphIsSymmetric() ) { m_graph[ vpos[v2] ]->setOutLinkColor(v1, color); emit setEdgeColor(v2, v1, color); } graphSetModified(GraphChange::ChangedEdgesMetadata); } /** * @brief Returns the color of the directed edge v1 -> v2 * @param v1 * @param v2 * @return */ QString Graph::edgeColor (const int &v1, const int &v2){ return m_graph[ vpos[v1] ]->outLinkColor(v2); } /** * @brief Changes the label of edge v1 -> v2 * @param v1 * @param v2 * @param weight */ void Graph::edgeLabelSet (const int &v1, const int &v2, const QString &label) { qDebug() << "Graph::edgeLabelSet() " << v1 << "[" << vpos[v1] << "] -> " << v2 << "[" << vpos[v2] << "]" << " label " << label; m_graph[ vpos[v1] ]->setOutEdgeLabel(v2, label); emit setEdgeLabel(v1,v2, label); graphSetModified(GraphChange::ChangedEdgesMetadata); } /** * @brief Returns the label of edge v1->v2 * @param v1 * @param v2 * @return */ QString Graph::edgeLabel (const int &v1, const int &v2) const { return m_graph [ vpos[v1] ]->outEdgeLabel(v2); } /** * @brief Toggles the visibility of edge labels. * @param toggle */ void Graph::edgeLabelsVisibilitySet (const bool &toggle) { initEdgeLabels = toggle; } /** * @brief Returns the outDegree (sum of outbound edge weights) of vertex v1 * @param v1 * @return */ int Graph::vertexDegreeOut (int v1) { qDebug()<< "Graph: vertexDegreeOut()"; return m_graph[ vpos[v1] ]->degreeOut(); } /** * @brief Returns the inDegree (sum of inbound edge weights) of vertex v1 * @param v1 * @return */ int Graph::vertexDegreeIn (int v1) { qDebug()<< "Graph: vertexDegreeIn()"; return m_graph[ vpos[v1] ]-> degreeIn(); } /** * @brief Returns a list of all vertices mutually connected to vertex v1 in the * current relation * @param v1 * @return QList */ QList Graph::vertexNeighborhoodList(const int &v1) { //qDebug()<< "Graph::vertexNeighborhoodList()"; return m_graph[ vpos[v1] ]-> neighborhoodList(); } /** * @brief Returns the set of all vertices mutually connected to vertex v1 in the * current relation * @param v1 * @return QList */ // // Only in Qt 5.15 //QSet Graph::vertexNeighborhoodSet(const int &v1) { // //qDebug()<< "Graph::vertexNeighborhoodList()"; // QList myNeightbors = m_graph[ vpos[v1] ]-> neighborhoodList(); // return QSet(myNeightbors.constBegin(),myNeightbors.constEnd()); //} /** * @brief Returns the number |V| of graph * If countAll = true, returns |V| where V the set of all (enabled or not) vertices * If countAll = false, it skips disabled vertices * If countAll = false and dropIsolates = true, it skips both disabled and isolated vertices * @param dropIsolates * @param countAll * @return */ int Graph::vertices(const bool &dropIsolates, const bool &countAll, const bool &recount) { if ( m_totalVertices!=0 && calculatedVertices && !recount) { qDebug()<< "Graph::vertices() - Graph not modified, vertices: " << m_totalVertices; return m_totalVertices; } m_totalVertices=0; VList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (countAll) { ++m_totalVertices; } else { if (dropIsolates && (*it)->isIsolated()){ qDebug()<< "Graph::vertices() - isolated vertex:" <<(*it)->name(); continue; } if ( !(*it)->isEnabled()) { qDebug()<< "Graph::vertices() - disabled vertex:" <<(*it)->name(); continue; } ++m_totalVertices; } } qDebug()<< "Graph::vertices() - Graph size:"<< m_graph.size() << "enabled vertices" << m_totalVertices; calculatedVertices=true; return m_totalVertices; } /** * @brief Returns a list of all isolated vertices inside the graph * Used by * Graph::graphMatrixAdjacencyCreate() * Graph::writeMatrixAdjacencyInvert() * Graph::centralityInformation() * @return */ QList Graph::verticesListIsolated(){ if ( calculatedIsolates ){ qDebug()<< "Graph::verticesListIsolated() - graph not modified and " "already calculated isolates. Returning list as is:" <isEnabled() ) // continue; if ((*it)->isIsolated()) { m_verticesIsolatedList << (*it)->name(); qDebug()<< "Graph::verticesListIsolated() - node " << (*it)->name() << " is isolated. Marking it." ; } } qDebug()<< "Graph::verticesListIsolated() - isolated vertices list:" < Graph::verticesList(){ qDebug()<< "Graph::verticesList()"; if ( !m_verticesList.isEmpty() && calculatedVerticesList ){ return m_verticesList; } VList::const_iterator it; m_verticesList.clear(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; m_verticesList << (*it)->name(); } calculatedVerticesList = true; return m_verticesList ; } /** * @brief Returns a QSet of all vertices numbers inside the graph * @return */ QSet Graph::verticesSet(){ qDebug()<< "Graph::verticesSet()"; if ( !m_verticesSet.isEmpty() && calculatedVerticesSet ){ return m_verticesSet; } VList::const_iterator it; m_verticesSet.clear(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; m_verticesSet << (*it)->name(); } calculatedVerticesSet = true; return m_verticesSet ; } /** * @brief Creates a subgraph (clique, star, cycle, line) with vertices in vList * Iff vList is empty, then fallbacks to the m_verticesSelected. * @param vList */ void Graph::verticesCreateSubgraph(QList vList, const int &type, const int ¢er) { if ( relations() == 1 && edgesEnabled()==0 ) { QString newRelationName = QString::number ( vList.size() ) + tr("-clique"); relationCurrentRename(newRelationName, true); } if (vList.isEmpty()) { vList = m_verticesSelected; } qDebug()<<"Graph::verticesCreateSubgraph() - type:" << type << "vList:" << vList; int progressCounter = 0; QString pMsg = tr("Creating subgraph. \nPlease wait..."); emit statusMessage( pMsg); emit signalProgressBoxCreate(vList.size(),pMsg); qreal weight; bool drawArrows = graphIsDirected(); int edgeType = ( graphIsUndirected() ) ? EdgeType::Undirected : EdgeType::Reciprocated; if (type == SUBGRAPH_CLIQUE) { for (int i=0; i < vList.size(); ++i ) { emit signalProgressBoxUpdate(++progressCounter); for (int j=i+1; j < vList.size(); ++j ) { if ( ! (weight=edgeExists( vList.value(i), vList.value(j) ) ) ) { if ( (weight=edgeExists( vList.value(j), vList.value(i) ) ) ) { edgeTypeSet( vList.value(j), vList.value(i), weight, edgeType ); } else { edgeCreate(vList.value(i), vList.value(j), 1.0, initEdgeColor, EdgeType::Undirected, drawArrows); edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } else { edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } } } else if (type == SUBGRAPH_STAR) { for (int j=0; j < vList.size(); ++j ) { emit signalProgressBoxUpdate(++progressCounter); if ( ! (weight=edgeExists( center, vList.value(j) ) ) ) { if ( center == vList.value(j)) continue; if ( (weight=edgeExists( vList.value(j), center ) ) ) { edgeTypeSet( vList.value(j), center, weight, edgeType ); } else { edgeCreate(center, vList.value(j), 1.0, initEdgeColor, EdgeType::Undirected, drawArrows); edgeTypeSet( center, vList.value(j), weight, edgeType ); } } else { edgeTypeSet( center, vList.value(j), weight, edgeType ); } } } else if (type == SUBGRAPH_CYCLE) { int j=0; for (int i=0; i < vList.size(); ++i ) { emit signalProgressBoxUpdate(++progressCounter); j= ( i == vList.size()-1) ? 0:i+1; if ( ! (weight=edgeExists( vList.value(i), vList.value(j) ) ) ) { if ( (weight=edgeExists( vList.value(j), vList.value(i) ) ) ) { edgeTypeSet( vList.value(j), vList.value(i), weight, edgeType ); } else { edgeCreate(vList.value(i), vList.value(j), 1.0, initEdgeColor, EdgeType::Undirected, drawArrows); edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } else { edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } } else if (type == SUBGRAPH_LINE) { int j=0; for (int i=0; i < vList.size(); ++i ) { emit signalProgressBoxUpdate(++progressCounter); if ( i == vList.size()-1 ) break; j= i+1; if ( ! (weight=edgeExists( vList.value(i), vList.value(j) ) ) ) { if ( (weight=edgeExists( vList.value(j), vList.value(i) ) ) ) { edgeTypeSet( vList.value(j), vList.value(i), weight, edgeType ); } else { edgeCreate(vList.value(i), vList.value(j), 1.0, initEdgeColor, EdgeType::Undirected, drawArrows); edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } else { edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } } else { emit signalProgressBoxKill(); return; } emit signalProgressBoxKill(); } /** * @brief Sets the graph modification status. * If there are major changes, then signalGraphModified is emitted * In any case, SignalGraphSavedStatus is emitted. * @param graphChangedFlag * @param signalMW */ void Graph::graphSetModified(const int &graphNewStatus, const bool &signalMW){ if ( graphNewStatus == GraphChange::ChangedNew ) { // this is called from: // graphFileLoaded() after successful loading qDebug()<<"Graph::graphSetModified() - new, thus saved..." "emit signalGraphModified()"; m_graphHasChanged=graphNewStatus; emit signalGraphModified(graphIsDirected(), m_totalVertices, edgesEnabled(), graphDensity()); return; } else if ( graphNewStatus == GraphChange::ChangedNone ) { // this is called from: // graphSaved() after successful saving qDebug()<<"Graph::graphSetModified() - no changes, graph is saved..."; m_graphHasChanged=graphNewStatus; emit signalGraphSavedStatus(true); return; } else if ( graphNewStatus > GraphChange::ChangedMajor ) { // This is called from any method that alters V or E in G: // thus all prior computations are invalid qDebug()<<"Graph::graphSetModified() - major changes!"; m_graphHasChanged=graphNewStatus; // Init all calculated* flags to false, as all prior computations // are now invalid and we need to recompute any of them calculatedGraphReciprocity = false; calculatedGraphSymmetry = false; calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices = false; calculatedVerticesList=false; calculatedVerticesSet = false; calculatedIsolates = false; calculatedTriad = false; calculatedAdjacencyMatrix = false; calculatedDistances = false; calculatedCentralities = false; calculatedDP = false; calculatedDC = false; calculatedPP = false; calculatedIRCC = false; calculatedIC = false; calculatedEVC=false; calculatedPRP = false; if (signalMW) { qDebug()<<"Graph::graphSetModified() - emit signalGraphModified()"; emit signalGraphModified(graphIsDirected(), m_totalVertices, edgesEnabled(), graphDensity()); return; } } else if ( graphNewStatus > GraphChange::ChangedMinorOptions) { // this is called from Graph methods that inflict minor changes, // i.e. changing vertex positions, labels, etc // We do not change status if current status is > ChangedMajor if ( m_graphHasChanged < GraphChange::ChangedMajor) { m_graphHasChanged = graphNewStatus; } qDebug()<<"Graph::graphSetModified() - minor changes but needs saving..."; emit signalGraphSavedStatus(false); return; } else { qDebug()<<"Graph::graphSetModified() - Strange. I should not reach this code..."; m_graphHasChanged=graphNewStatus; } } /** * @brief Returns true of graph is modified (edges/vertices added/removed). * else false * @return */ bool Graph::graphIsModified() const { qDebug() << "Graph::graphIsModified() - m_graphHasChanged:" << m_graphHasChanged ; if ( m_graphHasChanged > GraphChange::ChangedMajor && m_graphHasChanged != GraphChange::ChangedNew ) { return true; } return false; } /** * @brief Returns true if the graph is saved. * @return */ bool Graph::graphSaved() const { qDebug() << "Graph::graphSaved() - m_graphHasChanged:" << m_graphHasChanged ; return (m_graphHasChanged == 0 ) ? true: false; } /** * @brief Returns if a graph has been loaded from a file. * @return */ bool Graph::graphLoaded() const { qDebug() << "Graph::graphLoaded() - " << (( graphFileFormat() != FileType::UNRECOGNIZED ) ? true: false ); return ( graphFileFormat() != FileType::UNRECOGNIZED ) ? true: false; } /** * @brief Gets updates on the user-selected vertices and edges from GW and emits * their counts to MW * @param selectedVertices * @param selectedEdges */ void Graph::graphSelectionChanged(const QList selectedVertices, const QList selectedEdges) { m_verticesSelected = selectedVertices; m_selectedEdges = selectedEdges; qDebug() << "Graph::graphSelectionChanged() - Vertices" << m_verticesSelected << "Edges" < Graph::graphSelectedVertices() const{ return m_verticesSelected; } /** * @brief Returns count of user-selected vertices * @return */ int Graph::graphSelectedVerticesCount() const{ return m_verticesSelected.size(); } /** * @brief Returns min of user-selected vertices * @return */ int Graph::graphSelectedVerticesMin() const{ int min = RAND_MAX; foreach (int i, m_verticesSelected) { if (i < min) min = i; } return min; } /** * @brief Returns max of user-selected vertices * @return */ int Graph::graphSelectedVerticesMax() const{ int max = 0; foreach (int i, m_verticesSelected) { if (i > max ) max = i; } return max; } /** * @brief Returns a QList of user-selected edges in pair * @return */ QList Graph::graphSelectedEdges() const{ return m_selectedEdges; } /** * @brief Returns the count of user-selected edges * @return */ int Graph::graphSelectedEdgesCount() const { return m_selectedEdges.size(); } /** * @brief Returns the ratio of present edges to total possible edges * in the current relation. * @return */ qreal Graph::graphDensity() { if ( calculatedGraphDensity ) { qDebug()<< "Graph::graphDensity() - graph not modified and" "already calculated density. Returning last value:" << m_graphDensity; return m_graphDensity; } qDebug()<< "Graph::graphDensity() - computing..."; int V=vertices(); if (V!=0 && V!=1) { m_graphDensity = (graphIsUndirected()) ? (qreal) 2* edgesEnabled() / (qreal)(V*(V-1.0)) : (qreal) edgesEnabled() / (qreal)(V*(V-1.0)) ; } else { m_graphDensity = 0; } calculatedGraphDensity = true; return m_graphDensity ; } /** * @brief Returns true if the graph is weighted (valued), * i.e. if any e in |E| has value not 0 or 1 * Complexity: O(n^2) * @return */ bool Graph::graphIsWeighted(){ if ( calculatedGraphWeighted ) { qDebug()<< "Graph::graphIsWeighted() - graph not modified. Return: " << m_graphIsWeighted; return m_graphIsWeighted; } qDebug()<< "Graph::graphIsWeighted()"; qreal m_weight=0; VList::const_iterator it, it1; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Checking if the graph edges are valued. \nPlease wait..."); emit statusMessage( pMsg); emit signalProgressBoxCreate(N,pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ m_weight = edgeExists ( (*it1)->name(), (*it)->name() ) ; if ( m_weight != 1 && m_weight != 0 ) { qDebug()<< "Graph: graphIsWeighted() - true. Graph is edge-weighted."; graphSetWeighted(true); break; } } if (m_graphIsWeighted) { break; } } calculatedGraphWeighted = true; qDebug()<< "Graph::graphIsWeighted() - result" << m_graphIsWeighted; emit signalProgressBoxKill(); return m_graphIsWeighted; } /** * @brief Sets the graph to be weighted / valued edges. * @param toggle */ void Graph::graphSetWeighted(const bool &toggle){ qDebug() << "Graph::graphSetWeighted() - set m_graphIsWeighted =" << toggle; m_graphIsWeighted = toggle; } /** * @brief Returns the sum of vertices having edgesOutbound * @return */ int Graph::verticesWithOutboundEdges(){ return outboundEdgesVert; } /** * @brief Returns the sum of vertices having edgesInbound * @return */ int Graph::verticesWithInboundEdges(){ return inboundEdgesVert; } /** * @brief Returns the sum of vertices having reciprocal edges * @return */ int Graph:: verticesWithReciprocalEdges(){ return reciprocalEdgesVert; } /** * @brief called from Graph, when closing network, to terminate all processes * Also called indirectly when wc_spider finishes * @param reason */ void Graph::webCrawlTerminateThreads (QString reason){ qDebug() << "Graph::webCrawlTerminateThreads() - reason " << reason << "Checking wc_spiderThread..."; while (wc_spiderThread.isRunning() ) { qDebug() << "Graph::webCrawlTerminateThreads() - wc_spiderThread running. " "Calling wc_spiderThread.quit()"; wc_spiderThread.requestInterruption(); wc_spiderThread.quit(); wc_spiderThread.wait(); } qDebug() << "Graph::webCrawlTerminateThreads() - Checking wc_parserThread..."; while (wc_parserThread.isRunning() ) { qDebug() << "Graph::webCrawlTerminateThreads() - wc_parserThread running. " "Calling wc_parserThread.quit()"; wc_parserThread.quit(); wc_parserThread.wait(); } } /** * @brief Called by MW to start a web crawler thread... * @param seed * @param maxNodes * @param maxRecursion * @param extLinks * @param intLinks */ void Graph::webCrawl(const QString &seedUrl, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &intLinks, const bool &childLinks, const bool &parentLinks, const bool &selfLinks, const bool &extLinksIncluded, const bool &extLinksCrawl, const bool &socialLinks, const bool &delayedRequests){ relationCurrentRename(tr("web"), true); // TOFIX Due to multithreading, app crashes when crawler finishes its job. qDebug() << "Graph::webCrawl() - Graph thread:" << thread() << "seedUrl:" << seedUrl ; qDebug() << "Graph::webCrawl() - Creating wc_spider & wc_parser objects"; wc_parser = new WebCrawler_Parser(); wc_spider = new WebCrawler_Spider (); qDebug() << "Graph::webCrawl() - Moving out wc_parser, wc_spider from thread:" << wc_parser->thread() << "," << wc_spider->thread(); wc_parser->moveToThread(&wc_parserThread); wc_spider->moveToThread(&wc_spiderThread); qDebug() << "Graph::webCrawl() - Moved wc_spider to thread:" << wc_parser->thread() << "and wc_spider to thread:" << wc_spider->thread(); // qDebug() << "Graph::webCrawl() - Creating http object"; // QNetworkAccessManager *http = new QNetworkAccessManager(this); qDebug() << "Graph::webCrawl() - Connecting signals from/to parser & spider"; connect(this, &Graph::operateSpider, wc_spider, &WebCrawler_Spider::visitUrls); connect(wc_parser, &WebCrawler_Parser::signalCreateNode, this, &Graph::vertexCreateAtPosRandomWithLabel); connect(wc_parser, &WebCrawler_Parser::signalCreateEdge, this, &Graph::edgeCreateWebCrawler); // connect (wc_spider, &WebCrawler_Spider::getUrl, // http, &QNetworkAccessManager::get); // connect ( http, &QNetworkAccessManager::finished, // wc_parser, &WebCrawler_Parser::parse ); connect (wc_parser, &WebCrawler_Parser::startSpider, wc_spider, &WebCrawler_Spider::visitUrls ); connect (wc_spider, &WebCrawler_Spider::finished, this, &Graph::webCrawlTerminateThreads); connect (wc_parser, &WebCrawler_Parser::finished, this, &Graph::webCrawlTerminateThreads); connect(&wc_parserThread, &QThread::finished, wc_parser, &QObject::deleteLater); connect(&wc_spiderThread, &QThread::finished, wc_spider, &QObject::deleteLater); qDebug() << "Graph::webCrawl() - Starting wc_parser & wc_spider threads!"; wc_parserThread.start(); wc_spiderThread.start(); qDebug() << "Graph::webCrawl() - loading wc_parser & wc_spider. seedUrl:" << seedUrl; wc_parser->load(seedUrl, urlPatternsIncluded, urlPatternsExcluded, linkClasses, maxNodes, maxLinksPerPage, intLinks, childLinks, parentLinks, selfLinks, extLinksIncluded, extLinksCrawl, socialLinks); wc_spider->load ( //http, wc_parser, seedUrl, maxNodes, delayedRequests); qDebug() << "Graph::webCrawl() - Creating initial node 1, seedUrl:" << seedUrl; vertexCreateAtPosRandomWithLabel(1, seedUrl, false); qDebug() << "Graph::webCrawl() - Calling spider get() for that url!"; emit operateSpider(); qDebug("Graph::webCrawl() - reach the end - See the threads running? "); } /** * @brief Computes and returns the arc reciprocity of the graph. * Also computes the dyad reciprocity and fills parameters with values. * @return */ qreal Graph::graphReciprocity(){ qDebug()<< "Graph::graphReciprocity()"; if ( calculatedGraphReciprocity ){ qDebug() << "Graph::graphReciprocity() - graph not modified and " "already calculated reciprocity. Returning previous result: " << m_graphReciprocityArc; return m_graphReciprocityArc; } qDebug() << "Graph::graphReciprocity() - Computing..."; emit statusMessage ( (tr("Calculating the Arc Reciprocity of the graph...")) ); m_graphReciprocityArc=0; m_graphReciprocityDyad=0; m_graphReciprocityTiesReciprocated=0; m_graphReciprocityTiesNonSymmetric=0; m_graphReciprocityTiesTotal=0; m_graphReciprocityPairsReciprocated=0; m_graphReciprocityPairsTotal=0; qreal weight = 0, reciprocalWeight = 0; int y=0, v2=0, v1=0; QHash enabledOutEdges; QHash::const_iterator hit; VList::const_iterator it, it1; H_StrToBool totalDyads; H_StrToBool reciprocatedDyads; QString pair, reversePair; //initialize counters for ( it = m_graph.cbegin(); it != m_graph.cend(); ++it) { (*it)->setOutEdgesReciprocated(0); (*it)->setOutEdgesNonSym(0); (*it)->setInEdgesNonSym(0); } // Compute "arc" reciprocity // the number of ties that are involved in reciprocal relations // relative to the total number of actual ties (not possible ties) for ( it = m_graph.cbegin(); it != m_graph.cend(); ++it) { v1 = (*it) -> name(); if ( ! (*it)->isEnabled() ) continue; enabledOutEdges=(*it)->outEdgesEnabledHash(); hit=enabledOutEdges.cbegin(); while ( hit!=enabledOutEdges.cend() ){ v2 = hit.key(); y=vpos[ v2 ]; weight = hit.value(); m_graphReciprocityTiesTotal += weight; // Compute "dyad" reciprocity pair = QString::number(v1) + ">" + QString::number(v2) ; reversePair = QString::number(v2) + ">" + QString::number(v1) ; if ( !totalDyads.contains(pair) && !totalDyads.contains(reversePair) ) { totalDyads [pair] = true; } qDebug() << pair << "totalTies" << m_graphReciprocityTiesTotal << "totalDyads" << totalDyads.count(); if ( (reciprocalWeight = edgeExists(v2, v1) ) == weight) { (*it)->setOutEdgesReciprocated(); //increase reciprocated ties for ego (*it)->setOutEdgesReciprocated(); m_graphReciprocityTiesReciprocated +=reciprocalWeight ; pair = QString::number(v2) + ">" + QString::number(v1) ; reversePair = QString::number(v1) + ">" + QString::number(v2) ; if ( !reciprocatedDyads.contains(pair) && !reciprocatedDyads.contains(reversePair) ) { reciprocatedDyads [pair] = true; } qDebug() << pair << "reciprocal!" << "reciprocatedTies" << m_graphReciprocityTiesReciprocated << "reciprocatedDyads" << reciprocatedDyads.count(); } else { (*it)->setOutEdgesNonSym(); m_graph[y]->setInEdgesNonSym(); m_graphReciprocityTiesNonSymmetric++; } ++hit; } } //delete enabledOutEdges; m_graphReciprocityArc = (qreal) m_graphReciprocityTiesReciprocated / (qreal) m_graphReciprocityTiesTotal; m_graphReciprocityPairsReciprocated = reciprocatedDyads.count(); m_graphReciprocityPairsTotal = totalDyads.count(); m_graphReciprocityDyad = (qreal) m_graphReciprocityPairsReciprocated / (qreal) m_graphReciprocityPairsTotal; qDebug() << "Graph: graphReciprocity() - Finished. Arc reciprocity:" << m_graphReciprocityTiesReciprocated << "/" << m_graphReciprocityTiesTotal << "=" << m_graphReciprocityArc << endl << m_graphReciprocityPairsReciprocated << "/" << m_graphReciprocityPairsTotal << "=" << m_graphReciprocityDyad; calculatedGraphReciprocity = true; return m_graphReciprocityArc; } /** * @brief Writes reciprocity report to filename * @param fileName * @param considerWeights */ void Graph::writeReciprocity(const QString fileName, const bool considerWeights) { qDebug() << "Graph::writeReciprocity()"; Q_UNUSED(considerWeights); QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); m_graphReciprocityArc = graphReciprocity(); int rowCount=0; int progressCounter=0; int N = vertices(); qreal tiesSym=0; qreal tiesNonSym=0; qreal tiesOutNonSym=0; qreal tiesInNonSym=0; qreal tiesOutNonSymTotalOut=0; qreal tiesInNonSymTotalIn=0; QString pMsg = tr("Writing Reciprocity to file. \nPlease wait..."); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("RECIPROCITY (r) REPORT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("Reciprocity, r, is a measure of the likelihood of vertices " "in a directed network to be mutually linked.
" "SocNetV supports two different methods to index the degree of " "reciprocity in a social network:
" "- The arc reciprocity, which is the fraction of " "reciprocated ties over all actual ties in the network.
" "- The dyad reciprocity which is the fraction of " "actor pairs that have reciprocated ties over all " "pairs of actors that have any connection.
" "In a directed network, the arc reciprocity measures the proportion " "of directed edges that are bidirectional. If the reciprocity is 1, " "then the adjacency matrix is structurally symmetric.
" "Likewise, in a directed network, the dyad reciprocity measures " "the proportion of connected actor dyads that have bidirectional ties " "between them.
" "In an undirected graph, all edges are reciprocal. Thus the " "reciprocity of the graph is always 1.
" "Reciprocity can be computed on undirected, directed, and weighted graphs.") << "

"; outText << "

" << "" << tr("r range: ") <<"" << tr("0 ≤ r ≤ 1") << "

"; outText << "

" << "" << tr("Arc reciprocity: ") <<"" << tr("%1 / %2 = %3").arg(m_graphReciprocityTiesReciprocated).arg(m_graphReciprocityTiesTotal).arg(m_graphReciprocityArc) << "
" << tr("Of all actual ties in the network, %1% are reciprocated.").arg(m_graphReciprocityArc * 100) << "

"; outText << "

" << "" << tr("Dyad reciprocity: ") <<"" << tr("%1 / %2 = %3").arg(m_graphReciprocityPairsReciprocated).arg(m_graphReciprocityPairsTotal).arg(m_graphReciprocityDyad ) << "
" << tr("Of all pairs of actors that have any ties, %1% have a reciprocated connection.").arg(m_graphReciprocityDyad * 100) << "

"; outText << "

" << "
" << "" << tr("Reciprocity proportions per actor: ") <<"" << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; VList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; qDebug() << "Graph::writeReciprocity outnon - innon - rec" << (*it)->outEdgesNonSym() << (*it)->inEdgesNonSym() << (*it)->outEdgesReciprocated(); // Symmetric: Total number of reciprocated ties involving this actor divided by the number of ties to and from her. tiesSym =(qreal) (*it)->outEdgesReciprocated() / (qreal) ( (*it)->outEdges() + (*it)->inEdges()); // non Symmetric: One minus symmetric tiesNonSym = 1 - tiesSym; // nonSym Out/NonSym. Proportion of non-symmetric outgoing ties to the total non-symmetric ties. tiesOutNonSym = ((*it)->outEdgesNonSym() || (*it)->inEdgesNonSym()) ? (qreal) (*it)->outEdgesNonSym() / (qreal) ((*it)->outEdgesNonSym() + (*it)->inEdgesNonSym()) : 0; // nonSym In/NonSym. Proportion of non-symmetric incoming ties to the total non-symmetric ties. tiesInNonSym = ((*it)->outEdgesNonSym() || (*it)->inEdgesNonSym()) ? (qreal) (*it)->inEdgesNonSym() / (qreal) ((*it)->outEdgesNonSym() + (*it)->inEdgesNonSym()) : 0; // nonSym Out/Out. Proportion of non-symmetric outgoing ties to the total outgoing ties. tiesOutNonSymTotalOut = ( (*it)->outEdges() != 0) ? (qreal) (*it)->outEdgesNonSym() /(qreal) (*it)->outEdges() : 0; // nonSym In/In. Proportion of non-symmetric incoming ties to the total incoming ties. tiesInNonSymTotalIn = ( (*it)->inEdges() != 0) ? (qreal) (*it)->inEdgesNonSym() / (qreal) (*it)->inEdges() : 0; outText << "" <<"" <<""; } outText << "
" << tr("Actor") <<"" << tr("Label") <<"" << tr("Symmetric") <<"" << tr("nonSymmetric") <<"" << tr("nsym out/nsym") <<"" << tr("nsym in/nsym") <<"" << tr("nsym out/out") <<"" << tr("nsym in/in") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << tiesSym //<< ((eccentr == 0) ? "\xE2\x88\x9E" : QString::number(eccentr) ) << "" << tiesNonSym //<< ((eccentr == 0) ? "\xE2\x88\x9E" : QString::number(eccentr) ) << "" << tiesOutNonSym << "" << tiesInNonSym << "" << tiesOutNonSymTotalOut << "" << tiesInNonSymTotalIn << "
"; outText << "

"; outText << "" << tr("Symmetric ") << "" << tr("Proportion of reciprocated ties involving the actor to the total incoming and outgoing ties.") << "
" << "" << tr("nonSymmetric ") << "" << tr("One minus symmetric") << "
" << "" << tr("nonSym Out/NonSym ") << "" << tr("Proportion of non-symmetric outgoing ties to the total non-symmetric ties.") << "
" << "" << tr("nonSym In/NonSym ") << "" << tr("Proportion of non-symmetric incoming ties to the total non-symmetric ties.") << "
" << "" << tr("nonSym Out/Out ") << "" << tr("Proportion of non-symmetric outgoing ties to the total outgoing ties.") << "
" << "" << tr("nonSym In/In ") << "" << tr("Proportion of non-symmetric incoming ties to the total incoming ties") << "
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Reciprocity Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Returns TRUE if the adjacency matrix of the current relation is symmetric * @return bool */ bool Graph::graphIsSymmetric(){ qDebug() << "Graph::graphIsSymmetric() "; if ( calculatedGraphSymmetry ){ qDebug() << "Graph::graphIsSymmetric() - graph not modified and " "already calculated symmetry. Returning previous result: " << m_graphIsSymmetric; return m_graphIsSymmetric; } m_graphIsSymmetric=true; int v2=0, v1=0; qreal weight = 0; QHash enabledOutEdges; QHash::const_iterator hit; VList::const_iterator lit; for ( lit = m_graph.cbegin(); lit != m_graph.cend(); ++lit) { v1 = (*lit) -> name(); if ( ! (*lit)->isEnabled() ) continue; enabledOutEdges=(*lit)->outEdgesEnabledHash(); hit=enabledOutEdges.cbegin(); while ( hit!=enabledOutEdges.cend() ){ v2 = hit.key(); weight = hit.value(); if ( edgeExists ( v2, v1 ) != weight) { m_graphIsSymmetric=false; // qDebug() <<"Graph::graphIsSymmetric() - " // << " graph not symmetric because " // << v1 << " -> " << v2 << " weight " << weight // << " differs from " << v2 << " -> " << v1 ; break; } ++hit; } } //delete enabledOutEdges; qDebug() << "Graph: graphIsSymmetric() - Finished. Result:" << m_graphIsSymmetric; calculatedGraphSymmetry = true; return m_graphIsSymmetric; } /** * @brief Transforms the graph to symmetric (all edges reciprocal) */ void Graph::graphSymmetrize(){ qDebug()<< "Graph::graphSymmetrize"; VList::const_iterator it; int v2=0, v1=0, weight; qreal invertWeight=0; QHash enabledOutEdges; QHash::const_iterator it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph:graphSymmetrize() - iterate over edges of v1 " << v1; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges.cbegin(); while ( it1!=enabledOutEdges.cend() ){ v2 = it1.key(); weight = it1.value(); qDebug() << "Graph:graphSymmetrize() - " << " v1 " << v1 << " outLinked to " << v2 << " weight " << weight; invertWeight = edgeExists(v2,v1); if ( invertWeight == 0 ) { qDebug() << "Graph:graphSymmetrize(): s = " << v1 << " is NOT inLinked from y = " << v2 ; edgeCreate( v2, v1, weight, initEdgeColor, false, true, false, QString(), false); } else { qDebug() << "Graph: graphSymmetrize(): v1 = " << v1 << " is already inLinked from v2 = " << v2 ; if (weight!= invertWeight ) edgeWeightSet(v2,v1,weight); } ++it1; } } //delete enabledOutEdges; m_graphIsSymmetric=true; graphSetModified(GraphChange::ChangedEdges); } /** * @brief Creates a new symmetric relation by keeping only strong-ties (mutual links) * in the current relation. In the new relation, two actors are connected only if * they are mutually connected in the current relation. * @param allRelations */ void Graph::graphSymmetrizeStrongTies(const bool &allRelations){ qDebug()<< "Graph::graphSymmetrizeStrongTies()" << "initial relations"< outEdgesAll; QHash::const_iterator it1; QHash *strongTies = new QHash; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph::graphSymmetrizeStrongTies() - v" << v1 << "iterate over outEdges in all relations"; outEdgesAll=(*it)->outEdgesEnabledHash(allRelations); //outEdgesAllRelationsUniqueHash(); it1=outEdgesAll.cbegin(); while ( it1!=outEdgesAll.cend() ){ v2 = it1.key(); weight = it1.value(); y=vpos[ v2 ]; qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "->" << v2 << "=" << weight << "Checking opposite."; invertWeight = m_graph[y]->hasEdgeTo( v1,allRelations ) ; if ( invertWeight == 0 ) { qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "<-" << v2 << " does not exist. Weak tie. Continue." ; } else { if (!strongTies->contains(QString::number(v1)+"--"+QString::number(v2)) && !strongTies->contains(QString::number(v2)+"--"+QString::number(v1)) ){ qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "--" << v2 << " exists. Strong Tie. Adding"; strongTies->insert(QString::number(v1)+"--"+QString::number(v2), 1); } else { qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "--" << v2 << " exists. Strong Tie already found. Continue"; } } ++it1; } } relationAdd("Strong Ties",true); QHash::const_iterator it2; it2=strongTies->constBegin(); QStringList vertices; qDebug() << "Graph::graphSymmetrizeStrongTies() - creating strong tie edges"; while ( it2!=strongTies->constEnd() ){ vertices = it2.key().split("--"); qDebug() << "Graph::graphSymmetrizeStrongTies() - tie " < 0, where C the Cocitation Matrix. * Thus the actor pairs cited by more common neighbors will appear * with a stronger tie between them than pairs those cited by fewer * common neighbors. The resulting relation is symmetric. */ void Graph::graphCocitation(){ qDebug()<< "Graph::graphCocitation()" << "initial relations"<printMatrixConsole(true); VList::const_iterator it, it1; relationAdd("Cocitation",true); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || ( (*it)->isIsolated() && dropIsolates) ) { continue; } v1 = (*it)->name(); j = 0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); it1++){ qDebug()<< "Graph::graphCocitation() - (i,j)" << i+1<isEnabled() || ( (*it1)->isIsolated() && dropIsolates) ) { continue; } v2 = (*it1)->name(); if (v1==v2) { j++; qDebug()<< "Graph::graphCocitation() - skipping self loop" << v1<item(i, j) ) != 0 ) { qDebug()<< "Graph::graphCocitation() - creating edge" << v1 << "<->" << v2 << "because CT(" << i+1 << "," << j+1 << ") = " << weight; edgeCreate( v1, v2, weight, initEdgeColor, EdgeType::Undirected, true, false, QString(), false); } j++; } i++; } m_graphIsSymmetric=true; graphSetModified(GraphChange::ChangedEdges); qDebug()<< "Graph::graphCocitation()" << "final relations"< outEdgesAll; QHash::const_iterator it1; QHash *binaryTies = new QHash; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph::graphDichotomization() - v" << v1 << "iterate over outEdges in all relations"; outEdgesAll=(*it)->outEdgesEnabledHash(false); it1=outEdgesAll.cbegin(); while ( it1!=outEdgesAll.cend() ){ v2 = it1.key(); weight = it1.value(); qDebug() << "Graph::graphDichotomization() - " << v1 << "->" << v2 << "=" << weight << "Checking opposite."; if (weight>threshold) { if (!binaryTies->contains(QString::number(v1)+"--"+QString::number(v2) ) ) { qDebug() << "Graph::graphDichotomization() - " << v1 << "--" << v2 << " over threshold. Adding"; binaryTies->insert(QString::number(v1)+"--"+QString::number(v2), 1); } else { qDebug() << "Graph::graphDichotomization() - " << v1 << "--" << v2 << " exists. Binary Tie already found. Continue"; } } ++it1; } } relationAdd("Binary-"+QString::number(threshold),true); QHash::const_iterator it2; it2=binaryTies->constBegin(); QStringList vertices; qDebug() << "Graph::graphDichotomization() - creating binary tie edges"; while ( it2!=binaryTies->constEnd() ){ vertices = it2.key().split("--"); qDebug() << "Graph::graphDichotomization() - binary tie " < enabledOutEdges; QHash::const_iterator it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph::graphSetDirected() - iterate over edges of v1 " << v1; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges.cbegin(); while ( it1!=enabledOutEdges.cend() ){ v2 = it1.key(); weight = it1.value(); qDebug() << "Graph::graphSetDirected() - " << " v1 " << v1 << " -> " << v2 << " = " << " weight " << weight; edgeTypeSet(v1,v2, weight, EdgeType::Undirected); ++it1; } } //delete enabledOutEdges; m_graphIsSymmetric=true; graphSetModified(GraphChange::ChangedEdges, signalMW); } /** * @brief Returns true if graph is directed * @return */ bool Graph::graphIsDirected() { qDebug() << "Graph::graphIsDirected() -" << m_graphIsDirected; return m_graphIsDirected; } /** * @brief Returns true if graph is undirected * @return */ bool Graph::graphIsUndirected() { qDebug() << "Graph::graphIsUndirected() -" << !m_graphIsDirected; return !m_graphIsDirected; } /** * @brief Changes the direction type of an existing edge * Mainly called from Graph::verticesCreateSubgraph() * Also called from graphUndirectedSet * @param v1 * @param v2 * @param weight */ void Graph::edgeTypeSet(const int &v1, const int &v2, const qreal &weight, const int &dirType) { qDebug() << "Graph::edgeTypeSet(): " << v1 << " -> " << v2 << "edgeType" << dirType; if (dirType!=EdgeType::Directed) { // check for opposite edge qreal inverseWeight = edgeExists ( v2, v1 ) ; if ( inverseWeight == 0 ) { // if the opposite edge does not exist, add it qDebug() << "Graph::edgeTypeSet(): opposite " << v1 << " <- " << v2 << " does not exist - Add it to Graph." ; // Note: Even if dirType=EdgeType::Undirected we add the opposite edge as EdgeType::Reciprocated edgeAdd(v2,v1, weight, EdgeType::Reciprocated, "", initEdgeColor); } else { if ( dirType == EdgeType::Undirected ) { // if the opposite edge does exist, equal edge weights // TOFIX: how do we decide which of the two weights to keep? qDebug() << "Graph::edgeTypeSet(): opposite " << v1 << " <- " << v2 << " exists - equaling weights." ; if ( weight!= inverseWeight ) { edgeWeightSet(v2,v1,weight); } } else { // if dirType is EdgeType::Reciprocated we don't need to equalize weights } } emit signalEdgeType( v1, v2, dirType ); } //graphSetModified(GraphChange::ChangedEdges); } /** * @brief Returns true if vertices v1 and v2 are reachable. * @param v1 * @param v2 * @return */ bool Graph::graphReachable(const int &v1, const int &v2) { qDebug()<< "Graph::reachable()"; graphDistancesGeodesic(false); return ( m_graph[ vpos[v1] ] ->distance( v2) != RAND_MAX ) ? true: false; } /** * @brief Creates the reachability matrix XRM */ void Graph::graphMatrixReachabilityCreate() { qDebug() << "Graph::graphMatrixReachabilityCreate()"; graphDistancesGeodesic(false); VList::const_iterator it, jt; int N = vertices( false, false, true); int progressCounter=0; int source = 0 , target = 0; int i = 0, j = 0 ; int reachVal = 0; XRM.resize(N, N); QString pMsg = tr("Creating reachability matrix. \nPlease wait "); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); qDebug() << "Graph: graphMatrixReachabilityCreate() - writing matrix..."; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); source = (*it)->name(); if ( ! (*it)->isEnabled() ) { qDebug() << "Graph: graphMatrixReachabilityCreate() - " << source << "disabled. SKIP"; continue; } qDebug() << "Graph: graphMatrixReachabilityCreate() - " << "source" << source << "i" << i; for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt) { target = (*jt)->name(); if ( ! (*jt)->isEnabled() ) { qDebug() << "Graph: graphMatrixReachabilityCreate() - " << target << "disabled. SKIP"; continue; } qDebug() << "Graph: graphMatrixReachabilityCreate() - " << "target" << target << "j" << j; reachVal = ((*it)->distance( target ) != RAND_MAX ) ? 1 : 0; qDebug() << "Graph: graphMatrixReachabilityCreate() - setting XRM ("<< i <<","<< j << ") =" << reachVal; XRM.setItem( i, j, reachVal ); j++; } j=0; i++; } emit signalProgressBoxKill(); } /** * @brief Returns the geodesic distance (length of shortest path) * from vertex v1 to vertex v2 * @param v1 * @param v2 * @param considerWeights * @param inverseWeights * @return */ int Graph::graphDistanceGeodesic(const int &v1, const int &v2, const bool &considerWeights, const bool &inverseWeights){ qDebug() <<"Graph::graphDistanceGeodesic()"; graphDistancesGeodesic(false, considerWeights, inverseWeights, false); return m_graph[ vpos[v1] ]->distance(v2); } /** * @brief Returns the diameter of the graph, aka the largest geodesic distance * between any two vertices * @param considerWeights * @param inverseWeights * @return */ int Graph::graphDiameter(const bool considerWeights, const bool inverseWeights){ qDebug () << "Graph::graphDiameter()" ; graphDistancesGeodesic(false, considerWeights, inverseWeights, false); return m_graphDiameter; } /** * @brief Returns the average distance of the graph * @param considerWeights * @param inverseWeights * @param dropIsolates * @return */ qreal Graph::graphDistanceGeodesicAverage(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug() <<"Graph::graphDistanceGeodesicAverage() - Computing distances..."; graphDistancesGeodesic(false, considerWeights, inverseWeights, dropIsolates); qDebug() <<"Graph::graphDistanceGeodesicAverage() - " << "average distance:" << m_graphAverageDistance; return m_graphAverageDistance; } /** * @brief Returns the number of geodesics (shortest-paths) in the graph. * @return */ int Graph::graphGeodesics() { qDebug()<< "Graph::graphGeodesics()"; graphDistancesGeodesic(false, false,false,false); qDebug()<< "Graph::graphGeodesics() - geodesics:" << m_graphGeodesicsCount; return m_graphGeodesicsCount; } /** * @brief Checks if the graph is connected, in the sense of a topological space, * i.e., there is a path from any vertex to any other vertex in the graph. * Called from MW::slotConnectedness() * @return bool */ bool Graph::graphIsConnected() { qDebug() << "Graph::graphIsConnected() "; if ( calculatedDistances ) { qDebug()<< "Graph::graphIsConnected() - graph unmodified. Returning:" << m_graphIsConnected; return m_graphIsConnected; } graphDistancesGeodesic(false, false,false,false); return m_graphIsConnected; } /** * @brief Creates the matrix SIGMA of shortest paths (geodesics) between vertices * Each SIGMA(i,j) is the number of shortest paths (geodesics) from i and j * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::graphMatrixShortestPathsCreate(const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::graphMatrixShortestPathsCreate()"; graphDistancesGeodesic(false,considerWeights,inverseWeights, dropIsolates); VList::const_iterator it, jt; int N = vertices( dropIsolates, false, true); int progressCounter=0; int source = 0 , target = 0; int i = 0, j = 0 ; qDebug() << "Graph::graphMatrixShortestPathsCreate() - Resizing matrix to hold " << N << " vertices"; SIGMA.resize(N, N); QString pMsg = tr("Creating shortest paths matrix. \nPlease wait "); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); qDebug() << "Graph::graphMatrixShortestPathsCreate() - Writing shortest paths matrix..."; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); source = (*it)->name(); if ( (*it)->isIsolated() && dropIsolates ) { qDebug() << "Graph::graphMatrixShortestPathsCreate() - " << source << "isolated. SKIP"; continue; } if ( ! (*it)->isEnabled() ) { qDebug() << "Graph::graphMatrixShortestPathsCreate() - " << source << "disabled. SKIP"; continue; } qDebug() << "Graph::graphMatrixShortestPathsCreate() - source" << source << "i" << i; for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt) { target = (*jt)->name(); if ( (*jt)->isIsolated() && dropIsolates ) { qDebug() << "Graph::graphMatrixShortestPathsCreate() - " << target << "isolated. SKIP"; continue; } if ( ! (*jt)->isEnabled() ) { qDebug() << "Graph::graphMatrixShortestPathsCreate() - " << target << "disabled. SKIP"; continue; } qDebug() << "Graph::graphMatrixShortestPathsCreate() - " << "target" << target << "j" << j; qDebug() << "Graph::graphMatrixShortestPathsCreate() - setting SIGMA (" << i <<","<< j << ") =" << (*it)->shortestPaths( target ) ; SIGMA.setItem( i, j, (*it)->shortestPaths( target ) ); j++; } j=0; i++; } emit signalProgressBoxKill(); } /** * @brief Creates the matrix DM of geodesic distances between vertices * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::graphMatrixDistanceGeodesicCreate(const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::graphMatrixDistanceGeodesicCreate()"; graphDistancesGeodesic(false,considerWeights,inverseWeights, dropIsolates); VList::const_iterator it, jt; int N = vertices( dropIsolates, false, true); int progressCounter=0; int source = 0 , target = 0; int i = 0, j = 0 ; qDebug() << "Graph::graphMatrixDistanceGeodesicCreate() - " "Resizing distance matrix to hold " << N << " vertices"; DM.resize(N, N); QString pMsg = tr("Creating geodesic distances matrix. \nPlease wait "); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - Writing distances matrix..."; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); source = (*it)->name(); if ( (*it)->isIsolated() && dropIsolates ) { qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - " << source << "isolated. SKIP"; continue; } if ( ! (*it)->isEnabled() ) { qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - " << source << "disabled. SKIP"; continue; } qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - source" << source << "i" << i; for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt) { target = (*jt)->name(); if ( (*jt)->isIsolated() && dropIsolates ) { qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - " << target << "isolated. SKIP"; continue; } if ( ! (*jt)->isEnabled() ) { qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - " << target << "disabled. SKIP"; continue; } qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - " << "target" << target << "j" << j; qDebug() << "Graph: graphMatrixDistanceGeodesicCreate() - setting DM (" << i <<","<< j << ") =" << (*it)->distance( target ) ; DM.setItem( i, j, (*it)->distance( target ) ); j++; } j=0; i++; } emit signalProgressBoxKill(); } /** * @brief Computes the geodesic distances between all vertices: * In the process, it also computes many other centrality/prestige metrics: * * The so-called sigma matrix, where the (i,j) element is the number of shortest paths * from vertex i to vertex j, called sigma(i,j). * * The Diameter of the graph, m_graphDiameter, which is the length of the longest * shortest path between every (i,j) * * The Eccentricity of every node i which is the length of the longest shortest * path from i to every other node j * * The InfluenceRange and InfluenceDomain of each node. * * The centralities for every u in V (if centralities=true): * - Betweenness: BC(u) = Sum ( sigma(i,j,u)/sigma(i,j) ) for every s,t in V * - Stress: SC(u) = Sum ( sigma(i,j) ) for every s,t in V * - Eccentricity: EC(u) = 1/maxDistance(u,t) for some t in V * - Closeness: CC(u) = 1 / Sum( d(u,t) ) for every t in V * - Power: * @param centralities * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::graphDistancesGeodesic(const bool &computeCentralities, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::graphDistancesGeodesic()" << "centralities" << computeCentralities << "considerWeights:"<::const_iterator it2; int w=0, u=0,s=0, si=0, ui=0, wi=0; //int i=0; int progressCounter=0; qDebug() << "Graph::graphDistancesGeodesic() - Recomputing geodesic distances."; //drop isolated vertices from calculations (i.e. std C and group C). int N = vertices(dropIsolates,false,true); int E = edgesEnabled(); QString pMsg = tr("Computing geodesic distances. \nPlease wait..."); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N, pMsg ); m_graphIsSymmetric = graphIsSymmetric(); qDebug() << "Graph::graphDistancesGeodesic() - m_graphIsSymmetric" << m_graphIsSymmetric ; if ( E == 0 ) { for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1) { // Set all pair-wise distances to RAND_MAX (*it)->setDistance((*it1)->name(), RAND_MAX); // Set all pair-wise shortest-path counts (sigmas) to 0 (*it)->setShortestPaths((*it1)->name(), 0); } } if ( N < 2 ) { //singleton graph consisting of a single isolated node //is considered connected m_graphIsConnected = true; } else { //any non-empty and non-singleton graph with zero edges is disconnected m_graphIsConnected = false; } } else { qDebug() << "Graph::graphDistancesGeodesic() - Initializing variables"; qreal distances_sum_for_s = 0, maxEdgeWeightInNetwork=0, tempEdgeWeight=0; qreal CC=0, BC=0, SC= 0, eccentricity=0, EC=0, PC=0; qreal SCC=0, SBC=0, SSC=0, SEC=0, SPC=0; qreal tempVarianceBC=0, tempVarianceSC=0,tempVarianceEC=0; qreal tempVarianceCC=0, tempVariancePC=0; qreal sigma_u=0, sigma_w=0; qreal delta_u=0, delta_w=0; qreal d_sw=0, d_su=0; qreal pairDistance = 0; m_graphIsConnected = true; H_f_i::const_iterator hfi ; // for Power Centrality qDebug() << "Graph: graphDistancesGeodesic() - initialising centrality variables "; maxSCC=0; minSCC=RAND_MAX; nomSCC=0; denomSCC=0; groupCC=0; maxNodeSCC=0; minNodeSCC=0; sumSCC=0; sumCC=0; discreteCCs.clear(); classesSCC=0; maxSBC=0; minSBC=RAND_MAX; nomSBC=0; denomSBC=0; groupSBC=0; maxNodeSBC=0; minNodeSBC=0; sumBC=0; sumSBC=0; discreteBCs.clear(); classesSBC=0; maxSSC=0; minSSC=RAND_MAX; groupSC=0; maxNodeSSC=0; minNodeSSC=0;sumSC=0; sumSSC=0; discreteSCs.clear(); classesSSC=0; maxSPC=0; minSPC=RAND_MAX; nomSPC=0; denomSPC=0; groupSPC=0; maxNodeSPC=0; minNodeSPC=0; sumSPC=0;sumPC=0; discretePCs.clear(); classesSPC=0; maxEccentricity=0; minEccentricity=RAND_MAX; maxNodeEccentricity=0; minNodeEccentricity=0; discreteEccentricities.clear(); classesEccentricity=0; maxSPC=0; minSPC=RAND_MAX; maxNodeSPC=0; minNodeSPC=0; sumSPC=0; maxEC=0; minEC=RAND_MAX; nomEC=0; denomEC=0; groupEC=0; maxNodeEC=0; minNodeEC=0; sumEC=0; discreteECs.clear(); classesEC=0; m_graphDiameter=0; calculatedDistances = false; m_graphAverageDistance=0; m_graphSumDistance = 0; m_graphGeodesicsCount = 0; //non zero distances // Stores vertex pairs not connected // Vertices in keys have // Infinite Eccentricity // Zero Eccentricity Centrality // Zero Closeness Centrality m_vertexPairsNotConnected.clear(); qDebug() << " m_graphDiameter "<< m_graphDiameter << " m_graphAverageDistance " <clearDistance(); // Set all pair-wise shortest-path counts (sigmas) to 0 // (*it)->setShortestPaths((*it1)->name(), 0); (*it)->clearShortestPaths(); if (considerWeights && inverseWeights) { // find the max weight in the network. // it will be used for maxCC below tempEdgeWeight = (*it)->hasEdgeTo((*it1)->name()); if ( tempEdgeWeight > maxEdgeWeightInNetwork ) { maxEdgeWeightInNetwork = tempEdgeWeight; } } } //Zero centrality scores for each vertex if (computeCentralities) { qDebug() << " Graph:graphDistancesGeodesic() -" "Initializing actor centrality indices"; (*it)->setBC( 0.0 ); (*it)->setSC( 0.0 ); (*it)->setEccentricity( 0.0 ); (*it)->setEC( 0.0 ); (*it)->setCC( 0.0 ); (*it)->setIRCC( 0.0 ); (*it)->setPC( 0.0 ); } } qDebug() << "Graph: graphDistancesGeodesic() - " " initialising variables for max centrality scores"; if (m_graphIsSymmetric) { maxIndexBC= ( N == 2 ) ? 1 : ( N-1.0 ) * ( N-2.0 ) / 2.0; maxIndexSC= ( N == 2 ) ? 1 : ( N-1.0 ) * ( N-2.0 ) / 2.0; maxIndexCC=N-1.0; maxIndexPC=N-1.0; qDebug("############# m_graphIsSymmetric - maxIndexBC %f, maxIndexCC %f, maxIndexSC %f", maxIndexBC, maxIndexCC, maxIndexSC); } else { maxIndexBC= ( N == 2 ) ? 1 : ( N-1.0 ) * ( N-2.0 ); // fix N=2 case where maxIndex becomes zero maxIndexSC= ( N == 2 ) ? 1 : ( N-1.0 ) * ( N-2.0 ); maxIndexPC=N-1.0; maxIndexCC=N-1.0; qDebug("############# NOT SymmetricAdjacencyMatrix - maxIndexBC %f, maxIndexCC %f, maxIndexSC %f", maxIndexBC, maxIndexCC, maxIndexSC); } if (considerWeights && inverseWeights) { maxIndexCC = maxIndexCC * (1.0 / maxEdgeWeightInNetwork); } qDebug() << "*********** MAIN LOOP: " "for every s in V solve the Single Source Shortest Path (SSSP) problem..."; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { s=(*it)->name(); si=vpos[s]; distances_sum_for_s = 0; qDebug()<< "***** PHASE 1 (SSSP): " << "Source vertex s" << s << "vpos" << si; emit signalProgressBoxUpdate( ++progressCounter ); if ( ! (*it)->isEnabled() ) { qDebug()<< "***** PHASE 1 (SSSP): s" << s << "disabled. SKIP/CONTINUE"; continue; } if (computeCentralities) { qDebug()<< "***** PHASE 1 (SSSP): " "Empty Stack which will return vertices in " "order of their (non increasing) distance from s ..."; //- Complexity linear O(n) while ( !Stack.empty() ) { Stack.pop(); } qDebug()<< "***** PHASE 1 (SSSP): " "...and for each vertex: empty list Ps of predecessors"; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1) { (*it1)->clearPs(); } sizeOfNthOrderNeighborhood.clear(); } qDebug()<< "***** PHASE 1 (SSSP): " "Call BFS or dijkstra for s" << s << " vpos " << si << " to compute distance and shortest paths to every vertex t" ; if (!considerWeights) { BFS(s,si,computeCentralities, dropIsolates ); } else { dijkstra(s, si,computeCentralities, inverseWeights, dropIsolates); } qDebug()<< "***** PHASE 1 (SSSP): " "FINISHED BFS / DIJKSTRA ALGORITHM. " "Continuing to calculate centralities"; if (computeCentralities) { qDebug()<< "***** PHASE 2 (CENTRALITIES): " "s" << s << "vpos" << si << "CC" << CC; // Compute 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. // N is the sum Nd0 + Nd1 + Nd2 + ... + Ndi, that is the amount of nodes in the same component as the current node sizeOfComponent = 1; PC=0; hfi = sizeOfNthOrderNeighborhood.constBegin(); //FIXME do we need to check for disabled nodes somewhere? while (hfi != sizeOfNthOrderNeighborhood.constEnd()) { qDebug() << " sizeOfNthOrderNeighborhood.value("<< hfi.key() <<")" << hfi.value(); PC += ( 1.0 / hfi.key() ) * hfi.value(); sizeOfComponent += hfi.value(); ++hfi; } (*it)->setPC( PC ); sumPC += PC; if ( sizeOfComponent != 1 ) SPC = ( 1.0/(sizeOfComponent-1.0) ) * PC; else SPC = 0; (*it)->setSPC( SPC ); //Set std PC sumSPC += SPC; //add to sumSPC -- used later to compute mean and variance qDebug()<< "***** PHASE 2 (CENTRALITIES): " "s" << s << "vpos" << si << "PC" << PC; // Compute Betweenness Centrality qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "Start back propagation of dependencies." << "Set dependency delta[u]=0 on each vertex"; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ (*it1)->setDelta(0.0); // compute sum of distances from current vertex to every other vertex distances_sum_for_s += (*it)->distance( (*it1)->name() ) ; qDebug() << " Compute Centralities: " "For CC: sum of distances. distance(" << (*it)->name() << "," << (*it1)->name() << ") = " << (*it)->distance( (*it1)->name() ) << "new sum of distances for s =" << distances_sum_for_s; } qDebug() << " Compute Centralities: " "For CC: total sum of distances for s =" << distances_sum_for_s; m_graphSumDistance += distances_sum_for_s; // Compute Closeness Centrality if ( distances_sum_for_s != 0 && distances_sum_for_s < RAND_MAX) { // Connected actor: // There is a path from this actor to all others // Invert the sum of distances and set it as CC CC=1.0/distances_sum_for_s; } else { // Not connected actor. Cases: // a) Isolated: The actor has no outbound links // b) Disconnected graph: There is no path from this actor // to some of the other actors, which means her distance to // them is infinite // For these two cases, set CC as zero. CC=0; } (*it)->setCC( CC ); qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "Visit all vertices in reverse order of their discovery (from s = " << s << " ) to sum dependencies. Initial Stack size " << Stack.size(); while ( !Stack.empty() ) { w=Stack.top(); wi=vpos[w]; qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "Stack top is vertex w " << w << "This is the furthest vertex from s. Popping it."; Stack.pop(); QList lst=m_graph[wi]->Ps(); qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "preLOOP: Size of predecessors list Ps[w]"<< lst.size(); qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "LOOP over every vertex u in Ps of w"< 0) // just in case...do a sanity check for ( it2=lst.cbegin(); it2 != lst.cend(); it2++ ){ u=(*it2); ui=vpos[u]; sigma_u=m_graph[si]->shortestPaths(u); sigma_w=m_graph[si]->shortestPaths(w); delta_u=m_graph[ui]->delta(); delta_w=m_graph[wi]->delta(); qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "Selecting Ps[w] element u"<< u << "with delta_u" << delta_u << "sigma(s,u)"<< sigma_u << "sigma(s,w)" << sigma_w << "delta_w"<< delta_w; if ( m_graph[si]->shortestPaths(w) > 0 ) { //delta[u]=delta[u]+(1+delta[w])*(sigma[u]/sigma[w]) ; d_su=delta_u + ( 1.0 + delta_w ) * ( (qreal) sigma_u / (qreal)sigma_w); } else { d_su=delta_u; qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "zero shortest paths from s to w - " "using SAME DELTA for vertex u"; } qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "Assigning new delta d_su" << d_su << " to u" << u; m_graph[ui]->setDelta( d_su); } // end for qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "Adding delta_w to BC of w"; if (w!=s) { qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "w!=s. For this furthest vertex we need to add its new delta" << delta_w << "to old BC index:" << m_graph[wi]->BC(); d_sw = m_graph[wi]->BC() + delta_w; qDebug()<< "***** PHASE 2 (BC/ACCUMULATION): " "s" << s << "vpos" << si << "BC = d_sw" << d_sw; m_graph[wi]->setBC (d_sw); } // END if } // END while stack } // END if computeCentralities } // END for SSSP problem qDebug() << "*********** MAIN LOOP (SSSP problem): FINISHED."; // check if there are disconnected nodes // and get the distance sums qDebug() << "Checking if there are disconnected nodes"; m_graphIsConnected = true; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( ! (*it)->isEnabled() ) { qDebug()<< "actor i" << (*it)->name() << "disabled. SKIP/CONTINUE"; continue; } pairDistance = 0; for ( it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) { qDebug()<< " actor j" << (*it1)->name() << "disabled. SKIP/CONTINUE"; continue; } if ( (*it1)->name() == (*it)->name() ) { qDebug()<< " == actor j" << (*it1)->name() << "SKIP/CONTINUE"; continue; } pairDistance = (*it)-> distance ( (*it1)->name() ); if ( pairDistance == RAND_MAX) { m_vertexPairsNotConnected.insert((*it)->name(), (*it1)->name()); (*it)->setEccentricity( RAND_MAX ); m_graphIsConnected = false; qDebug()<< "actor i" << (*it)->name() << "has infinite eccentricity. " "There is no path from it to actor j" << (*it1)->name(); } else { qDebug()<< "actor i" << (*it)->name() <<"distanceSum" << (*it)->distanceSum(); (*it)->setDistanceSum( (*it)->distanceSum() + pairDistance); } } // end for qDebug()<< "actor i" << (*it)->name() <<"Final distanceSum" << (*it)->distanceSum(); if (computeCentralities) { // Compute Eccentricity (max geodesic distance) eccentricity = (*it)->eccentricity(); qDebug() << "actor" << (*it)->name() << "eccentricity" << eccentricity; if ( eccentricity != RAND_MAX ) { //Find min/max Eccentricity minmax( eccentricity, (*it), maxEccentricity, minEccentricity, maxNodeEccentricity, minNodeEccentricity) ; resolveClasses(eccentricity, discreteEccentricities, classesEccentricity ,(*it)->name() ); //Eccentricity Centrality is the inverted Eccentricity EC=1.0 / eccentricity; (*it)->setEC( EC ); //Set Eccentricity Centrality (*it)->setSEC( EC ); //Set std EC = EC sumEC+=EC; //set sum EC qDebug()<< "actor i" << (*it)->name() << "EC" << EC; } else { EC=0; (*it)->setEC( EC ); //Set Eccentricity Centrality (*it)->setSEC( EC ); //Set std EC = EC sumEC+=EC; //set sum EC qDebug()<< "actor i" << (*it)->name() << "EC=0 (disconnected graph)"; } } // end if compute centralities } // end for disconnected checking // Compute average path length... if (m_vertexPairsNotConnected.count()==0) { m_graphAverageDistance = m_graphSumDistance / ( N * ( N-1.0 ) ); qDebug() <<"Graph::graphDistancesGeodesic() - Average distance:" << m_graphAverageDistance ; } else { //TODO In not connected nets, it would be nice to ask the user what to do // with unconnected pairs (make M or drop (default?) qDebug() <<"Graph::graphDistancesGeodesic() - Average distance:" << m_graphAverageDistance ; m_graphAverageDistance = m_graphSumDistance / m_graphGeodesicsCount; } if (computeCentralities) { qDebug() << "Graph: graphDistancesGeodesic() - " "Computing 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/maxSPC SPC = (*it)->SPC(); //same as PC resolveClasses(SPC, discretePCs, classesSPC,(*it)->name() ); minmax( SPC, (*it), maxSPC, minSPC, maxNodeSPC, minNodeSPC) ; // Compute std BC, classes and min/maxSBC if (m_graphIsSymmetric) { qDebug()<< "Betweenness centrality must be divided by" <<" two if the graph is undirected"; (*it)->setBC ( (*it)->BC()/2.0); } BC=(*it)->BC(); sumBC+=BC; SBC = BC/maxIndexBC; (*it)->setSBC( SBC ); resolveClasses(SBC, discreteBCs, classesSBC); sumSBC+=SBC; minmax( SBC, (*it), maxSBC, minSBC, maxNodeSBC, minNodeSBC) ; // Compute std CC, classes and min/maxSCC CC = (*it)->CC(); sumCC+=CC; SCC = maxIndexCC * CC; (*it)->setSCC ( SCC ); resolveClasses(SCC, discreteCCs, classesSCC,(*it)->name() ); sumSCC+=SCC; minmax( SCC, (*it), maxSCC, minSCC, maxNodeSCC, minNodeSCC) ; //prepare to compute stdSC SC=(*it)->SC(); if (m_graphIsSymmetric){ (*it)->setSC(SC/2.0); SC=(*it)->SC(); qDebug() << "SC of " <<(*it)->name() << " divided by 2 (because the graph is symmetric) " << (*it)->SC(); } sumSC+=SC; qDebug() << "vertex " << (*it)->name() << " - " << " EC: "<< (*it)->EC() << " CC: "<< (*it)->CC() << " BC: "<< (*it)->BC() << " SC: "<< (*it)->SC() << " PC: "<< (*it)->PC(); } // end for qDebug() << "Graph: graphDistancesGeodesic() -" "Computing mean centrality values..."; // Compute mean values and prepare to compute variances meanSBC = sumSBC /(qreal) N ; varianceSBC=0; tempVarianceBC=0; meanSCC = sumSCC /(qreal) N ; varianceSCC=0; tempVarianceCC=0; meanSPC = sumSPC /(qreal) N ; varianceSPC=0; tempVariancePC=0; meanEC = sumEC /(qreal) N ; varianceEC=0; tempVarianceEC=0; qDebug() << "Graph: graphDistancesGeodesic() - " "Computing std centralities ..."; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ) { continue; } // Compute std SC, classes and min/maxSSC SC=(*it)->SC(); SSC=SC/sumSC; (*it)->setSSC(SSC); resolveClasses(SSC, discreteSCs, classesSSC); sumSSC+=SSC; minmax( SSC, (*it), maxSSC, minSSC, maxNodeSSC, minNodeSSC) ; //Compute numerator of groupSBC SBC=(*it)->SBC(); nomSBC +=(maxSBC - SBC ); //calculate BC variance tempVarianceBC = ( SBC - meanSBC ) ; tempVarianceBC *=tempVarianceBC; varianceSBC += tempVarianceBC; //Compute numerator of groupCC nomSCC += maxSCC- (*it)->SCC(); //calculate CC variance tempVarianceCC = ( (*it)->SCC() - meanSCC ) ; tempVarianceCC *=tempVarianceCC; varianceSCC += tempVarianceCC; //Compute numerator of groupSPC SPC=(*it)->SPC(); nomSPC +=(maxSPC - SPC ); //calculate PC variance tempVariancePC = ( (*it)->SPC() - meanSPC ) ; tempVariancePC *=tempVariancePC; varianceSPC += tempVariancePC; //calculate EC variance tempVarianceEC = ( (*it)->EC() - meanEC ) ; tempVarianceEC *=tempVarianceEC; varianceEC += tempVarianceEC; } // end for //compute final variances varianceSBC /= (qreal) N; varianceSCC /= (qreal) N; varianceSPC /= (qreal) N; varianceEC /= (qreal) N; // calculate SC mean value and prepare to compute variance meanSSC = sumSSC /(qreal) N ; varianceSSC=0; tempVarianceSC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ){ continue; } tempVarianceSC = ( (*it)->SSC() - meanSSC ) ; tempVarianceSC *=tempVarianceSC; varianceSSC += tempVarianceSC; } //calculate final SC variance varianceSSC /= (qreal) N; denomSPC = ( (N-2.0) ) / (2.0 ); //only for connected nets if (N < 3 ) denomSPC = N-1.0; //what if the net is disconnected (isolates exist) ? groupSPC = nomSPC/denomSPC; denomSCC = ( ( N-1.0) * (N-2.0) ) / (2.0 * N -3.0); if (N < 3 ) denomSCC = N-1.0; groupCC = nomSCC/denomSCC; //Calculate group Closeness centrality //nomSBC*=2.0; // denomSBC = (N-1.0) * (N-1.0) * (N-2.0); denomSBC = (N-1.0) ; // Wasserman&Faust - formula 5.14 groupSBC=nomSBC/denomSBC; //Calculate group Betweenness centrality calculatedCentralities=true; } // END if computeCentralities } // END else (aka E!=0) calculatedDistances=true; qDebug() << "Graph::graphDistancesGeodesic()- FINISHED computing distances"; emit signalProgressBoxKill(); } /** * Breadth-First Search (BFS) method for unweighted graphs (directed or not) INPUT: a 'source' vertex with vpos s and a boolean computeCentralities. (Implicitly, BFS uses the m_graph structure) OUTPUT: For every vertex t: d(s, t) is set to the distance of each t from s For every vertex t: s(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 , 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(const int &s, const int &si, const bool &computeCentralities, const bool &dropIsolates){ Q_UNUSED(dropIsolates); qDebug()<< "BFS:"; int u=0, ui=0 ,w=0, wi=0; int dist_u=0, temp=0, dist_w=0; int relation=0; //int weight=0; bool edgeStatus=false; H_edges::const_iterator it1; //set distance of s from s equal to 0 m_graph[si]->setDistance(s,0); //set sigma of s from s equal to 1 m_graph[si]->setShortestPaths(s,1); // qDebug("BFS: Construct a queue Q of integers and push source vertex s=%i to Q as initial vertex", s); queue Q; Q.push(s); qDebug()<< "BFS: LOOP: While Q not empty "; while ( !Q.empty() ) { u=Q.front(); Q.pop(); ui=vpos[u]; qDebug()<< "BFS: Dequeue: first element of Q is u"<isEnabled() ) { continue ; } if (computeCentralities){ qDebug()<< "BFS: Compute centralities: Pushing u" << u << "to global Stack "; Stack.push(u); } qDebug() << "BFS: LOOP over every edge (u,w) e E, that is all neighbors w of vertex u"; it1=m_graph [ ui ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ ui ] -> m_outEdges.cend() ){ relation = it1.value().first; if ( relation != relationCurrent() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } w = it1.key(); // weight = it1.value().second.first; wi=vpos[ w ]; qDebug("BFS: u=%i is connected with node w=%i of vpos wi=%i. ", u, w, wi); qDebug("BFS: Start path discovery"); //if distance (s,w) is infinite, w found for the first time. if ( m_graph [ si ]->distance( w ) == RAND_MAX ) { 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) = infinite and set it to zero"; dist_u=m_graph [ si ]->distance( u ); dist_w = dist_u + 1; qDebug() << "BFS: Setting dist_w = d ( s" << s << ", w"<setDistance(w,dist_w); m_graphSumDistance += dist_w; m_graphGeodesicsCount++; qDebug()<< "== BFS - d(" << s <<"," << w <<")=" << m_graph[si]->distance(w) ; 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,0)+1 ); qDebug()<<"BFS: Calculate CC: the sum of distances (will invert it l8r)"; m_graph [si]->setCC (m_graph [si]->CC() + dist_w); qDebug()<<"BFS: Calculate Eccentricity: the maximum distance "; if (m_graph [si]->eccentricity() < dist_w ) m_graph [si]->setEccentricity(dist_w); } // qDebug("BFS: Checking m_graphDiameter"); if ( dist_w > m_graphDiameter){ m_graphDiameter=dist_w; // qDebug() << "BFS: new m_graphDiameter = " << m_graphDiameter ; } } qDebug()<< "BFS: Start path counting"; //Is edge (u,w) on a shortest path from s to w via u? if ( m_graph[si]->distance(w) == m_graph[si]->distance(u) + 1) { temp=m_graph[si]->shortestPaths(w)+m_graph[si]->shortestPaths(u); qDebug()<< "BFS: Found a NEW SHORTEST PATH from s" << s << "to w"<< w << "via u"<< u << "Setting Sigma(s, w)"<< temp; if (s!=w) { m_graph[si]->setShortestPaths(w, temp); } if (computeCentralities){ qDebug()<< "BFS/SC: Computing centralities: Computing SC "; if ( s!=w && s != u && u!=w ) { qDebug() << "BFS: setSC of u="<SC()+1; m_graph[ui]->setSC( m_graph[ui]->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[wi]->appendToPs(u); } } ++it1; } } } /** * Dijkstra's algorithm for solving the SSSP problem in weighted graphs (directed or not). * It uses a min-priority queue prQ to provide constant time lookup of the minimum * distance. The priority queue is implemented with std::priority_queue INPUT: a 'source' vertex with vpos s and a boolean computeCentralities. (Implicitly, the algorithm uses the m_graph structure) OUTPUT: For every vertex t: d(s, t) is set to the distance of each t from s For every vertex t: s(s, t) is set to the number of shortest paths between s and t Also, if computeCentralities is true then it 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 prQ is pushed to a stack Stack */ void Graph::dijkstra(const int &s, const int &si, const bool &computeCentralities, const bool &inverseWeights, const bool &dropIsolates){ Q_UNUSED(dropIsolates); int u=0,ui=0, w=0, wi=0, v=0, temp=0; int relation=0; qreal weight=0, dist_u=0, dist_w=0, old_dist_w=0; bool edgeStatus=false; H_edges::const_iterator it1; VList::const_iterator it; qDebug() << "### dijkstra: Construct a priority queue prQ of all vertices-distances"; // TODO: Check prQ functioanality in weighted graphs, where edge weight denotes value (not cost) priority_queue, GraphDistancesCompare> prQ; //set d( s, s ) = 0 m_graph[si]->setDistance(s,0); //set sp ( s , s ) = 1 m_graph[si]->setShortestPaths(s,1); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { v=vpos[ (*it)->name() ]; if (v != s ){ // NOTE: d(i,j) init to RAND_MAX already done in graphDistancesGeodesic // qDebug() << " push " << v << " to prQ with infinite distance from s"; // prQ.push(GraphDistance(v,RAND_MAX)); //TODO // Previous node in optimal path from source // previous[v] := undefined } } qDebug() << "### dijkstra: push s" << s << "to prQ with 0 distance from s"; //crucial: without it the priority prQ would pop arbitrary node at first loop prQ.push(GraphDistance(s,0)); qDebug()<<"### dijkstra: prQ size "<< prQ.size(); qDebug() << "### dijkstra: LOOP: While prQ not empty "; while ( !prQ.empty() ) { u=prQ.top().target; ui=vpos[u]; qDebug()<< " *** dijkstra: take u"<< u << "vpos" << ui << " from prQ. It has minimum distance from s =" << s; prQ.pop(); if ( ! m_graph [ ui ]->isEnabled() ) continue ; if (computeCentralities){ qDebug()<< " *** dijkstra: Compute centralities: pushing u =" << u << " to global Stack "; Stack.push(u); } qDebug() << " --- dijkstra: LOOP over every edge ("<< u <<", w ) e E... "; it1=m_graph [ ui ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ ui ] -> m_outEdges.cend() ) { relation = it1.value().first; if ( relation != relationCurrent() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } w = it1.key(); wi=vpos[ w ]; weight = it1.value().second.first; qDebug()<<" --- dijkstra: edge (u, w) = ("<< u << ","<< w << ") =" << weight; if (inverseWeights) { //only invert if user asked to do so weight = 1.0 / weight; qDebug () << " --- dijkstra: inverting weight to " << weight; } qDebug() <<" --- dijkstra: Start path discovery"; dist_u=m_graph [ si ]->distance( 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 << "=" <distance( w ); qDebug() << " --- dijkstra: RELAXATION: check if dist_w =" << dist_w << " shorter than current d(s=" << s <<",w="<shortestPaths(w) + m_graph[si]->shortestPaths(u); // WRONG! We do not know for sure that we are in a shortest path!!! qDebug() <<" --- dijkstra: Found ANOTHER SP from s =" << s << " to w=" << w << " via u="<< u << " - Setting Sigma(s, w) = "<< temp; if (s!=w) { m_graph[si]->setShortestPaths(w, temp); } if (computeCentralities){ if ( s!=w && s != u && u!=w ) { qDebug() << " --- dijkstra: Compute Centralities: " "setSC of u" << u <<"to" << m_graph[ui]->SC()+1; m_graph[ui]->setSC(m_graph[ui]->SC()+1); } else { qDebug() << " --- dijkstra: Compute Centralities: " "Skipping setSC of u, because s=" <SC(); qDebug() << " --- dijkstra: Compute Centralities: " "Appending u="<< u << " to list Ps[w =" << w << "] with the predecessors of w on all shortest paths from s "; m_graph[wi]->appendToPs(u); } } else if (dist_w > 0 && dist_w < old_dist_w ) { qDebug() <<" --- dijkstra: dist_w " << dist_w << " < current d(s,w) =" << old_dist_w << " Pushing w" << w<< "to prQ with distance"<< dist_w << "from s"< which is sorted (minimum) // and also provides contain() m_graph[si]->setDistance(w,dist_w); m_graphGeodesicsCount++; qDebug() << " --- dijkstra: " "Set d ( s=" << s << ", w="<< w << " ) = "<< dist_w << "="<< m_graph[si]->distance(w); if ( dist_w > m_graphDiameter){ m_graphDiameter=dist_w; qDebug() << " --- dijkstra: " "New graph diameter =" << m_graphDiameter ; } if (s!=w) { qDebug() << " --- dijkstra: " "Found NEW shortest path from s =" << s << " to w =" << w << " via u ="<< u << " - Setting Sigma(s, w) = 1 "; m_graph[si]->setShortestPaths(w, 1); } if (computeCentralities){ sizeOfNthOrderNeighborhood.insert( dist_w, sizeOfNthOrderNeighborhood.value(dist_w,0)+1 ); qDebug() << " --- dijkstra: Compute Centralities: " "For PC: sizeOfNthOrderNeighborhood: number of nodes at distance " << dist_w << "from s is " << sizeOfNthOrderNeighborhood.value(dist_w,0); if (m_graph [si]->eccentricity() < dist_w ) { m_graph [si]->setEccentricity(dist_w); qDebug() << " --- dijkstra: Compute Centralities: " "For EC: max distance =" << m_graph [si]->eccentricity(); } qDebug() << " --- dijkstra: Compute Centralities: " "Appending u="<< u << " to list Ps[w =" << w << "] with the predecessors of w on all shortest paths from s "; m_graph[wi]->appendToPs(u); } } else { qDebug() << " --- dijkstra: " "NOT a new SP"; } ++it1; } // END loop for every outEdge of u qDebug() << " --- dijkstra: LOOP END over every edge ("<< u <<", w ) e E... "; } // END loop while prQ not empty qDebug() << "### dijkstra: LOOP END. prQ is empty - Returning."; } /** * @brief Computes minimum and maximum centralities during graphDistancesGeodesic() * @param C * @param v * @param max * @param min * @param maxNode * @param minNode */ void Graph::minmax(qreal C, GraphVertex *v, qreal &max, qreal &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(); } } /** * @brief Checks if score C is a new prominence class * If yes, it stores that number in a QHash type where the score is the key. * If no, increases the frequency of this prominence score by 1 * Called from graphDistancesGeodesic() * @param C * @param discreteClasses * @param classes */ void Graph::resolveClasses(qreal C, H_StrToInt &discreteClasses, int &classes){ int frq = 0; H_StrToInt::iterator it2; it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), 1); } else { frq = it2.value() ; discreteClasses.insert(QString::number(C), frq + 1); } } /** * @brief Overloaded method. It only adds displaying current vertex for debugging purposes. * @param C * @param discreteClasses * @param classes * @param vertex */ void Graph::resolveClasses(qreal C, H_StrToInt &discreteClasses, int &classes, int vertex){ int frq = 0; 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), 1); } else { frq = it2.value() ; discreteClasses.insert(QString::number(C), frq + 1); } } /** * @brief Graph::writeMatrixDistancesPlainText * Writes the matrix of distances to a file * @param fn * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeMatrixDistancesPlainText (const QString &fn, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug ("Graph::writeMatrixDistancesPlainText()"); graphMatrixDistanceGeodesicCreate(considerWeights, inverseWeights, dropIsolates); qDebug ("Graph::writeMatrixDistancesPlainText() writing to file"); QFile file (fn); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText(&file); outText.setCodec("UTF-8"); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "-Social Network Visualizer "<< VERSION <"; outText << tr("ECCENTRICITY (e) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The eccentricity e measures how far, at most, is each " " node from every other node.
" "In a connected graph, the eccentricity e of a vertex " "is the maximum geodesic distance between that vertex and all other vertices.
" "In a disconnected graph, the eccentricity e of all vertices " "is considered to be infinite.") << "

"; outText << "

" << "" << tr("e range: ") <<"" << tr("1 ≤ e ≤ \xE2\x88\x9E") << "

"; outText << ""; outText << "" <<"" <<"" << "" <<"" <<"" << "" <<""; VList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; eccentr = (*it)->eccentricity(); qDebug() << "Graph::writeEccentricity() - actor " << (*it)->name() << "eccentricity" << eccentr; if ( ! (*it) ->isEnabled() ) { qDebug() << "Graph::writeEccentricity() - actor disabled. SKIP."; continue; // do not print disabled nodes } outText << "" <<"" <<""; } outText << "
" << tr("Actor") << "" << tr("Label") << "" << tr("e") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << ((eccentr == 0 || eccentr == RAND_MAX ) ? "\xE2\x88\x9E" : QString::number(eccentr) ) << "
"; if ( minEccentricity == maxEccentricity) { outText << "

" << tr("All nodes have the same eccentricity.") << "

"; } else { outText << "

"; outText << "" << tr("Max e (Graph Diameter) = ") <<"" << maxEccentricity <<" (node "<< maxNodeEccentricity << ")" << "
" << "" << tr("Min e (Graph Radius) = ") <<"" << minEccentricity <<" (node "<< minNodeEccentricity << ")" << "
" << "" << tr("e classes = ") <<"" << classesEccentricity << "

"; } outText << "

"; outText << "" << tr("e = 1 ") <<"" << tr("when the node is connected to all others (star node).") <<"
" << "" << tr("e > 1 ") <<"" << tr("when the node is not directly connected to all others. " "Larger eccentricity means the actor is farther from others.") <<"
" << "" << tr("e = \xE2\x88\x9E ") <<"" << tr("there is no path from that node to one or more other nodes.") <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Eccentricity Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Computes 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 * @param considerWeights * @param inverseWeights */ void Graph::centralityInformation(const bool considerWeights, const bool inverseWeights){ qDebug()<< "Graph::centralityInformation()"; if ( calculatedIC ) { qDebug()<< "Graph::centralityInformation() - already computed. Return."; return; } discreteICs.clear(); sumIC=0; maxIC=0; t_sumIC=0; minIC=RAND_MAX; classesIC=0; varianceIC=0; VList::const_iterator it; int i=0, j=0; qreal m_weight=0, weightSum=1, diagonalEntriesSum=0, rowSum=0; qreal IC=0, SIC=0; /* Note: isolated nodes must be dropped from the AM Otherwise, the SIGMA matrix might be singular, therefore non-invertible. */ bool dropIsolates=true; bool symmetrize=true; int n=vertices(dropIsolates,false,true); graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); QString pMsg = tr("Computing Information Centralities. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(n,pMsg); WM.resize( n, n ); invM.resize(n, n); for (i=0; iisIsolated() ) { (*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) ; } qreal x=0; meanIC = sumIC /static_cast (n) ; varianceIC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ x = ( (*it)->SIC() - meanIC ) ; x *=x; varianceIC += x; } varianceIC /= (qreal) n; calculatedIC = true; WM.clear(); emit signalProgressBoxUpdate(n); emit signalProgressBoxKill(); } /** * @brief Writes the information centralities to file * @param fileName * @param considerWeights * @param inverseWeights */ void Graph::writeCentralityInformation(const QString fileName, const bool considerWeights, const bool inverseWeights){ qDebug() << "Graph::writeCentralityInformation()"; QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); centralityInformation(considerWeights, inverseWeights); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::IC, m_reportsChartType, distImageFileName); VList::const_iterator it; bool dropIsolates = true; // by default IC needs to exclude isolates int rowCount=0; int N = vertices(dropIsolates, false, true); int progressCounter = 0; QString pMsg = tr("Writing Information Centralities to file. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << htmlHead; outText << "

"; outText << tr("INFORMATION CENTRALITY (IC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The IC index, introduced by Stephenson and Zelen (1991), measures the " "information flow through all paths between actors weighted by " "strength of tie and distance.") << "
" << tr("IC' is the standardized index (IC divided by the sumIC).") << "
" << tr ("Warning: To compute this index, SocNetV drops all isolated " "nodes and symmetrizes (if needed) the adjacency matrix.
" "Read the Manual for more.") << "

"; outText << "

" << "" << tr("IC range: ") <<"" << tr("0 ≤ IC ≤ \xE2\x88\x9E") << "

"; outText << "

" << "" << tr("IC' range: ") <<"" << tr("0 ≤ IC' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("IC") << "" << tr("IC'") << "" << tr("%IC") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->IC() << "" << (*it)->SIC() << "" << (100* ((*it)->SIC())) << "
"; if ( minIC == maxIC) { outText << "

" << tr("All nodes have the same IC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max IC' = ") <<"" << maxIC <<" (node "<< maxNodeIC << ")" << "
" << "" << tr("Min IC' = ") <<"" << minIC <<" (node "<< minNodeIC << ")" << "
" << "" << tr("IC classes = ") <<"" << classesIC << "

"; } outText << "

"; outText << "" << tr("IC' Sum = ") <<"" << sumIC <<"
" << "" << tr("IC' Mean = ") <<"" << meanIC <<"
" << "" << tr("IC' Variance = ") <<"" << varianceIC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("IC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

"; outText << tr("GROUP INFORMATION CENTRALIZATION (GIC)") << "

"; outText << "

" << tr("Since there is no way to compute Group Information Centralization,
" "you can use Variance as a general centralization index.

") << "" << tr("Variance = ") <<"" << varianceIC << "

"; outText << "

" << tr("Variance = 0, when all nodes have the same IC value, i.e. a " "complete or a circle graph).
") << tr("Larger values of variance suggest larger variability between the " "IC' values.
") <<"(Wasserman & Faust, formula 5.20, p. 197)\n\n" << "

"; outText << "

 

"; outText << "

"; outText << tr("Information Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Writes the eigenvector centralities to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityEigenvector(const QString fileName, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates){ QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); centralityEigenvector(considerWeights, inverseWeights,dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::EVC, m_reportsChartType, distImageFileName); VList::const_iterator it; int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing Eigenvector Centrality scores to file. \nPlease wait...") ; emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << htmlHead; outText << "

"; outText << tr("EIGENVECTOR CENTRALITY (EVC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The Eigenvector Centrality of each node is the ith element of " "the leading eigenvector of the adjacency matrix, that is the " "eigenvector corresponding to the largest positive eigenvalue.
" "Proposed by Bonacich (1972), the Eigenvector Centrality is " "an extension of the simpler Degree Centrality because it gives " "each actor a score proportional to the scores of its neighbors. " "Thus, a node may have high EVC score if it has lots of ties or " "it has ties to other nodes with high EVC.
" "The eigenvector centralities are also known as Gould indices.") << "
" << tr("EVC' is the scaled EVC (EVC divided by max EVC).") << "
" << tr("EVC'' is the standardized index (EVC divided by the sum of all EVCs).") << "
" << "

"; outText << "

" << "" << tr("EVC range: ") <<"" << tr("0 ≤ EVC < 1 (The eigenvector has unit euclidean length) ") << "

"; outText << "

" << "" << tr("EVC' range: ") <<"" << tr("0 ≤ EVC' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText << fixed; outText << "" <<"" <<""; } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("EVC") << "" << tr("EVC'") << "" << tr("EVC''") << "" << tr("%EVC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->EVC() << "" << (*it)->SEVC() << "" << (*it)->EVC() / ( sumEVC ? sumEVC : 1) << "" << (100* ((*it)->SEVC())) << "
"; if ( minEVC == maxEVC) { outText << "

" << tr("All nodes have the same EVC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max EVC = ") <<"" << maxEVC <<" (node "<< maxNodeEVC << ")" << "
" << "" << tr("Min EVC = ") <<"" << minEVC <<" (node "<< minNodeEVC << ")" << "
" << "" << tr("EVC classes = ") <<"" << classesEVC << "

"; } outText << "

"; outText << "" << tr("EVC Sum = ") <<"" << sumEVC <<"
" << "" << tr("EVC Mean = ") <<"" << meanEVC <<"
" << "" << tr("EVC Variance = ") <<"" << varianceEVC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("EVC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

"; outText << tr("GROUP EIGENVECTOR CENTRALIZATION (GEC)") << "

"; outText << "

" << tr("Since there is no way to compute Group Eigenvector Centralization,
" "you can use Variance as a general centralization index.

") << "" << tr("Variance = ") <<"" << varianceEVC << "

"; outText << "

" << tr("Variance = 0, when all nodes have the same EVC value, i.e. a " "complete or a circle graph).
") << tr("Larger values of variance suggest larger variability between the " "EVC' values.
") << "

"; outText << "

 

"; outText << "

"; outText << tr("Eigenvector Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Computes Eigenvector centrality * @param considerWeights * @param inverseWeights */ void Graph::centralityEigenvector(const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::centralityEigenvector()"; if ( calculatedEVC ) { qDebug() << "Graph::centralityEigenvector() - Already computed. Return."; return; } emit statusMessage ( (tr("Calculating EVC scores...")) ); classesEVC=0; discreteEVCs.clear(); sumEVC=0; maxEVC=0; minEVC=RAND_MAX; varianceEVC=0; meanEVC=0; VList::const_iterator it; bool symmetrize=false; bool useDegrees=false; int i = 0; int N = vertices(dropIsolates); qreal *EVC = new (nothrow) qreal [N]; Q_CHECK_PTR( EVC ); qreal SEVC = 0; graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); QString pMsg = tr("Computing Eigenvector Centrality scores. \nPlease wait...") ; emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); if (useDegrees) { qDebug() << "Graph::centralityEigenvector() - Using outDegree for initial EVC vector"; emit statusMessage(tr("Computing outDegrees. Please wait...")); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (!(*it)->isIsolated() && dropIsolates) { continue; } EVC[i] = (*it)->degreeOut(); i++; } } else { qDebug() << "Graph::centralityEigenvector() - Using unit initial EVC vector"; for (int i = 0 ; i < N ; i++) { EVC[i] = 1; } } emit signalProgressBoxUpdate( N / 3); AM.powerIteration(EVC, sumEVC, maxEVC, maxNodeEVC, minEVC, minNodeEVC, 0.0000001, 500); emit signalProgressBoxUpdate(2 * N / 3); emit statusMessage(tr("Leading eigenvector computed. " "Analysing centralities. Please wait...")); i = 0; meanEVC = sumEVC / (qreal) N; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (!(*it)->isIsolated() && dropIsolates) { continue; } (*it) -> setEVC( EVC[i]); if ( maxEVC != 0 ) { SEVC = EVC[i] / maxEVC ; } else { SEVC = 0 ; } (*it) -> setSEVC( SEVC ); resolveClasses(SEVC, discreteEVCs, classesEVC); varianceEVC += (EVC[i]-meanEVC) * (EVC[i]-meanEVC) ; i++; } varianceEVC=varianceEVC/(qreal) N; // group eigenvector centralization measure is // S(cmax - c(vi)) divided by the maximum value possible, // where c(vi) is the eigenvector centrality of vertex vi. calculatedEVC=true; delete [] EVC; emit signalProgressBoxUpdate( N ); emit signalProgressBoxKill(); } /** * @brief Calculates the degree (outDegree) centrality of each vertex - diagonal included * @param weights * @param dropIsolates */ void Graph::centralityDegree(const bool &weights, const bool &dropIsolates){ qDebug("Graph::centralityDegree()"); if ( calculatedDC ) { qDebug() << "Graph::centralityDegree() - graph not changed - returning"; return; } qreal DC=0, nom=0, denom=0, SDC=0; qreal weight; classesSDC=0; discreteSDCs.clear(); sumSDC=0; sumDC=0; maxSDC=0; minSDC=RAND_MAX; varianceSDC=0; meanSDC=0; int N=vertices(dropIsolates); VList::const_iterator it, it1; QString pMsg = tr("Computing out-Degree Centralities. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); emit signalProgressBoxUpdate(N/3); 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=edgeExists( (*it)->name(), (*it1)->name() ) ) != 0.0 ) { // qDebug() << "Graph::centralityDegree() - vertex " // << (*it)->name() // << " has edge to = " << (*it1)->name(); if (weights) DC+=weight; else DC++; //check here if the matrix is symmetric - we need this below if ( edgeExists ( (*it1)->name(), (*it)->name() , true ) == 0 ) m_graphIsSymmetric = false; } } } (*it) -> setDC ( DC ) ; //Set OutDegree sumDC += DC; // store sumDC (for std calc below) qDebug() << "Graph:centralityDegree() - vertex " << (*it)->name() << " has DC = " << DC ; } emit signalProgressBoxUpdate(2*N/3); // 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 / (N-1.0) ); } else { SDC= ( DC / (sumDC) ); } (*it) -> setSDC( SDC ); //Set Standard DC // qDebug() << "Graph::centralityDegree() - vertex " // << (*it)->name() << " SDC " << (*it)->SDC (); sumSDC+=SDC; resolveClasses(SDC, discreteSDCs, classesSDC ); //qDebug() << "DC classes = " << classesSDC; if (maxSDC < SDC ) { maxSDC = SDC ; maxNodeSDC=(*it)->name(); } if (minSDC > SDC ) { minSDC = SDC ; minNodeSDC=(*it)->name(); } } if (minSDC == maxSDC) maxNodeSDC=-1; meanSDC = sumSDC / (qreal) N; // qDebug() << "Graph::centralityDegree() - sumSDC " << sumSDC // << " vertices " << N << " meanSDC = sumSDC / N = " << meanSDC; // Calculate Variance and the Degree Centralization of the whole graph. for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } SDC= (*it)->SDC(); nom+= (maxSDC-SDC); varianceSDC += (SDC-meanSDC) * (SDC-meanSDC) ; } varianceSDC=varianceSDC/(qreal) N; // qDebug() << "Graph::centralityDegree() - variance = " << varianceSDC; if (m_graphIsSymmetric) { // we divide by N-1 because we use std C values denom= (N-1.0)*(N-2.0) / (N-1.0); } else { denom=(N-1.0)*(N-1.0) / (N-1.0); } if (N < 3 ) { denom = N-1.0; } // qDebug () << "*** N is " << N << " nom " << nom << " denom is " << denom; if (!weights) { groupDC=nom/denom; } calculatedDC=true; emit signalProgressBoxUpdate(N); emit signalProgressBoxKill(); } /** * @brief Returns the IndexType of the given prominence index name * Called from MW::slotEditNodeFind, MW::slotLayoutRadialByProminenceIndex etc * @param prominenceIndexName */ int Graph::getProminenceIndexByName(const QString &prominenceIndexName) { qDebug()<< "Graph::getProminenceIndexByName() : " << prominenceIndexName; if ( prominenceIndexName.contains("Degree Centr") ){ return IndexType::DC; } else if ( prominenceIndexName.contains("Closeness Centr") && !prominenceIndexName.contains("IR")){ return IndexType::CC; } else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness") ){ return IndexType::IRCC; } else if ( prominenceIndexName.contains("Betweenness Centr")){ return IndexType::BC; } else if (prominenceIndexName.contains("Stress Centr")){ return IndexType::SC; } else if (prominenceIndexName.contains("Eccentricity Centr")){ return IndexType::EC; } else if (prominenceIndexName.contains("Power Centr")){ return IndexType::PC; } else if (prominenceIndexName.contains("Information Centr")){ return IndexType::IC; } else if (prominenceIndexName.contains("Eigenvector Centr")){ return IndexType::EVC; } else if (prominenceIndexName.contains("Degree Prestige")){ return IndexType::DP; } else if (prominenceIndexName.contains("PageRank Prestige")){ return IndexType::PRP; } else if (prominenceIndexName.contains("Proximity Prestige")){ return IndexType::PP; } else return 0; } /** * @brief Computes the distribution of a centrality index score. * The distribution is stored as Qt Series depending on the SeriesType parameter type * It is send to MW through signal/slot * @param index * @param type */ void Graph::prominenceDistribution(const int &index, const ChartType &type, const QString &distImageFileName) { qDebug() << "Graph::prominenceDistribution() - " << "index" << index << "chart type: " << type << "distImageFileName" << distImageFileName; QString pMsg = tr("Computing Centrality Distribution. \nPlease wait..."); emit statusMessage( pMsg ); H_StrToInt discreteClasses; QString seriesName; switch (index) { case 0: { break; } case IndexType::DC : { seriesName = ("(out)Degree"); discreteClasses = discreteSDCs; break; } case IndexType::CC : { seriesName = ("Closeness"); discreteClasses = discreteCCs; break; } case IndexType::IRCC : { seriesName = ("IRCC"); discreteClasses = discreteIRCCs; break; } case IndexType::BC : { seriesName = ("Betweenness"); discreteClasses = discreteBCs; break; } case IndexType::SC : { seriesName = ("Stress"); discreteClasses = discreteSCs; break; } case IndexType::EC : { seriesName = ("Eccentricity"); discreteClasses = discreteECs; break; } case IndexType::PC : { seriesName = ("Power"); discreteClasses = discretePCs; break; } case IndexType::IC : { seriesName = ("Information"); discreteClasses = discreteICs; break; } case IndexType::EVC : { seriesName = ("Eigenvector"); discreteClasses = discreteEVCs; break; } case IndexType::DP : { seriesName = ("Prestige Degree"); discreteClasses = discreteDPs; break; } case IndexType::PRP : { seriesName = ("Pagerank"); discreteClasses = discretePRPs; break; } case IndexType::PP : { seriesName = ("Proximity"); discreteClasses = discretePPs; break; } } switch (type) { case ChartType::None: emit signalPromininenceDistributionChartUpdate(Q_NULLPTR, Q_NULLPTR); break; case ChartType::Spline: emit statusMessage(tr("Creating prominence index distribution line chart...")); prominenceDistributionSpline(discreteClasses, seriesName,distImageFileName ); break; case ChartType::Area: emit statusMessage(tr("Creating prominence index distribution area chart...")); prominenceDistributionArea(discreteClasses, seriesName, distImageFileName ); break; case ChartType::Bars: emit statusMessage(tr("Creating prominence index distribution bar chart...")); prominenceDistributionBars(discreteClasses, seriesName, distImageFileName ); break; } } /** * @brief Computes the distribution of a centrality index scores. * The distribution data are returned as QSplineSeries series to MW * which in turn displays them on a Spline Chart * @param index * @param series */ void Graph::prominenceDistributionSpline(const H_StrToInt &discreteClasses, const QString &seriesName, const QString &distImageFileName) { qDebug() << "Graph::prominenceDistributionSpline()"; QLineSeries *series = new QLineSeries(); series->setName (seriesName); QValueAxis *axisX = new QValueAxis (); QValueAxis *axisY = new QValueAxis (); // Used only for the large chart exported to PNG for the HTML report QLineSeries *series1 = new QLineSeries(); series1->setName (seriesName); QValueAxis *axisX1 = new QValueAxis (); QValueAxis *axisY1 = new QValueAxis (); // The seriesPQ is a priority queue which will hold // the (value,frequency) pairs ordered from smallest to larger value // This is used further below to insert pairs to the series in an ordered way priority_queue, PairVFCompare> seriesPQ; QHash::const_iterator i; for (i = discreteClasses.constBegin(); i != discreteClasses.constEnd(); ++i) { qDebug() << "Graph::prominenceDistributionSpline() - discreteClasses: " << i.key() << ": " << i.value() ; seriesPQ.push(PairVF(i.key().toDouble(), i.value())); } unsigned int initialSize = seriesPQ.size(); qreal min = 0; qreal max = 0; qreal value = 0; qreal frequency = 0; qreal minF = RAND_MAX; qreal maxF = 0; while (!seriesPQ.empty()) { qDebug() << "Graph::prominenceDistributionSpline() - seriesPQ top is:" << seriesPQ.top().value << " : " << seriesPQ.top().frequency; value = seriesPQ.top().value; frequency = seriesPQ.top().frequency; series->append( value, frequency ); series1->append( value, frequency ); if ( frequency < minF ) { minF = frequency; } if ( frequency > maxF ) { maxF = frequency; } if ( initialSize == seriesPQ.size() ) { min = value; } if ( seriesPQ.size() == 1 ) { max = value; } seriesPQ.pop(); } axisX->setMin(min); axisX->setMax(max); axisY->setMin(minF); axisY->setMax(maxF+1.0); QPen sPen (QColor( "#209fdf" )); sPen.setWidthF(0.9); QBrush sBrush( QColor( "#ff0000") ); series->setBrush(sBrush); series->setPen(sPen); if (!distImageFileName.isEmpty() ) { qDebug() << "Graph::prominenceDistributionSpline() - " << "saving distribution image to" << distImageFileName ; axisX1->setMin(min); axisX1->setMax(max); axisY1->setMin(minF); axisY1->setMax(maxF+1.0); QChart *chart = new QChart(); QChartView *chartView = new QChartView( chart ); // Not needed? // If we do show it, then it will show a brief flash of the window to the user! // chartView->show(); chart->addSeries(series1); chart->setTitle(series1->name() + " distribution"); chart->setTitleFont(QFont("Times",12)); chart->legend()->hide(); //chart->createDefaultAxes(); chart->addAxis(axisX1, Qt::AlignBottom); series1->attachAxis(axisX1); chart->addAxis(axisY1, Qt::AlignLeft); series1->attachAxis(axisY1); chart->axes(Qt::Vertical).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setLabelsAngle(-90); // m_chart->axes(Qt::Horizontal).first()->setShadesVisible(false); chart->resize(2560,1440); chartView->resize(2561,1441); //QPixmap p = m_chart->getPixmap(); QPixmap p = chartView->grab(); p.save( distImageFileName, "PNG"); chartView->hide(); // Do not delete the ChartView // If we do delete it, then it will also delete the axes // which we have sent to MW to be displayed on the miniChart. // The result will be app crash... // chartView->deleteLater(); delete chartView; } qDebug() << "Graph::prominenceDistributionSpline() - emitting signal to update"; emit signalPromininenceDistributionChartUpdate(series, axisX, min, max, axisY, minF, maxF); } /** * @brief Computes the distribution of a centrality index scores. * The distribution data are returned as QAreaSeries series to MW * which in turn displays them on a Area Chart * @param index * @param series */ void Graph::prominenceDistributionArea(const H_StrToInt &discreteClasses, const QString &name, const QString &distImageFileName) { qDebug() << "Graph::prominenceDistributionArea()"; QAreaSeries *series = new QAreaSeries (); series->setName (name); QLineSeries *upperSeries = new QLineSeries(); QValueAxis *axisX = new QValueAxis(); QValueAxis *axisY = new QValueAxis(); // Used only for the large chart exported to PNG for the HTML report QAreaSeries *series1 = new QAreaSeries (); series1->setName (name); QValueAxis *axisX1 = new QValueAxis(); QValueAxis *axisY1 = new QValueAxis(); // The seriesPQ is a priority queue which will hold // the (value,frequency) pairs ordered from smallest to larger value // This is used further below to insert pairs to the series in an ordered way priority_queue, PairVFCompare> seriesPQ; QHash::const_iterator i; for (i = discreteClasses.constBegin(); i != discreteClasses.constEnd(); ++i) { qDebug() << "discreteClasses: " << i.key() << ": " << i.value() << endl; seriesPQ.push(PairVF(i.key().toDouble(), i.value())); } unsigned int initialSize = seriesPQ.size(); qreal min = 0; qreal max = 0; qreal value = 0; qreal frequency = 0; qreal minF = RAND_MAX; qreal maxF = 0; while (!seriesPQ.empty()) { qDebug() << seriesPQ.top().value << " : " << seriesPQ.top().frequency << endl; value = seriesPQ.top().value; frequency = seriesPQ.top().frequency; upperSeries->append( value, frequency ); if ( frequency < minF ) { minF = frequency; } if ( frequency > maxF ) { maxF = frequency; } if ( initialSize == seriesPQ.size() ) { min = value; } if ( seriesPQ.size() == 1 ) { max = value; } seriesPQ.pop(); } axisX->setMin(min); axisX->setMax(max); axisY->setMin(minF); axisY->setMax(maxF+1.0); series->setUpperSeries(upperSeries); QPen sPen (QColor( "#666" )); sPen.setWidthF(0.2); QBrush sBrush( QColor( "#209fdf") ); series->setBrush(sBrush); series->setPen(sPen); if (!distImageFileName.isEmpty()) { qDebug() << "Graph::prominenceDistributionArea() - " << "saving distribution image to" << distImageFileName ; axisX1->setMin(min); axisX1->setMax(max); axisY1->setMin(minF); axisY1->setMax(maxF+1.0); series1->setUpperSeries(upperSeries); QChart *chart = new QChart(); QChartView *chartView = new QChartView( chart ); // Not needed? // If we do show it, then it will show a brief flash of the window to the user! //chartView->show(); chart->addSeries(series1); chart->setTitle(series->name() + " distribution"); chart->setTitleFont(QFont("Times",12)); chart->legend()->hide(); // chart->createDefaultAxes(); chart->addAxis(axisX1, Qt::AlignBottom); series1->attachAxis(axisX1); chart->addAxis(axisY1, Qt::AlignLeft); series1->attachAxis(axisY1); chart->axes(Qt::Vertical).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setLabelsAngle(-90); // m_chart->axes(Qt::Horizontal).first()->setShadesVisible(false); chart->resize(2560,1440); chartView->resize(2561,1441); //QPixmap p = m_chart->getPixmap(); QPixmap p = chartView->grab(); p.save( distImageFileName, "PNG"); chartView->hide(); // Do not delete the ChartView // If we do delete it, then it will also delete the axes // which we have sent to MW to be displayed on the miniChart. // The result will be app crash... // chartView->deleteLater(); delete chartView; } emit signalPromininenceDistributionChartUpdate(series, axisX, min, max, axisY, minF, maxF); } /** * @brief Computes the distribution of a centrality index scores. * The distribution data are returned as QBarSeries series (with a QBarSet attached) * to MW which in turn displays them on a Bar Chart * @param index * @param series * @param set * @param strX */ void Graph::prominenceDistributionBars(const H_StrToInt &discreteClasses, const QString &name, const QString &distImageFileName) { qDebug() << "Graph::prominenceDistributionBars() - Creating chart type: bars"; QBarSeries *series = new QBarSeries(); series->setName (name); QBarSet *barSet = new QBarSet(""); QValueAxis *axisY = new QValueAxis; QBarCategoryAxis *axisX = new QBarCategoryAxis(); // Used only for the large chart exported to PNG for the HTML report QBarSeries *series1 = new QBarSeries(); series1->setName (name); QBarSet *barSet1 = new QBarSet(""); QValueAxis *axisY1 = new QValueAxis; QBarCategoryAxis *axisX1 = new QBarCategoryAxis(); // The seriesPQ is a priority queue which will hold // the (value,frequency) pairs ordered from smallest to larger value // This is used further below to insert pairs to the series in an ordered way priority_queue, PairVFCompare> seriesPQ; QHash::const_iterator i; for (i = discreteClasses.constBegin(); i != discreteClasses.constEnd(); ++i) { qDebug() << "discreteClasses: " << i.key() << ": " << i.value() << endl; seriesPQ.push(PairVF(i.key().toDouble(), i.value())); } unsigned int initialSize = seriesPQ.size(); QString min = QString(); QString max = QString(); QString value = QString(); qreal frequency = 0; qreal minF = RAND_MAX; qreal maxF = 0; while (!seriesPQ.empty()) { value = QString::number( seriesPQ.top().value, 'f', 6); frequency = seriesPQ.top().frequency; qDebug() << "value:"<< value << " : " << "frequency:"<< frequency << endl; axisX->append( value ); barSet->append( frequency ); if (!distImageFileName.isEmpty()) { axisX1->append( value ); barSet1->append( frequency ); } if ( frequency < minF ) { minF = frequency; } if ( frequency > maxF ) { maxF = frequency; } if ( initialSize == seriesPQ.size() ) { min = value; } if ( seriesPQ.size() == 1 ) { max = value; } seriesPQ.pop(); } // end while axisX->setMin(min); axisX->setMax(max); axisY->setMin(minF); axisY->setMax(maxF+1.0); qDebug() << "axisX min: " << axisX->min() << " max: " << axisX->max(); series->append( barSet ); QPen sPen (QColor( "#666" )); sPen.setWidthF(0.2); QBrush sBrush( QColor( "#209fdf") ); barSet->setBrush(sBrush); barSet->setPen(sPen); if (!distImageFileName.isEmpty()) { qDebug() << "Graph::prominenceDistributionBars() - " << "saving distribution image to" << distImageFileName ; series1->append( barSet1 ); axisX1->setMin(min); axisX1->setMax(max); axisY1->setMin(minF); axisY1->setMax(maxF+1.0); QChart *chart = new QChart(); QChartView *chartView = new QChartView( chart ); // Not needed? // If we do show it, then it will show a brief flash of the window to the user! //chartView->show(); chart->addSeries(series1); chart->setTitle(series->name() + " distribution"); chart->setTitleFont(QFont("Times",12)); chart->legend()->hide(); chart->addAxis(axisX1, Qt::AlignBottom); series1->attachAxis(axisX1); chart->addAxis(axisY1, Qt::AlignLeft); series1->attachAxis(axisY1); chart->axes(Qt::Vertical).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setLabelsAngle(-90); // m_chart->axes(Qt::Horizontal).first()->setShadesVisible(false); chart->resize(2560,1440); chartView->resize(2561,1441); //QPixmap p = m_chart->getPixmap(); QPixmap p = chartView->grab(); p.save( distImageFileName, "PNG"); chartView->hide(); // Do not delete the ChartView if the axes / series are the same! // If we do delete it, then it will also delete the axes // which we have sent to MW to be displayed on the miniChart. // The result will be app crash... delete chartView; } emit signalPromininenceDistributionChartUpdate(series, axisX, min.toDouble(), max.toDouble(), axisY, minF, maxF); } /** * @brief Writes the Degree Centrality to a file * @param fileName * @param considerWeights * @param dropIsolates */ void Graph::writeCentralityDegree ( const QString fileName, const bool considerWeights, const bool dropIsolates) { qDebug()<< "Graph:: writeCentralityDegree() - considerWeights " << considerWeights << " dropIsolates " <"; outText << tr("DEGREE CENTRALITY (DC) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("In undirected networks, the DC index is the sum of edges attached to a node u.
" "In directed networks, the index is the sum of outbound arcs from node u " "to all adjacent nodes (also called \"outDegree Centrality\").
" "If the network is weighted, the DC score is the sum of weights of outbound " "edges from node u to all adjacent nodes.
" "Note: To compute inDegree Centrality, use the Degree Prestige measure.") << "
" << tr("DC' is the standardized index (DC divided by N-1 (non-valued nets) or by sumDC (valued nets).") << "

"; outText << "

" << "" << tr("DC range: ") <<"" << tr("0 ≤ DC ≤ "); if (considerWeights) outText<< infinity; else outText << maxIndexDC; outText << "

"; outText << "

" << "" << tr("DC' range: ") <<"" << tr("0 ≤ DC' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; emit signalProgressBoxUpdate(++progressCounter); outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("DC") << "" << tr("DC'") << "" << tr("%DC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->DC() << "" << (*it)->SDC() << "" << (100* ((*it)->SDC())) << "
"; if ( minSDC == maxSDC) { outText << "

" << tr("All nodes have the same DC score.") << "

"; } else { outText << "

"; outText << "" << tr("DC Sum = ") <<"" << sumDC <<"

"; outText << "

"; outText << "" << tr("Max DC' = ") <<"" << maxSDC <<" (node "<< maxNodeSDC << ")" << "
" << "" << tr("Min DC' = ") <<"" << minSDC <<" (node "<< minNodeSDC << ")" << "
" << "" << tr("DC' classes = ") <<"" << classesSDC << "

"; } outText << "

"; outText << "" << tr("DC' Sum = ") <<"" << sumSDC <<"
" << "" << tr("DC' Mean = ") <<"" << meanSDC <<"
" << "" << tr("DC' Variance = ") <<"" << varianceSDC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("DC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } if (!considerWeights) { outText << "

"; outText << tr("GROUP DEGREE CENTRALIZATION (GDC)") << "

"; outText << "

"; outText << "" << tr("GDC = ") <<"" << groupDC << "

"; outText << "

" << "" << tr("GDC range: ") <<"" <<" 0 ≤ GDC ≤ 1" << "

"; outText << "

" << tr("GDC = 0, when all out-degrees are equal (i.e. regular lattice).") << "
" << tr("GDC = 1, when one node completely dominates or overshadows the other nodes.") << "
" << "(Wasserman & Faust, formula 5.5, p. 177)" << "
" << "(Wasserman & Faust, p. 101)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralization measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Degree Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Writes the closeness centralities to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityCloseness( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QElapsedTimer computationTimer; computationTimer.start(); qDebug() << "Graph::writeCentralityCloseness()" << "considerWeights"<"; outText << tr("CLOSENESS CENTRALITY (CC) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The CC index is the inverted sum of geodesic distances " "from each node u to all other nodes. " ) << "
" << tr("Note: The CC index considers outbound arcs only and " "isolate nodes are dropped by default. ") << "
" << tr("Read the Manual for more.") << "
" << tr("CC' is the standardized index (CC multiplied by (N-1 minus isolates)).") << "

"; outText << "

" << "" << tr("CC range: ") <<"" << tr("0 ≤ CC ≤ ")<< 1.0/maxIndexCC << tr(" ( 1 / Number of node pairs excluding u)") << "

"; outText << "

" << "" << tr("CC' range: ") <<"" << tr("0 ≤ CC' ≤ 1 (CC'=1 when a node is the center of a star graph)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; VList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("CC") << "" << tr("CC'") << "" << tr("%CC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->CC() << "" << (*it)->SCC() << "" << (100* ((*it)->SCC())) << "
"; if ( minSCC == maxSCC) { outText << "

" << tr("All nodes have the same CC score.") << "

"; } else { outText << "

"; outText << "" << tr("CC Sum = ") <<"" << sumCC <<"

"; outText << "

"; outText << "" << tr("Max CC' = ") <<"" << maxSCC <<" (node "<< maxNodeSCC << ")" << "
" << "" << tr("Min CC' = ") <<"" << minSCC <<" (node "<< minNodeSCC << ")" << "
" << "" << tr("CC' classes = ") <<"" << classesSCC << "

"; } outText << "

"; outText << "" << tr("CC' Sum = ") <<"" << sumSCC <<"
" << "" << tr("CC' Mean = ") <<"" << meanSCC <<"
" << "" << tr("CC' Variance = ") <<"" << varianceSCC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("CC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } if (!considerWeights) { outText << "

"; outText << tr("GROUP CLOSENESS CENTRALIZATION (GCC)") << "

"; outText << "

"; outText << "" << tr("GCC = ") <<"" << groupCC << "

"; outText << "

" << "" << tr("GCC range: ") <<"" <<" 0 ≤ GCC ≤ 1" << "

"; outText << "

" << tr("GCC = 0, when the lengths of the geodesics are all equal, " "i.e. a complete or a circle graph.") << "
" << tr("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.") << "
" << tr("This is exactly the situation realised by a star graph.") << "
" << "(Wasserman & Faust, formula 5.9, p. 186-187)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralization measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Closeness Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Computes an "improved" closeness centrality index, IRCC, which can be used * on disconnected graphs. * IRCC is an 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. * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::centralityClosenessIR(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug()<< "Graph::centralityClosenessIR()"; if ( calculatedIRCC ) { qDebug() << "Graph::centralityClosenessIR() - " " graph not changed - returning"; return; } graphDistancesGeodesic(false,considerWeights,inverseWeights,dropIsolates); // calculate centralities VList::const_iterator it, jt; int progressCounter = 0; qreal IRCC=0,SIRCC=0; qreal Ji=0; qreal dist=0; qreal sumD=0; qreal averageD=0; qreal N=vertices(dropIsolates,false, true); classesIRCC=0; discreteIRCCs.clear(); sumIRCC=0; maxIRCC=0; minIRCC=N-1; varianceIRCC=0; meanIRCC=0; QString pMsg = tr("Computing Influence Range Centrality scores. \n" "Please wait"); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); qDebug()<< "Graph::centralityClosenessIR() - dropIsolates"<< dropIsolates; qDebug()<< "Graph::centralityClosenessIR() - computing scores for actors: " << N; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); IRCC=0; sumD=0; Ji = 0; if ((*it)->isIsolated()) { continue; } for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt){ if ( (*it)->name() == (*jt)->name() ) { continue; } if ( ! (*jt)-> isEnabled() ) { continue; } dist = (*it)->distance( (*jt)->name() ); if (dist != RAND_MAX ) { sumD += dist; Ji ++; // compute |Ji| } qDebug()<< "Graph::centralityClosenessIR() - dist(" << (*it)->name() << ","<< (*jt)->name() << ") =" << dist << "sumD" << sumD << " Ji"<name() << " sumD"<< sumD << "distanceSum" << (*it)->distanceSum(); // sanity check for sumD=0 (=> node is disconnected) if (sumD != 0) { averageD = sumD / Ji; qDebug()<< "Graph::centralityClosenessIR() - averageD = sumD / Ji"< setIRCC ( IRCC ) ; (*it) -> setSIRCC ( IRCC ) ; // IRCC is a ratio, already std resolveClasses(IRCC, discreteIRCCs, classesIRCC); minmax( IRCC, (*it), maxIRCC, minIRCC, maxNodeIRCC, minNodeIRCC) ; } meanIRCC = sumIRCC / (qreal) N; 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/(qreal) N; calculatedIRCC=true; emit signalProgressBoxKill(); } /** * @brief Writes the "improved" closeness centrality indices to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityClosenessInfluenceRange(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); centralityClosenessIR(considerWeights,inverseWeights, dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::IRCC, m_reportsChartType,distImageFileName); int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing Influence Range Centrality scores. \n" "Please wait"); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("INFLUENCE RANGE CLOSENESS CENTRALITY (IRCC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The IRCC index of a node u is the ratio of the fraction of nodes " "reachable by node u to the average distance of these nodes from u " "(Wasserman & Faust, formula 5.22, p. 201)
" "Thus, this measure is similar to Closeness Centrality " "but it counts only outbound distances from each actor to other reachable nodes.
" "This measure is useful for directed networks which are " "not strongly connected (thus the ordinary CC index cannot be computed).
" "In undirected networks, the IRCC has the same properties and yields " "the same results as the ordinary Closeness Centrality.
" "Read the Manual for more. ") << "
" << tr("IRCC is standardized.") << "

"; outText << "

" << "" << tr("IRCC range: ") <<"" << tr("0 ≤ IRCC ≤ 1 (IRCC is a ratio)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; VList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("IRCC") << "" << tr("%IRCC'") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->IRCC() << "" << (100* ((*it)->SIRCC())) << "
"; if ( minIRCC == maxIRCC) { outText << "

" << tr("All nodes have the same IRCC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max IRCC = ") <<"" << maxIRCC <<" (node "<< maxNodeIRCC << ")" << "
" << "" << tr("Min IRCC = ") <<"" << minIRCC <<" (node "<< minNodeIRCC << ")" << "
" << "" << tr("IRCC classes = ") <<"" << classesIRCC << "

"; } outText << "

"; outText << "" << tr("IRCC Sum = ") <<"" << sumIRCC <<"
" << "" << tr("IRCC Mean = ") <<"" << meanIRCC <<"
" << "" << tr("IRCC Variance = ") <<"" << varianceIRCC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("IRCC DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

 

"; outText << "

"; outText << tr("Influence Range Closeness Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Writes Betweenness centralities to file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityBetweenness(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug() << "Graph::writeCentralityBetweenness()"; QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); graphDistancesGeodesic(true, considerWeights, inverseWeights, dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::BC, m_reportsChartType, distImageFileName); int rowCount=0, progressCounter=0; int N = vertices(); QString pMsg = tr("Writing Betweenness Centrality scores to file. \nPlease wait..."); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("BETWEENNESS CENTRALITY (BC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The BC index of a node u is the sum of δ(s,t,u) for all s,t ∈ V ") << tr("where δ(s,t,u) is the ratio of all geodesics between " "s and t which run through u. ") << "
" << tr("Read the Manual for more.") << "
" << tr("BC' is the standardized index (BC divided by (N-1)(N-2)/2 in symmetric nets or (N-1)(N-2) otherwise.") << "

"; outText << "

" << "" << tr("BC range: ") <<"" << tr("0 ≤ BC ≤ ")<< maxIndexBC << tr(" (Number of pairs of nodes excluding u)") << "

"; outText << "

" << "" << tr("BC' range: ") <<"" << tr("0 ≤ BC' ≤ 1 (BC'=1 when the node falls on all geodesics)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; VList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("BC") << "" << tr("BC'") << "" << tr("%BC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->BC() << "" << (*it)->SBC() << "" << (100* ((*it)->SBC())) << "
"; if ( minSBC == maxSBC) { outText << "

" << tr("All nodes have the same BC score.") << "

"; } else { outText << "

"; outText << "" << tr("BC Sum = ") <<"" << sumBC <<"

"; outText << "

"; outText << "" << tr("Max BC' = ") <<"" << maxSBC <<" (node "<< maxNodeSBC << ")" << "
" << "" << tr("Min BC' = ") <<"" << minSBC <<" (node "<< minNodeSBC << ")" << "
" << "" << tr("BC' classes = ") <<"" << classesSBC << "

"; } outText << "

"; outText << "" << tr("BC' Sum = ") <<"" << sumSBC <<"
" << "" << tr("BC' Mean = ") <<"" << meanSBC <<"
" << "" << tr("BC' Variance = ") <<"" << varianceSBC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("BC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } if (!considerWeights) { outText << "

"; outText << tr("GROUP BETWEENNESS CENTRALIZATION (GBC)") << "

"; outText << "

"; outText << "" << tr("GBC = ") <<"" << groupSBC << "

"; outText << "

" << "" << tr("GBC range: ") <<"" <<" 0 ≤ GBC ≤ 1" << "

"; outText << "

" << tr("GBC = 0, when all the nodes have exactly the same betweenness index.") << "
" << tr("GBC = 1, when one node falls on all other geodesics between " "all the remaining (N-1) nodes. ") << "
" << tr("This is exactly the situation realised by a star graph.") << "
" << "(Wasserman & Faust, formula 5.13, p. 192)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralization measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Betweenness Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Writes the Stress centralities to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityStress( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug() << "Graph::writeCentralityStress()"; QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); graphDistancesGeodesic(true, considerWeights, inverseWeights,dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::SC, m_reportsChartType, distImageFileName); VList::const_iterator it; int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing Stress Centralities. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("STRESS CENTRALITY (SC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The SC index of each node u is the sum of σ(s,t,u)):
" "the number of geodesics from s to t through u.") << "
" << tr("SC' is the standardized index (SC divided by sumSC).") << "

"; outText << "

" << "" << tr("SC range: ") <<"" << tr("0 ≤ SC ≤ ")<< maxIndexSC << "

"; outText << "

" << "" << tr("SC' range: ") <<"" << tr("0 ≤ SC' ≤ 1 (SC'=1 when the node falls on all geodesics)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("SC") << "" << tr("SC'") << "" << tr("%SC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->SC() << "" << (*it)->SSC() << "" << (100* ((*it)->SSC())) << "
"; if ( minSSC == maxSSC) { outText << "

" << tr("All nodes have the same SC score.") << "

"; } else { outText << "

"; outText << "" << tr("SC Sum = ") <<"" << sumSC <<"

"; outText << "

"; outText << "" << tr("Max SC' = ") <<"" << maxSSC <<" (node "<< maxNodeSSC << ")" << "
" << "" << tr("Min SC' = ") <<"" << minSSC <<" (node "<< minNodeSSC << ")" << "
" << "" << tr("BC classes = ") <<"" << classesSSC << "

"; } outText << "

"; outText << "" << tr("SC' Sum = ") <<"" << sumSSC <<"
" << "" << tr("SC' Mean = ") <<"" << meanSSC <<"
" << "" << tr("SC' Variance = ") <<"" << varianceSSC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("SC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

"; outText << tr("Stress Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Writes the Eccentricity centralities (aka Harary Graph Centrality) to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityEccentricity(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug() << "Graph::writeCentralityEccentricity()"; QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); graphDistancesGeodesic(true, considerWeights, inverseWeights,dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::EC, m_reportsChartType, distImageFileName); VList::const_iterator it; int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing Eccentricity Centralities to file. \nPlease wait...") ; emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("ECCENTRICITY CENTRALITY (EC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The EC score of a node u is the inverse maximum geodesic distance " "from u to all other nodes in the network.") << "
" << tr("This index is also known as Harary Graph Centrality. ") << tr("EC is standardized.") << "

"; outText << "

" << "" << tr("EC range: ") <<"" << tr("0 ≤ EC ≤ 1 ") << tr(" (EC=1 when the actor has ties to all other nodes)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("EC=EC'") << "" << tr("%EC'") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->EC() << "" << (100* ((*it)->SEC())) << "
"; if ( minEC == maxEC) { outText << "

" << tr("All nodes have the same EC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max EC = ") <<"" << maxEC <<" (node "<< maxNodeEC << ")" << "
" << "" << tr("Min EC = ") <<"" << minEC <<" (node "<< minNodeEC << ")" << "
" << "" << tr("EC classes = ") <<"" << classesEC << "

"; } outText << "

"; outText << "" << tr("EC Sum = ") <<"" << sumEC <<"
" << "" << tr("EC Mean = ") <<"" << meanEC <<"
" << "" << tr("EC Variance = ") <<"" << varianceEC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("EC DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

 

"; outText << "

"; outText << tr("Eccentricity Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Writes Power Centralities to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityPower(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug() << "Graph::writeCentralityPower()"; QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); graphDistancesGeodesic(true, considerWeights, inverseWeights, dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::PC, m_reportsChartType, distImageFileName); VList::const_iterator it; int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing Gil-Schmidt Power Centralities to file. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("POWER CENTRALITY (PC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The PC index, introduced by Gil and Schmidt, of a node u is the sum of the sizes of all Nth-order " "neighbourhoods with weight 1/n.") << "
" << tr("PC' is the standardized index: The PC score divided by the total number " "of nodes in the same component minus 1") << "

"; outText << "

" << "" << tr("PC range: ") <<"" << tr("0 ≤ PC ≤ ")<< maxIndexPC << "

"; outText << "

" << "" << tr("PC' range: ") <<"" << tr("0 ≤ PC' ≤ 1 (PC'=1 when the node is connected to all (star).)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("PC") << "" << tr("PC'") << "" << tr("%PC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->PC() << "" << (*it)->SPC() << "" << (100* ((*it)->SPC())) << "
"; if ( minSPC == maxSPC) { outText << "

" << tr("All nodes have the same PC score.") << "

"; } else { outText << "

"; outText << "" << tr("PC Sum = ") <<"" << sumPC <<"

"; outText << "

"; outText << "" << tr("Max PC' = ") <<"" << maxSPC <<" (node "<< maxNodeSPC << ")" << "
" << "" << tr("Min PC' = ") <<"" << minSPC <<" (node "<< minNodeSPC << ")" << "
" << "" << tr("PC classes = ") <<"" << classesSPC << "

"; } outText << "

"; outText << "" << tr("PC' Sum = ") <<"" << sumSPC <<"
" << "" << tr("PC' Mean = ") <<"" << meanSPC <<"
" << "" << tr("PC' Variance = ") <<"" << varianceSPC <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("PC' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } if (!considerWeights) { outText << "

"; outText << tr("GROUP POWER CENTRALIZATION (GPC)") << "

"; outText << "

"; outText << "" << tr("GPC = ") <<"" << groupSPC << "

"; outText << "

" << "" << tr("GPC range: ") <<"" <<" 0 ≤ GPC ≤ 1" << "

"; outText << "

" << tr("GPC = 0, when all in-degrees are equal (i.e. regular lattice).") << "
" << tr("GPC = 1, when one node is linked to all other nodes (i.e. star). ") << "
" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("Use mean or variance instead.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Power Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Computes the Degree Prestige (in-degree) of each vertex - diagonal included * Also the mean value and the variance of the in-degrees. * @param weights * @param dropIsolates */ void Graph::prestigeDegree(const bool &weights, const bool &dropIsolates){ qDebug()<< "Graph::prestigeDegree()"; if ( calculatedDP ) { qDebug() << "Graph::prestigeDegree() - " " graph not changed - returning"; return; } int N=vertices(dropIsolates); int v2=0, v1=0; int progressCounter = 0; VList::const_iterator it; QHash *enabledInEdges = new QHash; QHash::const_iterator hit; qreal DP=0, SDP=0, nom=0, denom=0; qreal weight; classesSDP=0; sumSDP=0; sumDP=0; maxSDP=0; minSDP=N-1; discreteDPs.clear(); varianceSDP=0; meanSDP=0; m_graphIsSymmetric = true; QString pMsg = tr("Computing Degree Prestige (in-Degree). \n Please wait ..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); qDebug()<< "Graph::prestigeDegree() - vertices" << N <<"graph modified. Recomputing..."; for ( it = m_graph.cbegin(); it != m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); v1 = (*it) -> name(); qDebug()<< "Graph::prestigeDegree() - computing DP for vertex" << v1 ; DP=0; if ( ! (*it)->isEnabled() ) { qDebug()<< "Graph::prestigeDegree() - vertex disabled. Continue."; continue; } qDebug() << "Graph::prestigeDegree() - Iterate over inbound edges of " << v1 ; enabledInEdges=(*it)->inEdgesEnabledHash(); hit=enabledInEdges->cbegin(); while ( hit!=enabledInEdges->cend() ){ v2 = hit.key(); qDebug() << "Graph::prestigeDegree() - inbound edge from" << v2; if ( ! edgeExists ( v2, v1) ) { //sanity check qDebug() << "Graph::prestigeDegree() - Cannot verify inbound edge" << v2 << "CONTINUE" ; ++hit; continue; } weight = hit.value(); if (weights) { DP+=weight; } else { DP++; } if ( edgeExists ( v1, v2) != weight) { m_graphIsSymmetric=false; } ++hit; } (*it) -> setDP ( DP ) ; //Set DP 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 / (N-1.0) ); //Set Standard InDegree } else { SDP =( DP / (sumDP) ); } (*it) -> setSDP( SDP ); sumSDP += SDP; qDebug() << "Graph::prestigeDegree - vertex " << (*it)->name() << " DP " << DP << " SDP " << (*it)->SDP (); resolveClasses(SDP, discreteDPs, classesSDP); qDebug("DP classes = %i ", classesSDP); if (maxSDP < SDP ) { maxSDP = SDP ; maxNodeDP=(*it)->name(); } if (minSDP > SDP ) { minSDP = SDP ; minNodeDP=(*it)->name(); } } if (minSDP == maxSDP) maxNodeDP=-1; meanSDP = sumSDP / (qreal) N; qDebug("Graph: sumSDP = %f, meanSDP = %f", sumSDP, meanSDP); // 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+= maxSDP-SDP; varianceSDP += (SDP-meanSDP) * (SDP-meanSDP) ; } varianceSDP=varianceSDP/(qreal) N; if (m_graphIsSymmetric) denom=(N-1.0)*(N-2.0); else denom=(N-1.0)*(N-1.0); if (N < 3 ) denom = N-1.0; if (!weights) { groupDP=nom/denom; qDebug("Graph: varianceSDP = %f, groupDP = %f", varianceSDP, groupDP); } delete enabledInEdges; calculatedDP=true; emit signalProgressBoxKill(); } /** * @brief Writes the Degree Prestige of each node to a file * @param fileName * @param considerWeights * @param dropIsolates */ void Graph::writePrestigeDegree (const QString fileName, const bool considerWeights, const bool dropIsolates) { QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); prestigeDegree(considerWeights, dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::DP, m_reportsChartType, distImageFileName); VList::const_iterator it; int N = vertices(); qreal maxIndexDP=N-1.0; int rowCount=0; int progressCounter = 0; QString pMsg = tr("Writing Degree Prestige (in-Degree) scores to file. \nPlease wait ..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("DEGREE PRESTIGE (DP)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The DP index, also known as InDegree Centrality, of a node u " "is the sum of inbound edges to that node from all adjacent nodes.
" "If the network is weighted, DP is the sum of inbound arc " "weights (Indegree) to node u from all adjacent nodes. ") << "
" << tr("DP' is the standardized index (DP divided by N-1).") << "

"; outText << "

" << "" << tr("DP range: ") <<"" << tr("0 ≤ DP ≤ "); if (considerWeights) outText<< infinity; else outText << maxIndexDP; outText << "

"; outText << "

" << "" << tr("DP' range: ") <<"" << tr("0 ≤ DP' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate( ++progressCounter ); rowCount++; outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("DP") << "" << tr("DP'") << "" << tr("%DP'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->DP() << "" << (*it)->SDP() << "" << (100* ((*it)->SDP())) << "
"; if ( minSDP == maxSDP) { outText << "

" << tr("All nodes have the same DP score.") << "

"; } else { outText << "

"; outText << "" << tr("DP Sum = ") <<"" << sumDP << "

"; outText << "

" << "" << tr("Max DP' = ") <<"" << maxSDP <<" (node "<< maxNodeDP << ")" << "
" << "" << tr("Min DP' = ") <<"" << minSDP <<" (node "<< minNodeDP << ")" << "
" << "" << tr("DP' classes = ") <<"" << classesSDP << "

"; } outText << "

"; outText << "" << tr("DP' Sum = ") <<"" << sumSDP <<"
" << "" << tr("DP' Mean = ") <<"" << meanSDP <<"
" << "" << tr("DP' Variance = ") <<"" << varianceSDP <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("DP' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } if (!considerWeights) { outText << "

"; outText << tr("GROUP DEGREE PRESTIGE (GDP)") << "

"; outText << "

"; outText << "" << tr("GDP = ") <<"" << groupDP << "

"; outText << "

" << "" << tr("GDP range: ") <<"" <<" 0 ≤ GDP ≤ 1" << "

"; outText << "

" << tr("GDP = 0, when all in-degrees are equal (i.e. regular lattice).") << "
" << tr("GDP = 1, when one node is chosen by all other nodes (i.e. star). ") << "
" << tr("This is exactly the situation realised by a star graph.") << "
" << "(Wasserman & Faust, p. 203)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralization measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Degree Prestige report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Computes Proximity Prestige of each vertex * Also the mean value and the variance of it.. */ void Graph::prestigeProximity( const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug()<< "Graph::prestigeProximity()"; if ( calculatedPP ) { qDebug() << "Graph::prestigeProximity() - " " graph not changed - returning"; return; } graphDistancesGeodesic(false,considerWeights, inverseWeights,inverseWeights); // calculate centralities VList::const_iterator it, jt; qreal PP=0; qreal dist=0; qreal Ii=0; qreal V=vertices(dropIsolates); classesPP=0; discretePPs.clear(); sumPP=0; maxPP=0; minPP=V-1; variancePP=0; meanPP=0; int progressCounter = 0; QString pMsg = tr("Computing Proximity Prestige scores. \nPlease wait ..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(V,pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); PP=0; Ii = 0; if ((*it)->isIsolated()){ continue; } for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt){ if ( (*it)->name() == (*jt)->name() ) { continue; } if ( ! (*jt)-> isEnabled() ) { continue; } dist = (*jt)->distance( (*it)->name() ); if (dist != RAND_MAX ) { PP += dist; Ii ++; // compute |Ii| } } qDebug()<< "Graph::prestigeProximity() - vertex" << (*it)->name() << "actors in influence domain Ii" << Ii << "actors in network"<< (V-1) << "fraction of actors who reach i |Ii|/(V-1)=" << Ii/ (V-1) << "distance to actors in Ii" << PP << "average distance to actors in Ii" << PP/ Ii << "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 resolveClasses(PP, discretePPs, 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 / 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/ V; qDebug() << "Graph::prestigeProximity - sumPP = " << sumPP << " meanPP = " << meanPP << " variancePP " << variancePP; calculatedPP=true; emit signalProgressBoxKill(); } /** * @brief Graph::writePrestigeProximity * Writes the proximity prestige indices to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writePrestigeProximity( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); prestigeProximity(considerWeights, inverseWeights, dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::PP, m_reportsChartType, distImageFileName); VList::const_iterator it; int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing Proximity Prestige scores to file. \nPlease wait ..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("PROXIMITY PRESTIGE (PP)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("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 " "(Wasserman & Faust, formula 5.25, p. 204)
" "Thus, it is similar to Closeness Centrality but it counts " "only inbound distances to each actor, thus it is a measure of actor prestige.
" "This metric is useful for directed networks which are " "not strongly connected (thus the ordinary CC index cannot be computed).
" "In undirected networks, the PP has the same properties and yields " "the same results as Closeness Centrality.
" "Read the Manual for more.
") << "

"; outText << "

" << "" << tr("PP range: ") <<"" << tr("0 ≤ PP ≤ 1 (PP is a ratio)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); rowCount++; outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("PP=PP'") << "" << tr("%PP") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->PP() << "" << (100* ((*it)->SPP())) << "
"; if ( minPP == maxPP) { outText << "

" << tr("All nodes have the same PP score.") << "

"; } else { outText << "

"; outText << "" << tr("Max PP = ") <<"" << maxPP <<" (node "<< maxNodePP << ")" << "
" << "" << tr("Min PP = ") <<"" << minPP <<" (node "<< minNodePP << ")" << "
" << "" << tr("PP classes = ") <<"" << classesPP << "

"; } outText << "

"; outText << "" << tr("PP Sum = ") <<"" << sumPP <<"
" << "" << tr("PP Mean = ") <<"" << meanPP <<"
" << "" << tr("PP Variance = ") <<"" << variancePP <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("PP DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

 

"; outText << "

"; outText << tr("Proximity Prestige report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Calculates the PageRank Prestige of each vertex * @param dropIsolates */ void Graph::prestigePageRank(const bool &dropIsolates){ qDebug()<< "Graph::prestigePageRank()"; if ( calculatedPRP ) { qDebug() << " graph not changed - return "; return; } discretePRPs.clear(); sumPRP=0; t_sumPRP=0; maxPRP=0; minPRP=RAND_MAX; classesPRP=0; variancePRP=0; // The parameter d is a damping factor which can be set between 0 and 1. // Google creators set d to 0.85. d_factor = 0.85; qreal PRP=0, oldPRP = 0; qreal SPRP=0; int iterations = 1; // a counter int referrer; qreal delta = 0.00001; // The delta where we will stop the iterative calculation qreal maxDelta = RAND_MAX; qreal sumInLinksPR = 0; // temp var for inlinks sum PR qreal transferedPRP = 0; qreal inLinks = 0; // temp var qreal outLinks = 0; // temp var qreal t_variance=0; int N = vertices(dropIsolates) ; VList::const_iterator it; H_edges::const_iterator jt; int relation=0; bool edgeStatus=false; QString pMsg = tr("Computing PageRank Prestige scores. \nPlease wait ..."); emit statusMessage( pMsg ) ; emit signalProgressBoxCreate(N,pMsg); 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 / (qreal) N ); // 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 ( edgesEnabled() == 0 ) { qDebug()<< "Graph::prestigePageRank() " <<" - all vertices are isolated and of equal PR. Stop"; return; } emit signalProgressBoxUpdate( N / 3); // begin iteration - continue until we reach our desired delta while (maxDelta > delta) { qDebug()<< "Graph::prestigePageRank() - ITERATION : " << iterations; sumPRP=0; maxDelta = 0; maxPRP=0; minPRP=RAND_MAX; maxNodePRP = 0; minNodePRP = 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 != relationCurrent() ){ ++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 << " vpos " << vpos[referrer]; if ( edgeExists( referrer , (*it)->name() ) ) { inLinks = m_graph[ vpos[referrer] ] ->inEdgesConst(); outLinks = m_graph[ vpos[referrer] ]-> outEdgesConst(); PRP = m_graph[ vpos[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) / (qreal) N + d_factor * sumInLinksPR; (*it) -> setPRP ( PRP ); 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 " << sumPRP; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { PRP = (*it)->PRP(); if ( PRP > maxPRP ) { maxPRP = PRP; maxNodePRP=(*it)->name(); } if ( PRP < minPRP ) { minPRP = PRP; minNodePRP=(*it)->name(); } } iterations++; } emit signalProgressBoxUpdate( 2* N / 3); if (N != 0 ) { meanPRP = sumPRP / (qreal) N ; } else { meanPRP = SPRP; } qDebug() << "sumPRP = " << sumPRP << " N = " << N << " meanPRP = " << meanPRP; // calculate std and min/max PRPs for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if (dropIsolates && (*it)->isIsolated()) { continue; } PRP=(*it)->PRP(); resolveClasses(PRP,discretePRPs,classesPRP); SPRP = PRP / maxPRP ; (*it)->setSPRP( SPRP ); qDebug()<< "Graph::prestigePageRank() vertex: " << (*it)->name() << " PR = " << PRP << " standard PR = " << SPRP << " t_sumPRP " << t_sumPRP; t_variance = ( PRP - meanPRP ) ; t_variance *=t_variance; qDebug() << "PRP " << (*it)->PRP() << " t_variance " << PRP - meanPRP << " t_variance^2" << t_variance ; variancePRP += t_variance; } qDebug() << "PRP' Variance " << variancePRP << " N " << N ; variancePRP = variancePRP / (qreal) N; qDebug() << "PRP' Variance: " << variancePRP ; calculatedPRP= true; emit signalProgressBoxUpdate( N); emit signalProgressBoxKill(); return; } /** * @brief Writes the PageRank scores of vertices to a file * @param fileName * @param dropIsolates */ void Graph::writePrestigePageRank(const QString fileName, const bool dropIsolates){ QElapsedTimer computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); prestigePageRank(dropIsolates); QString distImageFileName ; if ( m_reportsChartType != ChartType::None ) { distImageFileName = QFileInfo(fileName).canonicalPath() + QDir::separator() + QFileInfo(fileName).completeBaseName() + ".png"; } prominenceDistribution(IndexType::PRP,m_reportsChartType,distImageFileName); VList::const_iterator it; int rowCount=0; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Writing PageRank scores to file. \nPlease wait ..."); emit statusMessage( pMsg ) ; emit signalProgressBoxCreate(N,pMsg); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << htmlHead; outText << "

"; outText << tr("PAGERANK PRESTIGE (PRP)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The PRP is an importance ranking index for each node based on the structure " "of its incoming links/edges and the rank of the nodes linking to it.
" "For each node u the algorithm counts all inbound links (edges) to it, but it " "normalizes each inbound link from a node v by the outDegree of v.
" "The PR values correspond to the principal eigenvector of the normalized link matrix.
" "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 outbound edge weights of v. " "Therefore, nodes with high outLink weights give smaller percentage of their PR to node u." ) << "
" << tr("PRP' is the scaled PRP (PRP divided by max PRP).") << "

"; outText << "

" << "" << tr("PRP range: ") <<"" << tr("(1-d)/N = ") << ( ( 1- d_factor ) / N ) << tr(" ≤ PRP ") << "

"; outText << "

" << "" << tr("PRP' range: ") <<"" << tr("0 ≤ PRP' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ emit signalProgressBoxUpdate( ++progressCounter ); rowCount++; outText << fixed; if (dropIsolates && (*it)->isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("PRP") << "" << tr("PRP'") << "" << tr("%PRP'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->PRP() << "" << (*it)->SPRP() << "" << (100* ((*it)->SPRP())) << "
"; if ( minPRP == maxPRP) { outText << "

" << tr("All nodes have the same PRP score.") << "

"; } else { outText << "

"; outText << "" << tr("Max PRP = ") <<"" << maxPRP <<" (node "<< maxNodePRP << ")" << "
" << "" << tr("Min PRP = ") <<"" << minPRP <<" (node "<< minNodePRP << ")" << "
" << "" << tr("PRP classes = ") <<"" << classesPRP << "

"; } outText << "

"; outText << "" << tr("PRP Sum = ") <<"" << sumPRP <<"
" << "" << tr("PRP Mean = ") <<"" << meanPRP <<"
" << "" << tr("PRP Variance = ") <<"" << variancePRP <<"
"; outText << "

"; if ( m_reportsChartType != ChartType::None ) { outText << "

"; outText << tr("PRP' DISTRIBUTION") << "

"; outText << "

"; outText << ""; } outText << "

 

"; outText << "

"; outText << tr("PageRank Prestige report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Adds a little universal randomness :) */ void Graph::randomizeThings() { 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); } /** * @brief Create an erdos-renyi random network according to the given model * @param vert * @param model * @param edges * @param eprob * @param mode * @param diag */ void Graph::randomNetErdosCreate(const int &N, const QString &model, const int &m, const qreal &p, const QString &mode, const bool &diag) { qDebug() << "Graph::randomNetErdosCreate() - vertices " << N << " model " << model << " edges " << m << " edge probability " << p << " graph mode " << mode << " diag " << diag; if (mode=="graph") { graphSetDirected(false); } vpos.reserve(N); randomizeThings(); int progressCounter=0; int edgeCount = 0; qDebug() << "Graph::randomNetErdosCreate() - Creating nodes..."; QString pMsg = tr( "Creating Erdos-Renyi Random Network. \n" " Please wait..." ); emit signalProgressBoxCreate( (m != 0 ? m:N), pMsg ); for (int i=0; i< N ; i++) { int x=canvasRandomX(); int y=canvasRandomY(); qDebug("Graph: randomNetErdosCreate, new node i=%i, at x=%i, y=%i", i+1, x,y); vertexCreate( i+1, initVertexSize, initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape, initVertexIconPath, false ); } qDebug() << "Graph::randomNetErdosCreate() - Creating edges..."; if ( model == "G(n,p)") { qDebug() << "Graph::randomNetErdosCreate() - G(n,p) model..."; for (int i=0;i " << j+1; if (!diag && i==j) { qDebug()<< " Graph::randomNetErdosCreate() - skip because " << i+1 << " = " << j+1 << " and diag " << diag; continue; } if ( ( rand() % 100 + 1 ) / 100.0 < p ) { edgeCount ++ ; if (mode == "graph") { qDebug() << "Graph::randomNetErdosCreate() - " <<" create undirected Edge no " << edgeCount; edgeCreate(i+1, j+1, 1, initEdgeColor, EdgeType::Undirected, false, false, QString(), false); } else { qDebug() << "Graph::randomNetErdosCreate() - " <<" create directed Edge no " << edgeCount; edgeCreate(i+1, j+1, 1, initEdgeColor, EdgeType::Directed, true, false, QString(), false); } } // else // qDebug() << "Graph::randomNetErdosCreate() - do not create Edge"; } emit signalProgressBoxUpdate(++progressCounter ); } } else { qDebug() << "Graph::randomNetErdosCreate() - G(n,M) model..."; int source = 0, target = 0 ; do { source = rand() % N + 1; target = rand() % N + 1; qDebug() << "Graph::randomNetErdosCreate() - random pair " << " " << source << " , " << target ; if (!diag && source == target ) { qDebug() << "Graph::randomNetErdosCreate() - skip self loop pair "; continue; } if ( edgeExists(source, target) ) { qDebug() << "Graph::randomNetErdosCreate() - skip pair - exists"; continue; } edgeCount ++; if (mode == "graph") { qDebug() << "Graph::randomNetErdosCreate() - create " << " undirected Edge no " << edgeCount; edgeCreate(source, target, 1, initEdgeColor, EdgeType::Undirected, false, false, QString(), false); } else { qDebug() << "Graph::randomNetErdosCreate() - create " << " directed Edge no " << edgeCount; edgeCreate(source, target, 1, initEdgeColor, EdgeType::Directed, true, false, QString(), false); } emit signalProgressBoxUpdate(++progressCounter ); } while ( edgeCount != m ); } relationCurrentRename(tr("erdos-renyi"), true); emit signalProgressBoxUpdate((m != 0 ? m:N)); emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedVerticesEdges); } /** * @brief Creates a scale-free random-network network * @param N * @param power * @param m0 * @param m * @param alpha * @param mode */ void Graph::randomNetScaleFreeCreate (const int &N, const int &power, const int &m0, const int &m, const qreal &alpha, const QString &mode) { qDebug() << "Graph::randomNetScaleFreeCreate() - max nodes n" << N << "power" << power <<"edges added in every round m" < " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EdgeType::Undirected, false, false, QString(), false); } emit signalProgressBoxUpdate( ++progressCounter ); } qDebug()<< "Graph::randomNetScaleFreeCreate() - @@@@ " << " start network growth to " << N << " nodes with preferential attachment" ; for (int i= m0 ; i < N ; ++i) { x=x0 + radius * cos(i * rad); y=y0 + radius * sin(i * rad); qDebug() << "Graph::randomNetScaleFreeCreate() - ++++" << " adding new node i " << i+1 << " pos " << x << "," << y ; vertexCreate( i+1, initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape,initVertexIconPath, false ); emit signalProgressBoxUpdate( ++progressCounter ); // need to multiply by 2, since we have a undirected graph // and edgesEnabled reports edges/2 sumDegrees = 2 * edgesEnabled(); newEdges = 0; qDebug()<< "Graph::randomNetScaleFreeCreate() - repeat until we reach" << m << "new edges for node" < " "Creating pref.att. undirected edge " << i+1 << " <-> " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EdgeType::Undirected, false, false, QString(), false); newEdges ++; } else { qDebug() << "Graph::randomNetScaleFreeCreate() -----> " "Creating pref.att. directed edge " << i+1 << " <-> " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EdgeType::Directed, true, false, QString(), false); newEdges ++; } } } if ( newEdges == m ) break; } qDebug()<< "Graph::randomNetScaleFreeCreate() - " << m << "edges reached " "for node" << i+1; } relationCurrentRename(tr("scale-free"),true); qDebug() << "Graph::randomNetScaleFreeCreate() - finished. Calling " "graphSetModified()"; graphSetModified(GraphChange::ChangedVerticesEdges); emit signalProgressBoxKill(); layoutVertexSizeByIndegree(); } /** * @brief Creates a small world random network * @param vert * @param degree * @param beta */ void Graph::randomNetSmallWorldCreate (const int &N, const int °ree, const double &beta, const QString &mode) { qDebug() << "Graph:randomNetSmallWorldCreate() -. " << "vertices: " << N << "degree: " << degree << "beta: " << beta << "mode: " << mode << "First creating a ring lattice"; if (mode=="graph") { graphSetDirected(false); } randomNetRingLatticeCreate(N, degree, true); QString pMsg = tr("Creating Small-World Random Network. \n" "Please wait ..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N, pMsg); qDebug("******** Graph: REWIRING starts..."); int candidate=0; int progressCounter=1; for (int i=1;i>>>> REWIRING: Check if "<< i << " is linked to " << j; if ( edgeExists(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!"); edgeRemove(i, j, true); qDebug()<<">>>>> REWIRING: OK. Let's create a new edge!"; for (;;) { //do until we create a new edge candidate=rand() % (N+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 ( edgeExists(i, candidate) == 0) qDebug("<----> Random New Edge Experiment between %i and %i:", i, candidate); if (rand() % 100 > 0.5) { qDebug("Creating new link!"); edgeCreate(i, candidate, 1, initEdgeColor, EdgeType::Undirected, false, false, QString(), false); break; } } } else qDebug("Will not break link!"); } } emit signalProgressBoxUpdate( ++progressCounter ); } relationCurrentRename(tr("small-world"), true); emit signalProgressBoxKill(); layoutVertexSizeByIndegree(); graphSetModified(GraphChange::ChangedVerticesEdges); } /** * @brief Creates a random network where nodes have the same degree. * @param vert * @param degree */ void Graph::randomNetRegularCreate(const int &N, const int °ree, const QString &mode, const bool &diag){ qDebug() << "Graph::randomNetRegularCreate()"; Q_UNUSED(diag); if (mode=="graph") { graphSetDirected(false); } int x = 0, y = 0 ; qreal progressCounter=0; qreal progressFraction =(graphIsUndirected()) ? 2/(qreal) degree : 1/(qreal) degree; int target = 0; int edgeCount = 0; QList m_edges; QStringList firstEdgeVertices, secondEdgeVertices, m_edge; QString firstEdge, secondEdge; randomizeThings(); vpos.reserve(N); QString pMsg = tr( "Creating pseudo-random d-regular network. \n" "Please wait..." ); emit statusMessage( pMsg ); emit signalProgressBoxCreate (N, pMsg ); qDebug()<< "Graph::randomNetRegularCreate() - creating vertices"; for (int i=0; i< N ; i++) { x=canvasRandomX(); y=canvasRandomY(); qDebug() << "Graph::randomNetRegularCreate() - creating new vertex at " << x << "," << y; vertexCreate( i+1, initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape,initVertexIconPath, false ); } qDebug()<< "Graph::randomNetRegularCreate() - Creating initial edges"; if (mode=="graph") { for (int i=0;i (N-1)) target = target-N; qDebug()<< "Graph::randomNetRegularCreate() - undirected edge " << i+1 << "<->"<< target+1; m_edges.append(QString::number(i+1)+"->"+QString::number(target+1)); edgeCount ++; } } } else { for (int i=0;i (N-1)) target = target-N; qDebug()<< "Graph::randomNetRegularCreate() - directed edge " << i+1 << "->"<< target+1; m_edges.append(QString::number(i+1)+"->"+QString::number(target+1)); edgeCount ++; } } } qDebug()<< "Graph::randomNetRegularCreate() - Edges created:" << edgeCount << "Edge list count:" << m_edges.size() << "Now reordering all edges in pairs..."; //take randomly two edges, of different vertices and combine their source //and target vertices to two different edges for (int i = 1 ; i< m_edges.size(); ++i) { edgeCount = 0; firstEdgeVertices.clear(); secondEdgeVertices.clear(); firstEdgeVertices << ""; firstEdgeVertices << ""; secondEdgeVertices << ""; secondEdgeVertices << ""; while (firstEdgeVertices[0] == firstEdgeVertices[1] || firstEdgeVertices[0] == secondEdgeVertices[0] || firstEdgeVertices[0] == secondEdgeVertices[1] || firstEdgeVertices[1] == secondEdgeVertices[0] || firstEdgeVertices[1] == secondEdgeVertices[1] || secondEdgeVertices[0] == secondEdgeVertices[1] || m_edges.contains( firstEdgeVertices[0] + "->" + secondEdgeVertices[1] ) || m_edges.contains( secondEdgeVertices[0] + "->" + firstEdgeVertices[1] ) || (graphIsUndirected() && m_edges.contains( secondEdgeVertices[1] + "->" + firstEdgeVertices[0]) )|| (graphIsUndirected() && m_edges.contains( firstEdgeVertices[1] + "->" + secondEdgeVertices[0] ) ) ) { firstEdge = m_edges.at(rand() % m_edges.size()) ; firstEdgeVertices = firstEdge.split("->"); secondEdge = m_edges.at(rand() % m_edges.size()) ; secondEdgeVertices = secondEdge.split("->"); qDebug()<< "Graph::randomNetRegularCreate() - firstEdgeVertices:" << firstEdgeVertices << " secondEdgeVertices:" << secondEdgeVertices; } qDebug()<< "Graph::randomNetRegularCreate() - removing edges:" <" << firstEdgeVertices[1] << "and" << secondEdgeVertices[0] << "->" << secondEdgeVertices[1] << "edge list count:" << m_edges.size(); m_edges.append( firstEdgeVertices[0]+"->"+secondEdgeVertices[1]); m_edges.append(secondEdgeVertices[0]+"->"+firstEdgeVertices[1]); qDebug()<< "Graph::randomNetRegularCreate() - 2 new edges added:" << firstEdgeVertices[0] << "->" << secondEdgeVertices[1] <<"and" << secondEdgeVertices[0]<<"->"<"); qDebug() << "Graph::randomNetRegularCreate() -" << "Drawing undirected Edge no" << edgeCount << ":" << m_edge[0].toInt(0) << "<->" << m_edge[1].toInt(0); edgeCreate(m_edge[0].toInt(0), m_edge[1].toInt(0), 1, initEdgeColor, (graphIsUndirected()) ? EdgeType::Undirected : EdgeType::Directed, (graphIsUndirected()) ? false:true, false, QString(), false); edgeCount++; progressCounter +=progressFraction; qDebug() << "Graph::randomNetRegularCreate() -" << "progressCounter " << progressCounter << "fmod ( progressCounter, 1.0) = " << fmod ( progressCounter, 1.0); if ( fmod ( progressCounter, 1.0) == 0) { emit signalProgressBoxUpdate( (int) progressCounter ); } } relationCurrentRename(tr("d-regular"), true); emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedVerticesEdges); } /** * @brief Creates a random ring lattice network. * @param vert * @param degree * @param x0 * @param y0 * @param radius * @param updateProgress */ void Graph::randomNetRingLatticeCreate(const int &N, const int °ree, const bool updateProgress) { qDebug()<< "Graph::createRingLatticeNetwork()"; int x=0; int y=0; int progressCounter=0; double x0 = canvasWidth/2.0; double y0 =canvasHeight/2.0; double radius = canvasMaxRadius(); double rad= (2.0* M_PI/ N ); graphSetDirected(false); randomizeThings(); vpos.reserve(N); QString pMsg = tr( "Creating ring-lattice network. \n" "Please wait..." ); emit statusMessage( pMsg ); emit signalProgressBoxCreate (N, pMsg ); for (int i=0; i< N ; i++) { x=x0 + radius * cos(i * rad); y=y0 + radius * sin(i * rad); vertexCreate( i+1,initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape, initVertexIconPath, false); qDebug("Graph::createRingLatticeNetwork(): new node i=%i, at x=%i, y=%i", i+1, x,y); } int target = 0; for (int i=0;i latticeEdges; QStringList m_edge; QString edge; QString oppEdge; randomizeThings(); vpos.reserve(N); QString pMsg = tr( "Creating lattice network. \n" "Please wait..." ); emit statusMessage( pMsg ); emit signalProgressBoxCreate (N, pMsg ); // create vertices qDebug()<< "Graph::randomNetLatticeCreate() - creating vertices"; nCount = 0; canvasPadding = 20; nodeHPadding= ( canvasWidth ) / (double) ( length + 2); nodeVPadding= ( canvasHeight ) / (double) ( length + 2); qDebug()<< "Graph::randomNetLatticeCreate() - " "canvasPadding" << canvasPadding << "nodeHPadding"<"<< target << "OOB RIGHT"; continue; } if ( i % length == 1 && target == i - 1) { qDebug()<< "Graph::randomNetLatticeCreate() - " << i << "<->"<< target << "OOB LEFT"; continue; } if ( target > N ) { qDebug()<< "Graph::randomNetLatticeCreate() - " << i << "<->"<< target << "OOB DOWN"; target = target % N ; continue; } if ( target < 1 ) { qDebug()<< "Graph::randomNetLatticeCreate() - " << i << "<->"<< target << "OOB UP"; target = N - target ; continue; } qDebug()<< "Graph::randomNetLatticeCreate() - " << i << "<->"<< target << "OK"; edge = QString::number(i)+"<->"+QString::number(target); oppEdge = QString::number(i)+"<->"+QString::number(target); if ( !latticeEdges.contains(edge) && !latticeEdges.contains(oppEdge) ) { latticeEdges.append(QString::number(i)+"<->"+QString::number(target)); edgeCount ++; } } } //// up // target = i-j*length; //// pre //target = i-j; //// same // i //// next //target = i+j; //// down //target = i+j*length; } } } else { // directed graph } // draw edges for (int i = 0; i < latticeEdges.size(); ++i) { m_edge = latticeEdges.at(i).split("<->"); qDebug() << "Graph::randomNetLatticeCreate() -" << "Drawing undirected Edge no" << i + 1 << ":" << m_edge[0].toInt(0) << "<->" << m_edge[1].toInt(0); edgeCreate(m_edge[0].toInt(0), m_edge[1].toInt(0), 1, initEdgeColor, (graphIsUndirected()) ? EdgeType::Undirected : EdgeType::Directed, (graphIsUndirected()) ? false:true, false, QString(), false); // edgeCount++; progressCounter +=progressFraction; // qDebug() << "Graph::randomNetLatticeCreate() -" // << "progressCounter " << progressCounter // << "fmod ( progressCounter, 1.0) = " // << fmod ( progressCounter, 1.0); // if ( fmod ( progressCounter, 1.0) == 0) { // emit signalProgressBoxUpdate( (int) progressCounter ); // } } relationCurrentRename(tr("lattice"), true); emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedVerticesEdges); } /** * @brief Calculates and returns the number of walks of a given length between v1 and v2 * @param v1 * @param v2 * @param length * @return */ int Graph::walksBetween(int v1, int v2, int length) { graphWalksMatrixCreate(length); return XM.item(v1-1,v2-1); } /** * @brief Computes either the "Walks of given length" or the "Total Walks" matrix. * If length>0, it computes the Walks of given length matrix, XM=AM^l * where each element (i,j) denotes the number of walks of length l between vertex i and j. * If length=0, it computes the Total Walks matrix, XSM=Sum{AM^n} where each (i,j) * denotes the total number of walks of any length between vertices i and j. * NOTE: In the latter case, 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. * @param length * @param updateProgress */ void Graph::graphWalksMatrixCreate(const int &N, const int &length, const bool &updateProgress) { bool dropIsolates=false; bool considerWeights=true; bool inverseWeights=false; bool symmetrize=false; graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); if (length>0) { qDebug()<< "Graph::graphWalksMatrixCreate() - " "Calculating sociomatrix power" << length; QString pMsg = tr("Computing walks of length %1. \nPlease wait...").arg(length) ; emit statusMessage( pMsg ); if (updateProgress) { signalProgressBoxCreate(length,pMsg); } XM = AM.pow(length, false); if (updateProgress) { emit signalProgressBoxUpdate (length); } } else { qDebug()<< "Graph::graphWalksMatrixCreate() - " "Calculating all sociomatrix powers up to" << N-1; XM = AM; // XM will be the product matrix XSM = AM; // XSM is the sum of product matrices QString pMsg = tr("Computing sociomatrix powers up to %1. \nPlease wait...").arg(N-1) ; emit statusMessage( pMsg ); if (updateProgress) { signalProgressBoxCreate(N-1,pMsg); } for (int i=2; i <= (N-1) ; ++i) { emit statusMessage(tr("Computing all sociomatrix powers up to %1. " "Now computing A^%2. Please wait...").arg(N-1).arg(i)); XM*=AM; // qDebug() << "Graph::graphWalksMatrixCreate() i"<"; if (length>0) { outText << tr("WALKS OF LENGTH %1 MATRIX").arg(length); } else { outText << tr("TOTAL WALKS MATRIX"); } outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; if (length>0) { outText << "

" << tr("The Walks of length %1 matrix is a NxN matrix " "where each element (i,j) is the number of walks of " "length %1 between actor i and actor j, " "or 0 if no walk exists.
" "A walk is a sequence of edges and vertices, where each edge's " "endpoints are the two vertices adjacent to it. In a walk, " "vertices and edges may repeat.
" "Warning: Walks count unordered pairs of nodes. ").arg(length) << "

"; } else { outText << "

" << tr("The Total Walks matrix of a social network is a NxN matrix " "where each element (i,j) is the total number of walks of any " "length (less than or equal to %1) between actor i and actor j, " "or 0 if no walk exists.
" "A walk is a sequence of edges and vertices, where each edge's " "endpoints are the two vertices adjacent to it. In a walk, " "vertices and edges may repeat.
" "Warning: Walks count unordered pairs of nodes. ").arg(N-1) << "

"; } emit statusMessage ( tr("Writing Walks matrix to file:") + fn ); qDebug()<<"Graph::writeMatrixWalks() - Writing XM to file"; if (length > 0) { //XM.printHTMLTable(outText); writeMatrixHTMLTable(outText,XM,true); } else { writeMatrixHTMLTable(outText,XSM,true); //XSM.printHTMLTable(outText); } outText << "

 

"; outText << "

"; outText << tr("Walks report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Returns the influence range of vertex v1, namely the set of nodes who are * reachable from v1 (See Wasserman and Faust, pp.200-201, based on Lin, 1976). * The Influence Range of vertex v can also be defined as: * Ji = Sum [ D(v,j), iff D(v,j) != inf ] for every j in V, where j!=v and D the distance matrix * This function is for digraphs only * @param v1 * @return */ QList Graph::vertexinfluenceRange(int v1){ qDebug() << "Graph::vertexinfluenceRange() - vertex:"<< v1; graphDistancesGeodesic(false); VList::const_iterator jt; int N = vertices( false, false, true); int progressCounter=0; int target = 0; influenceRanges.clear(); influenceRanges.reserve(N); QString pMsg = tr("Creating Influence Range List. \nPlease wait "); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt) { emit signalProgressBoxUpdate(++progressCounter); target = (*jt)->name(); if ( ! (*jt)->isEnabled() ) { qDebug() << "Graph::vertexinfluenceRange() - target:" << target << "disabled. SKIP"; continue; } if ( graphDistanceGeodesic(v1, target) != RAND_MAX ) { qDebug() << "Graph::vertexinfluenceRange() - v1 can reach:" << target; influenceRanges.insert(v1,target); } } emit signalProgressBoxKill(); return influenceRanges.values(v1); } /** * @brief Returns the influence domain of vertex v1, namely the set of nodes who can * reach v1 * The Influence Domain Ii of vertex v can also be defined as: * Ii = Sum [ D(i,v), iff D(i,v) != inf ] for every in V, where i!=v and D the distance matrix * This function applies to digraphs only * @param v1 * @return */ QList Graph::vertexinfluenceDomain(int v1){ qDebug() << "Graph::vertexinfluenceDomain() - vertex:"<< v1; graphDistancesGeodesic(false); VList::const_iterator it; int N = vertices( false, false, true); int progressCounter=0; int source = 0; influenceDomains.clear(); influenceDomains.reserve(N); QString pMsg = tr("Creating Influence Domain List. \nPlease wait "); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); source = (*it)->name(); if ( ! (*it)->isEnabled() ) { qDebug() << "Graph::vertexinfluenceDomain() - " << source << "disabled. SKIP"; continue; } if ((*it)->distance( v1 ) != RAND_MAX ) { qDebug() << "Graph::vertexinfluenceDomain() - v1 reachable from:" << source; influenceDomains.insert(v1,source); } } emit signalProgressBoxKill(); return influenceDomains.values(v1); } /** Writes the reachability matrix X^R of the graph to a file */ void Graph::writeReachabilityMatrixPlainText(const QString &fn, const bool &dropIsolates) { qDebug("Graph::writeReachabilityMatrixPlainText() "); QFile file (fn); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText(&file); outText << "-Social Network Visualizer "<< VERSION <"; outText << tr("CLUSTERING COEFFICIENT (CLC) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The local Clustering Coefficient, introduced by Watts and Strogatz (1998) " "quantifies how close each node and its neighbors are to being a complete subgraph (clique).") << "
" << tr("For each node u, the local CLC score is the proportion of actual links between " "its neighbors divided by the number of links that could possibly exist between them.
" "The CLC index is used to characterize the transitivity of a network. A value close to one " "indicates that the node is involved in many transitive relations. " "CLC' is the normalized CLC, divided by maximum CLC found in this network.") << "

"; outText << "

" << "" << tr("CLC range: ") <<"" << tr("0 ≤ CLC ≤ 1 ") << "

"; outText << "

" << "" << tr("CLC range: ") <<"" << tr("0 ≤ CLC' ≤ 1 ") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate( ++progressCounter ); rowCount++; outText <" <<"" <<""; } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("CLC") << "" << tr("CLC'") << "" << tr("%CLC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(m_reportsLabelLength) : "-" ) << "" << (*it)->CLC() << "" << (*it)->CLC() / maxCLC << "" << 100 * (*it)->CLC() / maxCLC << "
"; if ( minCLC == maxCLC) { outText << "

" << tr("All nodes have the same local CLC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max CLC = ") <<"" << maxCLC <<" (node "<< maxNodeCLC << ")" << "
" << "" << tr("Min CLC = ") <<"" << minCLC <<" (node "<< minNodeCLC << ")" << "
" << "

"; } outText << "

" << "" << tr("CLC Mean = ") <<"" << averageCLC <<"
" << "" << tr("CLC Variance = ") <<"" << varianceCLC <<"
"; outText << "

"; outText << "

"; outText << tr("GROUP / NETWORK AVERAGE CLUSTERING COEFFICIENT (GCLC)") << "

"; outText << "

" << "" << tr("GCLC = ") <<"" << averageCLC << "

"; outText << "

" << tr("Range: 0 < GCLC < 1
") << tr("GCLC = 0, when there are no cliques (i.e. acyclic tree).
") << tr("GCLC = 1, when every node and its neighborhood are complete cliques.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Clustering Coefficient report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } //Writes the triad census to a file void Graph::writeTriadCensus( const QString fileName, const bool considerWeights) { qDebug() << "Graph::writeTriadCensus()"; QElapsedTimer computationTimer; computationTimer.start(); Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Computing triad census. Please wait....")) ); if ( !calculatedTriad) { if (!graphTriadCensus()){ qDebug() << "Error in graphTriadCensus(). Exiting..."; file.close(); return; } } int rowCount = 0; int N = vertices(); int progressCounter = 0; QList triadTypes; triadTypes << "003" ; triadTypes << "012" ; triadTypes << "102" ; triadTypes << "021D"; triadTypes << "021U"; triadTypes << "021C"; triadTypes << "111D"; triadTypes << "111U"; triadTypes << "030T"; triadTypes << "030C"; triadTypes << "201" ; triadTypes << "120D"; triadTypes << "120U"; triadTypes << "120C"; triadTypes << "210" ; triadTypes << "300" ; QString pMsg = tr("Writing Triad Census to file. \nPlease wait...") ; emit statusMessage( pMsg ); emit signalProgressBoxCreate(16,pMsg); outText << htmlHead; outText << "

"; outText << tr("TRIAD CENSUS (TRC) REPORT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("A Triad Census 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:
") << tr("- The first character is the number of mutual (M) dyads in the triad. Possible values: 0, 1, 2, 3.
" "- The second character is the number of asymmetric (A) dyads in the triad. Possible values: 0, 1, 2, 3.
" "- The third character is the number of null (N) dyads in the triad. Possible values: 0, 1, 2, 3.
" "- The fourth character is inferred 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\")") << "

"; outText << ""; outText << "" <<"" <<"" <<"" << "" <<""; for (int i = 0 ; i<=15 ; i++) { emit signalProgressBoxUpdate(++progressCounter); rowCount = i + 1; outText << "" <<"" <<""; } outText << "
" << tr("Type") << "" << tr("Census") // << "" // << tr("Expected Value") <<"
" << triadTypes[i] << "" << triadTypeFreqs[i] << "
"; outText << "

 

"; outText << "

"; outText << tr("Triad Census report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief Graph::writeCliqueCensus * Calls graphCliques() to compute all cliques (maximal connected subgraphs) of the network. * Then writes the results into a file, along with the Actor by clique analysis, * the Co-membership matrix and the Hierarchical clustering of overlap matrix * @param fileName * @param considerWeights */ bool Graph::writeCliqueCensus(const QString &fileName, const bool considerWeights) { QElapsedTimer computationTimer; computationTimer.start(); qDebug()<< "Graph::writeCliqueCensus() "; Q_UNUSED(considerWeights); QString varLocation = "Both"; bool dendrogram = true; QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } int N = vertices(); int cliqueCounter=0; int rowCounter = 0; int cliqueSize = 0; int actor2 = 0, actor1=0, index1=0, index2=0; qreal numerator = 0; QString listString; VList::const_iterator it, it2; QString pMsg = tr("Computing Clique Census and writing it to a file. \nPlease wait..."); emit statusMessage(pMsg); emit signalProgressBoxCreate(2*N,pMsg); // compute clique census pMsg = tr("Computing Clique Census. Please wait..") ; emit statusMessage ( pMsg ); qDebug() << "Graph::writeCliqueCensus() - calling graphCliques"; csRecDepth = 0; // Call graphCliques() to compute all cliques (maximal connected subgraphs) of the network. graphCliques(); pMsg = tr("Writing Clique Census to file. Please wait..") ; emit statusMessage ( pMsg ); QTextStream outText ( &file ); outText.setCodec("UTF-8"); outText << htmlHead; outText.setRealNumberPrecision(m_reportsRealPrecision); outText << "

"; outText << tr("CLIQUE CENSUS (CLQs) REPORT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("A clique is the largest subgroup of actors in the social network who are all " "directly connected to each other (maximal complete subgraph).
" "SocNetV applies the Bron–Kerbosch algorithm to produce a census of all maximal cliques " "in the network and reports some useful statistics such as disaggregation by vertex " "and co-membership information.
") << "

"; outText << "

" << "" << tr("Maximal Cliques found: ") <<"" << m_cliques.count() << "

"; outText << ""; outText << "" <<"" <<"" <<"" << "" <<""; foreach (QList clique, m_cliques) { ++cliqueCounter; outText << ""; listString.truncate(0); while (!clique.empty()) { listString += QString::number (clique.takeFirst()); if (!clique.empty()) listString += " "; } outText <<"" <<""; } outText << "
" << tr("Clique No") << "" << tr("Clique members") << "
" << cliqueCounter << "" << listString << "
"; /** * Write the actor by clique analysis matrix. * For each actor-clique pair, we compute the proportion of clique members adjacent */ outText << "

" << "" << tr("Actor by clique analysis: ") <<"" << tr("Proportion of clique members adjacent") << "

"; outText << ""; outText << "" <<"" <<""; for (int listIndex=0; listIndex" << listIndex+1 << ""; } outText <<"" << "" <<""; rowCounter = 0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ rowCounter++; actor1 = (*it)->name(); outText << "" <<""; foreach (QList clique, m_cliques) { numerator = 0; if (clique.contains( actor1 )){ outText <<""; } else { cliqueSize = clique.size(); while (!clique.empty()) { actor2 = clique.takeFirst(); if ( edgeExists( actor1, actor2) ) { numerator++; } } outText <<""; } } outText <<""; } outText << "
" << tr("Actor/Clique") << "
" << actor1 <<"" << "1.000" <<"" << fixed << (numerator/(qreal) cliqueSize) <<"
"; /** * Write the actor by actor analysis matrix. * For each pair, we compute their clique co-membership */ outText << "

" << "" << tr("Actor by actor analysis: ") <<"" << tr(" Co-membership matrix") << "

"; outText << ""; outText << "" <<"" <<""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ actor1 = (*it)->name(); outText << ""; } outText <<"" << "" <<""; rowCounter=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ actor1 = (*it)->name(); index1 = vpos[actor1]; rowCounter++; outText << "" <<""; for (it2=m_graph.cbegin(); it2!=m_graph.cend(); ++it2){ actor2 = (*it2)->name(); index2 = vpos[actor2]; outText <<""; } outText <<""; } outText << "
" << tr("Actor/Actor") << "" << actor1 << "
" << actor1 <<"" << qSetRealNumberPrecision(0)<< CLQM.item(index1, index2) <<"
"; /** * Write the Hierarchical clustering of overlap matrix */ outText << "

" << "" << tr("Hierarchical clustering of overlap matrix: ") <<"" << tr("Actors") << "

"; pMsg = tr("Computing HCA for Cliques. Please wait..") ; emit statusMessage ( pMsg ); if (! graphClusteringHierarchical(CLQM, varLocation, graphMetricStrToType("Euclidean"), Clustering::Complete_Linkage, false, true, true, false, true) ) { file.close(); emit statusMessage( "Error completing HCA analysis"); emit signalProgressBoxKill(); return false; } pMsg = tr("Writing HCA for Cliques. Please wait..") ; emit statusMessage ( pMsg ); writeClusteringHierarchicalResultsToStream(outText, N, dendrogram); outText << "

" << "" << tr("Clique by clique analysis: ") <<"" << tr("Co-membership matrix") << "

"; emit signalProgressBoxUpdate(2 * N); outText << "

" << "" << tr("Hierarchical clustering of overlap matrix: ") <<"" << tr("Clique") << "

"; outText << "

 

"; outText << "

"; outText << tr("Clique Census Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); return true; } /** * @brief Called from Graph::graphCliques to add a new clique (list of vertices) * Adds clique info to each clique member and updates co-membership matrix CLQM . * @param list * @return */ void Graph:: graphCliqueAdd(const QList &clique){ m_cliques.insert(clique.count(), clique); qDebug() << "Graph::graphCliqueAdd() - Added clique:" << clique << "of size" << clique.count() << "Total cliques:" << m_cliques.count(); int index1=0, index2=0, cliqueCount=0; foreach (int actor1, clique) { index1 = vpos[actor1]; qDebug() << "Graph::graphCliqueAdd() - Updating cliques in actor1:" << actor1 << "vpos:" << index1; m_graph[ index1 ]->cliqueAdd(clique); foreach (int actor2, clique) { index2 = vpos[actor2]; cliqueCount = CLQM.item(index1, index2); CLQM.setItem( index1, index2, ( cliqueCount + 1) ); qDebug() << "Graph::graphCliqueAdd() - Updated co-membership matrix CLQM" << "actor1:" << actor1 << "actor2:" << actor2 <<"old matrix element: (" << index1<<","< R, QSet P, QSet X) { csRecDepth ++ ; qDebug () << "Graph::graphCliques() - STARTS HERE. csRecDepth:" << csRecDepth << " - Check if we are at initialization step"; if (R.isEmpty() && P.isEmpty() && X.isEmpty()){ int V = vertices() ; P.reserve( V ); R.reserve( V ); X.reserve( V ); P=verticesSet(); qDebug() << "Graph::graphCliques() - initialization step. R, X empty and P=V(G): " << P; CLQM.zeroMatrix(V,V); //co-membership matrix CLQM m_cliques.clear(); VList::const_iterator it; int vertex=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { vertex = (*it)->name(); neighboursHash[ vertex ] = (*it) -> neighborhoodList().toSet(); // vertexNeighborhoodSet(vertex); qDebug() << "Graph::graphCliques() - initialization step. NeighborhoodList of v" << vertex << ": " <clearCliques(); } } qDebug() << "Graph::graphCliques() - check if P and X are both empty (which would mean we have a clique in R)..."; if (P.isEmpty() && X.isEmpty()) { qDebug() << "Graph::graphCliques() - P and X are both empty. MAXIMAL clique R=" << R; QList clique = R.values(); graphCliqueAdd(clique); csRecDepth -- ; return; } int v; QSet NBS; QSet Rnext, Pnext, Xnext; QSet::iterator i = P.begin(); int counter = 0; // Loop over vertices in P, randomly qDebug() << "Graph::graphCliques() - Start looping over vertices in P (randomly)"; while( i != P.end()) { counter ++ ; v = *i; qDebug() << "Graph::graphCliques() - CURRENT v:" << v << " P:" << P << " P.count=" < clique, m_cliques) { if ( size!=0 ) { if ( clique.size() != size) continue; } if (clique.contains( actor )){ cliqueCounter++; } } return cliqueCounter; } /** * @brief Graph::graphCliquesOfSize * Returns the number of maximal cliques of a given size * @param size * @return */ int Graph::graphCliquesOfSize(const int &size){ qDebug() << "Graph::graphCliquesOfSize()"; return m_cliques.values(size).count(); } /** * @brief Writes Hierarchical Clustering Analysis to a given file * @param fileName * @param matrix * @param similarityMeasure * @param method * @param considerWeights * @param inverseWeights * @param dropIsolates */ bool Graph::writeClusteringHierarchical(const QString &fileName, const QString &varLocation, const QString &matrix, const QString &metric, const QString &method, const bool &diagonal, const bool &dendrogram, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { QElapsedTimer computationTimer; computationTimer.start(); qDebug()<< "Graph::writeClusteringHierarchical() - matrix:" << matrix << "varLocation" << varLocation << "metric" << metric << "method" << method << "considerWeights:"<"; outText << tr("HIERARCHICAL CLUSTERING (HCA)"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Input matrix: ") << "" << matrix << "

"; outText << "

" << "" << tr("Distance/dissimilarity metric: ") <<"" << metric << "

"; outText << "

" << "" << tr("Clustering method/criterion: ") <<"" << method << "

"; outText << "

 

"; outText << "

" << "" << tr("Analysis results") <<"" << "

"; outText << "

" << "" << tr("Structural Equivalence Matrix: ") <<"" << "

"; emit signalProgressBoxUpdate( N /3); writeMatrixHTMLTable(outText,STR_EQUIV,true,false,false, dropIsolates); //STR_EQUIV.printHTMLTable(outText,true,false); outText << "

" << "" << tr("Hierarchical Clustering of Equivalence Matrix: ") <<"" << "

"; emit signalProgressBoxUpdate( 2* N /3); writeClusteringHierarchicalResultsToStream(outText, N, dendrogram); outText << "

 

"; outText << "

"; outText << tr("Hierarchical Cluster Analysis report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); qDebug()<< "Graph::writeClusteringHierarchical() - finished"; emit signalProgressBoxUpdate( N); emit signalProgressBoxKill(); return true; } /** * @brief Writes Hierarchical Clustering results to given output stream * Before running this methos, the method Graph::graphClusteringHierarchical() * must execute and return true. Otherwise, the result is unpredictable... * @param outText * @param N * @param dendrogram */ void Graph::writeClusteringHierarchicalResultsToStream(QTextStream& outText, const int N, const bool &dendrogram) { qDebug()<<"Graph::writeClusteringHierarchicalResultsToStream() - " << "N"<< N << "dendrogram" << dendrogram; QMap::const_iterator it; qreal level; outText << "
";
    outText <<"Seq" << "\t"<<"Level" << "\t"<< "Actors" <";

    if (dendrogram) {

        qDebug()<< "Graph::writeClusteringHierarchicalResultsToStream() -"
                << "Writing SVG dendrogram";

        outText << "

" << "" << tr("Clustering Dendrogram (SVG)") <<"" << "

"; int diagramMaxWidth = 1000; int diagramPaddingLeft=30; int diagramPaddingTop =30; int rowHeight = 15; int rowPaddingLeft = 5; int headerHeight = 10; int headerTextSize = 9; int actorTextSize = 12; int legendTextSize = 7; int maxSVGWidth = diagramMaxWidth + diagramPaddingLeft + rowPaddingLeft; int maxSVGHeight = 2 * diagramPaddingTop + (rowHeight * N); QMap clusterEndPoint; QPoint endPoint1, endPoint2, endPointLevel; QMap::const_iterator pit; //cluster names pair iterator QVector clusterVector; int actorNumber; qreal maxLevelValue; QString clusterName; QList legendLevelsDone; it = m_clustersPerSequence.constEnd(); it--; maxLevelValue = m_clusteringLevel.last() ; clusterVector.reserve(N); qDebug()<<"Graph::writeClusteringHierarchicalResultsToStream() -" << endl << "m_clustersPerSequence"<"; outText << ""; // print a legend on top outText << "" << "Actor" <<""; outText << "" << "Clusterings" <<""; // print actor numbers // and compute initial cluster end points for them. for ( int i=0; i < it.value().size() ; ++i ) { actorNumber = it.value().at(i); clusterEndPoint[QString::number(actorNumber)] = QPoint(diagramPaddingLeft,diagramPaddingTop+rowHeight*(i)); outText << ""; outText << "" << actorNumber <<""; outText << ""; // end actor name } // end for rows // begin drawing clustering paths/lines for ( pit= m_clusterPairNamesPerSeq.constBegin() ; pit != m_clusterPairNamesPerSeq.constEnd(); ++pit) { level = m_clusteringLevel.at ( pit.key() - 1); qDebug() << "seq" <"; //stroke-dasharray=\"5,5\" // print level vertical dashed line outText << ""; //print legend if (!legendLevelsDone.contains(level)) { outText << "" << fixed << level <<""; legendLevelsDone.append(level); } } outText << ""; //end dendrogram svg outText << ""; //end dendrogram div } // end if dendrogram } /** * @brief Performs an hierarchical clustering process (Johnson, 1967) on a given * NxN distance/dissimilarity matrix. The input matrix can be the * the adjacency matrix, the geodesic distance matrix or a derived from them * dissimilarities matrix using a user-specified metric, i.e. euclidean distance. * The method parameter defines how to compute distances (similarities) between * a new cluster the old clusters. Valid values can be: * - Clustering::Single_Linkage: "single-link" or "connectedness" or "minimum" * - Clustering::Complete_Linkage: "complete-link" or "diameter" or "maximum" * - Clustering::Average_Linkage: "average-link" or UPGMA * @param matrix * @param metric * @param method * @param considerWeights * @param inverseWeights * @param dropIsolates */ bool Graph::graphClusteringHierarchical(Matrix &STR_EQUIV, const QString &varLocation, const int &metric, const int &method, const bool &diagonal, const bool &diagram, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { Q_UNUSED (inverseWeights); qDebug() << "Graph::graphClusteringHierarchical() - " << "metric" << metric << "method" << graphClusteringMethodTypeToString(method) << "diagonal" << diagonal << "diagram" << diagram << "dropIsolates" << dropIsolates; qDebug() << "Graph::graphClusteringHierarchical() - STR_EQUIV matrix:"; //STR_EQUIV.printMatrixConsole(true); qreal min=RAND_MAX; qreal max=0; int imin, jmin, imax, jmax, mergedClusterIndex, deletedClusterIndex ; qreal distanceNewCluster; // temp vector stores cluster members at each clustering level QVector clusteredItems; // maps original and clustered items per their DSM matrix index // so that we know that at Level X the matrix index 0 corresponds to the cluster i.e. { 1,2,4} QMap m_clustersIndex; QMap::iterator it; QMap::iterator prev; QMap::const_iterator sit; // variables for diagram computation QVector clusterPairNames; QString cluster1, cluster2; Matrix DSM; //dissimilarities matrix. Note: will be destroyed in the end. // TODO: needs fix when distances matrix with -1 (infinity) elements is used. // compute, if needed, the dissimilarities matrix switch (metric) { case METRIC_NONE: DSM=STR_EQUIV; break; case METRIC_JACCARD_INDEX: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_MANHATTAN_DISTANCE: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_HAMMING_DISTANCE: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_EUCLIDEAN_DISTANCE: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_CHEBYSHEV_MAXIMUM: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; default: break; } int N = DSM.rows(); qDebug() << "Graph::graphClusteringHierarchical() -" << "initial matrix DSM contents:"; //DSM.printMatrixConsole(); if (DSM.illDefined()) { // DSM.clear(); // STR_EQUIV.clear(); emit statusMessage("ERROR computing dissimilarities matrix"); return false; } clusteredItems.reserve(N); if (diagram) { clusterPairNames.reserve(N); } m_clustersIndex.clear(); m_clustersPerSequence.clear(); m_clusteringLevel.clear(); m_clustersByName.clear(); m_clusterPairNamesPerSeq.clear(); // //Step 1: Assign each of the N items to its own cluster. // We have N unit clusters // int clustersLeft = N; int seq = 1 ; //clustering stage/level sequence number VList::const_iterator vit; int i = 0; for ( vit=m_graph.cbegin(); vit!=m_graph.cend(); ++vit){ // if (dropIsolates) { // if ((*vit)->isIsolated()) { // continue; // } // } // if (!(*vit)->isEnabled()) { // continue; // } if ((*vit)->isEnabled() && ( ! (*vit)->isIsolated() ) ) { clusteredItems.clear(); clusteredItems << (*vit)->name(); m_clustersIndex[i] = clusteredItems; if (diagram) { m_clustersByName.insert(QString::number(i+1),clusteredItems ); } i++; } } // for (int i = 0 ; i< N ; i ++ ) { // clusteredItems.clear(); // clusteredItems << i+1; // m_clustersIndex[i] = clusteredItems; // if (diagram) { // m_clustersByName.insert(QString::number(i+1),clusteredItems ); // } // } QString pMsg=tr("Computing Hierarchical Clustering. \nPlease wait..."); emit statusMessage(pMsg); emit signalProgressBoxCreate(N, pMsg); while (clustersLeft > 1) { emit signalProgressBoxUpdate(seq); qDebug() << endl << "Graph::graphClusteringHierarchical() -" <<"matrix DSM contents now:"; //DSM.printMatrixConsole(); // //Step 2. Find the most similar pair of clusters. // Merge them into a single new cluster. // DSM.NeighboursNearestFarthest(min, max, imin, jmin, imax, jmax); mergedClusterIndex = (imin < jmin ) ? imin : jmin; deletedClusterIndex = (mergedClusterIndex == imin ) ? jmin : imin; m_clusteringLevel << min; clusteredItems.clear(); clusteredItems = m_clustersIndex[mergedClusterIndex] + m_clustersIndex[deletedClusterIndex] ; qDebug() << "Graph::graphClusteringHierarchical() -" << "level"<< min << "seq" << seq <<"clusteredItems in level" < DSM.item(i,jmin) ) ? DSM.item(i,imin) : DSM.item(i,jmin); } qDebug() << "Graph::graphClusteringHierarchical() - " << " DSM("< 0, when two actors have some differences in their ties/distances, \n" "i.e. SMMC = 3 means the two actors have 3 differences in their tie/distance profiles to other actors."); } else { outText << tr("SMMC = 0, when there is no tie profile similarity at all.")< 0, when two actors have some matches in their ties/distances, \n" "i.e. SMMC = 1 means the two actors have their ties to other actors exactly the same all the time."); } outText << endl<< endl; outText << tr("Similarity Matrix by Matching Measure Report,\n"); outText << tr("Created by SocNetV ") << VERSION << ": " << actualDateTime.currentDateTime() .toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } /** * @brief Writes dissimilarity matrix based on a metric/measure to given html file * @param fileName * @param measure * @param varLocation * @param diagonal * @param considerWeights */ void Graph::writeMatrixDissimilarities(const QString fileName, const QString &metricStr, const QString &varLocation, const bool &diagonal, const bool &considerWeights) { qDebug()<< "Graph::writeMatrixDissimilarities()" << "metric" << metricStr << "varLocation" << varLocation << "diagonal"<"; outText << tr("DISSIMILARITIES MATRIX"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Variables in: ") <<"" << ((varLocation != "Rows" && varLocation != "Columns") ? "Concatenated rows + columns " : varLocation) << "

"; outText << "

" << "" << tr("Metric: ") << "" << metricStr << "

"; outText << "

" << "" << tr("Diagonal: ") <<"" << ((diagonal) ? "Included" : "Not included") << "

"; outText << "

" << "" << tr("Range: ") <<""; if (metric==METRIC_JACCARD_INDEX) outText << tr("0 < C < 1") ; else outText << tr("0 < C ") ; outText << "

"; outText << "

" << "
" << "" << tr("Analysis results ") <<"" << "

"; //DSM.printHTMLTable(outText); writeMatrixHTMLTable(outText,DSM, true); outText << "

"; outText << "" << tr("DSM = 0 ") <<"" << tr("when two actors have no tie profile dissimilarities. The actors have the same ties to all others.") <<"
" << "" << tr("DSM > 0 ") <<"" << tr("when the two actors have differences in their ties to other actors."); outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Dissimilarity Matrix Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Calls Matrix:distancesMatrix to compute the dissimilarities matrix DSM * of the variables (rows, columns, both) in given input matrix using the * user defined metric * @param INPUT_MATRIX * @param DSM * @param metric * @param varLocation * @param diagonal * @param considerWeights */ void Graph::graphMatrixDissimilaritiesCreate(Matrix &INPUT_MATRIX, Matrix &DSM, const int &metric, const QString &varLocation, const bool &diagonal, const bool &considerWeights){ qDebug()<<"Graph::graphMatrixDissimilaritiesCreate() -metric" << metric; DSM = INPUT_MATRIX.distancesMatrix(metric, varLocation, diagonal, considerWeights); // qDebug()<<"Graph::graphMatrixDissimilaritiesCreate() - matrix DSM:"; //DSM.printMatrixConsole(true); } /** * @brief Writes similarity matrix based on a matching measure to given html file * @param fileName * @param measure * @param matrix * @param varLocation * @param diagonal * @param considerWeights */ void Graph::writeMatrixSimilarityMatching(const QString fileName, const QString &measure, const QString &matrix, const QString &varLocation, const bool &diagonal, const bool &considerWeights) { QElapsedTimer computationTimer; computationTimer.start(); int measureInt = graphMetricStrToType( measure ); Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Examining pair-wise similarity of actors...")) ); Matrix SCM; int N = vertices(); if (matrix == "Adjacency") { graphMatrixAdjacencyCreate(); graphMatrixSimilarityMatchingCreate(AM, SCM, measureInt , varLocation, diagonal, considerWeights); } else if (matrix == "Distances") { graphDistancesGeodesic(); graphMatrixSimilarityMatchingCreate(DM, SCM, measureInt, varLocation, diagonal, considerWeights); } else { return; } QString pMsg = tr("Writing Similarity coefficients to file. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(1, pMsg); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << htmlHead; outText << "

"; outText << tr("SIMILARITY MATRIX: MATCHING COEFFICIENTS (SMMC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Input matrix: ") <<"" << matrix << "

"; outText << "

" << "" << tr("Variables in: ") <<"" << ((varLocation != "Rows" && varLocation != "Columns") ? "Concatenated rows + columns " : varLocation) << "

"; outText << "

" << "" << tr("Matching measure: ") << "" << measure << "

"; outText << "

" << "" << tr("Diagonal: ") <<"" << ((diagonal) ? "Included" : "Not included") << "

"; outText << "

" << "" << tr("SMMC range: ") <<""; if (measureInt==METRIC_HAMMING_DISTANCE) outText << tr("0 < C") ; else outText << tr("0 < C < 1") ; outText << "

"; outText << "

" << "
" << "" << tr("Analysis results ") <<"" << "

"; emit signalProgressBoxUpdate(0); //SCM.printHTMLTable(outText); writeMatrixHTMLTable(outText,SCM, true); outText << "

"; if (measureInt==METRIC_HAMMING_DISTANCE) { outText << "" << tr("SMMC = 0 ") <<"" << tr("when two actors are absolutely similar (no tie/distance differences).") <<"
" << "" << tr("SMMC > 0 ") <<"" << tr("when two actors have some differences in their ties/distances, " "i.e. SMMC = 3 means the two actors have 3 differences in their tie/distance profiles to other actors."); } else { outText << "" << tr("SMMC = 0 ") <<"" << tr("when there is no tie profile similarity at all.") <<"
" << "" << tr("SMMC > 0 ") <<"" << tr("when two actors have some matches in their ties/distances, " "i.e. SMMC = 1 means the two actors have their ties to other actors exactly the same all the time."); } outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Similarity Matrix by Matching Measure Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxUpdate(1); emit signalProgressBoxKill(); } /** * @brief Calls Matrix:similarityMatrix to compute the similarity matrix SCM * of the variables (rows, columns, both) in given input matrix using the * selected matching measure. * * @param AM * @param SCM * @param rows */ void Graph::graphMatrixSimilarityMatchingCreate (Matrix &AM, Matrix &SCM, const int &measure, const QString &varLocation, const bool &diagonal, const bool &considerWeights){ qDebug()<<"Graph::graphMatrixSimilarityMatchingCreate()"; QString pMsg = tr ("Computing Similarity coefficients matrix. \nPlease wait..."); emit signalProgressBoxCreate(1, pMsg); SCM.similarityMatrix(AM, measure, varLocation, diagonal, considerWeights); emit signalProgressBoxUpdate(1); emit signalProgressBoxKill(); } /** * @brief Calls Graph::graphMatrixSimilarityPearsonCreate() and * writes Pearson Correlation Coefficients to given file * @param fileName * @param considerWeights */ void Graph::writeMatrixSimilarityPearson(const QString fileName, const bool considerWeights, const QString &matrix, const QString &varLocation, const bool &diagonal) { QElapsedTimer computationTimer; computationTimer.start(); Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating Pearson Correlations...")) ); Matrix PCC; int N = vertices(); if (matrix == "Adjacency") { graphMatrixAdjacencyCreate(); graphMatrixSimilarityPearsonCreate(AM, PCC, varLocation,diagonal); } else if (matrix == "Distances") { graphDistancesGeodesic(); graphMatrixSimilarityPearsonCreate(DM, PCC, varLocation,diagonal); } else { return; } emit statusMessage ( tr("Writing Pearson coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << htmlHead; outText << "

"; outText << tr("PEARSON CORRELATION COEFFICIENTS (PCC) MATRIX"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Input matrix: ") <<"" << matrix << "

"; outText << "

" << "" << tr("Variables in: ") <<"" << ((varLocation != "Rows" && varLocation != "Columns") ? "Concatenated rows + columns " : varLocation) << "

"; outText << "

" << "" << tr("Diagonal: ") <<"" << ((diagonal) ? "Included" : "Not included") << "

"; outText << "

" << "" << tr("PCC range: ") <<"" << "-1 < C < 1" << "

"; outText << "

" << "" << "
" << tr("Analysis results ") <<"
" << "

"; //PCC.printHTMLTable(outText); writeMatrixHTMLTable(outText,PCC, true); outText << "

"; outText << "" << tr("PCC = 0 ") <<"" << tr("when there is no correlation at all.") <<"
" << "" << tr("PCC > 0 ") <<"" << tr("when there is positive correlation, " "i.e. +1 means actors with same patterns of ties/distances.") <<"
" << "" << tr("PCC < 0 ") <<"" << tr("when there is negative correlation, " "i.e. -1 for actors with exactly opposite patterns of ties.") <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Pearson Correlation Coefficients Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief * Calls Graph::graphSimilariyPearsonCorrelationCoefficients() and * writes Pearson Correlation Coefficients to given file * @param fileName * @param considerWeights */ void Graph::writeMatrixSimilarityPearsonPlainText(const QString fileName, const bool considerWeights, const QString &matrix, const QString &varLocation, const bool &diagonal) { Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating Pearson Correlations...")) ); Matrix PCC; if (matrix == "Adjacency") { graphMatrixAdjacencyCreate(); graphMatrixSimilarityPearsonCreate(AM, PCC, varLocation,diagonal); } else if (matrix == "Distances") { graphDistancesGeodesic(); graphMatrixSimilarityPearsonCreate(DM, PCC, varLocation,diagonal); } else { return; } emit statusMessage ( tr("Writing Pearson coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_reportsRealPrecision); outText << tr("PEARSON CORRELATION COEFFICIENTS (PCC) MATRIX") << endl< 0, when there is positive correlation, i.e. +1 means actors with same patterns of ties/distances.\n"); outText << tr( "PCC < 0, when there is negative correlation, i.e. -1 for actors with exactly opposite patterns of ties.\n"); outText <<"\n\n" ; outText << tr("Pearson Correlation Coefficients Report,\n"); outText << tr("Created by SocNetV ") << VERSION << ": " << actualDateTime.currentDateTime() .toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } /** * @brief * The Pearson product-moment correlation coefficient (PPMCC, PCC or Pearson's r) * is a measure of the linear dependence between two variables X and Y. * * As a normalized version of the covariance, the PPMCC is computed with the formula: * r =\frac{\sum ^n _{i=1}(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum ^n _{i=1}(x_i - \bar{x})^2} \sqrt{\sum ^n _{i=1}(y_i - \bar{y})^2}} * * It gives a value between +1 and −1 inclusive, where 1 is total positive linear * correlation, 0 is no linear correlation, and −1 is total negative linear correlation. * * In SNA, Pearson correlations can be used to track the similarity between actors, * in terms of structural equivalence. * * This method creates an actor by actor NxN matrix PCC where the (i,j) element * is the Pearson correlation coefficient of actor i and actor j. * If the input matrix is the adjacency matrix, the PCC of two nodes measures * how related (similar, inverse or not related at all) their patterns of ties tend to be. * A positive value means there is strong linear association of the two actors, * while a negative value means the inverse. For instance a value of -1 means * the two actors have exactly opposite ties to other actors, while a value of 1 * means the actors have identical patterns of ties to other actors * (they are connected to the same actors). * * The correlation measure of similarity is particularly useful when the data on ties are valued * @param AM * @param PCC * @param rows */ void Graph::graphMatrixSimilarityPearsonCreate (Matrix &AM, Matrix &PCC, const QString &varLocation, const bool &diagonal){ qDebug()<<"Graph::graphMatrixSimilarityPearsonCreate()"; PCC.pearsonCorrelationCoefficients(AM, varLocation,diagonal); qDebug()<<"Graph::graphMatrixSimilarityPearsonCreate() - matrix PCC"; //PCC.printMatrixConsole(true); } /** 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. */ qreal Graph::numberOfTriples(int v1){ qreal totalDegree=0; if (graphIsSymmetric()){ totalDegree=vertexEdgesOutbound(v1); return totalDegree * (totalDegree -1.0) / 2.0; } totalDegree=vertexEdgesOutbound(v1) + vertexEdgesInbound(v1); //FIXEM return totalDegree * (totalDegree -1.0); } /** * @brief Graph::clusteringCoefficientLocal * 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. * @param v1 * @return */ qreal Graph::clusteringCoefficientLocal(const int &v1){ if ( !graphIsModified() && (m_graph[ vpos [v1] ] -> hasCLC() ) ) { qreal clucof=m_graph[ vpos [v1] ] ->CLC(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - " << " Not modified. Returning previous clucof = " << clucof; return clucof; } qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - " << " Graph changed or clucof not calculated."; bool isSymmetric = false; if ( graphIsSymmetric() ) { isSymmetric = true; } else { isSymmetric = false; } qreal clucof=0, denom = 0 , nom = 0; int u1 = 0 , u2 = 0, k = 0; H_StrToBool neighborhoodEdges; neighborhoodEdges.clear(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - vertex " << v1 << "[" << vpos[v1] << "] " << " Checking adjacent edges " ; QHash reciprocalEdges ; reciprocalEdges = m_graph [ vpos[v1] ] -> reciprocalEdgesHash(); QHash::const_iterator it1; QHash::const_iterator it2; it1=reciprocalEdges.cbegin(); while ( it1 != reciprocalEdges.cend() ) { u1 = it1.key(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << v1 << "<->" << u1 << "[" << vpos[u1] << "] exists" << "weight " << it1.value(); if ( v1 == u1 ) { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "v1 == u1 - CONTINUE"; ++it1; continue; } it2=reciprocalEdges.cbegin(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Checking if neighbor" << u1 << "is connected to other neighbors of" << v1; while ( it2 != reciprocalEdges.cend() ){ u2 = it2.key(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Other neighbor" << u2 << "Check if there is an edge" << u1 << "[" << vpos[u1] << "]" << "->" << u2 ; if ( u1 == u2 ) { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "u1 == u2 - CONTINUE"; ++it2; continue; } if ( edgeExists( u1, u2 ) != 0 ) { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Connected neighbors: " << u1 << " -> " << u2; QString edge = QString::number(u1) + "->" + QString::number(u2); QString revedge = QString::number(u2) + "->" + QString::number(u1); if ( isSymmetric ) { if ( ! neighborhoodEdges.contains(edge) && ! neighborhoodEdges.contains(revedge) ) { neighborhoodEdges.insert(edge, true); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge added to neighborhoodEdges : " << edge; } else { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge not added, discovered previously : " << edge; } } else { if ( ! neighborhoodEdges.contains(edge) ) { neighborhoodEdges.insert(edge, true); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge added to neighborhoodEdges : " << edge; } else { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge not added, discovered previously : " << edge; } } } ++it2; } ++it1; } nom=neighborhoodEdges.count(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "neighborhoodEdges.count() =" << nom; if ( nom == 0) return 0; //stop if we're at a leaf. if ( isSymmetric ){ k=reciprocalEdges.count(); //k_{i} is the number of neighbours of a vertex denom = k * (k -1.0) / 2.0; qDebug() << "Graph::clusteringCoefficientLocal("<< 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::clusteringCoefficientLocal("<< v1 << ") - " << "Not symmetric graph. " << "Max edges in neighborhood" << denom ; } clucof = nom / denom; qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "CLUCOF = "<< clucof; m_graph[ vpos [v1] ] -> setCLC(clucof); reciprocalEdges.clear(); neighborhoodEdges.clear(); return clucof; } /** * @brief Computes local clustering coefficients and returns * the network average Clustering Coefficient * @param updateProgress * @return */ qreal Graph::clusteringCoefficient (const bool updateProgress){ qDebug()<< "Graph::clusteringCoefficient()"; averageCLC=0; varianceCLC=0; maxCLC=0; minCLC=1; qreal temp=0; qreal x=0; qreal N = vertices(); int progressCounter = 0; VList::const_iterator vertex; QString pMsg = tr("Computing Clustering Coefficient. \n" "Please wait..."); emit statusMessage(pMsg); emit signalProgressBoxCreate(N,pMsg); for ( vertex = m_graph.cbegin(); vertex != m_graph.cend(); ++vertex) { if (updateProgress) { emit signalProgressBoxUpdate(++progressCounter); } temp = clusteringCoefficientLocal( (*vertex)->name() ); if (temp > maxCLC) { maxCLC = temp; maxNodeCLC = (*vertex)->name(); } if ( temp < minCLC ) { minNodeCLC = (*vertex)->name(); minCLC= temp; } averageCLC += temp; } averageCLC = averageCLC / N ; qDebug() << "Graph::clusteringCoefficient() network average " << averageCLC; for ( vertex = m_graph.cbegin(); vertex != m_graph.cend(); ++vertex) { x = ( (*vertex)->CLC() - averageCLC ) ; x *=x; varianceCLC += x; } varianceIC /= N; if (updateProgress) { emit signalProgressBoxKill(); } return averageCLC; } /** * @brief Graph::graphTriadCensus * Conducts a triad census and updates QList::triadTypeFreqs, * which is the list carrying all triad type frequencies * Complexity:O(n!) * @return */ bool Graph::graphTriadCensus(){ 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 N = vertices(); int progressCounter = 0; VList::const_iterator v1; VList::const_iterator v2; VList::const_iterator v3; qDebug() << "Graph::graphTriadCensus()"; /* * 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]; } QString pMsg = tr("Computing Triad Census. \nPlease wait...") ; emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); for (v1=m_graph.cbegin(); v1!=m_graph.cend(); v1++) { emit signalProgressBoxUpdate( ++progressCounter ); 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 << ") = (" <name() << ","<< vert2->name() << ","<< vert3->name() << ") to m_triad "; m_triad<name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; isInLinked=false; foreach (GraphVertex *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 (GraphVertex *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 (GraphVertex *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 (GraphVertex *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; } /** * @brief Graph::graphName * If m_graphName is set on file loading, it returns that. * If m_graphName is empty, then returns current relation name * If m_graphName is empty and there is no current relation name, * then returns "noname" * @return */ QString Graph::graphName() const { if (m_graphName.isEmpty() ) { if ( !( relationCurrentName().isEmpty()) ) { return relationCurrentName(); } else { //TODO: Maybe we should use m_filename in this case? return "noname"; } } return m_graphName; } /** * @brief Graph::graphLoad * Our almost universal network loader. :) * It creates a new Parser object, * moves it to a another thread, * connects signals and slots and * calls its run() method. * @param m_fileName * @param m_codecName * @param m_showLabels * @param maxWidth * @param maxHeight * @param fileFormat * @param two_sm_mode * @return */ void Graph::graphLoad ( const QString m_fileName, const QString m_codecName, const int fileFormat, const int two_sm_mode, const QString delimiter){ qDebug() << "Graph::graphLoad() - clearing relations "; relationsClear(); qDebug() << "Graph::graphLoad() - "<< m_fileName << " calling parser.load() from thread " << this->thread(); file_parser = new Parser(); qDebug () << "Graph::graphLoad() - file_parser thread " << file_parser->thread() << " moving it to new thread "; file_parser->moveToThread(&file_parserThread); qDebug () << "Graph::graphLoad() - file_parser thread now " << file_parser->thread(); qDebug () << "Graph::graphLoad() - connecting file_parser signals "; connect(&file_parserThread, &QThread::finished, file_parser, &QObject::deleteLater); connect(file_parser, &Parser::addRelation, this, &Graph::relationAdd); connect ( file_parser, SIGNAL( relationSet (int) ), this, SLOT( relationSet (int) ) ) ; connect ( file_parser, &Parser::createNode, this, &Graph::vertexCreate ); connect ( file_parser, SIGNAL (createNodeAtPosRandom(const bool &)), this, SLOT(vertexCreateAtPosRandom(const bool &)) ); connect ( file_parser, SIGNAL (createNodeAtPosRandomWithLabel( const int ,const QString &, const bool &)), this, SLOT(vertexCreateAtPosRandomWithLabel( const int &,const QString &, const bool &) ) ); connect (file_parser, &Parser::edgeCreate, this,&Graph::edgeCreate); connect ( file_parser, SIGNAL(networkFileLoaded(int, QString, QString, int, int, int, const QString &) ), this, SLOT(graphFileLoaded( const int &, const QString &, const QString &, const int &, const int &, const int &, const QString &) ) ); connect ( file_parser, SIGNAL(removeDummyNode(int)), this, SLOT (vertexRemoveDummyNode(int)) ); connect ( file_parser, &Parser::finished, this, &Graph::graphLoadedTerminateParserThreads ); qDebug() << "Graph::graphLoad() - Starting file_parserThread "; file_parserThread.start(); qDebug() << "Graph::graphLoad() - calling file_parser->load() "; file_parser->load( m_fileName, m_codecName, initVertexSize, initVertexColor, initVertexShape, initVertexNumberColor, initVertexNumberSize, initVertexLabelColor, initVertexLabelSize, initEdgeColor, canvasWidth, canvasHeight, fileFormat, two_sm_mode, delimiter ); } /** * @brief Graph::graphLoadedTerminateParserThreads * @param reason */ void Graph::graphLoadedTerminateParserThreads(QString reason) { qDebug() << "Graph::graphLoadedTerminateParserThreads() - reason " << reason <<" Checking if file_parserThread is running..."; if (file_parserThread.isRunning() ) { qDebug() << "Graph::graphLoadedTerminateParserThreads() - deleting file_parser pointer"; delete file_parser; file_parser = 0; // see why here: https://goo.gl/tQxpGA qDebug() << "Graph::graphLoadedTerminateParserThreads() - file_parserThread running." "Calling file_parserThread.quit();"; file_parserThread.quit(); } } /** * @brief Updates MW with the loaded file type (0=nofile, 1=Pajek, 2=Adjacency etc) * Called from Parser on file parsing end or file error. * @param type * @param netName * @param aNodes * @param totalLinks * @param undirected */ void Graph::graphFileLoaded (const int &fileType, const QString &fName, const QString &netName, const int &totalNodes, const int &totalLinks, const int &edgeDirType, const QString &message) { if ( fileType == FileType::UNRECOGNIZED ) { qDebug() << "Graph::graphFileLoaded() - FileType::UNRECOGNIZED. " "Emitting signalGraphLoaded with error message " << message; emit signalGraphLoaded (fileType, QString(), QString(), 0, 0, message); return; } qDebug() << "Graph::graphFileLoaded()"; fileName = fName; if (netName != "") m_graphName=netName ; else m_graphName=(fileName.split("/").last()).split("/").first(); if ( edgeDirType == EdgeType::Directed ) { this->graphSetDirected(true); } else { this->graphSetDirected(false); } m_fileFormat = fileType; qDebug() << "Graph::graphFileLoaded() - " << " type" << fileType << " filename" << fileName << " name" << graphName() << " node " << totalNodes << " links" << totalLinks << " edgeDirType" << edgeDirType; qDebug() << "Graph::graphFileLoaded() - Call graphSetModified"; graphSetModified(GraphChange::ChangedNew); qDebug() << "Graph::graphFileLoaded() - emit signalGraphLoaded()"; emit signalGraphLoaded (fileType, fileName, graphName(), totalNodes, totalLinks, message); qDebug ()<< "Graph::graphFileLoaded() -check parser if running..."; } /** * @brief Returns the format of the last file opened * @return */ int Graph::graphFileFormat() const { return m_fileFormat; } /** * @brief Returns true if the fileFormat is supported for saving * @param fileFormat * @return */ bool Graph::graphFileFormatExportSupported(const int &fileFormat) const { if (m_graphFileFormatExportSupported.contains(fileFormat)) { return true; } return false; } /** * @brief Our almost universal graph saving method. :) * Actually it just checks the requested file type and * calls the corresponding saveGraphTo...() method * @param fileName * @param fileType * @return */ void Graph::graphSave(const QString &fileName, const int &fileType , const bool &saveEdgeWeights) { qDebug() << "Graph::graphSave()"; bool saved = false; m_fileFormat = fileType; switch (fileType) { case FileType::PAJEK : { qDebug() << "Graph::graphSave() - Pajek formatted file"; saved=graphSaveToPajekFormat(fileName, graphName(), canvasWidth, canvasHeight) ; break; } case FileType::ADJACENCY: { qDebug() << "Graph::graphSave() - Adjacency formatted file"; saved=graphSaveToAdjacencyFormat(fileName, saveEdgeWeights) ; break; } case FileType::GRAPHVIZ: { qDebug() << "Graph::graphSave() - GraphViz/Dot formatted file"; saved=graphSaveToDotFormat(fileName); break; } case FileType::GRAPHML: { qDebug() << "Graph::graphSave() - GraphML formatted file"; saved=graphSaveToGraphMLFormat(fileName); break; } default: { m_fileFormat = FileType::UNRECOGNIZED; qDebug() << "Graph::graphSave() - Error! Unrecognized fileType"; break; } }; if (saved) { graphSetModified(GraphChange::ChangedNone); } else { emit signalGraphSavedStatus(FileType::UNRECOGNIZED); } } /** Saves the active graph to a Pajek-formatted file Preserves node properties (positions, colours, etc) */ bool Graph::graphSaveToPajekFormat (const QString &fileName, \ QString networkName, int maxWidth, int maxHeight ) { qreal weight=0; QFileInfo fileInfo (fileName); QString fileNameNoPath = fileInfo.fileName(); networkName = (networkName == "") ? graphName().toHtmlEscaped(): networkName; networkName = (networkName == "unnamed") ? fileNameNoPath.toHtmlEscaped().left(fileNameNoPath.lastIndexOf('.')): networkName; qDebug () << " Graph::graphSaveToPajekFormat() - file: " << fileName.toUtf8() << "networkName" << networkName; maxWidth = (maxWidth == 0) ? canvasWidth:maxWidth ; maxHeight= (maxHeight== 0) ? canvasHeight:maxHeight; QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } QTextStream t( &f ); t.setCodec("UTF-8"); t<<"*Network "<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::graphSaveToPajekFormat: Arcs"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (jt=m_graph.begin(); jt!=m_graph.end(); jt++){ qDebug() << "Graph::graphSaveToPajekFormat: it=" << (*it)->name() << ", jt=" << (*jt)->name() ; if ( (weight=edgeExists ( (*it)->name(), (*jt)->name())) !=0 && ( edgeExists ((*jt)->name(), (*it)->name())) != weight ) { qDebug()<<"Graph::graphSaveToPajekFormat weight "<< weight << " color "<< (*it)->outLinkColor( (*jt)->name() ) ; t << (*it)->name() <<" "<<(*jt)->name()<< " "<outLinkColor( (*jt)->name() ); t <<"\n"; } } } t<<"*Edges \n"; qDebug() << "Graph::graphSaveToPajekFormat: Edges"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (jt=m_graph.begin(); jt!=m_graph.end(); jt++){ qDebug() << "Graph::graphSaveToPajekFormat: it=" << (*it)->name() << ", jt=" <<(*jt)->name() ; if ( ( weight=edgeExists((*it)->name(), (*jt)->name(), true) )!=0 ) { if ( (*it)->name() > (*jt)->name() ) continue; t << (*it)->name() <<" "<<(*jt)->name()<< " "<outLinkColor( (*jt)->name() ); t <<"\n"; } } } f.close(); emit statusMessage (tr( "File %1 saved" ).arg( fileNameNoPath )); return true; } /** * @brief Graph::graphSaveToAdjacencyFormat * @param fileName * @return */ bool Graph::graphSaveToAdjacencyFormat (const QString &fileName, const bool &saveEdgeWeights){ QFile file( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } QTextStream outText( &file ); outText.setCodec("UTF-8"); qDebug("Graph: graphSaveToAdjacencyFormat() for %i vertices", vertices()); writeMatrixAdjacencyTo(outText, saveEdgeWeights); file.close(); QString fileNameNoPath=fileName.split("/").last(); emit statusMessage (QString( tr("Adjacency matrix-formatted network saved into file %1") ).arg( fileNameNoPath )); return true; } bool Graph::graphSaveToDotFormat (QString fileName){ Q_UNUSED(fileName); return true; } /** * @brief Saves the current graph to fileName in GraphML format * @param fileName * @param networkName * @param maxWidth * @param maxHeight * @return */ bool Graph::graphSaveToGraphMLFormat (const QString &fileName, QString networkName, int maxWidth, int maxHeight) { qreal weight=0; int source=0, target=0, edgeCount=0, m_size=1, m_labelSize; QString m_color, m_labelColor, m_label; bool openToken; QFileInfo fileInfo (fileName); QString fileNameNoPath = fileInfo.fileName(); QString saveDirPath= fileInfo.canonicalPath(); qDebug () << "Graph::graphSaveToGraphMLFormat() - Save to directory:" << saveDirPath; QString iconsSubDir = fileInfo.baseName() + "_" + fileInfo.suffix() +"_images"; QString iconsDirPath = saveDirPath + "/" + iconsSubDir; QDir saveDir(saveDirPath); // Check if there are nodes with custom icons in the network if ( graphHasVertexCustomIcons()) { qDebug () << "Graph::graphSaveToGraphMLFormat() - Custom node icons exist." << "Creating images subdir" << iconsDirPath; // There are custom node icons in this net. // We need to save these custom icons to a folder // Create a subdir inside the directory where the actual network file // is about to be saved. All custom icons will be copied one-by-one there. if ( saveDir.mkpath( iconsDirPath ) ){ qDebug () << "Graph::graphSaveToGraphMLFormat() - created icons subdir" << iconsDirPath; } else { qDebug () << "Graph::graphSaveToGraphMLFormat() - ERROR creating subdir!"; } } else { qDebug () << "Graph::graphSaveToGraphMLFormat() - No custom node icons." << "Nothing to do"; } QString iconPath = QString(); QString iconFileName = QString (); QString copyIconFileNamePath = QString(); networkName = (networkName == "") ? graphName().toHtmlEscaped(): networkName; networkName = (networkName == "unnamed") ? fileNameNoPath.toHtmlEscaped().left(fileNameNoPath.lastIndexOf('.')): networkName; qDebug () << "Graph::graphSaveToGraphMLFormat() - file:" << fileName.toUtf8() << "networkName"<< networkName; maxWidth = (maxWidth == 0) ? (int)canvasWidth:maxWidth ; maxHeight= (maxHeight== 0) ? (int)canvasHeight:maxHeight; QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } QTextStream outText( &f ); outText.setCodec("UTF-8"); qDebug () << "Graph::graphSaveToGraphMLFormat() - codec used for saving stream: " << outText.codec()->name(); qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing xml version"; outText << "name() << "\"?> \n"; outText << " \n" ; outText << "" "\n"; qDebug()<< "Graph::graphSaveToGraphMLFormat() - 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"; // Check if there are nodes with custom icons in this network if ( graphHasVertexCustomIcons()) { // There are custom icons, so we will copy the default custom icon // to the subdir we created earlier iconPath = initVertexIconPath; iconFileName = QFileInfo(iconPath).fileName(); copyIconFileNamePath = iconsDirPath + "/" + iconFileName; if ( ! QFile(copyIconFileNamePath).exists() ) { if ( QFile::copy(iconPath, copyIconFileNamePath) ) { qDebug () << "Graph::graphSaveToGraphMLFormat() - default iconFile saved to: " << copyIconFileNamePath; } else { qDebug () << "Graph::graphSaveToGraphMLFormat() - ERROR saving default iconFile to: " << copyIconFileNamePath; } } else { qDebug () << "Graph::graphSaveToGraphMLFormat() - default iconFile already exists in: " << copyIconFileNamePath; } // And we write a new key (id 51) in our graphml for this default custom icon outText << " \n" " " << iconsSubDir + "/"+ iconFileName << " \n" " \n"; } // end check if custom icons exist outText << " \n" " " << initVertexLabelColor << " \n" " \n"; outText << " \n" " " << initVertexLabelSize << " \n" " \n"; outText << " \n" " 1.0 \n" " \n"; outText << " \n" " " << initEdgeColor << " \n" " \n"; outText << " \n" " " << ""<< " \n" " \n"; VList::const_iterator it; VList::const_iterator jt; QString relationName; int relationPrevious = relationCurrent(); for (int i = 0; i < relations(); ++i) { relationName = (m_relationsList.at(i).simplified()).remove("\""); relationSet( i , false); qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing graph tag. Relation" << relationName ; if (graphIsUndirected()) outText << " \n"; else outText << " \n"; qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing nodes data"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled () ) continue; qDebug() << "Graph::graphSaveToGraphMLFormat() - 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(); m_label = htmlEscaped(m_label); outText << " " << m_label <<"\n"; qDebug()<<"Graph::graphSaveToGraphMLFormat() - 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 ((*it)->shape() == "custom" ) { iconPath = (*it)->shapeIconPath(); iconFileName = QFileInfo(iconPath).fileName(); copyIconFileNamePath = iconsDirPath + "/" + iconFileName; if ( ! QFile(copyIconFileNamePath).exists() ) { if ( QFile::copy(iconPath, copyIconFileNamePath) ) { qDebug () << "Graph::graphSaveToGraphMLFormat() - iconFile for" << (*it)->name() << "saved to: " << copyIconFileNamePath; } else { qDebug () << "Graph::graphSaveToGraphMLFormat() - ERROR saving iconFile for" << (*it)->name() << "saved to: " << copyIconFileNamePath; } } else { qDebug () << "Graph::graphSaveToGraphMLFormat() - iconFile for" << (*it)->name() << "already exists in:" << copyIconFileNamePath; } outText << " " << iconsSubDir + "/"+ iconFileName <<"\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() << "Graph::graphSaveToGraphMLFormat() - writing edges data"; edgeCount=0; if (graphIsDirected()) { 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(); m_label = ""; weight= edgeExists( source,target ) ; if ( weight !=0 ) { ++edgeCount; m_color = (*it)->outLinkColor( target ); m_label = edgeLabel(source, target); m_label=htmlEscaped(m_label); qDebug()<< "Graph::graphSaveToGraphMLFormat() - edge no " << edgeCount << " from n1=" << source << " to n2=" << target << " with weight " << weight << " and color " << m_color.toUtf8() ; 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 ( !m_label.isEmpty()) { if (openToken) outText << "> \n"; outText << " " << m_label<<"" <<" \n"; openToken=false; } if (openToken) outText << "/> \n"; else outText << " \n"; } } } } else { for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { for (jt=it; jt!=m_graph.end(); jt++) { source=(*it)->name(); target=(*jt)->name(); weight= edgeExists( source,target ); m_label = ""; if ( weight !=0 ) { ++edgeCount; m_color = (*it)->outLinkColor( target ); m_label = edgeLabel(source, target); m_label=htmlEscaped(m_label); qDebug()<< "Graph::graphSaveToGraphMLFormat() - edge no " << edgeCount << " from n1=" << source << " to n2=" << target << " with weight " << weight << " and color " << m_color.toUtf8() ; 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 ( !m_label.isEmpty()) { if (openToken) outText << "> \n"; outText << " " << m_label<<"" <<" \n"; openToken=false; } if (openToken) outText << "/> \n"; else outText << " \n"; } } } } outText << " \n"; } outText << "\n"; f.close(); relationSet(relationPrevious, false); emit statusMessage( tr( "File %1 saved" ).arg( fileNameNoPath ) ); return true; } /** * @brief Sets the directory where reports are saved * This is used when exporting prominence distribution images to be used in * HTML reports. * @param dir */ void Graph::setReportsDataDir(const QString &dir) { m_reportsDataDir = dir; } /** * @brief Sets the precision (number of fraction digits) the app will use * when writing real numbers in reports. * @param precision */ void Graph::setReportsRealNumberPrecision(const int &precision) { m_reportsRealPrecision = precision; } /** * @brief Sets the length of labels in reports * @param length */ void Graph::setReportsLabelLength(const int &length){ m_reportsLabelLength = length; } /** * @brief Sets the chart type in reports * @param type */ void Graph::setReportsChartType(const int &type){ qDebug()<<"Graph::setReportsChartType() - type:" << type; if ( type == -1 ) { m_reportsChartType = ChartType::None; } else if ( type == 0 ) { m_reportsChartType = ChartType::Spline; } else if ( type == 1 ) { m_reportsChartType = ChartType::Area; } else if ( type == 2 ) { m_reportsChartType = ChartType::Bars; } } /** * @brief Writes a "famous" dataset to the given file * Datasets are hardcoded! They are exported in the given fileName... * @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 | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); QString datasetDescription=QString(); qDebug()<< " ... writing dataset "; if ( fileName == "Campnet.paj") { qDebug()<< " ... to " << fileName; datasetDescription = tr("Campnet dataset\n\n" "The dataset is the interactions among 18 people, " "including 4 instructors, " "participating in a 3-week workshop. \n" "Each person was asked to rank everyone else in terms of " "how much time they spent with them.\n" "This dataset shows only top 3 choices for each respondent" "(week 2 and week 3). Thus, there is a 1 for xij " "if person i listed person j as one of their top 3 interactors.\n\n" "The Camp data were collected by Steve Borgatti, " "Russ Bernard and Bert Pelto in 1992 at the NSF Summer " "Institute for Ethnographic Research Methods.\n " "During the 3-week workshop, all the participants and " "instructors were housed at the same motel and spent " "a great deal of time together. \n" "The participants were all faculty in Anthropology " "except Holly, who was a PhD student. "); outText << "*Network Campnet" << endl << "*Vertices 18" << endl << "1 \"HOLLY\" ic RGBF1F5D5 0.63046 0.575472 circle" << endl << "2 \"BRAZEY\" ic RGBF1F5D5 0.0991736 0.511006 circle" << endl << "3 \"CAROL\" ic RGBF1F5D5 0.576151 0.43239 circle" << endl << "4 \"PAM\" ic RGBF1F5D5 0.726092 0.371069 circle" << endl << "5 \"PAT\" ic RGBF1F5D5 0.709563 0.5 circle" << endl << "6 \"JENNIE\" ic RGBF1F5D5 0.876033 0.482704 circle" << endl << "7 \"PAULINE\" ic RGBF1F5D5 0.619835 0.286164 circle" << endl << "8 \"ANN\" ic RGBF1F5D5 0.864227 0.309748 circle" << endl << "9 \"MICHAEL\" ic RGBF1F5D5 0.489965 0.638365 box" << endl << "10 \"BILL\" ic RGBF1F5D5 0.475797 0.805031 box" << endl << "11 \"LEE\" ic RGBF1F5D5 0.0885478 0.267296 box" << endl << "12 \"DON\" ic RGBF1F5D5 0.645809 0.778302 box" << endl << "13 \"JOHN\" ic RGBF1F5D5 0.453365 0.290881 box" << endl << "14 \"HARRY\" ic RGBF1F5D5 0.593861 0.669811 box" << endl << "15 \"GERY\" ic RGBF1F5D5 0.362456 0.539308 box" << endl << "16 \"STEVE\" ic RGBF1F5D5 0.230224 0.5 box" << endl << "17 \"BERT\" ic RGBF1F5D5 0.218418 0.245283 box" << endl << "18 \"RUSS\" ic RGBF1F5D5 0.323495 0.29717 box" << endl << "*Arcs " << endl << "1 4 1 c black" << endl << "2 16 1 c black" << endl << "2 17 1 c black" << endl << "3 4 1 c black" << endl << "7 5 1 c black" << endl << "8 7 1 c black" << endl << "9 1 1 c black" << endl << "10 9 1 c black" << endl << "10 12 1 c black" << endl << "10 14 1 c black" << endl << "13 7 1 c black" << endl << "13 15 1 c black" << endl << "13 18 1 c black" << endl << "14 1 1 c black" << endl << "15 9 1 c black" << endl << "15 16 1 c black" << endl << "*Edges " << endl << "1 4 1 c black" << endl << "1 5 1 c black" << endl << "1 12 1 c black" << endl << "2 11 1 c black" << endl << "2 16 1 c black" << endl << "2 17 1 c black" << endl << "3 4 1 c black" << endl << "3 5 1 c black" << endl << "3 7 1 c black" << endl << "4 6 1 c black" << endl << "4 7 1 c black" << endl << "4 8 1 c black" << endl << "5 6 1 c black" << endl << "6 8 1 c black" << endl << "9 12 1 c black" << endl << "9 14 1 c black" << endl << "10 12 1 c black" << endl << "10 14 1 c black" << endl << "11 16 1 c black" << endl << "11 17 1 c black" << endl << "12 14 1 c black" << endl << "13 15 1 c black" << endl << "13 18 1 c black" << endl << "15 16 1 c black" << endl << "15 18 1 c black" << endl << "16 17 1 c black" << endl << "16 18 1 c black" << endl << "17 18 1 c black"; } if ( fileName == "Herschel_Graph.paj") { qDebug()<< " ... to " << fileName; datasetDescription = tr("Herschel graph \n\n" "The Herschel graph is the smallest nonhamiltonian " "polyhedral graph. \n" "It is the unique such graph on 11 nodes, " "and has 18 edges."); outText << "*Network Herschel_Graph" << endl << "*Vertices 11" << endl << "1 \"1\" ic red 0.48225 0.411308 circle" << endl << "2 \"2\" ic red 0.652297 0.591389 circle" << endl << "3 \"3\" ic red 0.479571 0.762504 circle"<< endl << "4 \"4\" ic red 0.849224 0.41395 circle"<< endl << "5 \"5\" ic red 0.48196 0.06 circle"<< endl << "6 \"6\" ic red 0.148625 0.413208 circle"<< endl << "7 \"7\" ic red 0.654193 0.198133 circle"<< endl << "8 \"8\" ic red 0.268771 0.593206 circle"<< endl << "9 \"9\" ic red 0.272785 0.19606 circle"<< endl << "10 \"10\" ic red 0.834746 0.0533333 circle"<< endl << "11 \"11\" ic red 0.134137 0.761837 circle"<< endl << "*Arcs "<< endl << "*Edges "<< endl << "1 3 1 c #616161"<< endl << "1 4 1 c #616161"<< endl << "1 5 1 c #616161"<< endl << "1 6 1 c #616161"<< endl << "2 3 1 c #616161"<< endl << "2 4 1 c #616161"<< endl << "2 7 1 c #616161"<< endl << "2 8 1 c #616161"<< endl << "3 11 1 c #616161"<< endl << "4 10 1 c #616161"<< endl << "5 9 1 c #616161"<< endl << "5 10 1 c #616161"<< endl << "6 9 1 c #616161"<< endl << "6 11 1 c #616161"<< endl << "7 9 1 c #616161"<< endl << "7 10 1 c #616161"<< endl << "8 9 1 c #616161"<< endl << "8 11 1 c #616161"; } else if ( fileName == "Krackhardt_High-tech_managers.paj" ) { qDebug()<< " ... to " << fileName; datasetDescription = tr("High-tech Managers\n\n" "Krackhardt's High-tech Managers is a famous social network " "of 21 managers of a high-tech US company. \n\n" "The company manufactured high-tech equipment " "and had just over 100 employees with 21 managers. " "David Krackhardt collected the data to assess the effects " "of a recent management intervention program. \n\n" "The network consists of 3 relations:\n" "- Advice\n" "- Friendship\n" "- Reports To\n" "Each manager was asked to whom do you go to for advice and who is your friend. " "Data for the \"whom do you report\" relation was taken from company documents. \n\n" "This data is used by Wasserman and Faust in their seminal network analysis book.\n\n" "Krackhardt D. (1987). Cognitive social structures. Social Networks, 9, 104-134."); outText << "*Network Krackhardt's High-tech managers"<< endl << "*Vertices 21"<< endl << "1 \"v1\" 0.6226 0.7207" << endl << "2 \"v2\" 0.6000 0.5533" << endl << "3 \"v3\" 0.6722 0.3928" << endl << "4 \"v4\" 0.7646 0.6000" << endl << "5 \"v5\" 0.3518 0.4775" << endl << "6 \"v6\" 0.7583 0.0784" << endl << "7 \"v7\" 0.6692 0.2475" << endl << "8 \"v8\" 0.7349 0.5030" << endl << "9 \"v9\" 0.5325 0.3892" << endl << "10 \"v10\" 0.5846 0.6311" << endl << "11 \"v11\" 0.4600 0.4733" << endl << "12 \"v12\" 0.8855 0.2566" << endl << "13 \"v13\" 0.1145 0.4786" << endl << "14 \"v14\" 0.3838 0.3270" << endl << "15 \"v15\" 0.5349 0.4455" << endl << "16 \"v16\" 0.6117 0.9216" << endl << "17 \"v17\" 0.7041 0.4144" << endl << "18 \"v18\" 0.4864 0.5808" << endl << "19 \"v19\" 0.5728 0.4802" << endl << "20 \"v20\" 0.6640 0.5041" << endl << "21 \"v21\" 0.7846 0.3329" << endl << "*Matrix :1 gives_advice_to"<< endl << "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"<< endl << "*Matrix :2 is_friend_of" <"; switch (matrix) { case MATRIX_ADJACENCY: outText << tr("ADJACENCY MATRIX REPORT"); break; case MATRIX_LAPLACIAN: outText << tr("LAPLACIAN MATRIX REPORT"); break; case MATRIX_DEGREE: outText << tr("DEGREE MATRIX REPORT"); break; case MATRIX_DISTANCES: outText << tr("DISTANCES MATRIX REPORT"); break; case MATRIX_GEODESICS: outText << tr("SHORTEST PATHS (GEODESICS) MATRIX REPORT"); break; case MATRIX_ADJACENCY_INVERSE: outText << tr("INVERSE ADJACENCY MATRIX REPORT"); break; case MATRIX_REACHABILITY: outText << tr("REACHABILITY MATRIX REPORT"); break; case MATRIX_ADJACENCY_TRANSPOSE: outText << tr("TRANSPOSE OF ADJACENCY MATRIX REPORT"); break; case MATRIX_COCITATION: outText << tr("COCITATION MATRIX REPORT"); break; case MATRIX_DISTANCES_EUCLIDEAN: outText << tr("EUCLIDEAN DISTANCE MATRIX REPORT"); break; case MATRIX_DISTANCES_HAMMING: outText << tr("HAMMING DISTANCE MATRIX REPORT"); break; case MATRIX_DISTANCES_JACCARD: outText << tr("JACCARD DISTANCE MATRIX REPORT"); break; case MATRIX_DISTANCES_MANHATTAN: outText << tr("MANHATTAN DISTANCE MATRIX REPORT"); break; default: break; } outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; switch (matrix) { case MATRIX_ADJACENCY: outText << "

" << tr("The adjacency matrix, AM, of a social network is a NxN matrix ") << tr("where each element (i,j) is the value of the edge from " "actor i to actor j, or 0 if no edge exists.") << "
" << "

"; writeMatrixHTMLTable(outText, AM , true,false,false); //AM.printHTMLTable(outText,true); break; case MATRIX_LAPLACIAN: outText << "

" << tr("The laplacian matrix L of a social network is a NxN matrix ") << tr("with L = D - A, where D the degree matrix and A the " "adjacency matrix. ") << "
" << tr("The elements of L are: " "
" "- Li,j = di, if i = j,
" "- Li,j = -1, if i ≠ j and there is an edge (i,j)
" "- and all other elements zero.
") << "
" << "

"; //AM.laplacianMatrix().printHTMLTable(outText,true,false,false); writeMatrixHTMLTable(outText, AM.laplacianMatrix() , true,false,false); break; case MATRIX_DEGREE: outText << "

" << tr("The degree matrix D of a social network is a NxN matrix ") << tr("where each element (i,i) is the degree of actor i " "and all other elements are zero.") << "
" << "

"; //AM.degreeMatrix().printHTMLTable(outText, true); writeMatrixHTMLTable(outText, AM.degreeMatrix() , true,false,false); break; case MATRIX_DISTANCES: outText << "

" << tr("The distance matrix of a social network is a NxN matrix " "where each element (i,j) is the geodesic distance " "(length of shortest path) from actor i to actor j, " "or infinity if no shortest path exists.") << "
" << "

"; writeMatrixHTMLTable(outText, DM, true); //DM.printHTMLTable(outText,true); break; case MATRIX_GEODESICS: outText << "

" << tr("The geodesics matrix of a social network is a NxN matrix ") << tr("where each element (i,j) is the number of shortest paths" "(geodesics) from actor i to actor j, " "or infinity if no shortest path exists.") << "
" << "

"; writeMatrixHTMLTable(outText, SIGMA, true); //SIGMA.printHTMLTable(outText,true); break; case MATRIX_ADJACENCY_INVERSE: if (!inverseResult) { outText << "

" << tr("The adjacency matrix is singular.") << "
" << "

"; }else { writeMatrixHTMLTable(outText, invAM, true); //invAM.printHTMLTable(outText,true); } break; case MATRIX_REACHABILITY: outText << "

" << tr("The reachability matrix R of a social network is a NxN matrix " "where each element R(i,j) is 1 if actors j is reachable from i " "otherwise 0.
" "Two nodes are reachable if there is a walk between them " "(their geodesic distance is non-zero).
" "Essentially the reachability matrix is a dichotomized " "geodesics matrix.") << "
" << "

"; writeMatrixHTMLTable(outText, XRM, true); //XRM.printHTMLTable(outText,true); break; case MATRIX_ADJACENCY_TRANSPOSE: outText << "

" << tr("The adjacency matrix AM of a social network is a NxN matrix " "where each element (i,j) is the value of the edge from " "actor i to actor j, or 0 if no edge exists. ") << "
" << tr("This is the transpose of the adjacency matrix, AMT, " "a matrix whose (i,j) element is the (j,i) element of AM.") << "

"; writeMatrixHTMLTable(outText, AM.transpose() , true); //AM.transpose().printHTMLTable(outText,true); break; case MATRIX_COCITATION: outText << "

" << tr("The Cocitation matrix, C = AT * A, is a " "NxN matrix where each element (i,j) is the number of " "actors that have outbound ties/links to both actors i and j.") << "
" << tr("The diagonal elements, Cii, of the Cocitation " "matrix are equal to the number of inbound edges of i (inDegree).") << "
" << tr("C is a symmetric matrix.") << "

"; writeMatrixHTMLTable(outText, AM.cocitationMatrix() , true); //AM.cocitationMatrix().printHTMLTable(outText,true); break; case MATRIX_DISTANCES_EUCLIDEAN: outText << "

" << tr("The Euclidean distances matrix is a " "NxN matrix where each element (i,j) is the Euclidean distance" "of the tie profiles between actors i and j, namely the " "square root of the sum of their squared differences.") << "
" << "

"; writeMatrixHTMLTable(outText, AM.distancesMatrix(METRIC_EUCLIDEAN_DISTANCE, varLocation, false, true ), true,false,false); //AM.distancesMatrix(METRIC_EUCLIDEAN_DISTANCE, varLocation, false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_HAMMING: outText << "

" << tr("The Hamming distances matrix is a " "NxN matrix where each element (i,j) is the Hamming distance" "of the tie profiles between actors i and j, namely the " "number of different ties to other actors.") << "
" << "

"; writeMatrixHTMLTable(outText, AM.distancesMatrix(METRIC_HAMMING_DISTANCE, varLocation, false, true ), true,false,false); //AM.distancesMatrix(METRIC_HAMMING_DISTANCE, varLocation, false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_JACCARD: outText << "

" << tr("The Jaccard distances matrix is a " "NxN matrix where each element (i,j) is the Jaccard distance" "of the tie profiles between actors i and j.") << "
" << "

"; writeMatrixHTMLTable(outText, AM.distancesMatrix(METRIC_JACCARD_INDEX, "Rows", false, true ), true,false,false); //AM.distancesMatrix(METRIC_JACCARD_INDEX, "Rows", false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_MANHATTAN: outText << "

" << tr("The Manhattan distances matrix is a " "NxN matrix where each element (i,j) is the Manhattan distance" "of the tie profiles between actors i and j, namely the " "sum of their absolute differences.") << "
" << "

"; writeMatrixHTMLTable(outText, AM.distancesMatrix(METRIC_MANHATTAN_DISTANCE, varLocation, false, true ), true,false,false); //AM.distancesMatrix(METRIC_MANHATTAN_DISTANCE, varLocation, false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_CHEBYSHEV: outText << "

" << tr("The Chebyshev distances matrix is a " "NxN matrix where each element (i,j) is the Chebyshev distance" "of the tie profiles between actors i and j, namely the greatest of their differences.") << "
" << "

"; writeMatrixHTMLTable(outText, AM.distancesMatrix(METRIC_CHEBYSHEV_MAXIMUM, varLocation, false, true ), true,false,false); //AM.distancesMatrix(METRIC_CHEBYSHEV_MAXIMUM, varLocation, false, true ).printHTMLTable(outText,true); break; default: break; } outText << "

 

"; outText << "

"; outText << tr("Matrix report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Writes the matrix M as HTML to specified text stream outText * It is the same as Matrix::printHTMLTable except that * this method omits disabled vertices, thus the table header is correct * @param outText * @param M * @param markDiag * @param plain * @param printInfinity */ void Graph::writeMatrixHTMLTable(QTextStream& outText, Matrix &M, const bool &markDiag, const bool &plain, const bool &printInfinity, const bool &dropIsolates) { Q_UNUSED(plain); qDebug () << "Graph::writeMatrixHTMLTable() -" << "markDiag" << markDiag << "plain" << plain << " dropIsolates " << dropIsolates; int rowCount=0, i=0, j=0; int N = vertices(); qreal maxVal, minVal, element; bool hasRealNumbers=false; VList::const_iterator it, jt; QString pMsg = tr("Writing matrix to file. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N, pMsg ); M.findMinMaxValues(minVal, maxVal, hasRealNumbers); outText << ( (hasRealNumbers) ? qSetRealNumberPrecision(3) : qSetRealNumberPrecision(0) ) ; qDebug () << "Graph::writeMatrixHTMLTable() - minVal" << minVal << "maxVal" << maxVal << "hasRealNumbers" << hasRealNumbers; outText << "
" << "" << "" << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || (dropIsolates && (*it)->isIsolated() ) ) { continue; } outText <<""; } outText << "" << "" << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( ! (*it)->isEnabled() || (dropIsolates && (*it)->isIsolated() ) ) { continue; } rowCount++; emit signalProgressBoxUpdate(rowCount); outText << ""; outText <<""; for (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt){ if ( ! (*jt)->isEnabled() || (dropIsolates && (*jt)->isIsolated() ) ) { continue; } outText << fixed << right; outText <<"name() ==(*jt)->name() )? " class=\"diag\">" : ">"); element = M.item(i,j); qDebug () << "Graph::writeMatrixHTMLTable() - M(" <"; j++; } outText <<""; i++; j=0; } outText << "
" << tr("Actor/Actor") << "" << (*it)->name() << "
" << (*it)->name() << "
"; outText << qSetFieldWidth(0) << endl ; outText << "

" << "" << ("Values: ") <<"" << ( (hasRealNumbers) ? ("real numbers (printed decimals 3)") : ("integers only" ) ) << "
" << "" << ("- Max value: ") <<"" << ( ( maxVal==RAND_MAX ) ? ( (printInfinity) ? infinity : QString::number(maxVal) ) + " (=not connected nodes, in distance matrix)" : QString::number(maxVal) ) << "
" << "" << ("- Min value: ") <<"" << ( ( minVal==RAND_MAX ) ? ( (printInfinity) ? infinity : QString::number(minVal) ) + + " (usually denotes unconnected nodes, in distance matrix)" : QString::number(minVal ) ) << "

"; emit signalProgressBoxKill(); } /** Exports the adjacency matrix to a given textstream */ void Graph::writeMatrixAdjacencyTo(QTextStream& os, const bool &saveEdgeWeights){ qDebug("Graph: adjacencyMatrix(), writing matrix with %i vertices", vertices()); VList::const_iterator it, it1; qreal weight=RAND_MAX; 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 = edgeExists( (*it)->name(), (*it1)->name() ) ) !=0 ) { //os << static_cast (weight) << " "; os << ((saveEdgeWeights) ? weight : 1 ) << " "; } else os << "0 "; } os << endl; } } /** Writes the adjacency matrix of G to a specified file fn */ void Graph::writeMatrixAdjacency (const QString fn, const bool &markDiag) { QElapsedTimer computationTimer; computationTimer.start(); qDebug()<<"Graph::writeMatrixAdjacency() to : " << fn; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); int sum=0; qreal weight=0; int rowCount=0; int N = vertices(); VList::const_iterator it, it1; QString pMsg = tr("Writing Adjacency Matrix to file. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N, pMsg ); outText << htmlHead; outText << "

"; outText << tr("ADJACENCY MATRIX"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The adjacency matrix of a social network is a NxN matrix ") << tr("where each element (i,j) is the value of the edge from " "actor i to actor j, or 0 if no edge exists.") << "
" << "

"; outText << "" << "" << "" << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; outText <<""; } outText << "" << "" << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ rowCount++; emit signalProgressBoxUpdate(rowCount); if ( ! (*it)->isEnabled() ) continue; outText << ""; outText <<""; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; outText <<"name() ==(*it1)->name() )? " class=\"diag\">" : ">"); if ( (weight = edgeExists ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText << (weight); } else { outText << 0 ; } outText << ""; } outText <<""; } outText << "
" << tr("Actor/Actor") << "" << (*it)->name() << "
" << (*it)->name() << "
"; qDebug("Graph: Found a total of %i edge",sum); if ( sum != edgesEnabled() ) qDebug ("Error in edge count found!!!"); else qDebug("Edge count OK!"); outText << "

 

"; outText << "

"; outText << tr("Adjacency matrix report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** Writes a visual representation of the adjacency matrix of G to a specified file fn This is called by MainWindow::slotViewAdjacencyMatrixPlotText() The resulting matrix HAS NO spaces between elements. */ void Graph::writeMatrixAdjacencyPlot (const QString fn, const bool &simpler) { QElapsedTimer computationTimer; computationTimer.start(); qDebug()<<"Graph::writeMatrixAdjacencyPlot() to : " << fn; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); VList::const_iterator it, it1; int sum=0; int rowCount=0; int N = vertices(); qreal weight=0; int progressCounter = 0; QString pMsg = tr("Plotting Adjacency Matrix. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N, pMsg ); if (!simpler) { outText << htmlHead; } else outText << htmlHeadLight; outText << "

"; outText << tr("ADJACENCY MATRIX PLOT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("This a plot of the network's adjacency matrix, a NxN matrix ") << tr("where each element (i,j) is filled if there is an edge from " "actor i to actor j, or not filled if no edge exists.") << "
" << "

"; if (!simpler) { outText << ""; outText << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); if ( ! (*it)->isEnabled() ) { continue; } rowCount++; outText << ""; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = edgeExists ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText <<""; } else { outText <<""; } } outText <<""; } outText << "
" << QString("\xe2\x96\xa0") << "" // << "  " << QString("\xe2\x96\xa1") << "
"; } else { outText << "

"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); if ( ! (*it)->isEnabled() ) { continue; } rowCount++; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = edgeExists ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText << QString("\xe2\x96\xa0") << " "; } else { outText << QString("\xe2\x96\xa1") << " "; } } outText << "
"<"; } qDebug("Graph: Found a total of %i edge",sum); if ( sum != edgesEnabled() ) qDebug ("Error in edge count found!!!"); else qDebug("Edge count OK!"); outText << "

 

"; outText << "

"; outText << tr("Adjacency matrix plot,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); emit signalProgressBoxKill(); } /** * @brief 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(), Graph::graphWalksMatrixCreate * and Graph::graphMatrixAdjacencyInvert() * @param dropIsolates * @param considerWeights * @param inverseWeights * @param symmetrize */ void Graph::graphMatrixAdjacencyCreate(const bool dropIsolates, const bool considerWeights, const bool inverseWeights, const bool symmetrize ){ qDebug() << "Graph::graphMatrixAdjacencyCreate() " << "dropIsolates" << dropIsolates << "considerWeights" << considerWeights << "inverseWeights" << inverseWeights << "symmetrize" << symmetrize; qreal m_weight=RAND_MAX; int i=0, j=0; int N = vertices(dropIsolates,false,true), progressCounter=0; VList::const_iterator it, jt; qDebug() << "Graph::graphMatrixAdjacencyCreate() -resizing AM to"<< N; AM.resize(N, N); QString pMsg = tr ("Creating Adjacency Matrix. \nPlease wait..."); emit statusMessage (pMsg); emit signalProgressBoxCreate(N, pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ qDebug() << "Graph::graphMatrixAdjacencyCreate() - i" << i << "name"<< (*it)->name(); emit signalProgressBoxUpdate(++progressCounter); if ( ! (*it)->isEnabled() || ( (*it)->isIsolated() && dropIsolates) ) { qDebug() << "Graph::graphMatrixAdjacencyCreate() - SKIP i" << i << "name"<< (*it)->name(); continue; } j=i; for (jt=it; jt!=m_graph.end(); jt++){ qDebug() << "Graph::graphMatrixAdjacencyCreate() - j" << j << "name"<< (*jt)->name(); if ( ! (*jt)->isEnabled() || ( (*jt)->isIsolated() && dropIsolates) ) { qDebug() << "Graph::graphMatrixAdjacencyCreate() - SKIP j" << j << "name" << (*jt)->name(); continue; } if ( (m_weight = edgeExists ( (*it)->name(), (*jt)->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 << ","<< j << ") = " << AM.item(i,j); if (i != j ) { if ( (m_weight = edgeExists ( (*jt)->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 << ","<< i << ") = " << AM.item(j,i); } j++; } i++; } calculatedAdjacencyMatrix=true; emit signalProgressBoxKill(); } bool Graph::graphMatrixAdjacencyInvert(const QString &method){ qDebug()<<"Graph::graphMatrixAdjacencyInvert() "; bool considerWeights=false; int i=0, j=0; bool isSingular=true; bool dropIsolates=true; // always drop isolates else AM will be singular int N = vertices(dropIsolates, false, true); graphMatrixAdjacencyCreate(dropIsolates, considerWeights); invAM.resize(N,N); if ( method == "gauss") { invAM.inverseByGaussJordanElimination(AM); } else { invAM.inverse(AM); } VList::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; } /** * @brief Calls Graph::graphMatrixAdjacencyInvert(method) to compute * the inverse of the adjacency matrix and writes it to file fn in HTML notation * @param fn * @param method */ void Graph::writeMatrixAdjacencyInvert(const QString &fn, const QString &method) { qDebug("Graph::writeMatrixAdjacencyInvert() "); int i=0, j=0; VList::const_iterator it, it1; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << "-Social Network Visualizer "<< VERSION < 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(); } /** * @brief Computes the Degree matrix of the graph and writes it to given file * @param fn */ void Graph::writeMatrixDegreeText(const QString &fn) { qDebug("Graph::writeMatrixDegreeText() "); // int i=0, j=0; // VList::const_iterator it, it1; graphMatrixAdjacencyCreate(); QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << AM.degreeMatrix(); file.close(); } /** * @brief Computes the Laplacian matrix of the graph and writes it to given file * @param fn */ void Graph::writeMatrixLaplacianPlainText(const QString &fn) { qDebug("Graph::writeMatrixLaplacianPlainText() "); // int i=0, j=0; // VList::const_iterator it, it1; graphMatrixAdjacencyCreate(); QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << AM.laplacianMatrix(); file.close(); } /** This method is automatically invoked when a QTimerEvent occurs UNUSED */ //void Graph::timerEvent(QTimerEvent *event) { // qDebug("Graph: timerEvent()"); // Q_UNUSED(event); // if (!graphIsModified()) { // qDebug("Timer will be KILLED since no vertex is movin any more..."); // killTimer(timerId); // timerId = 0; // } //} /** * @brief Repositions all nodes on random positions * Emits setNodePos(i, x,y) to tell GW that the node item should be moved. * @param maxWidth * @param maxHeight */ void Graph::layoutRandom(){ qDebug()<< "Graph::layoutRandom() "; double new_x=0, new_y=0; VList::const_iterator it; int N = vertices(); int progressCounter = 0; QString pMsg = tr("Embedding Random Layout. \n" "Please wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate (N,pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate (++progressCounter); new_x= canvasRandomX(); new_y= canvasRandomY(); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug()<< "Graph::layoutRandom() - " << " vertex " << (*it)->name() << " emitting setNodePos to new pos " << new_x << " , "<< new_y; emit setNodePos((*it)->name(), new_x, new_y); } emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedPositions); } /** * @brief Repositions all nodes on the periphery of * different circles with random radius * @param x0 * @param y0 * @param maxRadius */ void Graph::layoutRadialRandom(const bool &guides){ qDebug() << "Graph::layoutRadialRandom - "; double rad=0, new_radius=0, new_x=0, new_y=0; double i=0; double x0=canvasWidth/2.0; double y0=canvasHeight/2.0; double maxRadius = canvasMaxRadius(); //offset controls how far from the centre the central nodes be positioned qreal offset=0.06, randomDecimal=0; int vert=vertices(); int progressCounter=0; VList::const_iterator it; int N = vertices(); QString pMsg = tr("Embedding Random Radial layout. \n" "Please wait ...."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg ); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); randomDecimal = (qreal ) ( 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* M_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 setNodePos((*it)->name(), new_x, new_y); i++; if (guides) { emit addGuideCircle ( x0, y0, new_radius ); } } emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedPositions); } /** * @brief Repositions all nodes on the periphery of a circle with given radius * @param x0 * @param y0 * @param maxRadius */ void Graph::layoutCircular (const double &x0, const double &y0, const double &newRadius, const bool &guides){ qDebug() << "Graph::layoutCircular - "; double rad=0, new_x=0, new_y=0; double i=0; VList::const_iterator it; int N=vertices(); int progressCounter=0; QString pMsg=tr("Applying circular layout. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N, pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ emit signalProgressBoxUpdate(++progressCounter); if ( ! (*it)->isEnabled() ) { qDebug() << " vertex i" << (*it)->name() << " disabled. Continue"; continue; } //Calculate new position rad= (2.0* M_PI/ N ); new_x=x0 + newRadius * cos(i * rad); new_y=y0 + newRadius * 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 setNodePos((*it)->name(), new_x, new_y); i++; if (guides) { emit addGuideCircle ( x0, y0, newRadius ); } } emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedPositions); } /** * @brief Convenience method * Changes the size of all nodes to be proportional to their outDegree (Degree Centrality) * Calls layoutByProminenceIndex */ void Graph::layoutVertexSizeByOutdegree() { layoutByProminenceIndex(1,2); } /** * @brief Convenience method * Changes the size of all nodes to be proportional to their InDegree (Degree Prestige) * Calls layoutByProminenceIndex */ void Graph::layoutVertexSizeByIndegree() { layoutByProminenceIndex(10,2); } /** * @brief Applies a layout according to each actor's prominence index score. * The layout type can be radial (0), level (1), node sizes (2) or node colors (3), as follows: * layoutType=0 - Repositions all nodes on the periphery of concentric circles with radius analogous to their prominence index * layoutType=1 - Repositions all nodes on different top-down levels according to their centrality * layoutType=2 - Changes node sizes to be proportional to their prominence index score * layoutType=2 - Changes node colors to reflect their prominence index score (from red to green) * @param prominenceIndex, 0-12 * @param layoutType: 0,1,2,3 * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::layoutByProminenceIndex (int prominenceIndex, int layoutType, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { qDebug() << "Graph::layoutByProminenceIndex - " << "index = " << prominenceIndex << "type = " << layoutType; double i=0, std=0, norm=0; double new_x=0, new_y=0; qreal C=0, maxC=0; double x0=0, y0=0, maxRadius=0, new_radius=0, rad=0; double maxWidth=0, maxHeight=0; qreal offset=0; int new_size=0; int progressCounter=0; int N=vertices(); VList::const_iterator it; QColor new_color; switch (layoutType){ case 0: { // radial x0=canvasWidth/2.0; y0=canvasHeight/2.0; offset=0.06; maxRadius=canvasMaxRadius(); break; } case 1: { //level offset=50; maxHeight= canvasHeight-offset; maxWidth= canvasWidth-offset; break; } }; emit statusMessage(tr("Computing centrality/prestige scores. Please wait...")); //first compute centrality indices if needed if ( prominenceIndex == 0) { // do nothing } else if ( prominenceIndex == IndexType::DC ) { centralityDegree(true, dropIsolates); } else if ( prominenceIndex == IndexType::IRCC ){ centralityClosenessIR(); } else if ( prominenceIndex == IndexType::IC ) { centralityInformation(); } else if ( prominenceIndex == IndexType::EVC ){ centralityEigenvector(true, dropIsolates); } else if ( prominenceIndex == IndexType::DP ){ prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == IndexType::PRP ) { prestigePageRank(); } else if ( prominenceIndex == IndexType::PP ){ prestigeProximity(considerWeights, inverseWeights); } else{ graphDistancesGeodesic(true, considerWeights, inverseWeights, dropIsolates); } QString pMsg; switch (layoutType){ case 0: { // radial pMsg = tr("Embedding Radial layout by Prominence Score. \nPlease wait..."); break; } case 1: { // level pMsg = tr("Embedding Level layout by Prominence Score. \nPlease wait..."); break; } case 2: { // node size pMsg = tr("Embedding Node Size by Prominence Score layout. \nPlease wait..."); break; } case 3: { // node color pMsg = tr("Embedding Node Color by Prominence Score layout. \nPlease wait..."); break; } } emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { switch (prominenceIndex) { case 0: { C=0;maxC=0; break; } case IndexType::DC : { C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxSDC; break; } case IndexType::CC : { C=(*it)->CC(); std= (*it)->SCC(); maxC=maxSCC; break; } case IndexType::IRCC : { C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case IndexType::BC : { C=(*it)->BC(); std= (*it)->SBC(); maxC=maxSBC; break; } case IndexType::SC : { C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSSC; break; } case IndexType::EC : { C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case IndexType::PC : { C=(*it)->PC(); std= (*it)->SPC(); maxC=maxSPC; break; } case IndexType::IC : { C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case IndexType::EVC : { C=(*it)->EVC(); std= (*it)->SEVC(); maxC=1; break; } case IndexType::DP : { C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxSDP; break; } case IndexType::PRP : { C=(*it)->PRP(); std= (*it)->SPRP(); maxC=1; break; } case IndexType::PP : { C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; norm = std/maxC; emit signalProgressBoxUpdate( ++progressCounter); switch (layoutType){ case 0: { // radial qDebug () << "vertex" << (*it)->name() << "pos x" << (*it)->x() << "y"<< (*it)->y() << "C" << C << "stdC" << std << "maxC"<< maxC << "norm (std/maxC)" << norm << "maxradius" << maxRadius << "newradius" << maxRadius - (norm - offset)*maxRadius; //Compute new vertex position switch (static_cast (ceil(maxC)) ){ case 0: { qDebug("maxC=0. Using maxRadius"); new_radius=maxRadius; break; } default: { new_radius=(maxRadius- (norm - offset)*maxRadius); break; } }; rad= (2.0* M_PI/ N ); new_x=x0 + new_radius * cos(i * rad); new_y=y0 + new_radius * sin(i * rad); qDebug() << "Finished calculation. " "new radial pos: x"<< new_x << "y" << new_y; //Move vertex to new position (*it)->setX( new_x ); (*it)->setY( new_y ); emit setNodePos((*it)->name(), new_x, new_y); i++; emit addGuideCircle ( x0, y0, new_radius ); break; } case 1: { // level qDebug()<< "vertex" << (*it)->name() << "pos x"<< (*it)->x() << "y"<< (*it)->y() << "C" << C << "stdC" << std << "maxC "<< maxC << "norm (std/maxC)" << norm << "maxWidth " << maxWidth <<" maxHeight "< (ceil(maxC)) ){ case 0: { qDebug("maxC=0. Using maxHeight"); new_y=maxHeight; break; } default: { new_y=offset/2.0+maxHeight-(norm)*maxHeight; break; } }; new_x=offset/2.0 + rand() % ( static_cast (maxWidth) ); qDebug() << "Finished calculation. " "new level pos: x"<< new_x << "y" << new_y; //Move vertex to new position (*it)->setX( new_x ); (*it)->setY( new_y ); emit setNodePos((*it)->name(), new_x, new_y); i++; emit addGuideHLine(new_y); break; } case 2: { // node size qDebug () << "vertex" << (*it)->name() << "C=" << C << ", stdC=" << std << "maxC" << maxC << "initVertexSize " << initVertexSize << "norm (stdC/maxC) " << norm << ", (norm) * initVertexSize " << (norm *initVertexSize); //Calculate new node size switch (static_cast (ceil(maxC) )){ case 0: { qDebug()<<"maxC=0. Using initVertexSize"; new_size=initVertexSize; break; } default: { new_size=ceil ( initVertexSize/2.0 + (qreal) initVertexSize * (norm)); break; } }; //set new vertex size and emit signal to change node size qDebug() << "Finished calculation. " << "new vertex size "<< new_size << " call setSize()"; (*it)->setSize(new_size); emit setNodeSize((*it)->name(), new_size); break; } case 3: { // node color qDebug () << "vertex" << (*it)->name() << "C=" << C << ", stdC=" << std << "maxC" << maxC << "initVertexColor " << initVertexColor << "norm (stdC/maxC) " << norm; //Calculate new node color switch (static_cast (ceil(maxC) )){ case 0: { qDebug()<<"maxC=0. Using initVertexColor"; new_color = QColor (initVertexColor); break; } default: { // H = 0 (red) for most important nodes // H = 240 (blue) for least important nodes new_color.setHsv( 240 - norm * 240, 255, 255,255 ); break; } }; //change vertex color and emit signal to change node color as well qDebug ()<< "new vertex color "<< new_color << " call setSize()"; (*it)->setColor(new_color.name()); emit setNodeColor((*it)->name(), new_color.name()); break; } }; } emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedPositions); prominenceDistribution(prominenceIndex, m_reportsChartType); } /** * @brief Embeds a Force Directed Placement layout according to the initial Spring Embedder model proposed by Eades. * @param maxIterations * 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(const int maxIterations){ int iteration = 1 ; int progressCounter=0; qreal dist = 0; qreal f_rep=0, f_att=0; QPointF DV; qreal c4=0.1; //normalization factor for final displacement VList::const_iterator v1; VList::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; /* apply an initial random layout */ //layoutCircular(canvasWidth/2.0, canvasHeight/2.0, naturalLength/2.0 ,false); layoutRandom(); QString pMsg = tr ( "Embedding Eades Spring-Gravitational model. \n" "Please wait ...."); emit statusMessage( pMsg ); emit signalProgressBoxCreate (maxIterations, pMsg ); for ( iteration=1; iteration <= maxIterations ; iteration++) { //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 = graphDistanceEuclidean(DV); /** * calculate electric (repulsive) forces between * all vertices. */ f_rep = layoutForceDirected_F_rep ("Eades", 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->edgeExists( (*v1) ->name(), (*v2) -> name()) ) { f_att = layoutForceDirected_F_att ("Eades", 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) ; emit signalProgressBoxUpdate( ++progressCounter ); } //end iterations emit signalProgressBoxKill(); } /** * @brief Embeds a Force Directed Placement layout according to the Fruchterman-Reingold model. * 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. * @param maxIterations */ void Graph::layoutForceDirectedFruchtermanReingold(const int maxIterations){ int progressCounter=0; qreal dist = 0; qreal f_att, f_rep; QPointF DV; //difference vector 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); VList::const_iterator v1, v2; int iteration = 1 ; /* apply an initial circular layout */ //layoutCircular(canvasWidth/2.0, canvasHeight/2.0, optimalDistance/2.0,false); //layoutRandom(); qDebug() << "Graph: layoutForceDirectedFruchtermanReingold() "; qDebug () << "Graph: Setting optimalDistance = "<< optimalDistance << "...following Fruchterman-Reingold (1991) formula "; qDebug() << "Graph: canvasWidth " << canvasWidth << " canvasHeight " << canvasHeight; QString pMsg = tr( "Embedding Fruchterman & Reingold forces model. \n" "Please wait ..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(maxIterations,pMsg ); for ( iteration=1; iteration <= maxIterations ; iteration++) { //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() // << " vpos " << vpos[(*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 = graphDistanceEuclidean( DV ); //calculate repulsive force from _near_ vertices f_rep = layoutForceDirected_F_rep( "FR", 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 ( edgeExists ((*v1)->name(), (*v2)->name()) ) { //calculate attracting force f_att = layoutForceDirected_F_att ("FR", 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 } //end for v1 // limit the max displacement to the temperature t // prevent placement outside of the frame/canvas layoutForceDirected_FR_moveNodes( layoutForceDirected_FR_temperature (iteration) ); emit signalProgressBoxUpdate( ++progressCounter ); } emit signalProgressBoxKill(); } /** * @brief Embeds a Force Directed Placement layout according to the Kamada-Kawai model. * In this model, the network is considered to be a dynamic system * where every two actors are 'particles' mutually connected by a 'spring'. * Each spring has a desirable length, which corresponds to their graph * theoretic distance. In this way, the optimal layout of the graph * is the state with the minimum imbalance. The degree of * imbalance is formulated as the total spring energy: * the square summation of the differences between desirable * distances and real ones for all pairs of particles * Initially, the particles/actors are placed on the vertices of a regular n-polygon */ void Graph::layoutForceDirectedKamadaKawai(const int maxIterations, const bool considerWeights, const bool inverseWeights, const bool dropIsolates, const QString &initialPositions){ qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " << "maxIter " << maxIterations; VList::const_iterator v1, v2; int progressCounter=0, minimizationIterations=0; int i=0, j=0, m=0, pm=0, pnm=0, pn=0; int N=vertices(); // active actors qreal K=1; // constant qreal L=0; // the desirable length of a single edge. qreal L0=0; // the length of a side of the display square area qreal D=0; // the graph diameter Matrix l; // the original spring length between pairs of particles/actors Matrix k; // the strength of the spring between pairs of particles/actors Matrix LIN_EQ_COEF(2,2); // holds the coefficients of set of linear equations 11 and 12 qreal b[2]; // holds the right hand vector of linear equations 11 and 12 qreal partDrvtEx = 0; // partial derivative of E by Xm qreal partDrvtEy = 0; // partial derivative of E by Ym qreal partDrvtExSec_m = 0; // partial second derivative of E by Xm qreal partDrvtEySec_m = 0; // partial second derivative of E by Ym qreal partDrvtExEySec_m = 0; // partial second derivative of E by Xm Ym qreal partDrvtEyExSec_m = 0; // partial second derivative of E by Ym Xm qreal partDrvtEx_m = 0; // cache for partial derivative of E by Xm, for particle with max D_i qreal partDrvtEy_m = 0; // cache for partial derivative of E by Ym, for particle with max D_i qreal partDrvDenom = 0; qreal xm=0,ym=0; qreal xi=0,yi=0; qreal xpm=0, ypm=0; // cache for pos of particle with max D_i qreal dx=0, dy=0; qreal epsilon=0.1; qreal Delta_m=0; qreal Delta_max=epsilon + 0.0001; // Compute graph-theoretic distances dij for 1 <= i!=j <= n qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - Compute dij, where (i,j) in E"; graphMatrixDistanceGeodesicCreate(considerWeights,inverseWeights, dropIsolates); // Compute original spring length // lij for 1 <= i!=j <= n using the formula: // lij = L x dij // where L the desirable length of a single edge in the display pane // Since we are on a restricted display (a canvas), L depends on the // diameter D of the graph and the length L of a side of the display square: // L = L0 / D qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " "Compute lij = L x dij. lij will be symmmetric."; D = graphDiameter(considerWeights,inverseWeights); L0 = canvasMinDimension()-100; L = L0 / D; qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - L=" << L0 << "/" < e ) while (Delta_max > epsilon) { progressCounter++; emit signalProgressBoxUpdate( progressCounter ); if (progressCounter == maxIterations) { qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " "Reached maxIterations. BREAK"; break; } Delta_max = epsilon; // choose particle with largest Delta_m = max Delta_i // compute partial derivatives of E by xm and ym for every particle m // using equations 7 and 8 qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - Iteration" << progressCounter << "Choose particle with largest Delta_m = max Delta_i "; pnm = -1; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { pn = (*v1)->name(); m = vpos[pn]; xm = (*v1)->x(); ym = (*v1)->y(); qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " "Compute partial derivatives E for particle" << pn << " vpos m" << m << " pos"<< xm << ", "<< ym; if ( ! (*v1)->isEnabled() ) { qDebug() << " particle " << pn << " vpos m " << m << " disabled. Continue"; continue; } partDrvtEx = 0; partDrvtEy = 0; for (v2=m_graph.cbegin(); v2!=m_graph.cend(); ++v2) { i = vpos[(*v2)->name()]; xi = (*v2)->x(); yi = (*v2)->y(); qDebug () << " particle vpos i"<< i << " pos (" << xi << "," << yi << ")"; if ( ! (*v2)->isEnabled() ) { qDebug()<< " i "<< (*v2)->name()<< " disabled. Continue"; continue; } if (m == i) { qDebug() << " m==i, continuing"; continue; } partDrvtEx += k.item(m,i) * ( (xm - xi) - ( l.item(m,i) * (xm - xi) ) / sqrt( (xm - xi) * (xm - xi) + (ym - yi)*(ym - yi) ) ); partDrvtEy += k.item(m,i) * ( (ym - yi) - ( l.item(m,i) * (ym - yi) ) / sqrt( (xm - xi) * (xm - xi) + (ym - yi)*(ym - yi) ) ); } // end v2 for loop Delta_m = sqrt (partDrvtEx * partDrvtEx + partDrvtEy * partDrvtEy); qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - m" << m << " Delta_m" << Delta_m; if (Delta_m > Delta_max) { qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - m" << m << " Delta_m > Delta_max. " << " Setting new Delta_max = " << Delta_m; Delta_max = Delta_m; partDrvtEx_m = partDrvtEx; partDrvtEy_m = partDrvtEy; pnm = pn ; // store name of particle satisfying Delta_m = max Delta_i pm = m ; // store vpos of particle satisfying Delta_m = max Delta_i xpm = xm; // store particle x pos ypm = ym; // store particle y pos } } // end v1 for loop if (pnm < 0) { qDebug () << "Graph::layoutForceDirectedKamadaKawai() - " "No particle left with Delta_m > epsilon -- BREAK"; break; } // let pm the particle satisfying D_m = max_D_i m = pm ; xm = xpm; ym = ypm; qDebug () << "Graph::layoutForceDirectedKamadaKawai() - m"<< m << " has max Delta_m"<< Delta_max << " Starting minimizing Delta_m - " << " initial m pos " << xm << ym; minimizationIterations=0; // while ( D_m > e) do { if (minimizationIterations > 10) { qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " "Reached minimizationIterations threshold. BREAK"; break; } minimizationIterations++; qDebug () << "Graph::layoutForceDirectedKamadaKawai() - " "Started minimizing Delta_m for m"<< m << "First compute dx and dy by solving equations 11 and 12 "; // compute dx and dy by solving equations 11 and 12 partDrvtEx=0; partDrvtEy=0; partDrvtEx_m = 0; partDrvtEy_m = 0; partDrvtExSec_m=0; partDrvtEySec_m=0; partDrvtExEySec_m=0; partDrvtEyExSec_m=0; // first compute coefficients of the linear system equations for (v2=m_graph.cbegin(); v2!=m_graph.cend(); ++v2) { i = vpos[(*v2)->name()]; xi = (*v2)->x(); yi = (*v2)->y(); qDebug () << " m"<< m << " i"<< i << " pos_i (" << xi << "," << yi << ")"; if ( ! (*v2)->isEnabled() ) { qDebug()<< " i "<< (*v2)->name()<< " disabled. Continue"; continue; } if (i == m) { qDebug() << " m==i, continuing"; continue; } partDrvDenom = pow ( sqrt( (xm - xi) * (xm - xi) + (ym - yi)*(ym - yi) ) , 3 ); partDrvtEx_m += k.item(m,i) * ( (xm - xi) - ( l.item(m,i) * (xm - xi) ) / sqrt( (xm - xi) * (xm - xi) + (ym - yi)*(ym - yi) ) ); partDrvtEy_m += k.item(m,i) * ( (ym - yi) - ( l.item(m,i) * (ym - yi) ) / sqrt( (xm - xi) * (xm - xi) + (ym - yi)*(ym - yi) ) ); partDrvtExSec_m += k.item(m,i) * ( 1 - ( l.item(m,i) * (ym - yi) * (ym - yi) ) / partDrvDenom ); partDrvtExEySec_m += k.item(m,i) * ( ( l.item(m,i) * (xm - xi) * (ym - yi) ) / partDrvDenom ); partDrvtEyExSec_m += k.item(m,i) * ( ( l.item(m,i) * (xm - xi) * (ym - yi) ) / partDrvDenom ); partDrvtEySec_m += k.item(m,i) * ( 1 - ( l.item(m,i) * (xm - xi) * (xm - xi) ) / partDrvDenom ); } // end v2 for loop Delta_m = sqrt (partDrvtEx_m * partDrvtEx_m + partDrvtEy_m * partDrvtEy_m); qDebug () << "Graph::layoutForceDirectedKamadaKawai() - m"<< m << " new Delta_m" << Delta_m; LIN_EQ_COEF.setItem(0,0,partDrvtExSec_m); LIN_EQ_COEF.setItem(0,1,partDrvtExEySec_m); LIN_EQ_COEF.setItem(1,0,partDrvtEyExSec_m); LIN_EQ_COEF.setItem(1,1,partDrvtEySec_m); qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " " Jacobian Matrix of coefficients for linear system (eq. 11 & 12) is:"; //LIN_EQ_COEF.printMatrixConsole(); b[0] = - partDrvtEx_m; b[1] = - partDrvtEy_m; qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - right hand vector is: \n" << b[0] << " \n" << b[1]; qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - solving linear system..."; LIN_EQ_COEF.solve(b); qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - solved linear system."; dx=b[0]; dy=b[1]; qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - Solution \n b[0] = dx =" << dx << "\n b[1] = dy =" << dy; qDebug () << "Graph::layoutForceDirectedKamadaKawai() - m"<< m << " current m pos " << xm << ym << " new m pos " << xm +dx << ym+dy; if ( (xm + dx) < 50 || (xm + dx) > (canvasWidth-50) ) { qDebug () << "Graph::layoutForceDirectedKamadaKawai() - " "new xm out of canvas, setting random x"; xm = canvasRandomX(); } else { xm = xm + dx; } if ( (ym + dy) < 50 || (ym + dy) > (canvasHeight-50) ) { qDebug () << "Graph::layoutForceDirectedKamadaKawai() - " "new ym out of canvas, setting random y"; ym = canvasRandomY(); } else { ym = ym + dy; } qDebug () << "Graph::layoutForceDirectedKamadaKawai() - m"<< m << " new m pos " << xm << ym; // TODO CHECK IF WE HAVE REACHED A FIXED POINT LOOP } while (Delta_m > epsilon); qDebug () << "Graph::layoutForceDirectedKamadaKawai() - Finished minimizing Delta_m " "for particle" << pnm << "vpos" << m << "Minimized Delta_m"<< Delta_m << "moving it to new pos" << xm << ym; m_graph[m]->setX(xm); m_graph[m]->setY(ym); //emit setNodePos( pnm, xm, ym); } // end while (Delta_max > epsilon) { qDebug () << "Graph::layoutForceDirectedKamadaKawai() - " "Delta_max =< epsilon -- RETURN"; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { emit setNodePos( (*v1)->name(), (*v1)->pos().x(), (*v1)->pos().y()); } emit signalProgressBoxKill(); graphSetModified(GraphChange::ChangedPositions); } /** * @brief Reduces the temperature as the layout approaches a better configuration * @return qreal temperature */ qreal Graph::layoutForceDirected_FR_temperature(const int iteration) const{ qreal temp=0; qreal temperature=5.8309518948453; //limits the displacement of the vertex qDebug() << "Graph::layoutForceDirected_FR_temperature(): cool iteration " << iteration; // For the temperature (which limits the displacement of each vertex), // Fruchterman & Reingold suggested in their paper that it might start // at an initial high value (i.e. "one tenth the width of the frame" // and then decay to 0 in an inverse linear fashion // They also suggested "a combination of quenching and simmering", // which is a high initial temperature and then a constant low one. // This is what SocNetV does. In fact after 200 iterations we follow Eades advice // and stop movement (temp = 0) if (iteration < 10) { // quenching: starts at a high temperature ( canvasWidth / 10) and cools steadily and rapidly temp =canvasWidth / (iteration + 10.0) ; } else if (iteration > 200) { // follow Eades advice... temp = 0; } else { // simmering: stay at a constant low temperature temp = temperature; } qDebug() << "Graph::layoutForceDirected_FR_temperature() - iteration " << iteration << " temp " << temp; return temp; } /** * @brief Computes Optimal Distance. Used in Spring Embedder and FR algorithms. * @return qreal optimalDistance */ qreal Graph::computeOptimalDistance(const int &V){ qreal vertexWidth = (qreal) 2.0 * initVertexSize ; qreal screenArea = canvasHeight*canvasWidth; qreal vertexArea = qCeil ( qSqrt( screenArea / V ) ) ; // 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 QString model, const qreal &dist, const qreal &optimalDistance) { qreal f_att; if (model == "Eades") { qreal c_spring=2; f_att = c_spring * log10 ( dist / optimalDistance ); } else { // model -> FR f_att= ( dist * dist ) / optimalDistance; } return f_att; } qreal Graph::layoutForceDirected_F_rep( const QString model, const qreal &dist, const qreal &optimalDistance) { qreal f_rep; if (model == "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 { // model -> FR // To speed up our algorithm we use the grid-variant algorithm. 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 " <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 setNodePos to happen. if ( xvel < 1 && xvel > 0 ) xvel = 1 ; if ( yvel < 1 && yvel > 0 ) yvel = 1 ; //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 usable screen and adjust newPos.rx() = canvasVisibleX(newPos.x()); newPos.ry() = canvasVisibleY(newPos.y()); qDebug() << " Final new pos (" << newPos.x() << "," << newPos.y()<< ")"; (*v1)->setX( newPos.x() ); (*v1)->setY( newPos.y() ); emit setNodePos((*v1)->name(), newPos.x(), newPos.y()); //vertexPosSet(); } } /** * @brief Graph::layoutForceDirected_FR_moveNodes * @param temperature */ void Graph::layoutForceDirected_FR_moveNodes(const qreal &temperature) { qDebug() << "\n\n ***** layoutForceDirected_FR_moveNodes() \n\n " ; qDebug () << " temperature " << temperature; QPointF newPos; qreal xvel = 0, yvel = 0; VList::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() = canvasVisibleX (newPos.x()); newPos.ry() = canvasVisibleY (newPos.y()); //Move node to new position qDebug()<< " final new pos " << newPos.x() << "," << newPos.y()<< ")"; (*v1)->setX( newPos.x() ); (*v1)->setY( newPos.y() ); emit setNodePos((*v1)->name(), newPos.x(), newPos.y()); } } /** * @brief Helper method, return the human readable name of matrix type. * @param matrix */ QString Graph::graphMatrixTypeToString(const int &matrixType) const { QString matrixStr; switch (matrixType) { case MATRIX_ADJACENCY : matrixStr = "Adjacency Matrix" ; break; case MATRIX_DISTANCES: matrixStr = "Distances Matrix" ; break; case MATRIX_DEGREE: matrixStr = "Degree Matrix" ; break; case MATRIX_LAPLACIAN: matrixStr = "Laplacian Matrix" ; break; case MATRIX_ADJACENCY_INVERSE: matrixStr = "Adjacency Inverse" ; break; case MATRIX_GEODESICS: matrixStr = "Geodesics Matrix" ; break; case MATRIX_REACHABILITY: matrixStr = "Reachability Matrix" ; break; case MATRIX_ADJACENCY_TRANSPOSE: matrixStr = "Adjacency Transpose" ; break; case MATRIX_COCITATION: matrixStr = "Cocitation Matrix" ; break; case MATRIX_DISTANCES_EUCLIDEAN : matrixStr = "Euclidean distance matrix"; break; case MATRIX_DISTANCES_MANHATTAN: matrixStr = "Manhattan distance matrix"; break; case MATRIX_DISTANCES_JACCARD: matrixStr = "Jaccard distance matrix"; break; case MATRIX_DISTANCES_HAMMING: matrixStr = "Hamming distance matrix"; break; default: matrixStr = "-" ; break; } return matrixStr; } /** * @brief Helper method, return the matrix type of human readable matrix name . * @param matrix * @return */ int Graph::graphMatrixStrToType(const QString &matrix) const { if (matrix.contains("Hamming", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_HAMMING; } else if (matrix.contains("Jaccard", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_JACCARD; } else if (matrix.contains("Manhattan", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_MANHATTAN; } else if (matrix.contains("Euclidean", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_EUCLIDEAN; } else if (matrix.contains("Cocitation", Qt::CaseInsensitive)) { return MATRIX_COCITATION; } else if (matrix.contains("Adjacency Transpose", Qt::CaseInsensitive)) { return MATRIX_ADJACENCY_TRANSPOSE; } else if (matrix.contains("Reachability", Qt::CaseInsensitive)) { return MATRIX_REACHABILITY; } else if (matrix.contains("Geodesics", Qt::CaseInsensitive)) { return MATRIX_GEODESICS; } else if (matrix.contains("Adjacency Inverse", Qt::CaseInsensitive)) { return MATRIX_ADJACENCY_INVERSE; } else if (matrix.contains("Laplacian", Qt::CaseInsensitive)) { return MATRIX_LAPLACIAN; } else if (matrix.contains("Degree", Qt::CaseInsensitive)) { return MATRIX_DEGREE; } else if (matrix.contains("Adjacency", Qt::CaseInsensitive)) { return MATRIX_ADJACENCY; } else if (matrix.contains("Distances", Qt::CaseInsensitive)) { return MATRIX_DISTANCES; } else { return -1; } } /** * @brief Helper method, return the human readable name of metric type. * @param metric */ QString Graph::graphMetricTypeToString(const int &metricType) const { QString metricStr; switch (metricType) { case METRIC_SIMPLE_MATCHING : metricStr = "Simple / Exact matching" ; break; case METRIC_JACCARD_INDEX: metricStr = "Jaccard Index" ; break; case METRIC_HAMMING_DISTANCE: metricStr = "Hamming distance" ; break; case METRIC_COSINE_SIMILARITY: metricStr = "Cosine similarity" ; break; case METRIC_EUCLIDEAN_DISTANCE: metricStr = "Euclidean distance" ; break; case METRIC_MANHATTAN_DISTANCE: metricStr = "Manhattan distance" ; break; case METRIC_PEARSON_COEFFICIENT: metricStr = "Pearson Correlation Coefficient" ; break; case METRIC_CHEBYSHEV_MAXIMUM: metricStr = "Chebyshev distance" ; break; default: metricStr = "-" ; break; } return metricStr; } /** * @brief Helper method, return the identifier of a metric. * @param metricStr */ int Graph::graphMetricStrToType(const QString &metricStr) const { int metric=METRIC_SIMPLE_MATCHING; if (metricStr.contains("Simple",Qt::CaseInsensitive)) metric = METRIC_SIMPLE_MATCHING ; else if (metricStr.contains("Jaccard",Qt::CaseInsensitive)) metric =METRIC_JACCARD_INDEX ; else if (metricStr.contains("None",Qt::CaseInsensitive)) metric =METRIC_NONE; else if (metricStr.contains("Hamming",Qt::CaseInsensitive)) metric =METRIC_HAMMING_DISTANCE; else if (metricStr.contains("Cosine",Qt::CaseInsensitive)) metric =METRIC_COSINE_SIMILARITY; else if (metricStr.contains("Euclidean",Qt::CaseInsensitive)) metric =METRIC_EUCLIDEAN_DISTANCE; else if (metricStr.contains("Manhattan",Qt::CaseInsensitive)) metric =METRIC_MANHATTAN_DISTANCE; else if (metricStr.contains("Pearson ",Qt::CaseInsensitive)) metric = METRIC_PEARSON_COEFFICIENT; else if (metricStr.contains("Chebyshev",Qt::CaseInsensitive)) metric = METRIC_CHEBYSHEV_MAXIMUM; return metric; } /** * @brief Helper method, return the human readable name of clustering method type. * @return */ QString Graph::graphClusteringMethodTypeToString(const int &methodType) const { QString methodStr; switch (methodType) { case Clustering::Single_Linkage: methodStr = "Single-linkage (minimum)"; break; case Clustering::Complete_Linkage: methodStr = "Complete-linkage (maximum)"; break; case Clustering::Average_Linkage: methodStr = "Average-linkage (UPGMA)"; break; default: break; } return methodStr; } /** * @brief Helper method, return clustering method type from the human readable name of it. * @param method * @return */ int Graph::graphClusteringMethodStrToType(const QString &method) const { int methodType=Clustering::Average_Linkage; if (method.contains("Single", Qt::CaseInsensitive)) { methodType = Clustering::Single_Linkage; } else if (method.contains("Complete", Qt::CaseInsensitive)) { methodType = Clustering::Complete_Linkage; } else if (method.contains("Average", Qt::CaseInsensitive)) { methodType = Clustering::Average_Linkage; } return methodType; } /** * @brief Helper method, returns a nice qstring where all html special chars are encoded * @param str * @return */ QString Graph::htmlEscaped(QString str) const { str=str.simplified(); if (str.contains('&') ){ str=str.replace('&',"&"); } if (str.contains('<') ){ str=str.replace('<',"<"); } if (str.contains('>') ){ str=str.replace('>',">"); } if (str.contains('\"') ){ str=str.replace('\"',"""); } if (str.contains('\'') ){ str=str.replace('\'',"'"); } return str; } app-2.8/src/graph.h000077500000000000000000001346431377436340000142310ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graph.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 #include #include #include #include //FYI: stack is a wrapper around in C++, see: www.cplusplus.com/reference/stl/stack #include #include #include "global.h" #include "graphvertex.h" #include "matrix.h" #include "parser.h" #include "webcrawler.h" #include "graphicswidget.h" QT_BEGIN_NAMESPACE class QPointF; QT_END_NAMESPACE QT_CHARTS_BEGIN_NAMESPACE class QAbstractSeries; class QAbstractAxis; class QSplineSeries; class QBarSeries; class QAreaSeries; class QBarSet; class QBarCategoryAxis; QT_CHARTS_END_NAMESPACE QT_CHARTS_USE_NAMESPACE SOCNETV_USE_NAMESPACE class Chart; using namespace std; typedef QList VList; typedef QHash H_StrToInt; typedef QHash H_Int; typedef QHash H_f_i; typedef QPair pair_f_b; typedef QPair pair_i_fb; typedef QMultiHash H_edges; typedef QHash H_StrToBool; typedef QList L_int; typedef QVector V_int; typedef QVector V_str; /** TODO & KNOWN BUGS: \todo Group edge editing: i.e. change weight or color. \todo - CHECK weighted networks results (IRCC and distance matrix with other combinations) \todo - CHECK graphIsWeighted corner case results, when !graphIsModified. \bug Create d-regular, undirected, ask for closeness, it says we are on a disconnected graph \bug Crash on Graphml files with textlabels instead of nodenumbers (i.e. nets/killer.graphml) \bug wontfix Crash on Graphml files with html special chars in node/edge labels \bug wontfix Pajek files with no ic / labels without quotes are displayed without colors \bug wrong default edge colors (not the ones used by Settings) after loading GraphML files. \bug Resizing the MW view does not resize/reposition the layout guides TODO Change Menus to: Matrices Cohesion/Connectedness: Density, Reachability, and All distance and Walks, Connectivity, Reciprocity, Transitivity ?, Clu Cof Prominence: Centrality and Prestige Subgroups / Communities: Cliques (later clans etc), (later path distance MDS) Components, Blocks and Cutpoints, Structural Equivalence: HCA, Similarity (later MDS), Block modelling, CONCOR */ /** * @brief The Graph class * This is the main class for a Graph, used in conjuction with GraphVertex, Parser and Matrix objects. * Graph class methods are the interface to various analysis algorithms * GraphVertex class holds each vertex data (colors, strings, statistics, etc) * Matrix class holds the adjacency matrix of the network. * Parser class loads files of networks. */ class Graph: public QObject{ Q_OBJECT QThread file_parserThread; QThread wc_parserThread; QThread wc_spiderThread; public slots: int relationCurrent(); QString relationCurrentName() const; void relationCurrentRename(const QString &newName, const bool ¬ifyMW=false); /** Slots to signals from Parser */ void vertexCreate (const int &number, 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, const QString &iconPath = QString(), const bool &signalMW = false ); void graphFileLoaded(const int &fileType, const QString &fName=QString(), const QString &netName=QString(), const int &totalNodes=0, const int &totalLinks=0, const int &edgeDirType=0, const QString &message=QString()); void vertexRemoveDummyNode(int); void graphLoadedTerminateParserThreads (QString reason); void graphSelectionChanged(const QList selectedVertices, const QList selectedEdges); /** Slots to signals from GraphicsWidget and Parser*/ void edgeCreate (const int &v1, const int &v2, const qreal &weight, const QString &color , const int &type=0, const bool &drawArrows=true, const bool &bezier=false, const QString &label=QString(), const bool &signalMW=true); void edgeCreateWebCrawler (const int &source, const int &target); void edgeVisibilitySet(int relation, int, int, bool); //auxiliary vertexCreate functions void vertexCreateAtPos(const QPointF &p); void vertexCreateAtPosRandom(const bool &signalMW=false); void vertexCreateAtPosRandomWithLabel(const int &i, const QString &label, const bool &signalMW=false) ; /** Slots to signals from MainWindow */ void relationSet(int relNum=RAND_MAX, const bool notifyMW=true); void relationNext(); void relationPrev(); void canvasSizeSet(const int w, const int h); double canvasMaxRadius() const; qreal canvasMinDimension() const; double canvasVisibleX(const double &x) const ; double canvasVisibleY(const double &y) const ; double canvasRandomX() const; double canvasRandomY() const; void vertexIsolatedAllToggle ( const bool &toggle); void vertexClickedSet(const int &v); void edgeClickedSet(const int &v1, const int &v2, const bool &openMenu=false) ; void edgeFilterByWeight (qreal, bool); void edgeFilterByRelation(int relation, bool status); void edgeFilterUnilateral(const bool &toggle); void webCrawl(const QString &seedUrl, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &intLinks, const bool &childLinks, const bool &parentLinks, const bool &selfLinks, const bool &extLinksIncluded, const bool &extLinksCrawl, const bool &socialLinks, const bool &delayedRequests); QString htmlEscaped (QString str) const; signals: /** Signals to MainWindow */ void signalProgressBoxCreate(const int max=0, const QString msg="Please wait"); void signalProgressBoxKill(const int max=0); void signalProgressBoxUpdate(const int &count=0 ); void signalGraphSavedStatus(const int &status); void signalGraphModified(const bool &undirected, const int &vertices, const int &edges, const qreal &density); void signalGraphLoaded (const int &fileType, const QString &fileName=QString(), const QString &netName=QString(), const int &totalNodes=0, const int &totalLinks=0, const QString &message=QString() ); void statusMessage (const QString &message); void signalDatasetDescription(QString); void signalNodeClickedInfo(const int &number=0, const QPointF &p=QPointF(), const QString &label=QString(), const int &inDegree=0, const int &outDegree=0, const qreal &clc=0); // void signalEdgeClicked (const int &v1=0, // const int &v2=0, // const qreal &weight=0, // const int &type=0, // const bool &openMenu=false); void signalEdgeClicked (const MyEdge &edge=MyEdge(), const bool &openMenu=false); void signalRelationAddToMW(const QString &newRelation, const bool &changeRelation=true); void signalRelationsClear(); void signalRelationRenamedToMW(const QString newRelName); void signalRelationChangedToGW(int); void signalRelationChangedToMW(const int &relIndex=RAND_MAX); void signalSelectionChanged(const int &selectedVertices, const int &selectedEdges); void signalPromininenceDistributionChartUpdate(QAbstractSeries *series, QAbstractAxis *axisX=Q_NULLPTR, const qreal &min=0, const qreal &max=0, QAbstractAxis *axisY=Q_NULLPTR, const qreal &minF=0, const qreal &maxF=0); /** Signals to GraphicsWidget */ void signalDrawNode( const QPointF &p, const int &num, const int &size, const QString &nodeShape, const QString &nodeIconPath, const QString &nodeColor, const QString &numberColor, const int &numSize, const int &numDistance, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance ); //signal to GW to erase a node void signalRemoveNode (int ); //signal GW to draw an edge void signalDrawEdge ( const int &v1, const int &v2, const qreal &weight, const QString &label="", const QString &color="black", const int &type=0, const bool &drawArrows=true, const bool &bezier=false, const bool &weightNumbers=false); //signal to GW void signalRemoveEdge(const int &v1, const int &v2, const bool &removeOpposite); void setEdgeVisibility (int, int, int, bool); void setVertexVisibility(int, bool); void setNodePos(const int &, const qreal &, const qreal &); void signalNodesFound(const QList foundList); void setNodeSize(const int &v, const int &size); void setNodeShape(const int &v, const QString &shape, const QString &iconPath=QString()); void setNodeColor(const int &v, const QString &color); void setNodeLabel(const int &v, const QString &label); void setNodeNumberColor(const int &v, const QString &color); void setNodeNumberSize(const int &v, const int &size); void setNodeNumberDistance(const int &v, const int &distance); void setNodeLabelSize(const int &v, const int &size); void setNodeLabelColor(const int &v, const QString &color); void setNodeLabelDistance(const int &v, const int &distance); void setEdgeWeight (const int &v1, const int &v2, const qreal &weight); void signalEdgeType(const int &v1, const int &v2, const int &type); void setEdgeColor(const int &v1, const int &v2, const QString &color); void setEdgeLabel (const int &v1, const int &v2, const QString &label); void addGuideCircle(const double&, const double&, const double&); void addGuideHLine (const double&y0); /** Signals to Crawler threads */ void operateSpider(); public: enum GraphChange { ChangedNone = 0, ChangedMinorOptions = 1, ChangedVerticesMetadata = 2, ChangedEdgesMetadata = 3, ChangedPositions = 4, ChangedMajor = 10, ChangedVertices = 11, ChangedEdges = 12, ChangedVerticesEdges = 13, ChangedNew = 14, }; enum Clustering { Single_Linkage = 0, //"single-link" or minimum Complete_Linkage = 1, // "complete-link or maximum Average_Linkage = 2, //mean or "average-linkage" or UPGMA }; /* INIT AND CLEAR*/ Graph(GraphicsWidget *graphicsWidget); void initSignalSlots(); void clear(const QString &reason=""); ~Graph(); void setSocNetV_Version (QString ver) { VERSION = ver; } GraphicsWidget *canvas() { return m_canvas; } /*FILES (READ AND WRITE)*/ QString graphName() const; void graphLoad (const QString m_fileName, const QString m_codecName, const int format, const int two_sm_mode, const QString delimiter=QString()); void graphSave(const QString &fileName, const int &fileType, const bool &saveEdgeWeights=true); bool graphSaveToPajekFormat (const QString &fileName, QString networkName="", int maxWidth=0, int maxHeight=0); bool graphSaveToAdjacencyFormat (const QString &fileName, const bool &saveEdgeWeights=true); bool graphSaveToGraphMLFormat (const QString &fileName, QString networkName="", int maxWidth=0, int maxHeight=0); bool graphSaveToDotFormat (QString fileName); int graphFileFormat() const; bool graphFileFormatExportSupported(const int &fileFormat) const; QString graphMatrixTypeToString(const int &matrixType) const; int graphMatrixStrToType(const QString &matrix) const; QString graphMetricTypeToString(const int &metricType) const; int graphMetricStrToType(const QString &metricStr) const; QString graphClusteringMethodTypeToString(const int &methodType) const; int graphClusteringMethodStrToType(const QString &method) const; /* RELATIONS */ int relations(); void relationsClear(); void relationAdd(const QString &relName, const bool &changeRelation=false); /* VERTICES */ int vertexNumberMax(); int vertexNumberMin(); int vertexDegreeOut(int); int vertexDegreeIn(int); QList vertexNeighborhoodList(const int &v1); // Only in Qt 5.15 // QSet vertexNeighborhoodSet(const int &v1); bool vertexIsolated(const int &v1) const; int vertexExists(const int &v1 ); int vertexExists(const QString &label); bool vertexFindByNumber (const QStringList &numList); bool vertexFindByLabel (const QStringList &labelList); bool vertexFindByIndexScore(const int &index, const QStringList &thresholds); void vertexRemove (const int &v1); void vertexSizeInit (const int); void vertexSizeSet(const int &v, const int &newsize); int vertexSize(const int &v) const; void vertexShapeSetDefault (const QString, const QString &iconPath=QString()); void vertexShapeSet(const int &v, const QString &shape, const QString &iconPath=QString()); QString vertexShape(const int &v); QString vertexShapeIconPath(const int &v); bool graphHasVertexCustomIcons () const { return m_graphHasVertexCustomIcons; } void vertexColorInit (const QString &color); void vertexColorSet(const int &v, const QString &color); QColor vertexColor(const int &v) const; void vertexNumberColorInit (const QString &color); void vertexNumberColorSet(const int &v=0, const QString &color = "#000000" ); void vertexNumberSizeInit (const int &size); void vertexNumberSizeSet(const int &v, const int &newsize ); void vertexNumberDistanceInit (const int &distance); void vertexNumberDistanceSet(const int &v, const int &newDistance ); void vertexLabelSet(const int &v, const QString &label); QString vertexLabel(const int &v) const; void vertexLabelsVisibilitySet(bool toggle); void vertexLabelSizeInit(int newSize); void vertexLabelSizeSet(const int &v, const int &labelSize ); void vertexLabelColorInit(QString color); void vertexLabelColorSet(const int &v1, const QString &color); void vertexLabelDistanceInit (const int &distance); void vertexLabelDistanceSet(const int &v, const int &newDistance ); void vertexLabelDistanceAllSet (const int &newDistance); void vertexPosSet(const int &v, const int &x, const int &y); QPointF vertexPos(const int &v1) const; int vertexClicked() const; int vertices(const bool &dropIsolates=false, const bool &countAll=false, const bool &recount=false) ; int vertexEdgesOutbound (int i) ; int vertexEdgesInbound (int i) ; int verticesWithOutboundEdges(); int verticesWithInboundEdges(); int verticesWithReciprocalEdges(); QList verticesListIsolated(); QList verticesList(); QSet verticesSet(); void verticesCreateSubgraph(QList vList, const int &type = SUBGRAPH_CLIQUE, const int ¢er = 0); /* EDGES */ int edgesEnabled(); MyEdge edgeClicked(); qreal edgeExists(const int &v1, const int &v2, const bool &checkReciprocal=false); void edgeRemove (const int &v1, const int &v2, const bool &removeOpposite=false); void edgeRemoveSelected (SelectedEdge &selectedEdge, const bool &removeOpposite); void edgeRemoveSelectedAll(); bool edgeSymmetric(const int &v1, const int &v2); void edgeTypeSet(const int &v1, const int &v2, const qreal &w, const int &dirType=EdgeType::Directed); void edgeWeightSet (const int &v1, const int &v2, const qreal &w, const bool &undirected=false); qreal edgeWeight(const int &v1, const int &v2) const; void edgeWeightNumbersVisibilitySet (const bool &toggle); void edgeLabelSet(const int &v1, const int &v2, const QString &label); QString edgeLabel (const int &v1, const int &v2) const; void edgeLabelsVisibilitySet (const bool &toggle); void edgeColorInit(const QString &); void edgeColorSet(const int &v1, const int &v2, const QString &color); QString edgeColor (const int &v1, const int &v2); bool edgeColorAllSet(const QString &color, const int &threshold=RAND_MAX); /* GRAPH methods */ void graphSetModified(const int &graphNewStatus, const bool&signalMW=true); bool graphIsModified() const ; bool graphSaved() const; bool graphLoaded() const; QList graphSelectedVertices() const; int graphSelectedVerticesCount() const; int graphSelectedVerticesMin() const; int graphSelectedVerticesMax() const; QList graphSelectedEdges() const; int graphSelectedEdgesCount() const; int graphGeodesics(); qreal graphDensity(); bool graphIsWeighted(); void graphSetWeighted(const bool &toggle=true); qreal graphReciprocity(); bool graphIsSymmetric(); void graphSymmetrize(); void graphSymmetrizeStrongTies(const bool &allRelations=false); void graphCocitation(); void graphDichotomization(const qreal threshold); void graphSetDirected(const bool &toggle=true, const bool &signalMW=true); void graphSetUndirected(const bool &toggle=true, const bool &signalMW=true); bool graphIsDirected(); bool graphIsUndirected(); bool graphIsConnected(); void graphMatrixAdjacencyCreate(const bool dropIsolates=false, const bool considerWeights=true, const bool inverseWeights=false, const bool symmetrize=false ); bool graphMatrixAdjacencyInvert(const QString &method="lu"); void graphMatrixSimilarityMatchingCreate(Matrix &AM, Matrix &SEM, const int &measure=METRIC_SIMPLE_MATCHING, const QString &varLocation="Rows", const bool &diagonal=false, const bool &considerWeights=true); void graphMatrixSimilarityPearsonCreate (Matrix &AM, Matrix &PCC, const QString &varLocation="Rows", const bool &diagonal=false); void graphMatrixDissimilaritiesCreate(Matrix &INPUT_MATRIX, Matrix &DSM, const int &metric, const QString &varLocation, const bool &diagonal, const bool &considerWeights); /* REPORT EXPORTS */ void setReportsDataDir(const QString &reportsDir); void setReportsRealNumberPrecision (const int & precision); void setReportsLabelLength(const int &length); void setReportsChartType(const int &type); void writeDataSetToFile(const QString dir, const QString ); void writeMatrixAdjacencyTo(QTextStream& os, const bool &saveEdgeWeights=true); void writeReciprocity( const QString fileName, const bool considerWeights=false); void writeMatrix(const QString &fileName, const int &matrix=MATRIX_ADJACENCY, const bool &considerWeights=true, const bool &inverseWeights=false, const bool &dropIsolates=false, const QString &varLocation="Rows", const bool &simpler=false); void writeMatrixHTMLTable(QTextStream &outText, Matrix &M, const bool &markDiag=true, const bool &plain=false, const bool &printInfinity=true, const bool &dropIsolates=false); void writeMatrixAdjacency(const QString fileName, const bool &markDiag=true); void writeMatrixAdjacencyPlot(const QString fileName, const bool &simpler=false); void writeMatrixAdjacencyInvert(const QString &filename, const QString &method); void writeMatrixLaplacianPlainText(const QString &filename); void writeMatrixDegreeText(const QString &filename); void writeMatrixDistancesPlainText(const QString &fn, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates); void writeMatrixShortestPathsPlainText(const QString &fn, const bool &considerWeights, const bool &inverseWeights); void writeMatrixDissimilarities(const QString fileName, const QString &metricStr, const QString &varLocation, const bool &diagonal, const bool &considerWeights) ; void writeMatrixSimilarityMatchingPlain(const QString fileName, const int &measure=METRIC_SIMPLE_MATCHING, const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false, const bool &considerWeights=true); void writeMatrixSimilarityMatching(const QString fileName, const QString &measure="Simple", const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false, const bool &considerWeights=true); void writeMatrixSimilarityPearson(const QString fileName, const bool considerWeights, const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false); void writeMatrixSimilarityPearsonPlainText(const QString fileName, const bool considerWeights, const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false); void writeEccentricity( const QString fileName, const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); // 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 writeCentralityEigenvector(const QString, const bool &weigths=true, const bool &inverseWeights = false, const bool &dropIsolates=false); 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); bool writeClusteringHierarchical(const QString &fileName, const QString &varLocation, const QString &matrix = "Adjacency", const QString &metric = "Manhattan", const QString &method = "Complete", const bool &diagonal = false, const bool &dendrogram = false, const bool &considerWeights=true, const bool &inverseWeights=false, const bool &dropIsolates=false); void writeClusteringHierarchicalResultsToStream(QTextStream& outText, const int N, const bool &dendrogram = false); bool writeCliqueCensus( const QString &fileName, const bool considerWeights); void writeClusteringCoefficient(const QString, const bool); void writeTriadCensus(const QString, const bool); /* DISTANCES, CENTRALITIES & PROMINENCE MEASURES */ int graphConnectednessFull (const bool updateProgress=false) ; bool graphReachable(const int &v1, const int &v2) ; void graphMatrixReachabilityCreate() ; int graphDiameter(const bool considerWeights, const bool inverseWeights); int graphDistanceGeodesic(const int &v1, const int &v2, const bool &considerWeights=false, const bool &inverseWeights=true); qreal graphDistanceGeodesicAverage(const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void graphDistancesGeodesic(const bool &computeCentralities=false, const bool &considerWeights=false, const bool &inverseWeights=true, const bool &dropIsolates=false); void graphMatrixDistanceGeodesicCreate(const bool &considerWeights=false, const bool &inverseWeights=true, const bool &dropIsolates=false); void graphMatrixShortestPathsCreate(const bool &considerWeights=false, const bool &inverseWeights=true, const bool &dropIsolates=false) ; int getProminenceIndexByName(const QString &prominenceIndexName); void prominenceDistribution(const int &index, const ChartType &type, const QString &distImageFileName=QString()); void prominenceDistributionBars(const H_StrToInt &discreteClasses, const QString &name, const QString &distImageFileName); void prominenceDistributionArea(const H_StrToInt &discreteClasses, const QString &name, const QString &distImageFileName); void prominenceDistributionSpline(const H_StrToInt &discreteClasses, const QString &seriesName, const QString &distImageFileName); void centralityDegree(const bool &weights=true, const bool &dropIsolates=false); void centralityInformation(const bool considerWeights=false, const bool inverseWeights=false); void centralityEigenvector(const bool &considerWeights=false, const bool &inverseWeights=false, const bool &dropIsolates=false); void centralityClosenessIR(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); void prestigeDegree(const bool &weights, const bool &dropIsolates=false); void prestigePageRank(const bool &dropIsolates=false); void prestigeProximity(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); /* REACHABILITY AND WALKS */ int walksBetween(int v1, int v2,int length); void graphWalksMatrixCreate(const int &N=0, const int &length=0, const bool &updateProgress=false); void writeWalksTotalMatrixPlainText(const QString &fn); void writeWalksOfLengthMatrixPlainText(const QString &fn, const int &length); void writeMatrixWalks (const QString &fn, const int &length=0, const bool &simpler=false); QList vertexinfluenceRange(int v1); QList vertexinfluenceDomain(int v2); void writeReachabilityMatrixPlainText( const QString &fn, const bool &dropIsolates=false); qreal numberOfTriples(int v1); /* CLIQUES, CLUSTERING, TRIADS */ void graphCliques(QSet R=QSet(), QSet P=QSet(), QSet X=QSet() ); void graphCliqueAdd (const QList &clique); int graphCliquesContaining(const int &actor, const int &size=0); int graphCliquesOfSize(const int &size ); bool graphClusteringHierarchical(Matrix &STR_EQUIV, const QString &varLocation, const int &metric, const int &method, const bool &diagonal=false, const bool &diagram=false, const bool &considerWeights=true, const bool &inverseWeights=false, const bool &dropIsolates=false); qreal clusteringCoefficientLocal(const int &v1); qreal clusteringCoefficient (const bool updateProgress=false); bool graphTriadCensus(); void triadType_examine_MAN_label(int, int, int, GraphVertex*, GraphVertex*, GraphVertex* ); // void eccentr_JordanCenter(); // TODO /* LAYOUTS */ void layoutRandom(); void layoutRadialRandom(const bool &guides=true); void layoutCircular(const double &x0, const double &y0, const double &newRadius, const bool &guides=false); void layoutByProminenceIndex ( int prominenceIndex, int layoutType, const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); void layoutVertexSizeByIndegree(); void layoutVertexSizeByOutdegree(); void layoutForceDirectedSpringEmbedder(const int maxIterations); void layoutForceDirectedFruchtermanReingold(const int maxIterations); void layoutForceDirectedKamadaKawai(const int maxIterations=500, const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false, const QString &initialPositions="current"); qreal graphDistanceEuclidean(const QPointF &a, const QPointF &b); qreal graphDistanceEuclidean(const QPointF &a); int sign(const qreal &D); qreal layoutForceDirected_F_rep(const QString model, const qreal &dist, const qreal &optimalDistance); qreal layoutForceDirected_F_att(const QString model, const qreal &dist, const qreal &optimalDistance) ; void layoutForceDirected_Eades_moveNodes(const qreal &c4); void layoutForceDirected_FR_moveNodes(const qreal &temperature) ; qreal layoutForceDirected_FR_temperature(const int iteration) const; qreal computeOptimalDistance(const int &V); void compute_angles( const QPointF &Delta, const qreal &dist, qreal &angle1, qreal &angle2, qreal °rees1, qreal °rees2 ); /* CRAWLER */ void webCrawlTerminateThreads (QString reason); /**RANDOM NETWORKS*/ void randomizeThings(); void randomNetErdosCreate ( const int &N, const QString &model, const int &m, const qreal &p, const QString &mode, const bool &diag); void randomNetScaleFreeCreate (const int &N, const int &power, const int &m0, const int &m, const qreal &alpha, const QString &mode); void randomNetSmallWorldCreate(const int &N, const int °ree, const double &beta, const QString &mode); void randomNetRingLatticeCreate (const int &N, const int °ree, const bool updateProgress=false); void randomNetRegularCreate (const int &N, const int °ree, const QString &mode, const bool &diag); void randomNetLatticeCreate(const int &N, const int &length, const int &dimension, const int &neighborhoodLength, const QString &mode, const bool &circular=false); int factorial (int); /** vpos 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 vpos; // Stores the number of vertices at distance n from a given vertex H_f_i 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 */ VList m_graph; GraphicsWidget *m_canvas; Parser *file_parser; //file loader threaded class. WebCrawler_Parser *wc_parser; WebCrawler_Spider *wc_spider; /** private member functions */ void edgeAdd (const int &v1, const int &v2, const qreal &weight, const int &type, const QString &label, const QString &color ); /** methods used by graphDistancesGeodesic() */ void BFS(const int &s, const int &si, const bool &computeCentralities=false, const bool &dropIsolates=false); void dijkstra(const int &s, const int &si, const bool &computeCentralities=false, const bool &inverseWeights=false, const bool &dropIsolates=false); void minmax(qreal C, GraphVertex *v, qreal &max, qreal &min, int &maxNode, int &minNode ); void resolveClasses ( qreal C, H_StrToInt &discreteClasses, int &classes); void resolveClasses ( qreal C, H_StrToInt &discreteClasses, int &classes, int name); QList m_relationsList; QList m_graphFileFormatExportSupported; QList triadTypeFreqs; //stores triad type frequencies QList m_verticesList; QList m_verticesIsolatedList; QList m_verticesSelected; QSet m_verticesSet; QList m_selectedEdges; QMultiHash influenceRanges, influenceDomains; QMultiHash m_vertexPairsNotConnected; QHash m_vertexPairsUnilaterallyConnected; QMultiMap m_cliques; QHash > neighboursHash; QList m_clusteringLevel; QMap m_clustersPerSequence; QMap m_clustersByName; QMap m_clusterPairNamesPerSeq; Matrix SIGMA, DM, sumM, invAM, AM, invM, WM; Matrix XM, XSM, XRM, CLQM; stack Stack; /** used in resolveClasses and graphDistancesGeodesic() */ H_StrToInt discreteDPs, discreteSDCs, discreteCCs, discreteBCs, discreteSCs; H_StrToInt discreteIRCCs, discreteECs, discreteEccentricities; H_StrToInt discretePCs, discreteICs, discretePRPs, discretePPs, discreteEVCs; QString m_reportsDataDir; int m_reportsRealPrecision; int m_reportsLabelLength; ChartType m_reportsChartType; int m_fieldWidth, m_curRelation, m_fileFormat, m_vertexClicked; MyEdge m_clickedEdge; qreal edgeWeightTemp, edgeReverseWeightTemp; qreal meanSDC, varianceSDC; qreal meanSCC, varianceSCC; qreal meanIRCC, varianceIRCC; qreal meanSBC, varianceSBC; qreal meanSSC, varianceSSC; qreal meanEC, varianceEC; qreal meanSPC, varianceSPC; qreal meanIC, varianceIC; qreal meanEVC, varianceEVC; qreal meanSDP, varianceSDP; qreal meanPP, variancePP; qreal meanPRP, variancePRP; qreal minEccentricity, maxEccentricity; qreal minSDP, maxSDP, sumDP, sumSDP, groupDP; qreal minSDC, maxSDC, sumDC, sumSDC, groupDC; qreal minSCC, maxSCC, nomSCC, denomSCC, sumCC, sumSCC, groupCC, maxIndexCC; qreal minIRCC, maxIRCC, nomIRCC, denomIRCC, sumIRCC, groupIRCC; qreal minSBC, maxSBC, nomSBC, denomSBC, sumBC, sumSBC, groupSBC, maxIndexBC; qreal minSPC, maxSPC, nomSPC, denomSPC, t_sumIC, sumSPC, groupSPC, maxIndexPC; qreal minSSC, maxSSC, sumSC, sumSSC, groupSC, maxIndexSC; qreal minEC, maxEC, nomEC, denomEC, sumEC, groupEC, maxIndexEC; qreal minIC, maxIC, nomIC, denomIC, sumIC, maxIndexIC; qreal minEVC, maxEVC, nomEVC, denomEVC, sumEVC, sumSEVC, groupEVC; qreal minPRP, maxPRP, nomPRC, denomPRC, sumPC, t_sumPRP, sumPRP; qreal minPP, maxPP, nomPP, denomPP, sumPP, groupPP; qreal minCLC, maxCLC, averageCLC,varianceCLC, d_factor; int maxNodeCLC, minNodeCLC; int classesSDP, maxNodeDP, minNodeDP; int classesSDC, maxNodeSDC, minNodeSDC; int classesSCC, maxNodeSCC, minNodeSCC; int classesIRCC, maxNodeIRCC, minNodeIRCC; int classesSBC, maxNodeSBC, minNodeSBC; int classesSPC, maxNodeSPC, minNodeSPC; int classesSSC, maxNodeSSC, minNodeSSC; int classesEC, maxNodeEC, minNodeEC; int classesEccentricity, maxNodeEccentricity, minNodeEccentricity; int classesIC, maxNodeIC, minNodeIC; int classesPRP, maxNodePRP, minNodePRP; int classesPP, maxNodePP, minNodePP; int classesEVC, maxNodeEVC, minNodeEVC; qreal sizeOfComponent; /** General & initialisation variables */ int m_graphHasChanged; int m_totalVertices, m_totalEdges, m_graphDiameter, initVertexSize; int initVertexLabelSize, initVertexNumberSize; int initVertexNumberDistance, initVertexLabelDistance; bool order; bool initEdgeWeightNumbers, initEdgeLabels; qreal m_graphAverageDistance, m_graphGeodesicsCount; qreal m_graphDensity; qreal m_graphSumDistance; qreal m_graphReciprocityArc, m_graphReciprocityDyad; int m_graphReciprocityTiesReciprocated; int m_graphReciprocityTiesNonSymmetric; int m_graphReciprocityTiesTotal; int m_graphReciprocityPairsReciprocated; int m_graphReciprocityPairsTotal; bool m_graphHasVertexCustomIcons; int outboundEdgesVert, inboundEdgesVert, reciprocalEdgesVert; //int timerId; qreal canvasWidth, canvasHeight; bool calculatedEdges; bool calculatedVertices, calculatedVerticesList, calculatedVerticesSet; bool calculatedAdjacencyMatrix, calculatedDistances, calculatedCentralities; bool calculatedIsolates; bool calculatedEVC; bool calculatedDP, calculatedDC, calculatedPP; bool calculatedIRCC, calculatedIC, calculatedPRP; bool calculatedTriad; bool calculatedGraphSymmetry, calculatedGraphReciprocity; bool calculatedGraphDensity, calculatedGraphWeighted; bool m_graphIsDirected, m_graphIsSymmetric, m_graphIsWeighted, m_graphIsConnected; int csRecDepth; QString VERSION, fileName, m_graphName, initEdgeColor, initVertexColor, initVertexNumberColor, initVertexLabelColor; QString initVertexShape, initVertexIconPath; QString htmlHead, htmlHeadLight, htmlEnd; QDateTime actualDateTime; }; #endif app-2.8/src/graphicsedge.cpp000077500000000000000000000521451377436340000161040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsedge.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "global.h" #include "graphicswidget.h" #include "graphicsedge.h" #include "graphicsnode.h" #include "graphicsedgeweight.h" #include "graphicsedgelabel.h" // #include SOCNETV_USE_NAMESPACE GraphicsEdge::GraphicsEdge(GraphicsWidget *gw, GraphicsNode *from, GraphicsNode *to, const qreal &weight, const QString &label, const QString &color, const Qt::PenStyle &style, const int &type, const bool &drawArrows, const bool &bezier, const bool &weightNumbers, const bool &highlighting) : graphicsWidget(gw) { graphicsWidget->scene()->addItem(this); //add edge to scene to be displayed from->addOutLink( this ); // adds this new edge to sourceNode to->addInLink( this ); // adds this new edge to targetNode source=from; // saves the source node target=to; // saves the target node m_style = style; m_state = EDGE_STATE_REGULAR ; m_color=QColor(color); // saves the color of the edge m_drawArrows=drawArrows; // controls if edge will have arrows or not m_edgeDirType=type; m_minOffsetFromNode = 6; // controls the minimum offset from source/target node center m_offsetFromSourceNode=source->size()+m_minOffsetFromNode; // offsets edge from the centre of source node m_offsetFromTargetNode=target->size()+m_minOffsetFromNode; // offsets edge from the centre of target node m_arrowSize=4; // controls the width of the edge arrow m_weight = weight ; // saves the weight/value of this edge if ( fabs(m_weight) > 1 ) { m_width = 1+log ( 1+ log(fabs(m_weight) )) ; } else { m_width = fabs(m_weight) ; } m_Bezier = bezier; // controls if it will appear as line or curve m_label = label; m_drawLabel = !m_label.isEmpty(); m_drawWeightNumber = weightNumbers; // controls if weight number will be shown qDebug()<< "GraphicsEdge::GraphicsEdge(): " << source->nodeNumber() << "->" << target->nodeNumber() <<" = " << m_weight <<" label " << m_label <<" edgeType " << m_edgeDirType; if (m_drawWeightNumber) { addWeightNumber(); } if (m_drawLabel) addLabel(); m_hoverHighlighting = highlighting; setAcceptHoverEvents(true); setFlags(QGraphicsItem::ItemIsSelectable); //Edges have lower z than nodes. Nodes always appear above edges. setZValue(ZValueEdge); setBoundingRegionGranularity(0); // When using QGraphicsItem::ItemCoordinateCache // we should unset it before exporting canvas content to PDF. //setCacheMode (QGraphicsItem::ItemCoordinateCache); //setCacheMode(QGraphicsItem::DeviceCoordinateCache); adjust(); } void GraphicsEdge::showArrows(const bool &drawArrows){ prepareGeometryChange(); m_drawArrows=drawArrows; adjust(); } void GraphicsEdge::removeRefs(){ qDebug("GraphicsEdge: removeRefs()"); source->deleteOutLink(this); target->deleteInLink(this); } void GraphicsEdge::setColor( const QString &str) { m_color=QColor(str); prepareGeometryChange(); } QColor GraphicsEdge::color() const{ return m_color; } /** * @brief Called from Graph::graphSaveToPajekFormat() * @return */ QString GraphicsEdge::colorToPajek() { QString m_colorStr = m_color.name(); if (m_colorStr.startsWith("#")) { return ("RGB"+m_colorStr.right( m_colorStr.size()-1 )).toUpper() ; } return m_colorStr; } /** * @brief Called from MW when user wants to change an edge's weight. Updates both the width and the weightNumber * @param w */ void GraphicsEdge::setWeight(const qreal &w) { qDebug() << "GraphicsEdge::setWeight() " << w; prepareGeometryChange(); m_weight = w; if ( fabs(m_weight) > 1 ) { m_width = 1+log ( 1+ log(fabs(m_weight) )) ; } else { m_width = fabs(m_weight) ; } if (m_drawWeightNumber) weightNumber->setPlainText (QString::number(w)); } /** * @brief Returns the weight/value of this edge * @return */ qreal GraphicsEdge::weight() const { qDebug() << "GraphicsEdge::weight() " << m_weight; return m_weight; } void GraphicsEdge::addWeightNumber (){ // create edge weight item double x = -20 + ( source->x() + target->x() ) / 2.0; double y = -20 + ( source->y() + target->y() ) / 2.0; weightNumber = new GraphicsEdgeWeight (this, 7, QString::number(m_weight) ); weightNumber-> setPos(x,y); weightNumber-> setDefaultTextColor (m_color); m_drawWeightNumber = true; } /** * @brief Toggles visibility of weight numbers * @param toggle */ void GraphicsEdge::setWeightNumberVisibility (const bool &toggle) { if (m_drawWeightNumber) { if (toggle) weightNumber ->show(); else weightNumber ->hide(); } else{ if (toggle) addWeightNumber(); } } /** * @brief Called from MW when user wants to change an edge's label * @param label */ void GraphicsEdge::setLabel(const QString &label) { qDebug() << "GraphicsEdge::setLabel() " << label; prepareGeometryChange(); m_label = label; if (m_drawLabel) edgeLabel->setPlainText (m_label); } QString GraphicsEdge::label() const { return m_label; } void GraphicsEdge::addLabel (){ // create edge label item double x = 5+ ( source->x() + target->x() ) / 2.0; double y = 5+ ( source->y() + target->y() ) / 2.0; edgeLabel = new GraphicsEdgeLabel (this, 7, m_label ); edgeLabel-> setPos(x,y); edgeLabel-> setDefaultTextColor (m_color); m_drawLabel = true; } void GraphicsEdge::setLabelVisibility (const bool &toggle) { if (m_drawLabel) { if (toggle) edgeLabel ->show(); else edgeLabel ->hide(); } else{ if (toggle) addLabel(); } } GraphicsNode *GraphicsEdge::sourceNode() const { return source; } void GraphicsEdge::setSourceNode(GraphicsNode *node) { source = node; adjust(); } /** * @brief Called from graphicsNode to update edge offset from source node (i.e. when node size changes) * @param offset */ void GraphicsEdge::setSourceNodeSize(const int &size){ m_offsetFromSourceNode=size + m_minOffsetFromNode; adjust(); } /** * Returns the source node number * @return int */ int GraphicsEdge::sourceNodeNumber () { return source->nodeNumber(); } GraphicsNode *GraphicsEdge::targetNode() const { return target; } void GraphicsEdge::setTargetNode(GraphicsNode *node){ target = node; adjust(); } /** * @brief Called from graphicsNode to update edge offset from target node (i.e. when node size changes) * @param offset */ void GraphicsEdge::setTargetNodeSize(const int & size){ m_offsetFromTargetNode=size + m_minOffsetFromNode; adjust(); } /** * Returns the target node number * @return int */ int GraphicsEdge::targetNodeNumber() { return target->nodeNumber(); } /** * @brief Updates Minimum Offset From Node and calls adjust to update the edge * @param offset */ void GraphicsEdge::setMinimumOffsetFromNode(const int &offset) { m_minOffsetFromNode = offset; m_offsetFromTargetNode = target->size() + m_minOffsetFromNode; adjust(); } qreal GraphicsEdge::dx() const { return target->x() - source->x(); } qreal GraphicsEdge::dy() const { return target->y() - source->y(); } qreal GraphicsEdge::length() const { return sqrt ( dx() * dx() + dy() * dy() ) ; } /** * @brief Leaves some empty space (offset) from node - * make the edge weight appear on the centre of the edge */ void GraphicsEdge::adjust(){ // qDebug() << "GraphicsEdge::adjust()"; if (!source || !target) { return; } // QLineF line(source->x(), source->y(), target->x(), target->y()); //QPointF edgeOffset; //line_length = line.length(); // line_dx = line.dx(); // line_dy = line.dy(); line_length = length(); line_dx = dx(); line_dy = dy(); if (source!=target) { edgeOffset = QPointF( (line_dx * m_offsetFromTargetNode) / line_length, (line_dy * m_offsetFromTargetNode) / line_length); } else edgeOffset = QPointF(0, 0); prepareGeometryChange(); // sourcePoint = line.p1() + edgeOffset ; // targetPoint = line.p2() - edgeOffset ; sourcePoint = source->pos() + edgeOffset ; targetPoint = target->pos() - edgeOffset ; if (m_drawWeightNumber) { weightNumber->setPos( -20 + (source->x()+target->x())/2.0, -20+ (source->y()+target->y())/2.0 ); } if (m_drawLabel) { edgeLabel->setPos( 5+ (source->x()+target->x())/2.0, 5+ (source->y()+target->y())/2.0 ); } //Define the path upon which we' ll draw the line QPainterPath path(sourcePoint); //Construct the path if (source!=target) { if ( !m_Bezier){ // qDebug()<< "*** GraphicsEdge::paint(). Constructing a line"; path.lineTo(targetPoint); } else { qDebug() << "*** GraphicsEdge::paint(). Constructing a bezier curve"; QPointF c = QPointF( targetPoint.x() - sourcePoint.x(), targetPoint.y() - targetPoint.y()); path.cubicTo( sourcePoint, c, targetPoint); } } else { //self-link QPointF c1 = QPointF( targetPoint.x() -30, targetPoint.y() -30 ); QPointF c2 = QPointF( targetPoint.x() +30, targetPoint.y() -30 ); // qDebug()<< "*** GraphicsEdge::paint(). Constructing a bezier self curve c1 " // < 10) { angle = 0; if ( line_length > 0 ) angle = ::acos( line_dx / line_length ); // qDebug() << " acos() " << ::acos( line_dx / line_length ) ; if ( line_dy >= 0) angle = M_PI_X_2 - angle; // qDebug() << "*** GraphicsEdge::paint(). Constructing arrows. " // "First Arrow at target node" // << "target-source: " << line_dx // << " length: " << line_length // << " angle: "<< angle; QPointF destArrowP1 = targetPoint + QPointF(sin(angle - M_PI_3) * m_arrowSize, cos(angle - M_PI_3) * m_arrowSize); QPointF destArrowP2 = targetPoint + QPointF(sin(angle - M_PI + M_PI_3) * m_arrowSize, cos(angle - M_PI + M_PI_3) * m_arrowSize); // qDebug() << "*** GraphicsEdge::paint() destArrowP1 " // << destArrowP1.x() << "," << destArrowP1.y() // << " destArrowP2 " << destArrowP2.x() << "," << destArrowP2.y(); path.addPolygon ( QPolygonF() << targetPoint << destArrowP1 << destArrowP2 << targetPoint ); if (m_edgeDirType == EdgeType::Undirected || m_edgeDirType == EdgeType::Reciprocated ) { // qDebug() << "**** GraphicsEdge::paint() This edge is SYMMETRIC! " // << " So, we need to create Arrow at src node as well"; QPointF srcArrowP1 = sourcePoint + QPointF(sin(angle +M_PI_3) * m_arrowSize, cos(angle +M_PI_3) * m_arrowSize); QPointF srcArrowP2 = sourcePoint + QPointF(sin(angle +M_PI - M_PI_3) * m_arrowSize, cos(angle +M_PI - M_PI_3) * m_arrowSize); path.addPolygon ( QPolygonF() << sourcePoint << srcArrowP1 << srcArrowP2 < B */ void GraphicsEdge::setDirectionType(const int &dirType){ qDebug()<< "GraphicsEdge::setDirectionType() - " << source->nodeNumber() << "->" << target->nodeNumber() << "direction type" << dirType; prepareGeometryChange(); m_edgeDirType = dirType; m_drawArrows = true; if (m_edgeDirType==EdgeType::Undirected) { m_drawArrows = false; } adjust(); } /** * @brief returns the direction type of this edge * @return */ int GraphicsEdge::directionType() { return m_edgeDirType ; } void GraphicsEdge::setStyle( const Qt::PenStyle &style ) { m_style = style; } Qt::PenStyle GraphicsEdge::style() const{ return m_style; } /** * @brief GraphicsEdge::pen * @return */ QPen GraphicsEdge::pen() const { //qDebug() << "GraphicsEdge::pen() - returning pen " ; switch (m_state) { case EDGE_STATE_REGULAR: //qDebug() << "GraphicsEdge::pen() - returning pen for state REGULAR" ; if (m_weight < 0 ){ return QPen(m_color, m_width, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin); } return QPen(m_color, m_width, m_style, Qt::RoundCap, Qt::RoundJoin); break; case EDGE_STATE_HIGHLIGHT: // selected //qDebug() << "GraphicsEdge::pen() - returning pen for state HIGHLIGHTED" ; return QPen( QColor("red"), m_width, m_style, Qt::RoundCap, Qt::RoundJoin); case EDGE_STATE_HOVER: // hover //qDebug() << "GraphicsEdge::pen() - returning pen for state HOVER" ; return QPen(QColor("red"), m_width+1, m_style, Qt::RoundCap, Qt::RoundJoin); default: //qDebug() << "GraphicsEdge::pen() - returning pen for state DEFAULT" ; return QPen(m_color, m_width, m_style, Qt::RoundCap, Qt::RoundJoin); } } /** * @brief GraphicsEdge::setState * @param state */ void GraphicsEdge::setState(const int &state) { //NOTE: DO NOT USE HERE: prepareGeometryChange() m_state=state; } /** * @brief GraphicsEdge::paint * @param painter * @param option */ void GraphicsEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){ if (!source || !target) return; //qDebug() <<"@@@ GraphicsEdge::paint() on" << painter->paintEngine()->type(); //painter->setClipRect(); //if the edge is being dragged around, darken it! if (option->state & QStyle::State_Selected) { //setZValue(ZValueEdgeHighlighted); setState(EDGE_STATE_HOVER); } else if (option->state & QStyle::State_MouseOver) { if (m_hoverHighlighting) { setZValue(ZValueEdgeHighlighted); setState(EDGE_STATE_HOVER); } } else if (m_state==EDGE_STATE_HIGHLIGHT){ if (m_hoverHighlighting) { setZValue(ZValueEdgeHighlighted); setState(EDGE_STATE_HIGHLIGHT); } } else { setZValue(ZValueEdge); setState(EDGE_STATE_REGULAR); } // set painter pen to correct edge pen painter->setPen(pen()); // set painter brush to paint inside the arrow painter->setBrush( m_color ); painter->drawPath(m_path); } /** * @brief Called when the edge changes, i.e. moves, becomes disabled or changes its visibility * @param change * @param value * @return */ QVariant GraphicsEdge::itemChange(GraphicsItemChange change, const QVariant &value){ switch (change) { case ItemPositionHasChanged: { break; } case ItemEnabledHasChanged: { break; } case ItemSelectedHasChanged: { if (value.toBool()) { setZValue(ZValueEdgeHighlighted); setHighlighted(true); //source->setSelected(true); //target->setSelected(true); } else{ setZValue(ZValueEdge); setHighlighted(false); //source->setSelected(false); //target->setSelected(false); } break; } case ItemVisibleHasChanged: { break; } default: { break; } }; return QGraphicsItem::itemChange(change, value); } /** * @brief Returns the width of the edge as a function of edge weight * @return */ qreal GraphicsEdge::width() const{ // if ( fabs(m_weight) > 1 ) { // return 1+log ( 1+ log(fabs(m_weight) )) ; // } // return fabs(m_weight) ; return m_width; } /** * @brief Called from GraphicsNode to change the edge state and highlight it. * This is done, when the user hovers over the node. * @param flag */ void GraphicsEdge::setHighlighted(const bool &flag) { //qDebug()<< "GraphicsEdge::setHighlighted() - " << flag; if (flag && m_hoverHighlighting) { prepareGeometryChange(); setState(EDGE_STATE_HIGHLIGHT); } else { prepareGeometryChange(); setState(EDGE_STATE_REGULAR); } } /** * @brief Toggles edge highlighting on or off * @param toggle */ void GraphicsEdge::setHighlighting(const bool &toggle) { m_hoverHighlighting = toggle; } /** * @brief handles the events of a click on an edge * @param event */ void GraphicsEdge::mousePressEvent(QGraphicsSceneMouseEvent *e) { qDebug() << "GraphicsEdge::mousePressEvent() - click on an edge "; //setClicked(); QGraphicsItem::mousePressEvent(e); } GraphicsEdge::~GraphicsEdge(){ qDebug() << "GraphicsEdge::~GraphicsEdge() - self-destructing edge " << sourceNodeNumber()<< "->" << targetNodeNumber(); removeRefs(); if (m_drawWeightNumber) graphicsWidget->removeItem(weightNumber); if (m_drawLabel) graphicsWidget->removeItem(edgeLabel); this->hide(); graphicsWidget->removeItem(this); } app-2.8/src/graphicsedge.h000077500000000000000000000116641377436340000155520ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsedge.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSEDGE_H #define GRAPHICSEDGE_H #include #include #include //declares pair construct class GraphicsWidget; class QGraphicsSceneMouseEvent; class GraphicsNode; class GraphicsEdgeWeight; class GraphicsEdgeLabel; QT_USE_NAMESPACE using namespace std; static const int TypeEdge= QGraphicsItem::UserType+2; static const int ZValueEdge = 50; static const int ZValueEdgeHighlighted = 99; static const int EDGE_STATE_REGULAR = 0; static const int EDGE_STATE_HIGHLIGHT = 1; static const int EDGE_STATE_HOVER = 2; class GraphicsEdge : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: GraphicsEdge(GraphicsWidget *, GraphicsNode*, GraphicsNode*, const qreal &weight, const QString &label, const QString &color, const Qt::PenStyle &style, const int&type, const bool & drawArrows, const bool &bezier, const bool &weightNumbers=false, const bool &highlighting=true); ~GraphicsEdge(); enum { Type = UserType + 2 }; int type() const { return Type; } GraphicsNode *sourceNode() const; void setSourceNode(GraphicsNode *node); GraphicsNode *targetNode() const; void setTargetNode(GraphicsNode *node); int sourceNodeNumber(); int targetNodeNumber(); qreal dx() const; qreal dy() const; qreal length() const; void setSourceNodeSize(const int & size); void setTargetNodeSize(const int & size); void setMinimumOffsetFromNode(const int & offset); void removeRefs(); void setWeight( const qreal &w) ; qreal weight() const; void addWeightNumber (); //void deleteWeightNumber(); void setWeightNumberVisibility (const bool &toggle); void setLabel( const QString &label) ; QString label() const; void addLabel(); //void deleteLabel(); void setLabelVisibility (const bool &toggle); void showArrows(const bool &); void setDirectionType(const int &dirType=0); int directionType(); qreal width() const; QPen pen() const; void setState(const int &state); void setStyle( const Qt::PenStyle &style); Qt::PenStyle style() const; void setColor( const QString &str) ; //QString color() const ; QColor color() const; QString colorToPajek(); void setHighlighted (const bool &flag); void setHighlighting (const bool &toggle); QPainterPath shape() const; public slots: void adjust (); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QVariant itemChange(GraphicsItemChange change, const QVariant &value); void mousePressEvent(QGraphicsSceneMouseEvent *event); private: GraphicsWidget *graphicsWidget; GraphicsNode *source, *target; GraphicsEdgeWeight* weightNumber; GraphicsEdgeLabel* edgeLabel; QPainterPath m_path; QPointF sourcePoint, targetPoint; QPointF edgeOffset; qreal m_arrowSize; qreal m_minOffsetFromNode; qreal m_offsetFromTargetNode, m_offsetFromSourceNode; Qt::PenStyle m_style; int m_state; QString m_colorNegative, m_label; QColor m_color; qreal m_weight, m_width; int m_edgeDirType; qreal angle, line_length, line_dx, line_dy; bool m_Bezier, m_drawArrows, m_drawWeightNumber; bool m_drawLabel, m_hoverHighlighting; bool m_isClicked; }; #endif app-2.8/src/graphicsedgelabel.cpp000077500000000000000000000041271377436340000171010ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsedgelabel.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "graphicsedgelabel.h" #include "graphicsedge.h" #include #include GraphicsEdgeLabel::GraphicsEdgeLabel( GraphicsEdge *link , int size, QString labelText) : QGraphicsTextItem( 0) { qDebug()<< "GraphicsEdgeLabel:: creating new edgelabel and attaching it to link"; setPlainText( labelText ); setParentItem(link); //auto disables child items like this, when link is disabled. this->setFont( QFont ("Courier", size, QFont::Light, true) ); setZValue(ZValueEdgeLabel); } GraphicsEdgeLabel::~GraphicsEdgeLabel() { } app-2.8/src/graphicsedgelabel.h000077500000000000000000000037731377436340000165540ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsedgelabel.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSEDGELABEL_H #define GRAPHICSEDGELABEL_H #include class GraphicsEdge; static const int TypeEdgeLabel = QGraphicsItem::UserType+6; static const int ZValueEdgeLabel = 80; class GraphicsEdgeLabel: public QGraphicsTextItem { public: GraphicsEdgeLabel(GraphicsEdge * , int, QString); void removeRefs(); enum { Type = UserType + 6 }; int type() const { return Type; } ~GraphicsEdgeLabel(); private: }; #endif app-2.8/src/graphicsedgeweight.cpp000077500000000000000000000041401377436340000173040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsedgeweight.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "graphicsedgeweight.h" #include "graphicsedge.h" #include #include GraphicsEdgeWeight::GraphicsEdgeWeight( GraphicsEdge *link , int size, QString labelText) : QGraphicsTextItem( 0) { qDebug()<< "GraphicsEdgeWeight:: creating new edgeweight and attaching it to link"; setPlainText( labelText ); setParentItem(link); //auto disables child items like this, when link is disabled. this->setFont( QFont ("Courier", size, QFont::Light, true) ); setZValue(ZValueEdgeWeight); } GraphicsEdgeWeight::~GraphicsEdgeWeight() { } app-2.8/src/graphicsedgeweight.h000077500000000000000000000040031377436340000167470ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsedgeweight.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSEDGEWEIGHT_H #define GRAPHICSEDGEWEIGHT_H #include class GraphicsEdge; static const int TypeEdgeWeight = QGraphicsItem::UserType+5; static const int ZValueEdgeWeight = 80; class GraphicsEdgeWeight: public QGraphicsTextItem { public: GraphicsEdgeWeight(GraphicsEdge * , int, QString); void removeRefs(); enum { Type = UserType + 5 }; int type() const { return Type; } ~GraphicsEdgeWeight(); private: }; #endif app-2.8/src/graphicsguide.cpp000077500000000000000000000067161377436340000163000ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsguide.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "graphicsguide.h" #include "graphicswidget.h" GraphicsGuide::GraphicsGuide ( GraphicsWidget *gw, const double &x0, const double &y0, const double &radius ) : graphicsWidget ( gw ){ graphicsWidget->scene()->addItem ( this ); m_radius=radius; setZValue(ZValueGuide); circle=true; setPos(x0, y0); } GraphicsGuide::GraphicsGuide ( GraphicsWidget *gw, const double &y0, const int &width) : graphicsWidget ( gw ){ graphicsWidget->scene()->addItem ( this ); setPos(0, y0); m_width= width; setZValue(ZValueGuide); circle=false; } double GraphicsGuide::radius() { return m_radius; } bool GraphicsGuide::isCircle() { return (circle); } void GraphicsGuide::setCircle(const QPointF ¢er, const double &radius ) { setPos(center); m_radius=radius; circle = true; update(); } void GraphicsGuide::setHorizontalLine(const QPointF &origin, const int &width){ setPos(origin); m_width= width; circle=false; update(); } int GraphicsGuide::width() { return m_width; } /** Returns the bounding rectangle of the background circle*/ QRectF GraphicsGuide::boundingRect() const { if (circle) { return QRectF ( - m_radius-1, - m_radius-1, + 2 * m_radius + 1, + 2* m_radius +1 ); } else { return QRectF ( 1, -1, m_width, + 1 ); } } void GraphicsGuide::paint ( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * ){ Q_UNUSED(option); painter->setPen ( QPen ( QColor ( "red" ), 1, Qt::DotLine ) ); if (circle) { painter->drawEllipse ( QPointF(0,0), m_radius, m_radius ); } else { painter->drawLine ( 0 , 0, m_width , 0); } } void GraphicsGuide::die (){ this->prepareGeometryChange(); this->hide(); this->update(); graphicsWidget->scene()->removeItem(this); this->update(); } app-2.8/src/graphicsguide.h000077500000000000000000000050641377436340000157400ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsguide.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSGUIDE_H #define GRAPHICSGUIDE_H #include #include class GraphicsWidget; static const int TypeGuide = QGraphicsItem::UserType+7; static const int ZValueGuide = 10; class GraphicsGuide : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: GraphicsGuide(GraphicsWidget *, const double &x0, const double &y0, const double &radius ); GraphicsGuide(GraphicsWidget *, const double &y0, const int &width); bool isCircle(); void setCircle(const QPointF ¢er, const double &radius) ; void setHorizontalLine(const QPointF &origin, const int &width) ; double radius(); int width(); enum { Type = UserType + 7 }; int type() const { return Type; } void die(); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: GraphicsWidget *graphicsWidget; double m_radius; int m_width; bool circle; }; #endif app-2.8/src/graphicsnode.cpp000077500000000000000000000545721377436340000161330ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsnode.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "graphicsnode.h" #include #include #include #include #include #include #include "graphicswidget.h" #include "graphicsedge.h" #include "graphicsnodelabel.h" #include "graphicsnodenumber.h" GraphicsNode::GraphicsNode ( GraphicsWidget* gw, const int &num, const int &size, const QString &color, const QString &shape, const QString &iconPath, const bool &showNumbers, const bool &numbersInside, const QString &numberColor, const int &numberSize, const int &numDistance, const bool &showLabels, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance, const bool &edgeHighlighting, QPointF p ) : graphicsWidget (gw) { qDebug()<<"GraphicsNode::GraphicsNode() - New node:"<< num << "initializing..."; graphicsWidget->scene()->addItem(this); //Without this nodes don't appear on the screen... setFlags(ItemSendsGeometryChanges | ItemIsSelectable | ItemIsMovable ); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events //setCacheMode(QGraphicsItem::ItemCoordinateCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events setCacheMode(QGraphicsItem::NoCache); setAcceptHoverEvents(true); m_num=num; m_size=size; m_shape=shape; m_iconPath = iconPath; m_col_str=color; m_col=QColor(color); m_hasNumber=showNumbers; m_hasNumberInside = numbersInside; m_numSize = numberSize; m_numColor = numberColor; m_numberDistance=numDistance; m_hasLabel=showLabels; m_labelText = label; m_labelSize = labelSize; m_labelColor = labelColor; m_labelDistance=labelDistance; if (m_hasLabel) { addLabel(); } if (!m_hasNumberInside && m_hasNumber) { addNumber(); } m_edgeHighLighting = edgeHighlighting; setZValue(ZValueNode); setShape(m_shape,m_iconPath); setPos(p); qDebug()<< "GraphicsNode::GraphicsNode() - Created at pos:" << x()<<","<setTargetNodeSize(size); } foreach (GraphicsEdge *edge, outEdgeList) { qDebug("GraphicsNode: updating edges in outEdgeList"); edge->setSourceNodeSize(size); } setShape(m_shape); } /** * @brief Returns the esoteric size of the node. * Used by GraphicsEdge::GraphicsEdge() * @return */ int GraphicsNode::size() const{ return m_size; } /** * @brief Sets the shape of the node and prepares the corresponding QPainterPath * m_path which will be drawn by our painter in GraphicsNode::paint(). * The only exception is when the shape is 'custom'. In that case, the painter * will paint a pixmap with the custom node icon (loaded from iconPath). * However, even in that case we are still creating a QPainterPath, because this * is needed by QGraphicsNode::shape() function which is responsible for collision * detection and needs to return a path with an accurate outline of the item's shape. * Called every time the user needs to change the shape of an node. * @param shape */ void GraphicsNode::setShape(const QString shape, const QString &iconPath) { prepareGeometryChange(); m_shape=shape; qDebug()<< "GraphicsNode::setShape() - Node:" << nodeNumber() << "shape:" << m_shape << "iconPath" << iconPath << "pos:"<< x() << "," << y(); QPainterPath path; if ( m_shape == "circle") { path.addEllipse (-m_size, -m_size, 2*m_size, 2*m_size); } else if ( m_shape == "ellipse") { path.addEllipse(-m_size, -m_size, 2*m_size, 1.7* m_size); } else if ( m_shape == "box" || m_shape == "rectangle" || m_shape == "square" ) { //rectangle: for GraphML/GML compliance path.addRect (-m_size , -m_size , 1.8*m_size , 1.8*m_size ); } else if (m_shape == "roundrectangle" ) { //roundrectangle: GraphML only 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") { path.moveTo(-m_size,0.95* m_size) ; path.lineTo(m_size,0.95*m_size); path.lineTo( 0,-1*m_size); path.lineTo(-m_size,0.95*m_size) ; path.closeSubpath(); } else if ( m_shape == "star") { path.setFillRule(Qt::WindingFill); path.moveTo(-0.8*m_size,0.6* m_size) ; path.lineTo(+0.8*m_size,0.6*m_size); path.lineTo( 0,-1*m_size); path.lineTo(-0.8*m_size,0.6*m_size) ; path.closeSubpath(); path.moveTo(0, 1* m_size) ; path.lineTo(+0.8*m_size,-0.6*m_size); path.lineTo(-0.8*m_size,-0.6*m_size) ; path.lineTo(0, 1* m_size); path.closeSubpath(); } else if ( m_shape == "diamond"){ path.moveTo(-m_size, 0); path.lineTo( 0,-1*m_size); path.lineTo( m_size, 0); path.lineTo( 0, 1*m_size); path.lineTo(-m_size, 0) ; path.closeSubpath(); } else if ( m_shape == "custom" ) { path.addRect (-m_size , -m_size , 2*m_size , 2*m_size ); if (!iconPath.isEmpty()) { m_iconPath = iconPath; } } else if (m_shape == "bugs" || m_shape == "heart" || m_shape == "dice" || m_shape == "person" || m_shape == "person-b" ) { path.addRect (-m_size , -m_size , 2*m_size , 2*m_size ); // we update iconPath only if it's not empty // this is to allow internal GraphicsNode methods to call us // without always passing the current iconPath again and again. if (!iconPath.isEmpty()) { m_iconPath = iconPath; } else { if ( m_shape == "person" ) { m_iconPath = ":/images/person.svg"; } if ( m_shape == "bugs" ) { m_iconPath = ":/images/bugs.png"; } else if ( m_shape == "heart") { m_iconPath = ":/images/heart.svg"; } else if ( m_shape == "dice" ) { m_iconPath = ":/images/random.png"; } } } else { // If shape is not supported, we draw a circle. path.addEllipse (-m_size, -m_size, 2*m_size, 2*m_size); } m_path = path; update(); } /** * @brief Returns the shape of the node as a path in local coordinates. * The shape is used for many things, including collision detection, hit tests, * and for the QGraphicsScene::items() functions. * We could ommit reimplementing this and have the default QGraphicsItem::shape() * return a simple rectangular shape through boundingRect() but we opt to return * an accurate outline of the item's shape. * @return */ QPainterPath GraphicsNode::shape() const { //qDebug() << "GraphicsNode::shape()"; return (m_path); } /** * @brief Returns the bounding rectangle of the node: * The rectangle where all painting will take place. * @return */ QRectF GraphicsNode::boundingRect() const { //qDebug()<< "GraphicsNode::boundingRect() " << m_path.controlPointRect(); return m_path.controlPointRect(); //qreal adjust = 5; // return QRectF(-m_size -adjust , -m_size-adjust , 2*m_size+adjust , 2*m_size +adjust); } /** * @brief Does the actual painting using the QPainterPath created by the setShape() * Called by GraphicsView and GraphicsNode methods in every update() * @param painter * @param option */ void GraphicsNode::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { // painter->setClipRect( option->exposedRect ); //qDebug()<< "GraphicsNode::paint() " << m_col; if (option->state & QStyle::State_MouseOver) { // qDebug()<< "GraphicsNode::paint() mouse over " << m_col; painter->setBrush(m_col.darker(120)); setZValue(ZValueNodeHighlighted); } else { //qDebug()<< "GraphicsNode::paint() - no mouse over " << m_col; painter->setBrush(m_col); setZValue(ZValueNode); } if (m_shape == "custom") { QPixmap pix(m_iconPath); painter->drawPixmap(-m_size, -m_size, 2*m_size, 2*m_size, pix); } else if ( m_shape == "person" || m_shape == "person-b" || m_shape == "bugs" || m_shape == "heart" || m_shape == "dice" ) { // See: // https://techbase.kde.org/Development/Tutorials/Graphics/Performance // QImage image(m_iconPath); // painter->drawImage(QRectF(-m_size, -m_size, 2*m_size, 2*m_size) , image); QPixmap pix(m_iconPath); painter->drawPixmap(-m_size, -m_size, 2*m_size, 2*m_size, pix); } else { painter->setPen(QPen(QColor("#222"), 0)); painter->drawPath (m_path); } //@TODO FIX NUMBER SIZE WHEN TOGGLING IN/OUT OF NODE SHAPE if (m_hasNumberInside && m_hasNumber) { // m_path->setFillRule(Qt::WindingFill); painter->setPen(QPen(QColor(m_numColor), 0)); if (m_num > 999) { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize-1: 0.4*m_size, QFont::Normal)); painter->drawText(-0.8*m_size,m_size/3, QString::number(m_num) ); } else if (m_num > 99) { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize-1: 0.5*m_size, QFont::Normal)); painter->drawText(-0.6 * m_size,m_size/3, QString::number(m_num) ); } else if (m_num > 9 ) { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize: 0.66*m_size, QFont::Normal)); painter->drawText(-0.5*m_size,m_size/3,QString::number(m_num) ); } else { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize: 0.66*m_size, QFont::Normal)); painter->drawText(-0.33*m_size,m_size/3,QString::number(m_num) ); } } } /** * @brief Called when the node changes, i.e. moves, becomes disabled or changes its visibility * Propagates the changes to connected elements, i.e. edges, numbers, etc. * Checks if the node is inside the scene. * @param change * @param value * @return */ QVariant GraphicsNode::itemChange(GraphicsItemChange change, const QVariant &value) { switch (change) { case ItemPositionHasChanged: { //setCacheMode( QGraphicsItem::ItemCoordinateCache ); foreach (GraphicsEdge *edge, inEdgeList) //Move each inEdge of this node edge->adjust(); foreach (GraphicsEdge *edge, outEdgeList) //Move each outEdge of this node edge->adjust(); //Move its graphic number if ( m_hasNumber ) { if (!m_hasNumberInside) { //move it outside m_number -> setZValue(ZValueNodeNumber); m_number -> setPos( m_size+m_numberDistance, 0); } } if (m_hasLabel) { m_label->setPos( -m_size, m_labelDistance+m_size); } break; } case ItemEnabledHasChanged:{ qDebug() << "GraphicsNode::itemChange - enabled changed"; break; } case ItemSelectedHasChanged:{ if (value.toBool()) { setZValue(ZValueNodeHighlighted); m_size_orig = m_size; setSize(m_size * 2 - 1); setColor(m_col.darker(120)); if (m_edgeHighLighting) { foreach (GraphicsEdge *edge, inEdgeList) edge->setHighlighted(true); foreach (GraphicsEdge *edge, outEdgeList) edge->setHighlighted(true); } } else{ setZValue(ZValueNode); setSize(m_size_orig); setColor(m_col); if (m_edgeHighLighting) { foreach (GraphicsEdge *edge, inEdgeList) edge->setHighlighted(false); foreach (GraphicsEdge *edge, outEdgeList) edge->setHighlighted(false); } } break; } case ItemVisibleHasChanged: { break; } default: { break; } }; return QGraphicsItem::itemChange(change, value); } /** handles the events of a click on a node */ void GraphicsNode::mousePressEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mousePressEvent(event); } void GraphicsNode::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { update(); QGraphicsItem::mouseReleaseEvent(event); } /** * @brief GraphicsNode::hoverEnterEvent * on hover on node, it highlights all connected edges * @param event */ void GraphicsNode::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { QGraphicsItem::hoverEnterEvent(event); } /** * @brief GraphicsNode::hoverLeaveEvent * Stops the connected edges highlighting * @param event */ void GraphicsNode::hoverLeaveEvent(QGraphicsSceneHoverEvent *event){ QGraphicsItem::hoverLeaveEvent(event); } void GraphicsNode::setEdgeHighLighting(const bool &toggle) { m_edgeHighLighting = toggle; } /** * @brief GraphicsNode::addInLink * Called from a new connected in-link to acknowloedge itself to this node. * @param edge */ void GraphicsNode::addInLink( GraphicsEdge *edge ) { qDebug() << "GraphicsNode: addInLink() for "<< m_num; inEdgeList.push_back( edge); //qDebug ("GraphicsNode: %i inEdgeList has now %i edges", m_num, inEdgeList.size()); } void GraphicsNode::deleteInLink( GraphicsEdge *link ){ qDebug () << "GraphicsNode::deleteInLink() - to " << m_num << " inEdgeList size: " << inEdgeList.size(); inEdgeList.remove( link ); qDebug () << "GraphicsNode::deleteInLink() - deleted to " << m_num << " inEdgeList size: " << inEdgeList.size(); } void GraphicsNode::addOutLink( GraphicsEdge *edge ) { qDebug("GraphicsNode: addOutLink()"); outEdgeList.push_back( edge); // qDebug ("GraphicsNode: outEdgeList has now %i edges", outEdgeList.size()); } void GraphicsNode::deleteOutLink(GraphicsEdge *link){ qDebug () << "GraphicsNode::deleteOutLink() - from " << m_num << " outEdgeList size: " << outEdgeList.size(); outEdgeList.remove( link); qDebug () << "GraphicsNode::deleteOutLink() - deleted from " << m_num << " outEdgeList size now: " << outEdgeList.size(); } void GraphicsNode::addLabel () { qDebug()<< "GraphicsNode::addLabel()" ; m_label = new GraphicsNodeLabel (this, m_labelText, m_labelSize); m_label -> setDefaultTextColor (m_labelColor); m_label -> setPos( m_size, m_labelDistance+m_size); m_hasLabel = true; } GraphicsNodeLabel* GraphicsNode::label(){ if (!m_hasLabel) { addLabel(); } return m_label; } void GraphicsNode::deleteLabel(){ qDebug ("GraphicsNode: deleteLabel "); if (m_hasLabel) { m_hasLabel=false; m_label->hide(); graphicsWidget->removeItem(m_label); } qDebug () << "GraphicsNode::deleteLabel() - finished"; } void GraphicsNode::setLabelText (const QString &label) { qDebug()<< "GraphicsNode::setLabelText()"; prepareGeometryChange(); m_labelText = label; if (m_hasLabel) m_label->setPlainText(label); else addLabel(); m_hasLabel=true; } void GraphicsNode::setLabelColor ( const QString &color) { qDebug()<< "GraphicsNode::setLabelColor()"; prepareGeometryChange(); m_labelColor= color; if (m_hasLabel) m_label->setDefaultTextColor(color); else addLabel(); m_hasLabel=true; } void GraphicsNode::setLabelVisibility(const bool &toggle) { if (toggle){ if (m_hasLabel) { m_label->show(); } else { addLabel(); } } else { if (m_hasLabel) { m_label->hide(); } } m_hasLabel=toggle; } void GraphicsNode::setLabelSize(const int &size) { m_labelSize = size; if (!m_hasLabel) { addLabel(); } m_label->setSize(m_labelSize); } /** * @brief GraphicsNode::labelText * @return QString */ QString GraphicsNode::labelText ( ) { return m_labelText; } /** * @brief GraphicsNode::setLabelDistance * @param distance */ void GraphicsNode::setLabelDistance(const int &distance) { m_labelDistance = distance; if (!m_hasLabel) { addLabel(); } m_label->setPos( -m_size, m_size+m_labelDistance);; } void GraphicsNode::addNumber () { qDebug()<<"GraphicsNode::addNumber () " ; m_hasNumber=true; m_hasNumberInside = false; m_number= new GraphicsNodeNumber ( this, QString::number(m_num), m_numSize); m_number -> setDefaultTextColor (m_numColor); m_number -> setPos(m_size+m_numberDistance, 0); } GraphicsNodeNumber* GraphicsNode::number(){ return m_number; } void GraphicsNode::deleteNumber( ){ qDebug () << "GraphicsNode::deleteNumber()"; if (m_hasNumber && !m_hasNumberInside) { m_number->hide(); graphicsWidget->removeItem(m_number); m_hasNumber=false; } qDebug () << "GraphicsNode::deleteNumber() - finished"; } void GraphicsNode::setNumberVisibility(const bool &toggle) { qDebug() << "GraphicsNode::setNumberVisibility() " << toggle; if (toggle) { //show if (!m_hasNumber) { m_hasNumber=toggle; if ( !m_hasNumberInside ) addNumber(); else { setShape(m_shape); } } } else { // hide deleteNumber(); m_hasNumber=toggle; setShape(m_shape); } } void GraphicsNode::setNumberInside (const bool &toggle){ qDebug()<<"GraphicsNode::setNumberInside() " << toggle; if (toggle) { // set number inside deleteNumber(); } else { addNumber(); } m_hasNumber = true; m_hasNumberInside = toggle; setShape(m_shape); } /** * @brief GraphicsNode::setNumberSize * @param size */ void GraphicsNode::setNumberSize(const int &size) { m_numSize = size; if (m_hasNumber && !m_hasNumberInside) { m_number->setSize(m_numSize); } else if (m_hasNumber && m_hasNumberInside) { setShape(m_shape); } else { // create a nodeNumber ? } } /** * @brief GraphicsNode::setNumberColor * @param color */ void GraphicsNode::setNumberColor(const QString &color) { m_numColor = color; if (m_hasNumber){ if (m_hasNumberInside) { setShape(m_shape); } else { m_number -> setDefaultTextColor (m_numColor); } } } /** * @brief GraphicsNode::setNumberDistance * @param distance */ void GraphicsNode::setNumberDistance(const int &distance) { m_numberDistance = distance; if (m_hasNumber && !m_hasNumberInside) { m_number -> setPos( m_size+m_numberDistance, 0); } else if (m_hasNumber && m_hasNumberInside) { // do nothing } else { // create a nodeNumber ? } } GraphicsNode::~GraphicsNode(){ qDebug() << "GraphicsNode::~GraphicsNode() - self-destructing node "<< nodeNumber() << "inEdgeList.size = " << inEdgeList.size() << "outEdgeList.size = " << outEdgeList.size(); qDebug()<< "GraphicsNode::~GraphicsNode() - removing edges in inEdgeList"; foreach (GraphicsEdge *edge, inEdgeList) { // same as using qDeleteAll delete edge; } qDebug()<< "GraphicsNode::~GraphicsNode() - removing edges in outEdgeList"; foreach (GraphicsEdge *edge, outEdgeList) { // same as using qDeleteAll delete edge; } if ( m_hasNumber ) deleteNumber(); if ( m_hasLabel ) deleteLabel(); inEdgeList.clear(); outEdgeList.clear(); this->hide(); graphicsWidget->removeItem(this); } app-2.8/src/graphicsnode.h000077500000000000000000000130441377436340000155650ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsnode.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSNODE_H #define GRAPHICSNODE_H #include #include #include class GraphicsWidget; class QGraphicsSceneMouseEvent; class GraphicsEdge; class GraphicsNodeLabel; class GraphicsNodeNumber; using namespace std; static const int TypeNode = QGraphicsItem::UserType+1; static const int ZValueNode = 100; static const int ZValueNodeHighlighted = 110; /** * 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 GraphicsNode : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: GraphicsNode ( GraphicsWidget* gw, const int &num, const int &size, const QString &color, const QString &shape, const QString &iconPath, const bool &showNumbers, const bool &numbersInside, const QString &numberColor, const int &numberSize, const int &numDistance, const bool &showLabels, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance, const bool &edgeHighlighting, QPointF p ); ~GraphicsNode(); enum { Type = UserType + 1 }; int type() const { return Type; } QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); int nodeNumber() {return m_num;} void addInLink( GraphicsEdge *edge ) ; void deleteInLink(GraphicsEdge*); void addOutLink( GraphicsEdge *edge ) ; void deleteOutLink(GraphicsEdge*); void setSize(const int &); int size() const; void setShape (const QString, const QString &iconPath=QString()); QString nodeShape() {return m_shape;} void setColor(const QString &colorStr); void setColor(QColor color); QString color (); void addLabel(); GraphicsNodeLabel* label(); void deleteLabel(); void setLabelVisibility(const bool &toggle); void setLabelSize(const int &size); void setLabelText ( const QString &label) ; void setLabelColor (const QString &color) ; QString labelText(); void setLabelDistance(const int &distance); void addNumber () ; GraphicsNodeNumber* number(); void deleteNumber(); void setNumberVisibility(const bool &toggle); void setNumberInside(const bool &toggle); void setNumberSize(const int &size); void setNumberDistance(const int &distance); void setNumberColor(const QString &color); void setEdgeHighLighting(const bool &toggle) ; protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); signals: void nodeClicked(GraphicsNode*); void startEdge(GraphicsNode *); void adjustOutEdge(); void adjustInEdge(); void removeOutEdge(); void removeInEdge(); private: GraphicsWidget *graphicsWidget; QPainterPath m_path; QPointF newPos; QPolygonF *m_poly_t; int m_size, m_size_orig; int m_state; int m_numSize; int m_labelSize; int m_numberDistance; int m_labelDistance; int m_num; QString m_shape, m_iconPath; QString m_col_str, m_numColor, m_labelText, m_labelColor; QColor m_col; bool m_hasNumber, m_hasLabel, m_hasNumberInside, m_edgeHighLighting; /**Lists of elements attached to this node */ list inEdgeList, outEdgeList; GraphicsNodeLabel *m_label; GraphicsNodeNumber *m_number; }; #endif app-2.8/src/graphicsnodelabel.cpp000077500000000000000000000045101377436340000171160ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsnodelabel.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "graphicsnodelabel.h" #include "graphicsnode.h" #include #include GraphicsNodeLabel::GraphicsNodeLabel(GraphicsNode *jim , const QString &text, const int &size) : QGraphicsTextItem(jim) { source=jim; setParentItem(jim); //auto disables child items like this, when node is disabled. setPlainText( text ); setFont( QFont ("Times", size, QFont::Light, true) ); setZValue(ZValueNodeLabel); setAcceptHoverEvents(false); qDebug() << "GraphicsNodeLabel() - initialized"; } void GraphicsNodeLabel::setSize(const int &size) { prepareGeometryChange(); setFont( QFont ("Times", size, QFont::Black, false) ); //update(); } void GraphicsNodeLabel::removeRefs(){ source->deleteLabel(); } GraphicsNodeLabel::~GraphicsNodeLabel(){ } app-2.8/src/graphicsnodelabel.h000077500000000000000000000041551377436340000165700ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsnodelabel.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSNODELABEL_H #define GRAPHICSNODELABEL_H #include class GraphicsNode; static const int TypeLabel = QGraphicsItem::UserType+4; static const int ZValueNodeLabel = 80; class GraphicsNodeLabel : public QGraphicsTextItem{ public: GraphicsNodeLabel(GraphicsNode * , const QString &text, const int &size ); void removeRefs(); enum { Type = UserType + 4 }; int type() const { return Type; } void setSize(const int &size); ~GraphicsNodeLabel(); GraphicsNode* node() { return source; } private: GraphicsNode *source; }; #endif app-2.8/src/graphicsnodenumber.cpp000077500000000000000000000045521377436340000173350ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsnodenumber.cpp - description ------------------- copyright : (C) 2005-2021 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 "graphicsnodenumber.h" #include "graphicsnode.h" #include #include GraphicsNodeNumber::GraphicsNodeNumber( GraphicsNode *jim , const QString &labelText, const int &size) :QGraphicsTextItem(jim) { source=jim; setParentItem(jim); //auto disables child items like this, when node is disabled. setPlainText( labelText ); setFont( QFont ("Times", size, QFont::Black, false) ); setZValue(ZValueNodeNumber); setAcceptHoverEvents(false); qDebug() << "GraphicsNodeNumber() - initialized"; } void GraphicsNodeNumber::setSize(const int size) { prepareGeometryChange(); setFont( QFont ("Times", size, QFont::Black, false) ); //update(); } void GraphicsNodeNumber::removeRefs(){ source->deleteNumber(); } GraphicsNodeNumber::~GraphicsNodeNumber(){ } app-2.8/src/graphicsnodenumber.h000077500000000000000000000041671377436340000170040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicsnodenumber.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHICSNODENUMBER_H #define GRAPHICSNODENUMBER_H #include class GraphicsNode; static const int TypeNumber=QGraphicsItem::UserType+3; static const int ZValueNodeNumber = 90; class GraphicsNodeNumber : public QGraphicsTextItem { public: GraphicsNodeNumber(GraphicsNode * , const QString &labelText, const int &size); enum { Type = UserType + 3 }; void removeRefs(); int type() const { return Type; } GraphicsNode* node() { return source; } void setSize(const int size); ~GraphicsNodeNumber(); private: GraphicsNode *source; }; #endif app-2.8/src/graphicswidget.cpp000077500000000000000000001475641377436340000164750ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicswidget.cpp description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 "graphicsnode.h" #include "graphicsedge.h" #include "graphicsnodenumber.h" #include "graphicsnodelabel.h" #include "graphicsguide.h" #include "graphicsedgeweight.h" #include "graphicsedgelabel.h" /** Constructor method. Called when a GraphicsWidget object is created in MW */ GraphicsWidget::GraphicsWidget(QGraphicsScene *sc, MainWindow* m_parent) : QGraphicsView ( sc,m_parent) { qDebug() << "GW::GraphicsWidget(*sc, *MW)"; qRegisterMetaType("SelectedEdge"); qRegisterMetaType >(); secondDoubleClick=false; transformationActive = false; m_nodeLabel=""; m_zoomIndex=250; m_currentScaleFactor = 1; m_currentRotationAngle = 0; clickedEdge=0; edgesHash.reserve(150000); nodeHash.reserve(10000); m_edgeHighlighting = true; m_edgeMinOffsetFromNode=6; m_nodeNumberVisibility = true; m_nodeLabelVisibility = true; /* "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." * The user can change this from Settings. */ scene() -> setItemIndexMethod(QGraphicsScene::BspTreeIndex); //NoIndex (for anime) //setInteractive(false); connect ( scene() , &QGraphicsScene::selectionChanged, this, &GraphicsWidget::getSelectedItems); } /** * @brief creates a QString of the edge name - used for indexing edgesHash * @param v1 * @param v2 * @param relation * @return */ QString GraphicsWidget::createEdgeName(const int &v1, const int &v2, const int &relation) { edgeName = QString::number((relation != -1) ? relation : m_curRelation) + QString(":") + QString::number(v1) + QString(">")+ QString::number(v2); return edgeName; } /** http://thesmithfam.org/blog/2007/02/03/qt-improving-qgraphicsview-performance/#comment-7215 */ void GraphicsWidget::paintEvent ( QPaintEvent * event ){ // qDebug()<<"GraphicsWidget::paintEvent "; // QPaintEvent *newEvent=new QPaintEvent(event->region().boundingRect()); // QGraphicsView::paintEvent(newEvent); // delete newEvent; QGraphicsView::paintEvent(event); } /** * @brief Clears the scene and all hashes, lists, var etc */ void GraphicsWidget::clear() { qDebug() << "GW::clear() - clearing hashes"; nodeHash.clear(); edgesHash.clear(); m_selectedNodes.clear(); m_selectedEdges.clear(); scene()->clear(); m_curRelation=0; clickedEdge=0; firstNode=0; secondDoubleClick=false; qDebug() << "GW::clear() - finished clearing hashes"; } /** * @brief Changes the current relation * Called from Graph::signalRelationChangedToGW(int) signal. * @param relation */ void GraphicsWidget::relationSet(int relation) { qDebug() << "GraphicsWidget::relationSet() to " << relation; m_curRelation = relation; } /** * @brief Adds a new node onto the scene * Called from Graph::vertexCreate method primarily when we load files * It is also called in the end when the user presses "Add Node" button or * the user double clicks (mouseDoubleClickEvent() calls Graph::vertexCreate) * @param num * @param nodeSize * @param nodeColor * @param numberColor * @param numberSize * @param nodeLabel * @param labelColor * @param labelSize * @param p * @param nodeShape * @param showLabels * @param numberInsideNode * @param showNumbers */ void GraphicsWidget::drawNode( const QPointF &p, const int &num, const int &nodeSize, const QString &nodeShape, const QString &nodeIconPath, const QString &nodeColor, const QString &numberColor, const int &numberSize, const int &numberDistance, const QString &nodeLabel, const QString &labelColor, const int &labelSize, const int &labelDistance ) { qDebug()<< "GW::drawNode() - Draw new node:" << num << " at:" << p.x() << ", "<< p.y() << "shape"<setDirectionType(type); } // qDebug()<< "Scene items now: "<< scene()->items().size() << " - GW items now: "<< items().size(); } /** * @brief Creates a new edge, when the user middle-clicks on two nodes consecutively * On the first middle-click, it saves the first node (source). * On the second middle-click, it saves the second node as target and emits the signal * userMiddleClicked() to MW which will notify activeGraph, * which in turn will signal back to drawEdge(). * @param node */ void GraphicsWidget::startEdge(GraphicsNode *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() ); emit setCursor(Qt::ArrowCursor); secondDoubleClick=false; } else{ qDebug()<<"GW::startEdge() - this is the first double click."; firstNode=node; secondDoubleClick=true; emit setCursor( Qt::PointingHandCursor ); } } /** * @brief Called when the user clicks or double-clicks on a node. * Clears clickedEdge and emits the userClickedNode signal to Graph to - display node info on the status bar - notify context menus for the clicked node. * @param node */ void GraphicsWidget::setNodeClicked(GraphicsNode *node){ if ( node ) { qDebug () << "GW::nodeClicked() - Emitting userClickedNode()"; if (clickedEdge) { setEdgeClicked(0); } emit userClickedNode(node->nodeNumber()); } else { } } /** * @brief Called when the user clicks on an edge. Emits the userClickedEdge signal to Graph which is used to: - display edge info on the status bar - notify context menus for the clicked edge. * @param edge */ void GraphicsWidget::setEdgeClicked(GraphicsEdge *edge, const bool &openMenu){ if (edge) { qDebug() <<"### GW::edgeClicked() - setting new clicked edge"; clickedEdge=edge; qDebug() <<"### GW::edgeClicked() - emitting userClickedEdge() to MW"; emit userClickedEdge(edge->sourceNode()->nodeNumber(), edge->targetNode()->nodeNumber(), openMenu); } else { qDebug() <<"### GW::edgeClicked() - with zero parameters. Unsetting clickedEdge"; clickedEdge=0; emit userClickedEdge(0,0,openMenu); } } /** * @brief Called from activeGraph to update node coordinates on the canvas * while creating random networks. * @param num * @param x * @param y */ void GraphicsWidget::moveNode(const int &num, const qreal &x, const qreal &y){ qDebug() << " GW: moveNode() " << num << ": " << x << y; nodeHash.value(num)->setPos(x,y); // qDebug() << "GW: moveNode() node reports x, y as " // << nodeHash.value(number)->x() << nodeHash.value(number)->x(); } /** * @brief Removes a node from the scene. * Called from Graph signalEraseNode(int) * @param number */ void GraphicsWidget::removeNode(const int &number){ qDebug() << "GW::removeNode() - node " << number << " scene items: " << scene()->items().size() << " view items: " << items().size() << " nodeHash items: "<< nodeHash.count(); if ( nodeHash.contains(number) ) { qDebug() << "GW::removeNode() - found node" << number<< " Deleting :)" ; delete nodeHash.value(number); } qDebug() << "GW::removeNode() - node erased! "; // << " scene items now: " << scene()->items().size() // << " view items: " << items().size() // << " nodeHash items: "<< nodeHash.count() // << " edgesHash items: "<< edgesHash.count() ; } /** * @brief Remove an edge from the graphics widget. * Called from MW/Graph when erasing edges using vertex numbers * Also called when transforming directed edges to undirected. * @param sourceNode * @param targetNode */ void GraphicsWidget::removeEdge(const int &source, const int &target, const bool &removeOpposite){ edgeName = createEdgeName(source,target); qDebug() << "GW::removeEdge() - " << edgeName << "removeOpposite"<items().size() << " view items: " << items().size() << " edgesHash.count: " << edgesHash.count(); if ( edgesHash.contains(edgeName) ) { int directionType = edgesHash.value(edgeName)->directionType(); delete edgesHash.value(edgeName); if (directionType == EdgeType::Reciprocated) { if (!removeOpposite) { drawEdge(target, source, 1,""); } } qDebug() << "GW::removeEdge() - Deleted edge" << edgeName << " scene items: " << scene()->items().size() << " view items: " << items().size() << " edgesHash.count: " << edgesHash.count(); } else { //check opposite edge. If it exists, then transform it to directed edgeName = createEdgeName(target, source); qDebug() << "GW::removeEdge() - Edge did not exist, checking for opposite:" << edgeName; if ( edgesHash.contains(edgeName) ) { qDebug() << "GW::removeEdge() - Opposite edge exists. Check if it is reciprocated"; if ( edgesHash.value(edgeName)->directionType() == EdgeType::Reciprocated ) { edgesHash.value(edgeName)->setDirectionType(EdgeType::Directed); return; } } qDebug() << "GW::removeEdge() - No such edge to delete"; } } /** * @brief Removes a node item from the scene. * Called from GraphicsNode::~GraphicsNode() to remove itself from nodeHash, scene and * be deleted * @param node */ void GraphicsWidget::removeItem( GraphicsNode *node){ int i=node->nodeNumber(); qDebug() << "GW::removeItem(node) - number: " << i; if (firstNode == node) { qDebug() << "GW::removeItem(node) - number: " << i << "previously set as source node for a new edge. Unsetting."; secondDoubleClick = false; emit setCursor(Qt::ArrowCursor); } nodeHash.remove(i); scene()->removeItem(node); node->deleteLater (); qDebug() << "GW::removeItem(node) - node erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief Removes an edge item from the scene. * Called from GraphicsEdge::~GraphicsEdge() to remove itself from edgesHash and scene and * be deleted * @param edge * */ void GraphicsWidget::removeItem( GraphicsEdge * edge){ qDebug() << "GW::removeItem(edge) - calling edgeClicked(0)" ; setEdgeClicked(0); edgeName = createEdgeName(edge->sourceNodeNumber(), edge->targetNodeNumber() ) ; qDebug() << "GW::removeItem(edge) - removing edge from edges hash" ; edgesHash.remove(edgeName); qDebug() << "GW::removeItem(edge) - removing edge scene" ; scene()->removeItem(edge); qDebug() << "GW::removeItem(edge) - calling edge->deleteLater()" ; edge->deleteLater(); qDebug() << "GW::removeItem(edge) - edge erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief Removes an edge weight number item from the scene. * @param edgeWeight */ void GraphicsWidget::removeItem( GraphicsEdgeWeight *edgeWeight){ qDebug() << "GW::removeItem(edgeWeight) - of edge" << "GW items now: " << items().size(); scene()->removeItem(edgeWeight); edgeWeight->deleteLater(); qDebug() << "GW::removeItem(edgeWeight) - edgeWeight erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief Removes an edge label item from the scene. * @param edgeLabel */ void GraphicsWidget::removeItem( GraphicsEdgeLabel *edgeLabel){ qDebug() << "GW::removeItem(edgeLabel) - of edge" << "GW items now: " << items().size(); scene()->removeItem(edgeLabel); edgeLabel->deleteLater(); qDebug() << "GW::removeItem(edgeLabel) - edgeLabel erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief Removes a node label item from the scene. * @param nodeLabel */ void GraphicsWidget::removeItem( GraphicsNodeLabel *nodeLabel){ qDebug() << "GW::removeItem(label) - of node " << nodeLabel->node()->nodeNumber() << "GW items now: " << items().size(); scene()->removeItem(nodeLabel); nodeLabel->deleteLater(); qDebug() << "GW::removeItem(label) - label erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief Removes a node number item from the scene. * @param nodeNumber */ void GraphicsWidget::removeItem( GraphicsNodeNumber *nodeNumber){ qDebug() << "GW::removeItem(number) - of node " << nodeNumber->node()->nodeNumber() << "GW items now: " << items().size(); scene()->removeItem(nodeNumber); nodeNumber->deleteLater(); qDebug() << "GW::removeItem(number) - number erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief Sets the color of an node. * Called from MW when the user changes the color of a node (right-clicking). * @param nodeNumber * @param color * @return */ bool GraphicsWidget::setNodeColor(const int &nodeNumber, const QString &color){ qDebug() << "GW::setNodeColor() : " << color; nodeHash.value(nodeNumber) -> setColor(color); return true; } /** * @brief Sets the shape of an node. Called from MW when the user changes the shape of a node * @param nodeNumber * @param shape * @return */ bool GraphicsWidget::setNodeShape(const int &nodeNumber, const QString &shape, const QString &iconPath){ qDebug() << "GW::setNodeShape() : " << shape; nodeHash.value(nodeNumber) -> setShape(shape,iconPath); return true; } /** * @brief GraphicsWidget::setNodeLabelsVisibility * @param toggle */ void GraphicsWidget::setNodeLabelsVisibility (const bool &toggle){ qDebug()<< "GW::setNodeLabelsVisibility()" << toggle; foreach ( GraphicsNode *m_node, nodeHash) { m_node->setLabelVisibility(toggle); } m_nodeLabelVisibility = toggle; } /** * @brief Sets the label of an node. Called from MW. * @param nodeNumber * @param label * @return */ bool GraphicsWidget::setNodeLabel(const int &nodeNumber, const QString &label){ qDebug() << "GW::setNodeLabel() : " << label; nodeHash.value(nodeNumber) -> setLabelText (label); return true; } /** * @brief Toggles node numbers displayed inside or out of nodes * Called from MW * @param numIn */ void GraphicsWidget::setNumbersInsideNodes(const bool &toggle){ qDebug()<< "GW::setNumbersInsideNodes" << toggle; foreach ( GraphicsNode *m_node, nodeHash) { m_node->setNumberInside(toggle); } m_nodeNumbersInside=toggle; } /** 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::setInitNodeSize() " << size; m_nodeSize=size; } /** * @brief Passes initial zoom setting * Called from MW on startup * @param zoomIndex */ void GraphicsWidget::setInitZoomIndex(int zoomIndex) { m_zoomIndex = zoomIndex; } /** * Changes the visibility of a Node */ void GraphicsWidget::setNodeVisibility(int number, bool toggle){ if ( nodeHash.contains (number) ) { qDebug() << "GW::setNodeVisibility() - node" << number << " set to " << toggle; nodeHash.value(number) -> setVisible(toggle); nodeHash.value(number) -> setEnabled(toggle); return; } qDebug() << "GW: setNodeVisibility(): cannot find node " << number; } /** * @brief Changes the size of a node * @param number * @param size * @return */ bool GraphicsWidget::setNodeSize(const int &number, const int &size ){ qDebug () << "GW::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 Changes the size of all nodes. * @param size * @return */ void GraphicsWidget::setNodeSizeAll(const int &size ){ qDebug() << "GW::setAllNodeSize() "; foreach ( GraphicsNode *m_node, nodeHash ) { qDebug() << "GW::setAllNodeSize(): "<< m_node->nodeNumber() << " to new size " << size ; m_node -> setSize(size); } } /** * @brief Toggles the visibility of node numbers * @param toggle */ void GraphicsWidget::setNodeNumberVisibility(const bool &toggle){ qDebug()<< "GW::setNodeNumberVisibility()" << toggle; foreach ( GraphicsNode *m_node, nodeHash) { m_node->setNumberVisibility(toggle); } m_nodeNumberVisibility = toggle; } /** * @brief Changes the color of a node number * @param nodeNumber * @param color */ void GraphicsWidget::setNodeNumberColor(const int &nodeNumber, const QString &color) { qDebug () << " GraphicsWidget::setNodeNumberColor() - node:"<< nodeNumber << " new number color"<< color; if ( nodeHash.contains (nodeNumber) ) { if (!color.isNull()){ nodeHash.value(nodeNumber) ->setNumberColor(color) ; } } qDebug() << "GW::setNodeNumberColor(): cannot find node " << nodeNumber; } /** * @brief Changes the size of the number of a node * @param number * @param size */ bool GraphicsWidget::setNodeNumberSize(const int &number, const int &size){ qDebug () << " GraphicsWidget::setNodeNumberSize() - node: "<< number << " new number size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ nodeHash.value(number) ->setNumberSize(size) ; return true; } } qDebug() << "GW::setNodeNumberSize() - cannot find node " << number; return false; } /** * @brief Changes the distance of the number of a node. * @param number * @param distance */ bool GraphicsWidget::setNodeNumberDistance(const int &number, const int &distance ){ qDebug () << "GW::setNodeNumberDistance() - node: "<< number << " new number distance "<< distance; if ( nodeHash.contains (number) ) { if (distance>=0){ nodeHash.value(number) ->setNumberDistance(distance) ; return true; } } qDebug() << "GW::setNodeNumberDistance() - cannot find node " << number; return false; } /** * @brief Changes the label color of a node to 'color'. * @param number * @param color */ bool GraphicsWidget::setNodeLabelColor(const int &number, const QString &color){ qDebug () << "GW::setNodeLabelColor() - node number: "<< number << " new Label color"<< color; if ( nodeHash.contains (number) ) { nodeHash.value(number) ->setLabelColor(color); return true; } qDebug() << "GW:setNodeLabelColor() - cannot find node " << number; return false; } /** * @brief Changes the label size of a node to 'size'. * @param number * @param size */ bool GraphicsWidget::setNodeLabelSize(const int &number, const int &size){ qDebug () << "GW::setNodeLabelSize() - node number: "<< number << " new Label size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ qDebug() << "GW::setNodeLabelSize(): for "<< number << " to " << size ; nodeHash.value(number) ->setLabelSize(size); return true; } } qDebug() << "GW:setNodeLabelSize() - cannot find node " << number; return false; } /** * @brief Changes the distance of a node label * @param number * @param distance */ bool GraphicsWidget::setNodeLabelDistance( const int &number, const int &distance ){ qDebug () << "GW::setNodeLabelDistance() - node number: "<< number << " new label distance "<< distance; if ( nodeHash.contains (number) ) { if (distance>=0){ qDebug() << "GW::setNodeLabelDistance(): for "<< number << " to " << distance ; nodeHash.value(number) ->setLabelDistance(distance) ; return true; } } qDebug() << "GW::setNodeLabelDistance() - cannot find node " << number; return false; } /** * @brief Checks if a node with label or number 'text' exists and returns it * @param text * @return */ GraphicsNode* GraphicsWidget::hasNode( QString text ){ bool ok = false; foreach ( GraphicsNode *candidate, nodeHash) { if ( candidate->nodeNumber()==text.toInt(&ok, 10) || ( candidate->labelText() == text) ) { qDebug() << "GW: hasNode(): Node " << text << " found!"; return candidate; break; } } return 0; //dummy return. } /** * @brief Highlights all nodes in list * Called by Graph::signalNodesFound() * @param list */ void GraphicsWidget::setNodesMarked(QList list){ qDebug() << "GW::setNodesMarked()" << list; foreach ( int nodeNumber, list) { if ( nodeHash.contains (nodeNumber) ) { qDebug() << "GW::setNodesMarked() - setSelected node:"<< nodeNumber; nodeHash.value(nodeNumber) ->setSelected(true) ; } else { qDebug() << "GW::setNodesMarked() - cannot find node:"<< nodeNumber; } } } /** * @brief GraphicsWidget::setEdgeLabel * Sets the label of an edge. * Called from MW when the user changes the label of an edge (right-clicking). * @param source * @param target * @param label */ void GraphicsWidget::setEdgeLabel(const int &source, const int &target, const QString &label){ edgeName = createEdgeName( source, target ); qDebug()<<"GW::setEdgeLabel() -" << edgeName << " new label " << label; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setLabel(label); } } /** * @brief Sets the color of an edge. * Called from MW when the user changes the color of an edge (right-clicking). * Also called from Graph when all edge colors need to be changed. * @param source * @param target * @param color */ void GraphicsWidget::setEdgeColor(const int &source, const int &target, const QString &color){ edgeName = createEdgeName( source, target ); qDebug()<<"GW::setEdgeColor() -" << edgeName << " new color " << color; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setColor(color); } } /** * @brief Changes the direction type of an existing edge * @param source * @param target * @param directionType * @return */ bool GraphicsWidget::setEdgeDirectionType(const int &source, const int &target, const int &dirType){ qDebug() << "GW::setEdgeDirectionType() : " << source << "->" << target << "type" << dirType; edgeName = createEdgeName( source, target ); qDebug()<<"GW::setEdgeDirectionType() - checking edgesHash for:" << edgeName ; if ( edgesHash.contains (edgeName) ) { qDebug()<<"GW::setEdgeDirectionType() - edge exists in edgesHash. " << " Transforming it to reciprocated"; edgesHash.value(edgeName) -> setDirectionType(dirType); return true; } return false; } /** * @brief Sets the weight of an edge. Called from MW when the user changes the weight of an edge (right-clicking). * @param source * @param target * @param weight * @return */ bool GraphicsWidget::setEdgeWeight(const int &source, const int &target, const qreal &weight){ edgeName = createEdgeName( source, target ); qDebug()<<"GW::setEdgeWeight() -" << edgeName << " new weight " << weight; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setWeight(weight); return true; } else { //check opposite edge. If it exists, then transform it to directed edgeName = createEdgeName(target, source); qDebug() << "GW::setEdgeWeight() - Edge did not exist, checking for opposite:" << edgeName; if ( edgesHash.contains(edgeName) ) { qDebug() << "GW::setEdgeWeight() - Opposite edge exists. Check if it is reciprocated"; edgesHash.value(edgeName) -> setWeight(weight); return true; } qDebug() << "GW::setEdgeWeight() - No such edge to delete"; } return false; } /** * @brief Toggles the visibility of all edge arrows * @param toggle */ void GraphicsWidget::setEdgeArrowsVisibility(const bool &toggle){ qDebug()<< "GW::setEdgeArrowsVisibility()" << toggle; // QList list = scene()->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++) { // if ( (*item)->type() ==TypeEdge){ // GraphicsEdge *edge = (GraphicsEdge*) (*item); // edge->showArrows(toggle); // } // } foreach ( GraphicsEdge *m_edge, edgesHash) { m_edge->showArrows(toggle); } } /** * @brief Changes the offset of an edge (or all edges) from source and target nodes. * @param source * @param target * @param offset */ void GraphicsWidget::setEdgeOffsetFromNode(const int &source, const int &target, const int &offset){ qDebug() << "GW::setEdgeOffsetFromNode() : " << source << "->" << target << " = " << offset; if (source && target) { QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); qDebug()<<"GW::setEdgeWeight() -" << edgeName << " new offset " << offset; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setMinimumOffsetFromNode(offset); return; } } // if source == target == 0, then we change all edges'offset. else { QList list = scene()->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { if ( (*item)->type() ==TypeEdge){ GraphicsEdge *edge = (GraphicsEdge*) (*item); edge->setMinimumOffsetFromNode(offset); } } } } /** * @brief Toggles all edge weight numbers visibility * @param toggle */ void GraphicsWidget::setEdgeWeightNumbersVisibility (const bool &toggle){ qDebug()<< "GW::setEdgeWeightNumbersVisibility()" << toggle; foreach ( GraphicsEdge *m_edge, edgesHash) { m_edge->setWeightNumberVisibility(toggle); } } /** * @brief Toggles all edge labels visibility * @param toggle */ void GraphicsWidget::setEdgeLabelsVisibility (const bool &toggle){ qDebug()<< "GW::setEdgeLabelsVisibility() " << toggle << "for edgesHash.count: " << edgesHash.count(); foreach ( GraphicsEdge *m_edge, edgesHash) { m_edge->setLabelVisibility(toggle); } } /** * @brief Toggles edge highlighting when hovering over a single edge or all edges * connected to a node when the user hovers over that node. * Called from MW * @param numIn */ void GraphicsWidget::setEdgeHighlighting(const bool &toggle){ qDebug()<< "GW::setEdgeHighlighting" << toggle; foreach ( GraphicsNode *m_node, nodeHash) { m_node->setEdgeHighLighting(toggle); } foreach ( GraphicsEdge *m_edge, edgesHash) { m_edge->setHighlighting(toggle); } m_edgeHighlighting = toggle; } /** * @brief Changes the visibility of an GraphicsView edge (number, label, edge, etc) * @param relation * @param source * @param target * @param toggle */ void GraphicsWidget::setEdgeVisibility(int relation, int source, int target, bool toggle){ edgeName = createEdgeName( source, target, relation ); qDebug()<<"GW::setEdgeVisibility() - trying to set edge"< setVisible(toggle); edgesHash.value(edgeName) -> setEnabled(toggle); return; } edgeName = createEdgeName( target, source, relation ); qDebug()<<"GW: setEdgeVisibility() - trying to set edge"< setVisible(toggle); edgesHash.value(edgeName) -> setEnabled(toggle); return; } qDebug()<<"GW::setEdgeVisibility() - Cannot find edge" << edgeName << "or the opposite in the edgesHash"; } /** * @brief Changes the visibility of all items of certain type (i.e. number, label, edge, etc) * @param type * @param visible */ 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( const double&x0, const double&y0, const double&radius){ GraphicsGuide *circ=new GraphicsGuide (this, x0, y0, radius); circ->show(); } void GraphicsWidget::addGuideHLine(const double &y0){ GraphicsGuide *line=new GraphicsGuide (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){ GraphicsGuide *guide = qgraphicsitem_cast (*item); qDebug()<< "GW: removeAllItems - located element"; guide->die(); guide->deleteLater (); delete *item; } } } void GraphicsWidget::clearGuides(){ qDebug()<< "GW: clearGuides"; this->removeAllItems(TypeGuide); } /** * @brief Clears any clickedNode info and sets a selection rectangle * in the scene, which signals QGraphicsScene::selectionChanged signal to update * selectedNodes and selectedEdges. * Called from MW. */ void GraphicsWidget::selectAll(){ QPainterPath path; path.addRect(0,0, this->scene()->width() , this->scene()->height()); scene()->setSelectionArea(path); emit userClickedNode(0); qDebug() << "GraphicsWidget::selectAll() - selected items now: " << selectedItems().count(); } /** * @brief Clears any clickedNode info and any previous selection rect * in the scene, which again signals selectionChanged() to update selectedNodes * and selectedEdges to zero. * Called from MW. */ void GraphicsWidget::selectNone(){ qDebug() << "GraphicsWidget::selectNone()"; emit userClickedNode(0); scene()->clearSelection(); } /** * @brief Emits selected nodes and edges to Graph and MW * Called by QGraphicsScene::selectionChanged signal whenever the user * makes a selection on the canvas. * Emits selectedNodes and selectedEdges lists to * Graph::graphSelectionChanged() which then signals to * MW::slotEditSelectionChanged to display counts on app window. */ void GraphicsWidget::getSelectedItems() { qDebug() <<"GW::getSelectedItems() -" << "selectedNodes"<< selectedNodes() << "selectedEdges"<< selectedEdges(); emit userSelectedItems(selectedNodes(), selectedEdges()); } /** * @brief Returns a QList of all selected QGraphicsItem(s) * @return a QList of all selected QGraphicsItem(s) */ QList GraphicsWidget::selectedItems(){ qDebug() <<"GW::selectedItems()"; return scene()->selectedItems(); } /** * @brief Returns a QList of selected node numbers * Called by GW::getSelectedItems and MW::selectedNodes * @return a QList of integers: the selected node numbers */ QList GraphicsWidget::selectedNodes() { m_selectedNodes.clear(); foreach (QGraphicsItem *item, scene()->selectedItems()) { if (GraphicsNode *node = qgraphicsitem_cast(item) ) { m_selectedNodes.append(node->nodeNumber()); } } qDebug() <<"GW::selectedNodes() - " << m_selectedNodes.count(); return m_selectedNodes; } /** * @brief Returns a QList of selected directed edges structs in the form of v1,v2 * * @return a QList of selected directed edges structs */ QList GraphicsWidget::selectedEdges() { m_selectedEdges.clear(); foreach (QGraphicsItem *item, scene()->selectedItems()) { if (GraphicsEdge *edge= qgraphicsitem_cast(item) ) { SelectedEdge selEdge = qMakePair( edge->sourceNodeNumber(), edge->targetNodeNumber()); m_selectedEdges << selEdge; } } qDebug() <<"GW::selectedEdges() - " << m_selectedEdges.count(); return m_selectedEdges; } /** * @brief Starts the new node creation process when the user double-clicks somewhere: Emits userDoubleClicked to Graph::vertexCreate(), which in turn calls this->drawNode() to create and displays node info on MW status bar Yes, it's a full circle! * @param e */ void GraphicsWidget::mouseDoubleClickEvent ( QMouseEvent * e ) { if (this->dragMode() == QGraphicsView::RubberBandDrag ) { if ( QGraphicsItem *item= itemAt(e->pos() ) ) { if (GraphicsNode *node = qgraphicsitem_cast(item)) { qDebug() << "GW::mouseDoubleClickEvent() - on a node!" << "Scene items:"<< scene()->items().size() << "GW items:" << items().size() << "Starting new edge!"; startEdge(node); QGraphicsView::mouseDoubleClickEvent(e); return; } else if ( (*item).type() == TypeLabel){ QGraphicsView::mouseDoubleClickEvent(e); return; } qDebug() << "GW::mouseDoubleClickEvent() - on something, not a node!" << "Scene items:"<< scene()->items().size() << "GW items:" << items().size(); } QPointF p = mapToScene(e->pos()); qDebug()<< "GW::mouseDoubleClickEvent() - on empty space. " << "Scene items:"<< scene()->items().size() << " GW items:" << items().size() << " Emit userDoubleClickNewNode to create new vertex at:" << e->pos() << "~"<< p; emit userDoubleClickNewNode(p); } QGraphicsView::mouseDoubleClickEvent(e); } void GraphicsWidget::mousePressEvent( QMouseEvent * e ) { if (this->dragMode() == QGraphicsView::RubberBandDrag ) { QPointF p = mapToScene(e->pos()); qDebug() << "GW::mousePressEvent() - Single click on a node at:" << e->pos() << "~"<< p; // bool ctrlKey = (e->modifiers() == Qt::ControlModifier); if ( QGraphicsItem *item = itemAt(e->pos() ) ) { // // if user clicked on some item // if (GraphicsNode *node = qgraphicsitem_cast(item)) { // // if user clicked on a node // qDebug() << "GW::mousePressEvent() - Single click on a node at:" << e->pos() << "~"<< p << "SelectedItems " << scene()->selectedItems().count() << "Setting selected and emitting nodeClicked"; setNodeClicked(node); if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() - Right-click on node. " "Emitting openNodeMenu() "; emit openNodeMenu(); } if ( e->button()==Qt::MidButton) { qDebug() << "GW::mousePressEvent() - Middle-click on node. " "Calling startEdge() "; startEdge(node); } QGraphicsView::mousePressEvent(e); return; } if (GraphicsEdge *edge = qgraphicsitem_cast(item)) { // // if user clicked on an edge // qDebug() << "GW::mousePressEvent() - Single click on edge at:" << e->pos() << "~"<< p << "SelectedItems:" << scene()->selectedItems().count(); if ( e->button()==Qt::LeftButton ) { qDebug() << "GW::mousePressEvent() - Left click on an edge "; setEdgeClicked(edge); } else if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() - Right click on an edge." << "Emitting openEdgeContextMenu()"; setEdgeClicked(edge, true); } else { setEdgeClicked(edge); } QGraphicsView::mousePressEvent(e); return; } } else { if ( e->button() == Qt::RightButton ) { // // user clicked on empty space, but with right button // so we open the context menu // qDebug() << "GW::mousePressEvent() - Right click on empty space at:" << e->pos() << "~"<< p << "SelectedItems:" << scene()->selectedItems().count() << "Emitting openContextMenu(p)"; emit openContextMenu(p); } else { // // user left-clicked on empty space // qDebug() << "GW::mousePressEvent() - Left click on empty space at:" << e->pos() << "~"<< p << "SelectedItems:" << scene()->selectedItems().count() << "Emitting userClickOnEmptySpace(p)"; setEdgeClicked(0); emit userClickOnEmptySpace(p); } } } QGraphicsView::mousePressEvent(e); } /** * @brief Called when user releases a mouse button, after a click. * First sees what was in the canvas position where the user clicked * If a node was underneath, it calls userNodeMoved() signal for every node * in scene selectedItems * @param e */ void GraphicsWidget::mouseReleaseEvent( QMouseEvent * e ) { if (this->dragMode() == QGraphicsView::RubberBandDrag ) { QPointF p = mapToScene(e->pos()); if ( QGraphicsItem *item= itemAt(e->pos() ) ) { if (GraphicsNode *node = qgraphicsitem_cast(item)) { qDebug() << "GW::mouseReleaseEvent() - at:" << e->pos() << "~"<< p << "On a node. Selected items:" << scene()->selectedItems().count() << "Emitting userNodeMoved() for all selected nodes"; Q_UNUSED(node); foreach (QGraphicsItem *item, scene()->selectedItems()) { if (GraphicsNode *nodeSelected = qgraphicsitem_cast(item) ) { emit userNodeMoved(nodeSelected->nodeNumber(), nodeSelected->x(), nodeSelected->y()); } } QGraphicsView::mouseReleaseEvent(e); } if (GraphicsEdge *edge= qgraphicsitem_cast(item)) { Q_UNUSED(edge); qDebug() << "GW::mouseReleaseEvent() at:" << e->pos() << "~"<< p << "On an edge. Selected items:" << scene()->selectedItems().count(); QGraphicsView::mouseReleaseEvent(e); return; } } else{ qDebug() << "GW::mouseReleaseEvent() at:" << e->pos() << "~"<< p <<"On empty space. Selected items:" << scene()->selectedItems().count(); } } QGraphicsView::mouseReleaseEvent(e); } /** Calls the scaleView() when the user spins the mouse wheel It passes delta as new m_scale */ void GraphicsWidget::wheelEvent(QWheelEvent *e) { bool ctrlKey = (e->modifiers() == Qt::ControlModifier); QPoint numDegrees = e->angleDelta() / 8; qDebug() << "GW: Mouse wheel event - numDegrees = " << numDegrees; if (ctrlKey) { if ( numDegrees.x() > 0 || numDegrees.y() > 0) zoomIn(1); else if ( numDegrees.x() < 0 || numDegrees.y() < 0) zoomOut(1); } } /** * @brief Called from MW (magnifier button and menu icon) to decrease the zoom level of the scene. * By default it decreases zoom by 1 * @param level * */ void GraphicsWidget::zoomOut (int level){ qDebug() << "GW: ZoomOut(): zoom index "<< m_zoomIndex << " - level " << level; m_zoomIndex-=level; if (m_zoomIndex <= 0) { m_zoomIndex = 0; } emit zoomChanged(m_zoomIndex); } /** * @brief Called from MW (magnifier button and menu icon) to increase the zoom level of the scene. * By default it increases zoom by 1 * @param level * */ void GraphicsWidget::zoomIn(int level){ qDebug() << "GW: ZoomIn(): index "<< m_zoomIndex << " + level " << level; m_zoomIndex+=level; if (m_zoomIndex > 500) { m_zoomIndex=500; } if (m_zoomIndex < 0) { m_zoomIndex = 0; } emit zoomChanged(m_zoomIndex); } /** * @brief Initiated from MW zoomSlider and rotateSlider widgets * @param value * */ void GraphicsWidget::changeMatrixScale(int value) { transformationActive = true; // Since the max value will be 500, the scaleFactor will be max 2 ^ 8 = 32 qreal scaleFactor = pow(qreal(2), ( value - 250) / qreal(50) ); m_currentScaleFactor = scaleFactor ; qDebug() << "GW: changeMatrixScale(): value " << value << " m_currentScaleFactor " << m_currentScaleFactor << " m_currentRotationAngle " << m_currentRotationAngle; resetTransform(); scale(m_currentScaleFactor, m_currentScaleFactor); rotate(m_currentRotationAngle); } /** * @brief GraphicsWidget::rotateLeft */ void GraphicsWidget::rotateLeft(){ m_currentRotationAngle-=5; emit rotationChanged(m_currentRotationAngle); } /** * @brief GraphicsWidget::rotateRight */ void GraphicsWidget::rotateRight() { m_currentRotationAngle+=5; emit rotationChanged(m_currentRotationAngle); } /** * @brief GraphicsWidget::changeMatrixRotation * @param angle */ void GraphicsWidget::changeMatrixRotation(int angle){ transformationActive = true; m_currentRotationAngle = angle; qDebug() << "GW: changeMatrixRotation(): angle " << angle << " m_currentRotationAngle " << m_currentRotationAngle << " m_currentScaleFactor " << m_currentScaleFactor; resetTransform(); scale(m_currentScaleFactor, m_currentScaleFactor); rotate(angle); } /** * @brief Resets the transformation matrix to the identity matrix ( default zoom and scale ) */ void GraphicsWidget::reset() { m_currentRotationAngle=0; m_currentScaleFactor = 1; m_zoomIndex=250; this->ensureVisible(QRectF(0, 0, 0, 0)); emit zoomChanged(m_zoomIndex); emit rotationChanged(m_currentRotationAngle); } /** * @brief Repositions guides then emits resized() signal to MW and eventually Graph * which does the node repositioning maintaining proportions * @param e */ void GraphicsWidget::resizeEvent( QResizeEvent *e ) { if (transformationActive) { transformationActive = false; return; } int w=e->size().width(); int h=e->size().height(); int w0=e->oldSize().width(); int h0=e->oldSize().height(); fX= (double)(w)/(double)(w0); fY= (double)(h)/(double)(h0); foreach (QGraphicsItem *item, scene()->items()) { if ( (item)->type() == TypeGuide ){ if (GraphicsGuide *guide = qgraphicsitem_cast (item) ) { if (guide->isCircle()) { guide->die(); guide->deleteLater (); delete item; } else { qDebug()<< "GW::resizeEvent() - Horizontal GraphicsGuide " << " original position (" << guide->x() << "," << guide->y() << ") - width " << guide->width() << ") - will move to (" << guide->x()*fX << ", " << guide->y()*fY << ")" << " new width " << (int) ceil( (guide->width() *fX )); guide->setHorizontalLine( mapToScene(guide->pos().x()*fX, guide->pos().y()*fY), (int) ceil( (guide->width() *fX ))); } } } } //update the scene width and height with that of the graphicsWidget scene()->setSceneRect(0, 0, (qreal) ( w ), (qreal) ( h ) ); qDebug () << "GW::resizeEvent() - old size: (" << w0 << "," << h0 << ") - new size: (" << w << "," << h << ")" << " fX,fY: (" << fX << ","<< fY << ") scene size: (" << scene()->width() << "," << scene()->height() << ")"; emit resized( w , h ); } /** Destructor. */ GraphicsWidget::~GraphicsWidget(){ qDebug() << "GW::~GraphicsWidget() - calling clear()"; clear(); } app-2.8/src/graphicswidget.h000077500000000000000000000204571377436340000161310ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphicswidget.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 //#include class MainWindow; class GraphicsNode; class GraphicsEdge; class GraphicsNodeNumber; class GraphicsNodeLabel; class GraphicsGuide; class GraphicsEdgeWeight; class GraphicsEdgeLabel; typedef QHash H_StrToEdge; typedef QHash H_NumToNode; using namespace std; typedef QPair SelectedEdge; Q_DECLARE_METATYPE(SelectedEdge) class GraphicsWidget : public QGraphicsView { Q_OBJECT public: GraphicsWidget(QGraphicsScene *m_scene, MainWindow *m_parent); ~GraphicsWidget(); void clear(); QString createEdgeName(const int &v1, const int &v2, const int &relation=-1); void setInitNodeSize(int); void setInitZoomIndex (int); GraphicsNode* hasNode(QString text); void setNodesMarked(QList list); QList selectedItems(); QList selectedNodes(); QList selectedEdges(); void selectAll(); void selectNone(); void removeItem(GraphicsEdge*); void removeItem(GraphicsEdgeWeight *edgeWeight); void removeItem(GraphicsEdgeLabel *edgeLabel); void removeItem(GraphicsNode*); void removeItem(GraphicsNodeNumber*); void removeItem(GraphicsNodeLabel*); void setNumbersInsideNodes(const bool &toggle); 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 getSelectedItems(); void relationSet(int relation); void drawNode(const QPointF &p, const int &num, const int &nodeSize, const QString &nodeShape, const QString &nodeIconPath, const QString &nodeColor, const QString &numberColor, const int &numberSize, const int &numberDistance, const QString &nodeLabel, const QString &labelColor, const int &labelSize, const int &labelDistance); void removeNode(const int &number); void setNodeVisibility(int, bool ); //Called from Graph via MW void setNodeClicked(GraphicsNode *); void moveNode(const int &num, const qreal &x, const qreal &y); bool setNodeSize(const int &nodeNumber, const int &size=0); void setNodeSizeAll(const int &size=0); bool setNodeShape(const int &nodeNumber, const QString &shape, const QString &iconPath=QString()); bool setNodeColor(const int &, const QString &color); void setNodeNumberColor(const int &nodeNumber, const QString &color); void setNodeNumberVisibility(const bool &toggle); bool setNodeNumberSize(const int &, const int &size=0); bool setNodeNumberDistance(const int &, const int &distance=0); void setNodeLabelsVisibility(const bool &toggle); bool setNodeLabelColor(const int &number, const QString &color="green"); bool setNodeLabelSize(const int &, const int &size=0); bool setNodeLabel(const int & , const QString &label); bool setNodeLabelDistance(const int &, const int &distance=0); void drawEdge(const int &source, const int &target, const qreal &weight, const QString &label="", const QString &color="black", const int &type=0, const bool &drawArrows=true, const bool &bezier=false, const bool &weightNumbers=false); void removeEdge(const int &source, const int &target, const bool &removeOpposite=false); void setEdgeVisibility (int relation, int, int, bool); bool setEdgeDirectionType(const int &, const int &, const int &dirType=false); bool setEdgeWeight(const int &, const int &, const qreal &); void setEdgeLabel(const int &, const int&, const QString &); void setEdgeColor(const int &, const int&, const QString &); void setEdgeClicked(GraphicsEdge *, const bool &openMenu=false); void setEdgeOffsetFromNode(const int &source, const int &target, const int &offset); void setEdgeArrowsVisibility(const bool &toggle); void setEdgeWeightNumbersVisibility (const bool &toggle); void setEdgeLabelsVisibility(const bool &toggle); void setEdgeHighlighting(const bool &toggle); void startEdge(GraphicsNode *node); void clearGuides(); void addGuideCircle( const double&x0, const double&y0, const double&radius); void addGuideHLine(const double &y0); void zoomIn(int level = 1); void zoomOut(int level = 1); void rotateLeft(); void rotateRight(); void changeMatrixScale(const int value); void changeMatrixRotation(int angle); void reset(); signals: void userDoubleClickNewNode(const QPointF &); void userMiddleClicked(const int &, const int &); void userClickOnEmptySpace(const QPointF &p); void openNodeMenu(); void openContextMenu(const QPointF p); void userNodeMoved(const int &, const int &, const int &); //void userSelectedItems(const int nodes, const int edges); void userSelectedItems(const QList selectedNodes, const QList selectedEdges); void userClickedNode(const int &nodeNumber); void userClickedEdge(const int &source, const int &target, const bool &openMenu=false); void zoomChanged(const int); void rotationChanged(const int); void resized(const int, const int); void setCursor(Qt::CursorShape); private: H_NumToNode nodeHash; //This is used in drawEdge() method H_StrToEdge edgesHash; // helper hash to easily find edges QList m_selectedNodes; QList m_selectedEdges; int m_curRelation, m_nodeSize; int m_currentRotationAngle; int m_zoomIndex, markedEdgeSourceOrigSize, markedEdgeTargetOrigSize; int m_edgeMinOffsetFromNode; double m_currentScaleFactor; qreal fX,fY, factor; QString m_nodeLabel, m_numberColor, m_labelColor; QString edgeName; bool transformationActive; bool secondDoubleClick, clickedEdgeExists; bool m_nodeNumbersInside, m_nodeNumberVisibility, m_nodeLabelVisibility; bool m_edgeHighlighting; GraphicsNode *firstNode, *secondNode; GraphicsNode *markedEdgeSource; GraphicsNode *markedEdgeTarget; GraphicsEdge *clickedEdge; }; #endif app-2.8/src/graphvertex.cpp000077500000000000000000001031021377436340000160040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphvertex.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 //used for qDebug messages #include "graph.h" #include "graphvertex.h" #include "graphicsnode.h" GraphVertex::GraphVertex(Graph* parentGraph, const 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, const QString &iconPath ): m_graph (parentGraph) { qDebug() << "GraphVertex::GraphVertex() - vertex:"<< name << "initializing..."; 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_iconPath=iconPath; m_x=p.x(); m_y=p.y(); //FIXME m_outLinkColors list need update when we remove vertices/edges //m_outLinkColors.reserve(2000); m_outEdgeLabels.reserve(2000); m_outEdges.reserve(2000); m_inEdges.reserve(2000); m_neighborhoodList.reserve(1000); m_outEdgesCounter = 0; m_inEdgesCounter = 0; m_outDegree = 0; m_inDegree = 0; m_localDegree = 0; m_Eccentricity = 0; m_distanceSum = 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_enabled = true; connect (this, SIGNAL (setEdgeVisibility ( int, int, int, bool) ), m_graph, SLOT (edgeVisibilitySet (int, int, int, bool)) ); // GraphicsNode *jim= new GraphicsNode ( m_graph->canvas(), // m_name, // m_size, // m_color, // m_shape, // m_iconPath, // true, // true, // m_numberColor, // m_numberSize, // 8, // false, // m_label, // m_labelColor, // m_labelSize, // 8, // true, // p // ); } /** * @brief constructor with default values * @param name */ GraphVertex::GraphVertex(const int &name) { qDebug() << "GraphVertex::GraphVertex() - "<< 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; } /** * @brief Changes the current relation of this vertex to newRel * @param newRel */ void GraphVertex::relationSet(int newRel) { qDebug() << "GraphVertex::relationSet() - vertex:" << name() << "current relation:" << m_curRelation << "setting new relation: " << newRel; // first make false all edges of current relation edgeFilterByRelation(m_curRelation, false); // then make true all edges of new relation edgeFilterByRelation(newRel, true); // update current relation m_curRelation=newRel; } /** * @brief returns the vertex color to pajek format * @return */ QString GraphVertex::colorToPajek(){ if (m_color.startsWith("#")) { return ("RGB"+m_color.right( m_color.size()-1 )).toUpper() ; } return m_color; } /** * @brief Adds an outbound edge to vertex v2 with weight w * @param target * @param weight */ void GraphVertex::edgeAddTo (const int &v2, const qreal &weight, const QString &color, const QString &label) { qDebug() <<"GraphVertex::edgeAddTo() - new outbound edge" << name() << " -> "<< v2 << " weight "<< weight << " relation " << m_curRelation; // do not use [] operator - silently creates an item if key do not exist m_outEdges.insert( v2, pair_i_fb(m_curRelation, pair_f_b(weight, true) ) ); setOutLinkColor(v2, color); setOutEdgeLabel(v2, label); } /** * @brief GraphVertex::setOutEdgeEnabled * @param target * @param status */ void GraphVertex::setOutEdgeEnabled (const int target, bool status){ qDebug () << "GraphVertex::setOutEdgeEnabled - set outEdge to " << target << " as " << status << ". Finding outLink..."; QMutableHashIterator < int, pair_i_fb > it1 (m_outEdges); int linkTarget=0; qreal 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(pair_i_fb(m_curRelation, pair_f_b(weight, status) )); emit setEdgeVisibility (m_curRelation, m_name, target, status ); } } else { } } } /** * @brief Adds an inbound edge from vertex v1 * @param source * @param weight */ void GraphVertex::edgeAddFrom (const int &v1, const qreal &weight) { qDebug() <<"GraphVertex::edgeAddFrom() - new inbound edge" << name() << " <- "<< v1 << " weight "<< weight << " relation " << m_curRelation; m_inEdges.insert( v1, pair_i_fb (m_curRelation, pair_f_b(weight, true) ) ); } void GraphVertex::changeOutEdgeWeight(const int &target, const qreal &weight){ qDebug() << "GraphVertex::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.insert( target, pair_i_fb(m_curRelation, pair_f_b(weight, true) ) ); qDebug() << " *** m_outEdges.count " << m_outEdges.count(); } /** * @brief Removes outbound edge to vertex v2 * @param v2 */ void GraphVertex::edgeRemoveTo (const int v2) { qDebug() << "GraphVertex: edgeRemoveTo() - vertex " << m_name << " has " <0) { qDebug() << "GraphVertex::edgeRemoveTo() - 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 << " Erasing outEdge from m_outEdges "; it1=m_outEdges.erase(it1); } else { ++it1; } } qDebug() << "GraphVertex::edgeRemoveTo() - vertex " << m_name << " now has " << outEdges() << " out-edges"; } else { qDebug() << "GraphVertex::edgeRemoveTo() - vertex " << m_name << " has no edges" ; } } /** * @brief Removes the inbound edge from vertex v2 * @param v2 */ void GraphVertex::edgeRemoveFrom(const int v2){ qDebug() << "GraphVertex::edgeRemoveFrom() - vertex " << m_name << " has " << inEdges() << " in-edges. RemovingEdgeFrom " << v2 ; if (inEdges()>0) { qDebug() << "GraphVertex::edgeRemoveFrom() - 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 << " Erasing inEdge from m_inEdges "; it=m_inEdges.erase(it); } else { ++it; } } qDebug() << "GraphVertex::edgeRemoveFrom() - vertex " << m_name << " now has " << inEdges() << " in-links" ; } else { qDebug() << "GraphVertex::edgeRemoveFrom() - vertex " << m_name << " has no edges"; } } /** * @brief Filters out edges over or under a specified weight (m_threshold) * @param m_threshold * @param overThreshold */ void GraphVertex::edgeFilterByWeight(qreal m_threshold, bool overThreshold){ qDebug() << "GraphVertex::edgeFilterByWeight of vertex " << this->m_name; int target=0; qreal weight=0; QMutableHashIterator < int, pair_i_fb > 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() << "GraphVertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(pair_i_fb(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "GraphVertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(pair_i_fb(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } else { if ( weight <= m_threshold ) { qDebug() << "GraphVertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(pair_i_fb(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "GraphVertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(pair_i_fb(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } } } } /** * @brief Filters out unilateral (non-reciprocal) edges If allRelations is true, then all relations are checked * @param toggle */ void GraphVertex::edgeFilterUnilateral(const bool &toggle){ qDebug() << "GraphVertex::edgeFilterUnilateral() of vertex " << this->m_name; int target=0; qreal weight=0; QMutableHashIterator < int, pair_i_fb > it (m_outEdges); while ( it.hasNext()) { it.next(); if ( it.value().first == m_curRelation ) { target=it.key(); weight = it.value().second.first; if (hasEdgeFrom(target)==0) { // \todo != weight would be more precise? if ( !toggle ) { qDebug() << "GraphVertex::edgeFilterUnilateral() - unilateral edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(pair_i_fb(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "GraphVertex::edgeFilterUnilateral() - unilateral edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(pair_i_fb(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } } } } /** * @brief Filters out all edges of a given relation * @param relation */ void GraphVertex::edgeFilterByRelation(int relation, bool status ){ qDebug() << "GraphVertex::edgeFilterByRelation() - Vertex" << name() << "Setting edges of relation" << relation << "to" << status; int target=0; qreal weight =0; int edgeRelation=0; QMutableHashIterator < int, pair_i_fb > it1 (m_outEdges); while ( it1.hasNext()) { it1.next(); edgeRelation = it1.value().first; if ( edgeRelation == relation ) { target=it1.key(); weight = it1.value().second.first; qDebug() << "GraphVertex::edgeFilterByRelation() - outLink" << m_name << " -> " << target << " of relation" << relation << "Emitting to GW to be" << status ; it1.setValue(pair_i_fb(relation, pair_f_b(weight, status) )); emit setEdgeVisibility ( relation, m_name, target, status ); } else { } } } /** * @brief Returns the number of active outbound arcs, aka the number of * outEdges, from this vertex for the current relation * @return int */ int GraphVertex::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 Returns the number of active outbound arcs * Needs to have outEdges called before the call to this method * @return int */ int GraphVertex::outEdgesConst() const { return m_outEdgesCounter; } /** * @brief Returns a qhash of all enabled outEdges in the active relation * @return QHash* */ QHash GraphVertex::outEdgesEnabledHash(const bool &allRelations){ //qDebug() << " GraphVertex::outEdgesEnabledHash() vertex " << this->name(); QHash enabledOutEdges; qreal 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 (!allRelations) { 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() << " GraphVertex::outEdgesEnabledHash() count:" // << enabledOutEdges->count(); } } } else { if ( !enabledOutEdges.contains(it1.key() )) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; enabledOutEdges.insert(it1.key(), m_weight); // qDebug() << " GraphVertex::outEdgesEnabledHash() count:" // << enabledOutEdges->count(); } } } ++it1; } // qDebug() << " GraphVertex::outEdgesEnabledHash() vertex " << this->name() // << " outEdges count:" // << enabledOutEdges->count(); return enabledOutEdges; } /** * @brief Returns a qhash of all edges to neighbors in all relations * @return */ QHash* GraphVertex::outEdgesAllRelationsUniqueHash() { qDebug() << "GraphVertex::outEdgesAllRelationsUniqueHash() - v " << this->name(); QHash *outEdgesAll = new QHash; qreal m_weight=0; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { if ( !outEdgesAll->contains(it1.key() )) { m_weight=it1.value().second.first; outEdgesAll->insert(it1.key(), m_weight); qDebug() << "GraphVertex::outEdgesAllRelationsUniqueHash() -" << this->name() << "->" << it1.key() << "relation"<< it1.value().first; } ++it1; } qDebug() << "GraphVertex::outEdgesAllRelationsUniqueHash() - v " << this->name() << " outEdges count:" << outEdgesAll->count(); return outEdgesAll; } /** * @brief Returns a qhash of all reciprocal edges to neighbors in the active relation * @return QHash* */ QHash GraphVertex::reciprocalEdgesHash(){ m_reciprocalEdges.clear(); qreal m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); // qDebug() << "GraphVertex::reciprocalEdgesHash() - of vertex " // << this->name() // << " - outEdges " << m_outEdges.count() // << " - Checking all edges for reciprocality"; 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()) == m_weight ) { // qDebug() << "GraphVertex::reciprocalEdgesHash() - of vertex " // << this->name() // << "Found reciprocal edge with " << it1.key(); m_reciprocalEdges.insert(it1.key(), m_weight); } } } ++it1; } qDebug() << "GraphVertex::reciprocalEdgesHash() - vertex" << this->name() << "reciprocalEdges:" << m_reciprocalEdges.count(); return m_reciprocalEdges; } /** * @brief Returns a list of all neighbors mutually connected to this vertex in the active relation. * The returned list does not include the vertex itself, even if it self-connected. * Same as calling GraphVertex::reciprocalEdgesHash().keys() which returns a QList of int keys, * where each key is a vertex reciprocally connected to this one. * @return QList */ QList GraphVertex::neighborhoodList(){ m_neighborhoodList.clear(); qreal 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->name() != it1.key() && this->hasEdgeFrom (it1.key()) == m_weight ) { m_neighborhoodList << it1.key(); // qDebug() << "GraphVertex::neighborhoodList() - mutually connected neighbor=" // << it1.key() // << " m_neighborhoodList.count()" // << m_neighborhoodList.count(); } } } ++it1; } // qDebug() << "GraphVertex::neighborhoodList() - of vertex " << this->name() // << "final list" // <localDegree(); return m_neighborhoodList; } /** * @brief Returns the number of active inbound arcs, aka the number of * inEdges, to this vertex for the current relation * @return int */ int GraphVertex::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 Returns a qhash of all enabled inEdges in the active relation * @return QHash* */ QHash* GraphVertex::inEdgesEnabledHash() { qDebug() << "GraphVertex::inEdgesEnabledHash()"; QHash *enabledInEdges = new QHash; qreal 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; enabledInEdges->insert(it1.key(), m_weight); } } ++it1; } return enabledInEdges; } /** * @brief Returns the number of active inbound arcs * Needs to have inEdges called before the call to this method * @return int */ int GraphVertex::inEdgesConst() const { return m_inEdgesCounter; } /** * @brief Returns the degreeOut (the sum of all enabled outEdges weights) of this vertex * @return int */ int GraphVertex::degreeOut() { qDebug() << "GraphVertex::degreeOut()"; m_outDegree=0; qreal 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; } int GraphVertex::outDegreeConst() { return m_outDegree; } /** * @brief Returns the degreeIn (the sum of all enabled inEdges weights) of this vertex * @return int */ int GraphVertex::degreeIn() { qDebug() << "GraphVertex::degreeIn()"; m_inDegree=0; qreal 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; } int GraphVertex::inDegreeConst() { return m_inDegree; } /** localDegree is the degreeOut + degreeIn minus the edges counted twice. */ int GraphVertex::localDegree(){ int v2=0; int relation = 0; bool edgeStatus=false; m_localDegree = (degreeOut() + degreeIn() ); 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() << "GraphVertex:: localDegree() for " << this->name() << "is " << m_localDegree; return m_localDegree; } /** * @brief GraphVertex::hasEdgeTo * Checks if this vertex is outlinked to v2 and returns the weight of the edge * only if the outbound edge is enabled. * @param v2 * @return */ qreal GraphVertex::hasEdgeTo(const int &v2, const bool &allRelations){ qreal m_weight=0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.find(v2); while (it1 != m_outEdges.end() && it1.key() == v2 ) { if (!allRelations) { if ( it1.value().first == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; // qDebug()<< "GraphVertex::hasEdgeTo() - "<< this->name() // << "->" << v2 << " = "<< m_weight; return m_weight; } else { // qDebug()<< "GraphVertex::hasEdgeTo() - "<< this->name() // << "->" << v2 << " = "<< m_weight // << " but edgeStatus " << edgeStatus; return 0; } } } else { m_weight=it1.value().second.first; // qDebug()<< "GraphVertex::hasEdgeTo() - "<< this->name() // << "->" << v2 << " = "<< m_weight // << "relation"<name() // << "<-" << v2 << " = "<< m_weight; return m_weight; } else { // qDebug()<< "GraphVertex::hasEdgeFrom() - "<< this->name() // << "<-" << v2 << " = "<< m_weight // << " but edgeStatus " << edgeStatus; return 0; } } } else { m_weight=it1.value().second.first; // qDebug()<< "GraphVertex::hasEdgeFrom() - "<< this->name() // << "<-" << v2 << " = "<< m_weight // << "relation"<name() << ", " << v2 << ") = 0 "; return 0; } /** * @brief Sets distance to vertex v1 to dist * @param v1 * @param dist */ void GraphVertex::setDistance (const int &v1, const qreal &d) { // qDebug() <<"GraphVertex::setDistance() - dist" // << name() << " --> "<< v1 << " = "<< d // << " relation " << m_curRelation; m_distance.insert( v1, pair_i_f(m_curRelation, d ) ); } /** * @brief Reserves N items for the distance hash. See QHash Algorithmic Complexity * * Not to be used on large nets, atm. * @param N */ void GraphVertex::reserveDistance (const int &N) { m_distance.reserve(N); } /** * @brief Returns geodesic distance to vertex v1 * If d to v1 has not been set previously, then return RAND_MAX * @param v1 */ qreal GraphVertex::distance (const int &v1) { qreal d=RAND_MAX; int relation=0; H_distance::const_iterator it1=m_distance.constFind(v1); while (it1 != m_distance.constEnd() && it1.key() == v1 ) { relation = it1.value().first; if ( relation == m_curRelation ) { d = it1.value().second; break; } ++it1; } // qDebug() <<"GraphVertex::distance() - d(" // << name() << " --> "<< v1 << ") = "<< d; return d; } /** * @brief Removes all items from m_distance hash dictionary */ void GraphVertex::clearDistance() { m_distance.clear(); } /** * @brief Sets shortest paths to vertex v1 to sp * @param v1 * @param sp */ void GraphVertex::setShortestPaths (const int &v1, const int &sp) { // qDebug() <<"GraphVertex::setShortestPaths() - sp" // << name() << " --> "<< v1 << " = "<< sp // << " relation " << m_curRelation; m_shortestPaths.insert( v1, pair_i_i( m_curRelation, sp ) ); } /** * @brief Returns number of shortest paths to vertex v1 * If it has not been set previously, then return 0 * @param v1 */ int GraphVertex::shortestPaths (const int &v1) { int sp=0; int relation=0; H_shortestPaths::const_iterator it1=m_shortestPaths.constFind(v1); while (it1 != m_shortestPaths.constEnd() && it1.key() == v1 ) { relation = it1.value().first; if ( relation == m_curRelation ) { sp = it1.value().second; break; } ++it1; } // qDebug() <<"GraphVertex::shortestPaths() - sp (" // << name() << "->"<< v1 << ") = "<< sp; return sp; } /** * @brief Reserves N items for the ShortestPaths hash. See QHash Algorithmic Complexity * Not to be used on large nets, atm. * @param N */ void GraphVertex::reserveShortestPaths (const int &N) { m_shortestPaths.reserve(N); } /** * @brief Removes all items from m_shortestPaths hash dictionary */ void GraphVertex::clearShortestPaths() { m_shortestPaths.clear(); } /** * @brief GraphVertex::cliques * Returns the number of cliques sized size this vertex belongs to * @param size * @return */ int GraphVertex::cliques (const int &ofSize) { return m_cliques.values( ofSize ).size(); } /** * @brief GraphVertex::cliqueAdd * @param clique */ void GraphVertex::cliqueAdd (const QList &clique) { qDebug()<<"GraphVertex::cliqueAdd() - vertex:" << name() << "in a clique with:" << clique; m_cliques.insert(clique.size(), clique); } /** * @brief GraphVertex::clearPs */ void GraphVertex::clearPs() { myPs.clear(); } void GraphVertex::appendToPs(const int &vertex ) { qDebug()<<"GraphVertex::appendToPs() - vertex:" << name() << "adding" << vertex << " to myPs"; myPs.append(vertex); } L_int GraphVertex::Ps(void) { return myPs; } GraphVertex::~GraphVertex() { qDebug() << " GraphVertex::~GraphVertex() - destroying my data"; m_outEdges.clear(); m_outEdges.squeeze(); m_inEdges.clear(); m_inEdges.squeeze(); m_reciprocalEdges.clear(); m_reciprocalEdges.squeeze(); m_outLinkColors.clear(); m_outLinkColors.squeeze(); m_outEdgeLabels.clear(); m_outEdgeLabels.squeeze(); clearPs(); m_shortestPaths.clear(); m_shortestPaths.squeeze(); m_distance.clear(); m_distance.squeeze(); m_neighborhoodList.clear(); m_cliques.clear(); m_cliques.squeeze(); } app-2.8/src/graphvertex.h000077500000000000000000000330761377436340000154650ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt graphvertex.h - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 GRAPHVERTEX_H #define GRAPHVERTEX_H #include #include #include #include #include #include #include #include using namespace std; class QPointF; class Graph; typedef QList L_int; typedef QHash H_IntToStr; typedef QHash H_StrToInt; typedef QPair pair_f_b; typedef QPair pair_i_fb; typedef QMultiHash < int, pair_i_fb > H_edges; typedef QPair pair_i_f; typedef QHash < int, pair_i_f > H_distance; typedef QPair pair_i_i; typedef QHash < int, pair_i_i > H_shortestPaths; class GraphVertex : public QObject{ Q_OBJECT public: GraphVertex(Graph* parentGraph, const 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, const QString &iconPath); GraphVertex(const int &name); ~GraphVertex(); int name() const { return m_name; } void setName (const int &name) { m_name=name; } void setEnabled (const bool &flag ) { m_enabled=flag; } bool isEnabled () const { return m_enabled; } void relationSet(int newRel) ; void edgeAddTo (const int &v2, const qreal &weight, const QString &color=QString(), const QString &label=QString()); void edgeAddFrom(const int &v1, const qreal &weight); void changeOutEdgeWeight (const int &target, const qreal &weight); void setOutEdgeEnabled (const int, bool); void edgeRemoveTo (const int target); void edgeRemoveFrom(const int source); QHash outEdgesEnabledHash(const bool &allRelations=false); QHash* outEdgesAllRelationsUniqueHash(); QHash* inEdgesEnabledHash(); QHash reciprocalEdgesHash(); QList neighborhoodList(); int outEdges(); int outEdgesConst() const ; int inEdges(); int inEdgesConst() const ; int degreeOut(); int outDegreeConst(); int degreeIn(); int inDegreeConst(); int localDegree(); qreal distance(const int &v1) ; void setDistance (const int &v1, const qreal &d) ; void reserveDistance(const int &N); void clearDistance(); int shortestPaths(const int &v1) ; void setShortestPaths(const int &v1, const int &sp) ; void reserveShortestPaths(const int &N); void clearShortestPaths(); /* sets eccentricity */ void setEccentricity (const qreal &c){ m_Eccentricity=c;} qreal eccentricity() { return m_Eccentricity;} /* Returns true if there is an outLink from this vertex */ bool isOutLinked() { return (outEdges() > 0) ? true:false;} qreal hasEdgeTo(const int &v, const bool &allRelations=false); /* Returns true if there is an outLink from this vertex */ bool isInLinked() { return (inEdges() > 0) ? true:false;} qreal hasEdgeFrom (const int &v, const bool &allRelations=false); bool isIsolated() { return !(isOutLinked() | isInLinked()) ; } void setIsolated(bool isolated) {m_isolated = isolated; } void edgeFilterByWeight(qreal m_threshold, bool overThreshold); // void filterEdgesByColor(qreal m_threshold, bool overThreshold); void edgeFilterByRelation(int relation, bool status); void edgeFilterUnilateral(const bool &toggle=false); void setSize(const int &size ) { m_size=size; } int size() const { return m_size; } void setShape(const QString &shape, const QString &iconPath = QString()) { m_shape=shape; m_iconPath=iconPath;} QString shape() const { return m_shape; } QString shapeIconPath() {return m_iconPath; } 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 setNumberDistance (const int &distance) { m_numberDistance=distance; } int numberDistance() const { return m_numberDistance; } 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 &size) { m_labelSize=size; } int labelSize() const { return m_labelSize; } void setLabelDistance (const int &distance) { m_labelDistance=distance; } int labelDistance() const { return m_labelDistance; } void setX(const qreal &x) { m_x=x; } qreal x() const { return m_x; } void setY(const qreal &y) { m_y=y; } qreal y() const { return m_y; } QPointF pos () const { return QPointF ( x(), y() ); } void setPos (QPointF &p) { m_x=p.x(); m_y=p.y(); } //returns displacement vector QPointF & disp() { return m_disp; } void set_dispX (qreal x) { m_disp.rx() = x ; } void set_dispY (qreal y) { m_disp.ry() = y ; } void setOutLinkColor(const int &v2, const QString &color) { m_outLinkColors[v2]=color; } QString outLinkColor(const int &v2) { return ( m_outLinkColors.contains(v2) ) ? m_outLinkColors.value(v2) : "black"; } void setOutEdgeLabel(const int &v2, const QString &label) { m_outEdgeLabels[v2]=label; } QString outEdgeLabel(const int &v2) const { return ( m_outEdgeLabels.contains(v2) ) ? m_outEdgeLabels.value(v2) : QString(); } void setDelta (const qreal &c){ m_delta=c;} /* Sets vertex pair dependancy */ qreal delta() { return m_delta;} /* Returns vertex pair dependancy */ void clearPs() ; void appendToPs(const int &vertex ) ; L_int Ps(void); //used in reciprocity report void setOutEdgesReciprocated(int outEdgesSym=-1) { m_outEdgesSym = (outEdgesSym!=-1) ? outEdgesSym : m_outEdgesSym+1; } int outEdgesReciprocated() { return m_outEdgesSym; } void setOutEdgesNonSym(int outEdgesNonSym=-1) { m_outEdgesNonSym = (outEdgesNonSym!=-1) ? outEdgesNonSym : m_outEdgesNonSym+1; } int outEdgesNonSym() { return m_outEdgesNonSym; } void setInEdgesNonSym(int inEdgesNonSym=-1) { m_inEdgesNonSym = (inEdgesNonSym!=-1) ? inEdgesNonSym : m_inEdgesNonSym+1; } int inEdgesNonSym() { return m_inEdgesNonSym; } void setDC (const qreal &c){ m_DC=c;} /* Sets vertex Degree Centrality*/ void setSDC (const qreal &c ) { m_SDC=c;} /* Sets standard vertex Degree Centrality*/ qreal DC() { return m_DC;} /* Returns vertex Degree Centrality*/ qreal SDC() { return m_SDC;} /* Returns standard vertex Degree Centrality*/ void setDistanceSum (const qreal &c) { m_distanceSum = c; } qreal distanceSum () { return m_distanceSum; } void setCC (const qreal &c){ m_CC=c;} /* sets vertex Closeness Centrality*/ void setSCC (const qreal &c ) { m_SCC=c;} /* sets standard vertex Closeness Centrality*/ qreal CC() { return m_CC;} /* Returns vertex Closeness Centrality*/ qreal SCC() { return m_SCC; } /* Returns standard vertex Closeness Centrality*/ void setIRCC (const qreal &c){ m_IRCC=c;} /* sets vertex IRCC */ void setSIRCC (const qreal &c ) { m_SIRCC=c;} /* sets standard vertex IRCC */ qreal IRCC() { return m_IRCC;} /* Returns vertex IRCC */ qreal SIRCC() { return m_SIRCC; } /* Returns standard vertex IRCC*/ void setBC(const qreal &c){ m_BC=c;} /* sets s vertex Betweenness Centrality*/ void setSBC (const qreal &c ) { m_SBC=c;} /* sets standard vertex Betweenness Centrality*/ qreal BC() { return m_BC;} /* Returns vertex Betweenness Centrality*/ qreal SBC() { return m_SBC; } /* Returns standard vertex Betweenness Centrality*/ void setSC (const qreal &c){ m_SC=c;} /* sets vertex Stress Centrality*/ void setSSC (const qreal &c ) { m_SSC=c;} /* sets standard vertex Stress Centrality*/ qreal SC() { return m_SC;} /* Returns vertex Stress Centrality*/ qreal SSC() { return m_SSC; } /* Returns standard vertex Stress Centrality*/ void setEC(const qreal &dist) { m_EC=dist;} /* Sets max Geodesic Distance to all other vertices*/ void setSEC(const qreal &c) {m_SEC=c;} qreal EC() { return m_EC;} /* Returns max Geodesic Distance to all other vertices*/ qreal SEC() { return m_SEC;} void setPC (const qreal &c){ m_PC=c;} /* sets vertex Power Centrality*/ void setSPC (const qreal &c ) { m_SPC=c;} /* sets standard vertex Power Centrality*/ qreal PC() { return m_PC;} /* Returns vertex Power Centrality*/ qreal SPC() { return m_SPC; } /* Returns standard vertex Power Centrality*/ void setIC (const qreal &c){ m_IC=c;} /* sets vertex Information Centrality*/ void setSIC (const qreal &c ) { m_SIC=c;} /* sets standard vertex Information Centrality*/ qreal IC() { return m_IC;} /* Returns vertex Information Centrality*/ qreal SIC() { return m_SIC; } /* Returns standard vertex Information Centrality*/ void setDP (const qreal &c){ m_DP=c;} /* Sets vertex Degree Prestige */ void setSDP (const qreal &c ) { m_SDP=c;} /* Sets standard vertex Degree Prestige */ qreal DP() { return m_DP;} /* Returns vertex Degree Prestige */ qreal SDP() { return m_SDP;} /* Returns standard vertex Degree Prestige */ void setPRP (const qreal &c){ m_PRC=c;} /* sets vertex PageRank*/ void setSPRP (const qreal &c ) { m_SPRC=c;} /* sets standard vertex PageRank*/ qreal PRP() { return m_PRC;} /* Returns vertex PageRank */ qreal SPRP() { return m_SPRC; } /* Returns standard vertex PageRank*/ void setPP (const qreal &c){ m_PP=c;} /* sets vertex Proximity Prestige */ void setSPP (const qreal &c ) { m_SPP=c;} /* sets standard vertex Proximity Prestige */ qreal PP() { return m_PP;} /* Returns vertex Proximity Prestige */ qreal SPP() { return m_SPP; } /* Returns standard vertex Proximity Prestige */ qreal CLC() { return m_CLC; } void setCLC(const qreal &clucof) { m_CLC=clucof; m_hasCLC=true; } bool hasCLC() { return m_hasCLC; } void setEVC (const qreal &c){ m_EVC=c;} /* Sets vertex Degree Centrality*/ void setSEVC (const qreal &c ) { m_SEVC=c;} /* Sets standard vertex Degree Centrality*/ qreal EVC() { return m_EVC;} /* Returns vertex Degree Centrality*/ qreal SEVC() { return m_SEVC;} /* Returns standard vertex Degree Centrality*/ int cliques (const int &ofSize); void cliqueAdd (const QList &clique); void clearCliques() { m_cliques.clear(); } //Hashes of all outbound and inbound edges of this vertex. H_edges m_outEdges, m_inEdges; //Hash dictionary of this vertex pair-wise distances to all other vertices for each relationship //The key is the relationship //The value is a QPair < int target, qreal weight > H_distance m_distance; H_shortestPaths m_shortestPaths; signals: void setEdgeVisibility (int, int, int, bool); protected: private: Graph *m_graph; int m_name, m_outEdgesCounter, m_inEdgesCounter, m_outDegree, m_inDegree, m_localDegree; int m_outEdgesNonSym, m_inEdgesNonSym, m_outEdgesSym; int m_value, m_size, m_labelSize, m_numberSize, m_numberDistance, m_labelDistance; int m_curRelation; bool m_reciprocalLinked, m_enabled, m_hasCLC, m_isolated; double m_x, m_y; qreal m_Eccentricity, m_CLC; qreal m_delta, m_EC, m_SEC; qreal m_DC, m_SDC, m_DP, m_SDP, m_CC, m_SCC, m_BC, m_SBC, m_IRCC, m_SIRCC, m_SC, m_SSC; qreal m_PC, m_SPC, m_SIC, m_IC, m_SPRC, m_PRC; qreal m_PP, m_SPP, m_EVC, m_SEVC; qreal m_distanceSum; QString m_color, m_numberColor, m_label, m_labelColor, m_shape, m_iconPath; QPointF m_disp; QMultiHash m_reciprocalEdges; L_int myPs; QMultiHash m_cliques; L_int m_neighborhoodList; H_IntToStr m_outLinkColors, m_outEdgeLabels; //FIXME vertex coords }; #endif app-2.8/src/icon.rc000077500000000000000000000000611377436340000142170ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "images/socnetv.ico" app-2.8/src/images/000077500000000000000000000000001377436340000142065ustar00rootroot00000000000000app-2.8/src/images/about_24px.svg000066400000000000000000000011251377436340000167150ustar00rootroot00000000000000app-2.8/src/images/add.png000066400000000000000000000040611377436340000154450ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs íÀ,tIMEØ2)œ'±IDAT8¦Yøþÿ7*;aí/ þ-þ"ýÿúûïðòÚÐÚþЄ¡Ûäÿþüýþþþÿÿÿ®‰(L;÷?þIþMÿJ:(B5òôÿòáèøüýþÿÿÿPó åR þ…ÿþþÿþþÿÿþÿ  RÈ9, qÁÐðØûÿý³ŠL;ò4 üP3!+$ ÿýúøöú úý ã îÕÞðòýþ ýþ@ÿÿþ*- þúîùñ×äùÎþêô>)áâÿþÿ5þ(  "þ þÞâ  ý.ýÿûúþÿ   ÿÿ ¸ÚÏá8!ÖßÃÊîïÿü ÿÿýöýöÿÿøýöb¡QcŸòôö 2ýÿSÛàO¯”þ@ßÅÎÃÎøÒØ åêþý÷âý÷ßýñþñûêûôÚÔæû  ÿ ÿ<'Ô÷¶Ë›ýwýûðæáùöùáÿýôüöæúüýøøúûûüúùòÿS‹ìüA&ì³-++÷ö cèßëéþq»+Ùí ðòþõöùö÷ùûûý!¸$ûüÿM)  E1 -*Èæ!Tšôö÷Ýãêêðõ# êîôÿŠ’ùµÂóÊíþ÷äXFá2%Úçñ< ôö;ȾÎøÿÎÖâÿÿÿÿÑèþöïøþùÔÝïé»ËìÒÓø&þ086FíóùþþþýýþüüýûüýûüýûüýüýýýýþþþÙØÚÝïýîúÑÄÌ×úüþþþ! úûýùøøîïï¸þøý×»ÇÍ: ú÷õ»#"!ö÷÷ééêØÐÎ^ׯ¢ÉúûÕ¸Ñ?cûö÷ú÷øùúûü×Å¡ÌÿýòõúÿùùýûûýÿüúÍÊÅ÷ÿÿ·ÂžÊR½ x¦0IEND®B`‚app-2.8/src/images/add_48px.svg000066400000000000000000000002161377436340000163410ustar00rootroot00000000000000app-2.8/src/images/addrelation.png000066400000000000000000000014111377436340000171770ustar00rootroot00000000000000‰PNG  IHDR D¤ŠÆ&PLTE  ®®°´´¶ÂÂÅÂÂÇÌÌÐÏÏÒïïýÏÏÒÍÍѯ¯²¯¯²¶¶¸ÂÂÆÁÁÇÌÌÑ¢¢¥££¥¸¸½¹¹¾˜˜žœœ¡œœ¢¢¡¡¥¤¤ª¥¥ª¦¦©¦¦ª©©«©©¬ªª¬ªª­¬¬±­­¯­­°­­²°°²°°³¶¶º¼¼Â½½Â½½Ã¾¾ÃÆÆÊÇÇËÌÌÐÌÌÑÍÍÐÍÍÑÞÞÞßßßààßàààáááâââãããäääåååæææçççèèçèèèéééêêêëëëìììííìíííîîíîîîïïïððïðððñññòòñòòòóóóôôôõõôõõõööõööö÷÷ö÷÷÷øøøùùùúúúûûûüüüýýûýýüýýýþþýþþþÿÿþÿÿÿe[tRNSÍëñòõõýýþþþþo¿_©}IDATxÚÅÌyS‚@Æñí¾ïÓn;¤;+5-e%ŒÜ@ ˆxÿo¢Ÿ+3L3ìß}€™øò ÔTzœD™f–³([±TæJÓ‚@®”Àc¹(X¸¯Ê²\©TdQpW¯U\ŧ:¨Õêw¢…çÆ' ZMÐh4…Òj½€îŸ`"=Þâ6h©Ÿ7é»q„–®Wë¯m¥ÝÆÌ_»¸¼¼æ9×õ·î‚1VM›ùáÙét0VÎaäØÐTMÓÔ„AmÛq=…ÓΠر4øY4pƒÂ„Ã`KÁ Ù…`³§ëP@Óƒ ªªæÞ7!Øp !:t:1ø„ÁUºÁ–gRJ ÃàáŒð=Í܆ ÷ŘmÛ¦9ì(…“Mº|ÐÊA°÷GQ†Aðnßw>z½že™ûÌ縃•ÈqƘ·zOÌ!4 Mâ$öúý¾ë†§(Á?§ qø¾ïÅÊAÂ0.Á#¸ ¤8! ‚©ÀI (ÓXæñªŒ‚Tg³ËIEND®B`‚app-2.8/src/images/adjacencyplot.png000066400000000000000000000002541377436340000175350ustar00rootroot00000000000000‰PNG  IHDR szzôsIDATX…c` €‘‘ñ?:¦‡8аñÿ뚨Á1²85Ô:E`@ÓÀ€ƒZÇ5.õ£@Q8š <`ã“פˆ:E`4 x. e\ã2Ô( GsÁ€çl|rÒÆÐm–¸Fv.H [NL3wIEND®B`‚app-2.8/src/images/appsettings.png000066400000000000000000000063711377436340000172640ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞbKGDÿÿÿ ½§“ pHYs  šœtIMEà #,á+åe †IDATxÚÕ[}p\Õuÿû$Ë–+­dó!ûÝ÷Þ®Ö661Ø„–Æ|81 2c0-“¦Ã´i‚›¡$M™¶i'Ãtʤ“iZÒIÃÐd’)IÓNÜàÌ4 ™@i°°¤IûöÝg‹b –,Ëbw߯ì[±–öSZÉÊ™ÙÑj÷¾sïùÝ{>ïYA‹(å8 ‚éÿ]­— °B€«LúÆ<_íY×q xZ_Oò§€Èü8E™6¥¦L­îí-¼tìZIÒ &žÖðÁíÞ¼øâFWüuÙMòb'Dä&ߘWôEÁœ<9ýlÒ¶‘ ClܸQMŽ#pù öc"ò<€Ÿ’|0†SK€„_!ùyWX?k"‘Ç…¼Jü²“’td‚®mß àX*’—dÃð­V ZƉü(€[+ _üš»)r—ðgúóL å8}~·šðñó­]oË9  P#>ä9Îz?°!ƽŸú®ìïG!ŠnpMÝÅŠ,_²< j`ྔãt áï¾ùMŒçr Úà4+«}‘tÝé÷ýeïëQ[뀼C2j¨Íò‡®mˆHg!Š4€M ‚Ü9KpÇHd²YÀæ :§’Z#cÌâòíz*PF7ØA²)#,JuÍü,Ô”ÖéøÆØÄĺ”Ö»FŒ9ê9Ê nÓ*P2VžÖË<ÇQç¡>“‘e5¦óñ@E—:MëûûÅÓzµ§õ_ÈA’;8òß<ÇÙä’Z×diÕóížã\Jò!/ëI$&wíÜ9úßǬ]‹Ž+011ÞDb9É?ÐrC5-¿ÈU‰®® ·§çDO"±*ŸÏÿ¶ˆ|‡ä¹­7‘x*cÌ)Okœk|ú=þ_ŒÒlû 7Å_‡”È£w¬]û؇Ÿë& …ÂWüNKÕª2EÄñƒuƾ¦”Ú• ‚¡Ò†6€+³§õ#$ï¬0dJDNøG^'ù Xš”QJ}8Ù†`,ügH~¼ Ó’I_æÜ•_,Û @ûp1ÉM² ©ÀºT ¹\nÉÇ\ÔÂ…ø†Œ0Hà„g B+è#¹@üJ·hÞ}Ù0¼½a7˜ÏçÛIîi•ð"ò#ÿ à°eYÃþ¶Öø´ë.ÏGQŠä¹ƒämó˜~B)õP-.*àz’ÿ>O^R"{ ¼ê3yžŽƒ¡:þ9^ÇrëH~À sÿNߘïºZ#[%(ªøÆ<àïc=m–Έȗ®Ý¶íêŒ1/ZJMÎЈð ”:ç3 Ãíù §›þiß÷j_ñ¤\#qXéÚö÷ìiBøaùcߘýå©î|¨œ‡§õ’_ÃìšÁ¬£`O6 Ÿh:Éf§#½v¥~ÀË ®õˆˆÜê³ßîëC:™œ·ð¥PwëµmøÆ<%À-ž©óØ ˜W8Z2žÖi/‘ì®ÁgDDnó9Z-àhIÑŶá‡!<­×‘Ü_#‰ÊÅ›ñõxV …ßCÊuAr‚dªFÔuV€{ü0|r!…€Óãã¥ýTO"ñó¸ÓQE®K{º»’èîFOww[O"ÑÖÓÕe­êíU«zzdï]wñÀÁƒõOë^’G8UŒÍW}c>çõõÁ]”Ю´«õ@~±ÆñÎà4EÆ@¾+"cÆüX€ç*F‚zÍttt`(“;YExƒÒÑñ'ň¢ ‹E"Å}Ëó×®mï°­bò¸\3ÉòÒ\C‘Ûe¦åŸ‘]’ÏçX[e!7úÆ<ÕHÞÝj*yOë-$ÌÄ7#r“$G‘ìðKÙò¿Ê"ª‰ ¸IDÎfYørH®ð(É]s!ÝFòJO¸l*µù^[[ÛäP&sáÒ¼"ð“®m? `×XlWv¸¸Éßù?C™ ±HDŽÈÌ€¹äñCJdd©$üVq-ÃÍWØøkŠdÿæ GŒyk©0qºÝlM⤒¢«hª$%ñqÛ|ùå\xïý¢çëM$oDä3Ù0¼ºÀš&ç,@d^}õ‚PŠkYÖÍ"òyç*y’k Ó²–Ž Ãu«âW"@ñ"§"¥P¶oÍšRQä¤eYß0Yåø{ðz6»”Žúº*°ä'’¶íúǟכ0«"4zâÄû¾Õ÷Ǽ]e2íiÝ·T$O:NwiSªÐVG<­?,¤®ÖHj]¹(šŒu[Dž­Â0 µ„<@Ý{½$ÿ+"ÿ2é8«³Æ cÌl’®‹L–÷Tá· ä¶tÙñ…¢«·n€«bÖÝ‘û<­·Ötg®m À½–UrÀõJd"³€e°Óáý,ö(5“@&Ù¯f†”)Çéðlû>^CxØ*";âÂéâ ¯uÉxomVøX%Î@$Rå5¶”ë.¢è~÷7Èä¡”ë®ð„”ë"c 6§ÓŠäÃs4)‘ Õïy%á» …Â#þ¬ &ºP(|½“÷;΢ËåãçÎý €+æÀbJ€Ÿd‚ §J…Bá+~kÌ>æiýIÈEÑ‚ ïi sâ<­÷¸{Žl^A±ˆÕïM»Ï—äæÀl9É/zZ_„!ÛFÚóLøXݶü ª·Íµ¶ùÆ?Ï | ¿¿m|jê•9Vˆ "Ãîô9i×ÅP‹"Ťë¢ðÞ{FGKÂÿ3€-5ù¡ˆ¼`MܫܿºP)õéL<“´í"Ó×`¶}}óX먈|Á7æ[%75ïËѲ{}Oëß$ùeÉqH)uÃH6ûÞtŒìºËó@£¨@{OOÏÏ TNk=­Ÿ'yÍ<Ö|À¿,[¶ìž7FFÎ̈́ҳ—%“Ö¹\îo ìÓõªs+¥’™ x³ßu1\ã–xWŠä>ÿð€ç¸ßRê†lŠeY—ˆÈµl€Oær¹ÿõ´þ FÑÊt*uÐWl˜­eW]q¾1÷R)!Ùéi½}2—;Fàsu„D¾› ‚7“ŽSSøòPf¢E‘ˆÈÍÎttv>788øÞ¬(QëËAîpYyú ù€ç!2ìAÍbjÊqVGdŠÀ/ ðq’ w†0ê‡aS%>iÄe* éi½•ä“zœë$ŠMRÅëôã ÏÄùúJ’kâKljͥs2ÆJÝêÁãÚq`P½¦K[åWà®m¿çáM—©b7Åø¥âbƼ“+žõÃðÚFÇ7=aIø”ëö¡ù¥òyÛã\£#~ß’Ì’Àæ¤Ö›J¤å”ÂÝ(Š®«“,](ꌀh¨]¾iN½_„¸ Ø]½‰²@Þ”tœU ¢ïÒÉärÀ"”Ç8("·’\/J}Pk•{[)unA€|>ïÕŠÆÊñ‘]Jdˆ|#ög6J]ë‡áu¾1÷twŸöƒà°²¬´ˆ¼P-Qð­lîöý³É²Ó9í gÛ7øQà‹È-¾1/Ïm?‘?C½¶z‘c>¤Ê0J,IÇé&ù ÉO”=‘‘û|c©å¾[rDd@Dþ©Æ@DîðyyºÒÿ)"_B¼zµ†#ÊÃè‘lžm#cŸ‘»E¤ "O‰ÈNߘG<­áiÝð˜'§äeŽ b‰&D©øAPñ˜nÞ´ ccc{<\ä{ÛÛÛ|cd„Õâøt2©ò¹Ü*ˆL–ßù5ëçJyߘW|c®WÀq„7nYÖv?^ðªèßÀk¯ÀaoÖvç¬$|y?ñÎ;‘†'§ÆÇ'æad[T§sœK”HÇP6ÔËS®ÛU(öøH•!F€[ü0<¼Ð^fÞÍ}éµk‘·,ŒÁÿÅ@œ÷+òJ4’ÍŽ{Z?Kr{•SxJ”:¾ÁÅ¢_s—¬³§õ&’OX `\ŠM!I_D~Pê8_húµ9wi›ö-yIEND®B`‚app-2.8/src/images/arrow_down_48px.svg000066400000000000000000000002301377436340000177660ustar00rootroot00000000000000app-2.8/src/images/assessment_48px.svg000066400000000000000000000003611377436340000177770ustar00rootroot00000000000000app-2.8/src/images/avdistance.png000066400000000000000000000024661377436340000170450ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿ.l— pHYs  šœtIMEÞ 8#âÉeiTXtCommentCreated with GIMPd.ešIDATXÃí—]lUÇ3Ûí÷‚]¦Ýn»vcúE "RC1âƒXƒ"’²ÑÄD|Ð5†Q|0š`|0 j\_0"ˆ1j0Ò¢Ò’Bù((íìn?¶le·Û–Ý™ëwšÍ¦K»¥}ó$“;™¹÷ž3ÿû?çüGhZ¹êpøˆìöÏÅžîµ€ÈT9~´Í2w™Ì9@ž“×b˜’6ªi€°X Tc@pI¹ $.ötßž/Êûë·Tä; \G§ç+†Ñkx4<4b w\µ¢ÃãñdÛ`¨Ðu]™Ï×opÖ®r4uDâÄ|¾ˆÀï×ÐJaIw^cÞ,Î*îþõÕõåÄõñO^uWU%ص{”m[ÃO®ÔU×Ô  |àóEŠ-çUUI|¾ˆû”>ž@#ðÆ`ç° pdy?là:-@ P ´ßÝ/-±†6¿_…ìÓÏB!;~¿ÆÆÉŠ·­,xOæùYàeàP6Ì×¹Ëåªøa¤{»»¶hÙ¶­ø|$ Ïh¥4´ŸëU$ Ã@%°¸œ¶Ç   ×,ðxVêýö¼}ìP¥Óé˶_  vNÞ˜ Ø;JÛÇÜÉ ka©©©QMÓ|x x@ÑaÆÁáááßMÓ,–|HÈB4aÁˆD >ú™ˆÕÚÖê),)Ü«ªêJ*•:>9Ò×Ý·Ôf³½<'‹Ú—Š¢|688koeö ¯€%Ó9ð…ʼn€ÒÚÖú`É’’›3ÁÞõKfÒ…À‡º®ÿ™%3Df/xØ&™é^iUXR¸`Ýë¨[^@àJ€®3]¸¼.#x-xHUÔOu]¿2çn¨ëú éügY&€¿]™óUUÝP·¼Ž2­Œ2­l:w­; …ÞŹm¦nˆ®ë—eÈÖÍîÞ ²äƒ¢*†$Ù(`Þ£§2õÀ\ÒJu»Ý‡nÙ,Ø£‘(ÑH”À•»‹ÅOËéù¹öê{š×ë-0 Ã'„xÓ^`_Þ¼¹yÆy]§ºZû{û{$ÛsAÀ6‡/_'„øx(0RÆáñØø[ÅKŠcªª–›¦ùo,;vᯠûú{û²—$3êIº™™¤+ Q]]íTå%àUÀ-uÁ'†a|‡£@‘¼,¸ïHòNÊûä\@Ý_¿¥&ßQPut*ÐYé4ŒÕcÞ=¿Ž^­½¥LPeøZQ”ƒý ®×68k79šŠNGâ¤7 ´RèëœPÅqS5êº_Åðúí¯4¯i íB„ˆ`Ð.š×4‰oÚ“ £çc¶ÒÆ‚;w–?½ùötJ:&‰„Ê©^±þzÿÈy¶‹#™dWÊ– n©˜Mº©-±†çý~-œE±<+½hÇw~¸Ï®ÕáÎP,h¥Ð~®ïz®3Íò--1[=åZ±iµVSÔNèûJ§1X,æbè¦èœ¼‘}=‘#”¹þÆYª¦0£°LÉÅS¹–ÿ-Wû ¾÷mH IEND®B`‚app-2.8/src/images/back.png000066400000000000000000000031311377436340000156120ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs íÀ,tIMEÓ *•IMöæIDATxÚÅ—]Œ[GÇwî½þÚ¬÷#.Iv6P%¤‘6{¡D "¢ˆ– )/-m +ÈCߪ¾TBJ‰‡"õ¥È"%´* UÔˆ ‘ºÞî.]ª¶†lƒãMöÃ^Çö½¾3<øîÚŽílŠ Œôð3óÿŸã™3çÀÿyhÅxìÌô.PGAŽ£(Ù´‡šAɤwÉâ|étZ},ÆÎLŸžƒ$ˆˆXÝ@:8ÍK&þ+–úù9ÀK§Óò?0vfúšx%¿ˆÐ!Ò‘A"Ñ8;’%>¿½@Ù‹×F°j‹à,ƒòe8O[üò€ÓIˆ¶×çˆo%²é|ûÁû?µ‰û·'ˆEDÛšÜR¿þ}•ËïÂ[×L¨Ì×'÷5‹—¾”Òé´¿¡€ur= ÉûyôÀ¿xè`ŠMF×h©;¶º<[äg—Ln.,€_éÍXêÜWÅæHè]É;vlãéãeŽ}nÓø’¼ñv‰l¾Æð}Q©Z0œŠrhÊ>×5PÁ–¼Ú÷Ù!¦gY–ŸÉdT›€±3oíºÉðð0ÏœTìÜÃóU®Ì•9u$Å;\¸Zà¾>£ÍÆÐ5öïŒs«$¹¾  >“÷v]s×CÜÓ— €ä~ž<´L2‘ Zk¿IsóUNI­ÿ>u$Åì?«]ÿžoMró¶Éÿ(C¤ÿ,’)À<½á}æ4¨ïâ± ìÙ„¨6¼ŸsZÈׯµ›NG{/P(`gJòf¶¼Ò¶¼Ü[Òf¦,ËrPò 4“-©^öŒ8Tœö«»Xô:’í›G²Çä£K¼6a‚{ÉË€mŒ}ï­]Àab›ùÒîeü ?Z lÙ•üWWîJîùŠå’‹B‡Øf°F'k'öZÑ …µ 2H*Y¢b·{ÓüÕ?/u%^XqøpEãjn¸ñ1bƒ½fßA`jMÀ(ÀÖ>‰ªµVïOßrÏäÍkWÊWs#-ó‘h@Döñ†=Æ@Ü¥|‡÷O=¼­«‡Ý¢Ò<·ž^&]š "Ø$Öȧ"F2êR©ÉuÜü£ŒËß­9Q= ”˜FsJ®xnÛþÿ²k( v C¨ÿÛzüJCÁz18kQòåÞuœø±÷±z.Œ¼Ï‚ò@yY@ašAÓ©Ø>¦Þš¿üCÉÅgÅ]7Þh˜M/Ží¸á]Ë¢ãÕw–Þ%Üe„m8ö£î‡ë^†.À]®t Àa 7½ˆ©yÄv|ý'Þ=‹ˆ{-XÛÃÔ<°Aú³VüOóë©Hÿ_(—øôpOG²Ç_°9ÿT²£ˆÇ_(m‰l®RÿÿýòëèT[Èd2œÓfóÂúŠ_+oO $éÄŒvüq²Ê׬DÛæ—¦ow´_ƒ]“äo\¯{¯½üSà&Pl>]ó @v>GÌðèºñìùmºÙöF]b†Gv>W7tò/Åz “ɨ“–‘Ï« Ç—Š5vEˆ˜:¦®µáÍ¿•9¼¿€ü*ßÑÆÔ5>™)Pu>>.€Ä¤ÿÍ#DúÏ¢écQSðÉ­ ö IûÐu³eMx¬¬¬òÎ Á‡ UjžéÏâä_´¢¦ÂCw ¨6—å]“PD ˜T>‰{M]›G ÕW¿ÅK«>¶Û´Xú³øå×-ýÕ߇‡m (têŽîÚšk€ôý“µ{1û""{æîðI Ÿ/KPËâ2a’©«¡€ àwjVï©9 £a†‰‡ˆ…ßÄz2«—Ú`‡p6jNÿ ÍJö‡WÞIEND®B`‚app-2.8/src/images/box.png000066400000000000000000000004411377436340000155030ustar00rootroot00000000000000‰PNG  IHDRVÎŽWbKGDÿÿÿ ½§“ pHYsÄÄ•+tIMEß 8%ôüž1tEXtCommentCreated with GIMPW‰IDAT8Ëí”± Ã@ç,9T3߀ ¸¢ûÜPà*ÜÁw!8üÎm¤Po>Úð`na IÌÌB¼÷8çDTÕžªt™ŒMÃ+%©ÇÐLÐ <ª €K©ŒNÐ :&¨ÞpÎ4/ŸåXg$Æh}Ûrýp¯ ¸ ÷i’RŸñSù 3\ƒIEND®B`‚app-2.8/src/images/bugs.png000066400000000000000000000033451377436340000156610ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà %l-ýnrIDATXÃí–]ŒTgÇçkÎÌœ3³3»3³»³»À. h¶UQ>Bw[ˆ6bªØšLS1Ô*öÂ-­Æ¶Ñx§õ-šV[ÒVé…!¥†JÖRü E„mYvf—afw˜ï9ç¼çõÂm¢Mh' 7&>×Ïûþïÿù„ÿSLÓ¼ÐÛ~ (v8þh;¾j>Û¶·~»¤”¾mÛÛíƒ|µ6ð„išÕ0ný”ãÜ0G¯ä8 k‡ §‹ýjvvöaß÷§¯…”J¥½K‡†F\xs“em¾Ö'¾rþÙ××ç{žw®ìvó*Z­ÖÌ!8wc­¦}Ñ0¾pÈu­èÀÀIWˆfljjýÇêÎLf‡eYŸÿÓ5/ÄÁÁÁŠmÛ"ÑÈñ¯mNþ%Ý“”‹ú“òÁ­¡ƒŠØ044ôÝÁÁÁcך@txxx àèþÑßýðÛÝù 뺥ª¤®äMk{äO Ž¿ðô²'}ddä@iXk“€ÖÙÕó¡gwɯ®¿¥°qf¢fýáäõض‰®kŒ×X7܈mùº³"¤…£¯ÿ=t1Ÿ¿ôü5MA õ‰ï¨rÙ`B®Z=*O:%wìØ!Óé´ŒÇãRÕ?Ú†L,ÿy»Àí*€­õ/¾f^/• p‡'N0==ã8x®ƒïû|jÕâA)å¡kÖf$²u‹Poh—ã’Wí£åkô¤l ©ã¶Â,.´XéIÆZNäñV- L]µöÛ¾wM¤CžÁ–YYé|2%7É_)W¯Y#Ïß(ÝÁNY9‰*ǰäMƒC~ĶW^Í R‰ÄŠPWç®G«ŠkQEG" ™Õøô蛸^áyÜr÷y Á:>0<’)•ítï^ `çÎm×€‡7†B¡ŸôööŽn=Ÿ?üz_|u-dòáZÀð0¡Ÿ=ÆË¹I2Ù,Š¢PrZ|ÙŽpšèõ:/-ZD  vdr».¤Sw¥R©»9ò·J¥r©³³“F£qe¢ÑèžX,6R.—o=}úômÁP`Ãï£ažµmTEAFñë îI¥èqUáþ®8ݦ‰ªüC×ùž®óÛE½Üf³ÙÛ3™Ìý®ëÞ‹Åv‹Åÿþí{ †1àºî õ‘Þ…â™°N¿ãðb&ƒï$8'A;–«E„– JC@I áMB&Sp«PG²›Hé7IEND®B`‚app-2.8/src/images/centrality_48px.svg000066400000000000000000000014411377436340000177700ustar00rootroot00000000000000app-2.8/src/images/chevron_left_48px.svg000066400000000000000000000002271377436340000202710ustar00rootroot00000000000000app-2.8/src/images/chevron_right_48px.svg000066400000000000000000000002271377436340000204540ustar00rootroot00000000000000app-2.8/src/images/circle.png000066400000000000000000000007251377436340000161610ustar00rootroot00000000000000‰PNG  IHDRVÎŽWbKGDÿÿÿ ½§“ pHYsÄÄ•+tIMEß 8ÕHj„tEXtCommentCreated with GIMPW=IDAT8Ë­“QjÂ@EïKÆ8 ¨éBJÓ$+(nA%~+Tì¢ ÚnEÈKDé6 ýŒÜ~A1±F0´–c81†ÏÖ§ƒÿäË%[ÆðS„J³aÛó¸Qe¥¤×íò§BpœÀžïs¦ç²–1WIÙìxÏ:¹tªÌE8KÀáHÔg )‚=)Žª2´·àxl6‘ç9,Ën%Úpp'œ(Š ÆÜ,H]qß±l˜‡ø©-Zˆ`š$§‡mÏã®Æúøð·§lTÙó}n¯”¼ø>¿V«òFÖiÊŽçq.¢DP|ჵՒcfIBW„µ77|²–®ßF£RÁņó<§ªâ8F•ó¿k!Îpz”IEND®B`‚app-2.8/src/images/circular.png000066400000000000000000000041001377436340000165130ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYs¥¥pÄÞUtEXtSoftwarewww.inkscape.org›î<½IDATX…—ypÕÕÇ?ç÷~YÈÂHBBó=@ ‚¬‚ e+”›–‚RY:Œ¢ãX@­TŽb‘N‡‚ 8PdK2¬B(‹°Ä°C–¼÷;ý#ïáÏç Z3w~÷Üû=÷|ï=÷œ{¯¨*?ôåMxÚ™•Õæ—MÂCº\,¿óå¿¶Ÿ}sçç/ÿ âøÌøL₱¹iC=brMM­CDÞš@…§TªmFC†¤tmÜÐl÷× '?SUë'‘ ۞ϴ,5,K 4**4ø(yˆ¤Y"âî-]ÒwԻˎŒŒ 24© ==zHqqeÅOY® Ë˲ûÄFq"ZQQµQUÛ0·€S^¡{÷–½F OݲEƒ‡Ãp“–ròdå àµM@DB‰ÀÕkÍ 4·hÞ°kaÑ¥ÿ_~káâúGE5zà³¶Ö2M‡¸ƒ‚†_°ª~¯m©Ô-msŸ¾Do½ôè i>ýq@Û?,Îþ´âÊËwjîί^ûѳ#"BFúµåÇx0h 4öÓŸü>¿ ·ÏÁµ%°ˆ÷ñb‡ Jì3q¢3†¾ã‰ªRðræ”Nšs²~ÃÉòM›OÏ.ªj­×tya– ª`\wXVTõd=. ž9uJçäöÎèäóçoïß³§l…Ù=#&þ£Uç¤D†¹\–Ù§OlšZºrÓç§Ïùd8Óƒ @Á œp¦Ed °SUË|\]ûÎÛS¦Oí’`Ô¡¡f˜‘–5,%%2LUED4:ªÃéŒàc8VD¦í;àÍ pÁ\ «àøئªGUõÏ@’ˆäúŒ!q±³MS\.—eĶ Ï2vì<ÿqQÑåJ·[‡¸¿þúv;ýåŸÚƒlUý@U+Š2Ê….3`î˜1²€Piê™íàKñ&/TU¿©¬>ò˜TEEU±¨*csC’ÚE´iVy ðÒúÕkŽn´È>QÕG¶¶DU=ëÇE1À}U½á‘s€ ªz`ØÐ¤äìÞqóZ´l”^RRYZrêzwgÀÿŽï ¤ú‹!åé‹"lò >ý±žÿUÅ›š¨êïLºŠD¾"²`¼:Â|gêë[»¬ª—€‰ð4•ˆH+» > £í,ÿûª]ð n†®¾+0{69kײqófJߟ™™Dû`Z ‚A+aÍr8ò(@œ§ÔãD<çUìoB•—€Bõë°Ü—ÀúõìR¥Ú[.d±Æø± ÆBõ‹0ÉF`(ð½ü|—Âu7uî±€r8mÇ„†Ò85•ö¶øxºÚå^Ð;R$è }틈—€ÃÛzFõÚ:X»Îí…še°åݺ£÷ñWSÑÂBÙÛŽc»]Þ{ ¯ìNÀ^$DUy—k¬mé€ÀyÔ˜ß9T•„r–,aéš5ücÎ~úbò!gì\GçÃÏX^äÚ÷Àˆg$¼Ñ4¤l#óu7ž …ê EÜtÀa?-íaÈì_¤Þµ3ïlÍÝyöìšT6}Z—©6p0ÑSZþ?¨;šÇÛÛ‚ƒÍ@3 GU1;wl‘ß;+¶…ËeIf˜èòKwÇ«Tõ¡ˆ‘žªº_DnˆHKU½ "Tõ~>O~ÈUÕ7&MìÐcÀÏØ›Ÿ^TtéÊñ¯¾ù5€ä0 ±T‘à 3Äž\Tµh("™ªú¸õ’Ȭu°aœ^-ò—ö"Í|Œ‡/_ˆˆ)"mRR"—;æ©^δ¨ üÉÛvψ™ `:te]ÇÍR“ÚE4:UzÝ]Xtécõ¬ŸÄvI‘ÉÀ–Q0m$ h&Œ>7Eä¼u7©m@+êRó•ØÖ#DDÝnËaš†«I“à81QU:wjž8sF·‚ÔÔÈ.@Þ|jDÀ¬R¨ª…‡µðÐÁ~`40Æ»‰Xl·£_ÍΘëÚoï?ªyåþý{󪽖½ìqø9LüÞß<ý²v+T[Ps ª'xBÊï#wìÑ=æÕ·ÞèÿÞóãÛOðFF}FRÛºgÄtóÔ“Ó!b.Ì_ «ó`p=c´µÕûýýáÄÇÝöŒÏŸÜ1dÔÈÔÉNgtìîÝe'ÿ¾±tæ¦Í¥‡ýéÄÄ„…Œ“63))rpeeÕñ-ÿ<óaQñån@‘ªžò§Sï»@Uo‹ÈÙ½cKûæÄ·7ÎùôÕŠªY"2‹º‹Çwž[ãr³__Úï÷–¥†ªf‚ô**¾œ¥ªê³ãÿ±ðíŒÂZ·nlš† À²Ôp»¬öÔ]Á2D$IDD$]D†DE5Ès¹,Ó²Ô0 Ãê›ß¼YÆO²ñDååwný{OÙÖªªG8⮸Zu¿ìâ·ùööÐ ÎVU=Ühš†Ë4 —úß37_­¸wýI6êÝ"2a|û‘í›f_þlë¶3~ýÙ£uÔˆg“çÆÇ‡çܸy¿dßþò•Ÿ¬ûªèIãÿk®Õo«)Í×IEND®B`‚app-2.8/src/images/clear_48px.svg000066400000000000000000000003221377436340000166750ustar00rootroot00000000000000app-2.8/src/images/clique.png000066400000000000000000000026521377436340000162030ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYsøøÏÁæetIMEÚõ»ìI*IDATXõVmLSW~N­ÂZÛjˆãÃR\c;¨·éBÄØ`³³ šIîæœËL¦û5‘,.ΈIã¢?öcáC¢“ ÑÍ\" åK*…lÁÅ2¡¥håìÇlõ¶µl¼ÉMî}Þ¯ç¾ç¼ç=„RŠéˆRI>MMÅz™ŒYÿ䉉ëéãæM|âvÓpâp BˆN‡ŠïõþºÒlwu5vwwÓ+3F@¥"û\b, ¤ÿj‚½U^-¥ÔíG,=5õwìønK^^Ú+‘‘°õ÷¿­ÓUýZSS €ø¸¸$ÍêÕÅq±±ñ.·{¸£³³²ŽãÎð”>Ã0¯Ãå J2>™“|"vnÛvì=ƒ!͇%.^±ÏžÍç P!—'¿_XxaGAÁbŸA¯Í¦Íݰ!î—êêbr¹É§1>J!ð½Ç.Z”£ÏÍ]éo5o2ÒÓ³9:Ý¡ÉÉ )11r‹^¿òêÔÀÔyç„Z&‡·|ï ¤Ò¥R‰„×.2"B*PÈå)|JV»03##×ooÇ…`ÉÏb[º»ñƒïû»w¯[m¶q>[çð°]011áåSz½^<}úÔã76âÈö~ö&ŸÏIû ¹_SJG}Ø_·V\¼xÅ뚦Ëju7™Íg„·»º,”R%!dŠAÕåËŽöŽŽK|‰8Cù#ì¡ôthÅbfݳg¦F«ƒ5ç òzi¥¿ýOÕÕ;Ýcc§2”JMÌÂ…bKOãV[Ûé–ŽŽóJ¥15öY,.»Õê²[­®Š²2Ç›k×Âÿ`ÈR«i–ZMŸJ)|˜à;@ìdL844tŸòVÿ…\®x<2âlko?×ÒÚúOÎÊR«Ëý6&€ZŽ#Ù Þô €iŸ„“Ôr WÏ'‚ÿ+¹?ž­ÑPBˆŒ2ë?Ï‚pþŒÁ0ŒgbÂÔ"0ÿœ ¦«mmØ××G[Â&09y]Cƒ€qð%€½Î©Çu¥Ší;{ŸuvÒ){H`â-MIA¡L™H‚}UUh§!تÕ8Η66åm*ö BÈeJép@7£JÅ”íu:ÄÀü­ìPY¡–*3ÚTŸÓë“ÙwœæÝ„„QBÂó䓤˜e……ø6ÄÚÇSÊ(C‘LHøwR¾P´4¼ÃW>ˆŒdvek4»‚ÄÞý2Ýäñ`<`FE!Ó”,µú”Óiê eg³áFÀ ˜ÍøùD†ä$_D"ÓÅZŽn |åJò^ÃÉ@ú=N¶öÞ=œX±1j»ßtÚßñ„D‚k×PêïÌfZZô'ûâMJì¦Å„mjmE¥ôYÈs ?Ÿˆ‰aŽ€Xlª¼q%õõ´þe—cùrbP(°5!Ñ?|hºb·£«¡ü/ªÓºûdIRÒÒ%‰‰Û¥R©¸Çf³665•RJ½a 4:ƒ=:­ÖPzüx·o|w47»ŒÃUóÂ%—0!$zíš5_nÎË‹õab‘G^­ß´©$ÜxaX–’²AŸ——ìÏ ¡LKËœqÑÑÑ1Ñ"ïÆ™3{vÔŒ¸m±\ªã¸>ÝÀà`ïŒí-¯¨¨xätN©BeUÕÃßëꎅ½§¦Ó†„AŽNwPµjUÎܹsÅv‡ÃQÏqßܶXjÂõ7¶Á¯ºƒ(k•IEND®B`‚app-2.8/src/images/cliquenew.png000066400000000000000000000027201377436340000167110ustar00rootroot00000000000000‰PNG  IHDR@?PÎÊbKGDÿÿÿ ½§“ pHYs  šœtIMEà .8ãÆtEXtCommentCreated with GIMPW8IDAThÞíšKoEÇ»±½ë·×ëØkcc› ÎÃ1–—œ  "$âÂÃB€¸qAHHpàDQ®QDBÜ8pÍ!Üø ñ$ñ!òÊ‚$vbosØèŒfzz^»ëµKZÍlWÏÔ£{ºªÿÕ°Ëi_eO/¯%à`}·8þðzL«Ú• àân0þ &8¤~fT­³OýˆeNJø-KÓÀö §”Uµ-WªÈ¢SõR([gœ…RöGn‚ ·~½Î@© XjU‡ãLr‹ª‹Q&¹ Ì̵ªÖ¡B;[žÌZ{…VŽóUW˜UJ¾ç§@]ᨂ¬ªõkM«Åû¼ºFYÝ%§ª î’S×(+È)àoéײô~t©.>PȪN>TЭ€5`¹U ï,€*Ôýaà*p2ÕšCøøLø­fü1ì¤Ó ÿ»A¹oÖ¬æ(€ÌN7þœÓ«µK[ÈËšÁEàWÛŽ£×Hâýç´¦ck3eGÑAQ|Ú‹)¼!­iÖ0ÚS~ŽlFj×”íóÛ‚ ¿Ã•­ŒÎ ?׬Æ÷‹‚ÇL¤Ï Ö'£<#}zšÉð¬6ê… ÎÒïˆÁA4ÜLŸDN9æñC¢B•Õ"G>-ã&7/äú¸‹?! ´}¡ôÏÐåé#ëä#!uYe\!Ðoè’0µ4}²$·!«\MP9Ê”²0Ή¥Ûú²è nK²:a9ºåˆ \$ݳõ‚¬ ðSÅÎq¿:³¡p[›7duÝY&Çêå.¸Á ¿‡è»æÝ÷€M ÝauÎä+Èj.nÂ×–ºe€¾üV©-€6#{ºßW`0„¾‚ʧqá¶T +ŸPe¢¥ë@"ºÛ@V÷ûÝ¿Û: L6¶‰NIÀmËÀÈêp8# އHVÂÒç–Ï•¤ßàÕÝ­á¶a¨n@¦ ü•4ñÉØæäÂ6‚Š6ïöȆE×?E÷bË0qÈ ÌÛ^‡–¡%š8 ï#ÖxZ%S‚òšÏB7GyvÁÕ<$íK†gÒ©+3íþ$ä~"†È>÷ûäÿX@*]HÓL«±Žï%°]u¶Ï=r=ìB’’ØL…vÀ !whq`mlé²Ô/ÕÂj±`™8ŠBæ îÑ ƒþH¿ÇÒt@‡åh8ßâ0/÷£!äŒi à“–‘¤'m„ ž²üvÝ+· 23­9O—TØ_/œð… %¿‚ˆa”:\ÈŽNç-®©z8 [„µ(ã·ZÏ{|«NòrÚ&ÆT‹~Sò eF´òع/Æ0°\o˜Üd¤-ô¥‡¸nK§¿aàÕ·¤îcd»G%Ø´Cܯ­øãeÅÙoÄuÀ‚O(2ѢLj9Ͻh!sÆÕ|¬!U"ú±«yÁ ÌPPR£ñ 2_òÈ7Fiµ‰ðA—‚^ûîg„7" :êÁëuÍž|PD 4"†¶¶k×wd»-hìš^ð]¥û7-Þ{«]¸Wwoá? wCd¾%at­R;m^mÄ ¸ƒÕçrÀI"”½Òr€Uù,+°’„õ÷&:eÞˆßMuʼ'¾›î”yPùl¹EdÉ]>K¦Õ|2÷höhZ—þâ:Ò#´QãçIEND®B`‚app-2.8/src/images/close_24px.svg000066400000000000000000000003231377436340000167070ustar00rootroot00000000000000app-2.8/src/images/clucof.png000066400000000000000000000006501377436340000161700ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà :ç2y5IDATXÃíVÁ ƒ0 <£>»?$èt”ÖQÚ9ø±oÜ¡idHŒCé£~Dræìœ'Ø{&ÍFÊ\ô·ùÅXÞUgÖ_÷Á¥õž ðØÄí΀™Á£07¡T†TúM ÔUÅA×CZï€o»Žt€31,ƒ»üа üðˆPñQà›’`iC ¸P‚¥sYjB€þñ¾åutà.뼘}ðpmÎ À!¸Ñ4|Æ“´ó⤹÷kqAŸßµ]=;)kýcRL®á$ú]¦”@RÚ%ivó¢»^Z§”@¨ÿÌÀÚ¼ o'h¿†~²úÆ$1 J(äXN[’XÅês7ûwÇ-–|W¬±c–²Xò¿-ÿ‰01`µä¹l³Ib_@Û¡Jhçî“IEND®B`‚app-2.8/src/images/clustering.png000066400000000000000000000003621377436340000170740ustar00rootroot00000000000000‰PNG  IHDR szzô¹IDATX…í–Áƒ0 CýóøêpHÔÔ›Ð"åRÑäÕ1à…‡|¼x,±§"âIñ €R¦zH ”5`²CX²âLØÊ&8Ùå¾V÷Pm´ñÔ×OÈÉÊÈÆtŒF‘p  ¼ '`ní©$WÇAÅ«èQ¸=4ß”Ÿ&(À»kìynðu ûkHX‹\Þûk-–Óþ$@þ|ŒOÔòu£:3ÅþÀC°ÅºïÄ0ójIEND®B`‚app-2.8/src/images/cocitation.png000066400000000000000000000003541377436340000170520ustar00rootroot00000000000000‰PNG  IHDR szzô³IDATX…í–A€ {åÞüÿüO=‘ܱÛj¢Mz¢ÝYˆHE¡"Zӣㆧ›@ðTß4°•¢m"x_jBäz Òà Ì@/:2€ê©ðè>ªˆ«ŸuŒË:»Z“ý!U½–aÂa!LZ`›2~Vûo6Ý`¿À¼ Ó[0kdÀ â!ú«Ø ¦¾†ÈDú<`FQØP²2RÁ(¾9–¿Ê€e" ŽL¤ÃYqùφ Š©MœIEND®B`‚app-2.8/src/images/cocitation_48px.svg000066400000000000000000000012261377436340000177470ustar00rootroot00000000000000app-2.8/src/images/code_48px.svg000066400000000000000000000003121377436340000165200ustar00rootroot00000000000000app-2.8/src/images/color.png000066400000000000000000000026651377436340000160430ustar00rootroot00000000000000‰PNG  IHDRÄ´l; pHYs  šœgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF+IDATxÚbøÿÿ?-0@–Ä €„­,x.ÕG,¼•þÂÿÿeÛ² drËÝafè½#31çÄ¡ªð#Ìüªê"¢Øû92"î9ÆÀöh­ADn>„b°ˆˆƒƒ‘‘H=ŸµµuGggçË 6ü?pàÀÿC‡ý_³fÍ Øÿ˜˜˜gòòò% … LLL(Ü`­BBB  @ ÑÛ¶mûÿû÷ïÿØÀû÷ïÿïÞ½ûssóÿÈÈÈÿœœœf €¾‚ @`ƒÁÁ &Æô*ˆ|êÔ©ÿ¸À¯_¿þûöíÿ›7oþ¯\¹òMMÍÿ°°°ÿ@syA®† @pƒA–*Ι3çÿŸ?p 3ôÕ«Wÿ/\¸ð¿­­íIIÉ™NX„0222Ø``pm‰‹;zûÎÿøÀç/_þ¿xñâÿÓ§Oÿ?~üÿ¤I“Àaîããó h¦Ì`€, r-#£ìÌ%Kþÿ üÁcðÇŸ?ÿ?{þüÿÝ»wÿÏš5ëÿ¼yóþOŸ>ý^^Þ11±j˜«’æ€@Íʪüĵkÿ?#åLaáÿóÙÙÿ¯ææþ¿Ôpˆï&%ý?æïÿÿÜž=ÿ_#oéÒ¥ÿg̘ñÁ‚ÿgÏžý¿®®î¿¥¥å5˜¹78;!áæ+›ÿ•”þ¿–“ûÿˆ_ˆ‰ý¿ÂÀð÷q…¬ìÿÅË—ÿ_¶z5Ø¥ †‚âäòÖÖÖÿááá Ãd@‘@ƒ¹¹YO :ÃÄôÿ„ àÿëÊÊÿ Y¦ ð¿ÇØøc\ÜÿΉÿϦçÅ«Vý_L ËÌŸ?ìê©S§þïêêúŸ ô!//o(ùØ`q{¼¼ÿs6æfdüÏ ùŸ ŠÒööÿ]ÀpŸ·uëÿ•[¶ü_¿iÓÿõ@ÃW¬XññâÅà ˜2eÊÿ &€#°²²ò¿ŠŠÊJPØ`YíóÜÜÿs³cùÎÿ7Ãz½nãÆÿ®[¿œë@.]´ äZP¤ íîîþßtDmmí`qpd.@ ffš½À0ãã›'¬¤Ôêíëû°§§çÿÚµkÿƒ²3(#,[¶ làÂ… Áá 2t"0x@†‚·¡¡l°¯¯ï/Pq@`ƒ¹¬v†cö Êrìì쮀2(,Z´èÿܹsÁ S˜KAòõõõÿ«««ÿWUUýE $@ 6²Œ00l†™Ê ɉ Â""M¹Àä6yòd°+a†‚" æR˜¡ÿmllµq<¹!a 6€²••AE¢{TT8‚úûûáÞ¹TV€ -++ûŸLëÀò¦¤ €Àû~ ¾½@'20LrE4 kjk3€sªª··÷'ëᆂ\YTTô?((è???P;H1@ ¶d`pÿŸ“ó¨òÿ]Ýÿ§€ L)¾0ˆ )pÙÎ ++;!00ðZZÚÿ¬¬¬ÿ©©©ÿMMMŸ¥ü‘} @`ƒç00ô£ô?0ºÿSü` üÏg`xˆ¬Ž%´€ep4°ünfggÏòåу €ÀË10ØÎºòˆ°ðÿCÀ\×($ô“‡!Y(8À¥€*iiiX0a€l€àj±<œÐ —PªÆ€Á¶0i6_gÍ\ËËIEND®B`‚app-2.8/src/images/color_layout_48px.svg000066400000000000000000000012601377436340000203240ustar00rootroot00000000000000app-2.8/src/images/colorize.png000066400000000000000000000035031377436340000165430ustar00rootroot00000000000000‰PNG  IHDRàw=øgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ÕIDATxÚbüÿÿ?-@1ÒÚ€b<˜ÆÀ²ƒ‘ˆ¡‚ +ÿü``db°`áå÷øÎ,dÇÂÉ«*&-ÁÀÈÎÎðõÍ[†/ï^ÿøñýÓÅoo^nì?À°íê{†2üE· €P-â¿ÄÿþfˆåS1Ì4ñSþ+ëÌðKÔ”[„ƒ›™æ  Yß®ïexqã0Ã÷G'þسgéÊs S¿`8ýç€B±àß/W.1é>Yï2à †\2 »¯10¨‰00p°00\~òŸÁRî7ƒ<ïw†7oxÜ| ¿¿cøpk7ÃÍÕõo–l¿U?ýÃ4˜Äc ÏÖ³ëÐH™ÉÉÊ« ùÁpóÞ;†i×äd™¸Xn=``åûÎ ÿxЂ ²J ¿30>yÀ lïÏ`Vn$Â*>éÏ¿âs/34üûÏ𠀘Á†GIXzMR *ãdþúŒáÏë+ ÿ}càáå`˜ýž‹á Ã{6 >ÿcÈSúÀ ~o;Ãùù@wžeøjÃo V! quS&ÁÏg,>>}ùôîG† Äò÷ƒ’€º^—ˆ†çÁ®¤‹Ë¶>9ÊÏÅÀæç(ok‘X¦>ÍÀáõ/~`øÏð‡óƒçO)I†{oø~Þfø­,ÌÀÂqH³2˜ú%³‡Þo¬9üôí!€baä`KQòŠ—¾¾kí÷ÔO ýd8ŠÁÓOzÍÓÜ´Æ:ZóÃ?†ÿ¿ÿ0°òÿb`ø Œ,!1† ™„þðk3°°³20ÆÀ hŒ,!Nq]3]¥€K∅WV)–_Ó€AâáuŽ:ß³?¾ÿãbc`fçàˆç–Vä`øõ™åç •Àø ˜þé¿ÿ˜X9ÿ~b`áåa`ü ”uŒ‡ è6`X~zÎ .ÄÂ`¨Ìå @,¼rʼ ÿ¿2(8¸1Š ú½¹}Õ”¤DU´們øñ0¬ÿB0È’ÿà|Æÿ 6ÐÌß_ÄN û³0}\gâç`àåáS –ï^±2üzÇÀð탊<ƒš0—ý†í7`p<…úBƒÙ ô_ˆo@‰Äçå0Ùrí»´PŒA˜ç-+@±<ºta·ø‰}þâ:z ß5Ãù÷Oý„Ch0ÿ”ÿa1ÌgŸ8~üÏð—í.Ãÿ÷â ?¾2°1~û@̧þ;*ûþ’æ¯'÷D8Yù‰,Œÿ-úûBÃñwþ Ä¿€Aó þÀxúÈÌËð’I‚ýÓ+ž·w½e8zéÛ€b~ÿƒáÓ¡ +?zwâÌ‘k¹ÞÜ–æâdæãäÌ¢?0Ÿ@-æP&Ytòφ÷V“$ Þʸ0¼v„ÿÕ-†“wÿ¿ fP.þþ›áß· N?gØqæÎ÷=žÜbþ®-*%ÄÀøhØï_CÁAöáÏ×? _Óþÿ ÌŒßz¿2\”äc¸óï"ïg¿<Á`rë ÃþÛ ûˆ½ô{ýáÕé' ;¾½áåøm&&%.Z!¾øÌðó§8õ^.û,†æ ,2xÈ0Uí*Ãá ›™w0H>½Ï`x–añE†9ÄŒ­ ÿõá×µ× §$>ªÉq)sñ²‚ƒâï·? ÷ž$3ü÷føõXÞÉ3üxðA÷ÿN†ïÒÊ ^¬q ü_î3DÆðà<óE—¦3®Šâëo†o¿~ý{¨Åÿ=TNš—‘á'㟿 ·oz1<{ Éðó3Ó Ÿ=d0æZÏàqÿƒèÃë aî3|¿ôûÿŒS 3®½eX @Ìøj#`xÈóŠ(£…+0ç¾Æ5Ã'vOq-†//þ2H°ô2ˆ ^``Z.ðíÃÇ¿fc8ºöCãÏ¿ ï¯@ ÀbãòßÏ_-ù˜ÿÊ r32q^fàç»ÍÀÎý†Aä{ ƒ÷j`îedø T{X¸.<ÎpbÝu†Šw?î€Ò@1S¯ò±3k0T[(2{éÈ1«Iñÿbæ9¬ÀÚäûF†WÀªaÿÿwÞaعó>Ãô/¿ž€BTˆ#)¸²ƒ2'3ƒ9°tçåd`gca`üþœÌ?SîñGŸ®‚ëRPm¡ˆ‘‚’~Í 5ô²"€,CÉȨ£IEND®B`‚app-2.8/src/images/colorize_48px.svg000066400000000000000000000005061377436340000174410ustar00rootroot00000000000000app-2.8/src/images/communities_48px.svg000066400000000000000000000046751377436340000201620ustar00rootroot00000000000000app-2.8/src/images/connect.png000066400000000000000000000015131377436340000163450ustar00rootroot00000000000000‰PNG  IHDR ê‚£AsBIT|dˆ pHYs²-ð5tEXtSoftwarewww.inkscape.org›î<ÈIDATH‰½–]H“QÇçÕ1—ˆ¤#[¨8 óœåIS뺺 "Cè* ¢‹$/½ ©»n‚nº™„h ¢H-É ´Ò)ŒådÓ§ ?P›nº³8ïáyÞÿç㜣D„T˜Rʨªâ©ÝÎ¥œN­­±êóñÓãá±ß/_í Èv:v» ŠÚ^Ñ(¡în¼"’xM oþìï^==ü(*âB*Tõöâ?,Bh}Pk+ï ݵ..æJK 9‡ùØlØ´ÃÍf¬YYñýL&LÚá33|'Ïoi‰yípàk_ß66vbÕãá]JºÝjå|{;žpøßf›˜`¥¾žA¥âQJ]3›ù\[K_u5öÆFrçæX#0=M¿ÛÍ ­p¥Tð8)"·ö2€Rà·ˆøöhšmxø€eÀžH\ºµvà%`¬@¿ˆx Ö¤ÜÜæ€Ó‰ÆéPž Ü‘^¥Ô9YH88IÅYÀÀØþ>R|` Ð ˜ŽýctõeÀ´ÎH&sGšs¥Ô}à °¼‘`ÂÁ1,á³])•ÜÚ€@“RJý8ð¨ds¬Š—%m±,F] €*vÕ0³™êQÀ¡ãlØ©¹RJUTÐUVÆÍº:r ±ŒŒ°49‰w|œ;á0·³ˆ app-2.8/src/images/cursor-hand-scroll.svg000066400000000000000000000022451377436340000204530ustar00rootroot00000000000000 app-2.8/src/images/cursor-move.svg000066400000000000000000000024351377436340000172140ustar00rootroot00000000000000 app-2.8/src/images/cursor-pointer.svg000066400000000000000000000042371377436340000177300ustar00rootroot00000000000000 app-2.8/src/images/cursor.svg000066400000000000000000000023541377436340000162500ustar00rootroot00000000000000 app-2.8/src/images/degreematrix.png000066400000000000000000000012011377436340000173660ustar00rootroot00000000000000‰PNG  IHDR22?ˆ±bKGDÿÿÿ ½§“ pHYs  šœtIMEà %ÃR&IDAThÞíš=/Q†ŸY+[…DøJˆF"j*F#QùJ­Ê ¿@´l!Q @DØõ•H®æ&;÷ž{ÇΚӜÌξ3÷Ý™½ó¼w7 éÑ•:Î=R=c$Š]²¨dã76Wäx(‹+“òe29&„x »À° Òÿ{#¢Y“¾HŸidh·ŽFÎD3&ý4>ÓÈ‘lO9i‰¦_z+>ÓÈ£l×cûòšj+´m$ÍZÒûŸòWÒ‡¥_Z±Ö'9”>é8îH_”¾]„¾Ó­5"{žã¼Ì#5ïÀ¾Ãô›Wÿ=v—'»éF¬9 ]Vu6«è·2RñW…F€$úÕ¢¸+9{Çx-ŠkŒxÃx Šk#€WŒ× ¸6xÅx ŠgE€k`Å7ƧiÊk£–·Ö½¼¿‘pÜià%ãyÏ™G4(žrN0Œ× xZ˜n€%ߟgÅW­Ê';âXäJ£ @ÒÆã54TÐXù§F¬b@Œ÷òÐspŒ÷¼Ѭ¨k´61 (Ækµ61 (ÆkµI1À™~5+êmÒ/_ƒ¾æm¦_ÍŠºF›"ï2°UÆkµi1 f€ó¿Äx_ý ,”ÝHÌwq#uÊSæÇšÀf|g™ŒDY;Ëò7'ÒÆZa|·Õ'@Ø€Âq%°ûIEND®B`‚app-2.8/src/images/delete.png000066400000000000000000000014771377436340000161670ustar00rootroot00000000000000‰PNG  IHDRÄ´l;gAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<ÑIDAT8Ë}•KhSA†“æÑI([5îZ»P²±Ú…]µ¾v)-BIWíÎ ¢àº«„Š+QDE ŠUñ°V#­¢Å¶’j•¾4ÍÃãnÎdæNÒ>æÞÎ?fÎc®‡ˆ<&øó~¨Pgk m¡ òÚÊœ!òŠQ^laà¯aêw´JÛ"âá5…Žé¿¡!*uwS¡,f6ƒ(ÏWiS)*õôP)TÚ8hpæÍã—`Zìí¥BK åðþ]›oUæÊ´4R¾xè Ö óòIз±à¤>R—´©}mV:ô”Ö²éñˆÚ¥Ö ÌåH;Á–5.ú¨Ì)mBÖê‹Þú4ñ‚MNœÊቬóiŠˆ¦YÖ¨SykužO>ŽìäÚ hëMí=©—–ñ+IEND®B`‚app-2.8/src/images/diameter.png000066400000000000000000000003231377436340000165040ustar00rootroot00000000000000‰PNG  IHDR szzôšIDATX…í–Y À D½‡÷¿L/ÕþJjö……Æ™Gª©cÜèϜz°Ø ¡Öë `Êó:°[d5ÏÒÓF¹á¹æëîÍ2'õáÊ2gàs¤9Uó3ÕBHÛî@¦¤DÓ œlÔ˜.À|HÚâïŸÀî~çÏE`¨ˆkžŠ)¥¿Œ¾B±úB²vEQæœþŒ¯€Ú¶IEND®B`‚app-2.8/src/images/diamond.png000066400000000000000000000003601377436340000163260ustar00rootroot00000000000000‰PNG  IHDRVÎŽWbKGDÿÿÿ ½§“ pHYsÄÄ•+tIMEß 8¬”Ò tEXtCommentCreated with GIMPWXIDAT8˽ÔI @ãÿÿ/^”Ú`Á[±‹c| îã"„g":i'£‘þÍÈ@d®ð yaè †.rcS5lò§I‹-m¿t ¥+R^ZÙ7"‹í| i .ÒIEND®B`‚app-2.8/src/images/disconnect.png000066400000000000000000000016121377436340000170450ustar00rootroot00000000000000‰PNG  IHDR ê‚£AsBIT|dˆ pHYs²-ð5tEXtSoftwarewww.inkscape.org›î<IDATH‰µ–QH“QÇ÷3[bé¨Z.ÁÀÔÈ+£H0Ó¢ Þz饬ž‚I) ’¢§èEˆ^z©‚¨ˆR$,™H- ZÉBG–éÜÝ&›ž4™K›[ßwáÀǽÿïü8çžsïU"‚C)eTUqÅédo~>릦ˆ ñÓç£9ˆˆé¬©©á×ˤúÅãèÖVü.EĸÛÍã`p"8ÑnÞä[q1;­ˆºøömKEÐÓÓèúzžfïuI $ÿ_LJép› {^^j]v6Ù¦Ã?}âeO:•nlŒaÓáÀǶ6>ÏÌ,-èè âóñÄ’j·ÛÙÚØˆ/ý»Ø<&ª«y("(+¥”Ûfcd×.ÚvìÀ¹oƒƒDº» ö÷óÀë冈ˆ©p¥Ôzà>0#" ss«€2à—ˆ -ø!­”2›©EzÛZ€`p.Ç_Z†;Z©“IÑ:À1À<ÿ²¦ùZ ¯5\NŠÜäƒÀ†åúK¯ÕDÆ¿Ã`³VêJ­œ[Ù>›žŠÈ4üeÐN 4\‡Žíp4!yiÝÿqd}ó!ðD`SF>ÒÀþ¹ïSÀŠÔiðj¨²~¸”%· Rà ‡,+€÷@ÐÌlkÍ÷} 5tk8kü ÀpX½H!æ…àQ 3‚E@°*aÎ|:W¦EšlJDPJ©ÊJšÊË9±{7ù7’ÓÕÅXo/þžNG£³Å%"-i Ëéóêjzú.]¢©¢‚µªz/æ¡HU£¸ €…ÕÕ¸Uñ„9ÀSVF=0&Ö™¡8ææúõœ æþ~ #ÒCãt’^û|جv;LžLÖH"x»»é /ìvü¦‰áóa¸Ïÿ¤ªfCgýþ÷ìv|†YSƒõÔ)jG;qðĪUêèà*ÓÄsø07ŠŠØ 4yÃÍ‘Ä+o€‚Y6aù½½ôµ·Su‘qÀ^à{UÝ÷¸=(î!°¸lRUó?‰È| دªw"dïÅÀ[3òòJÒÒÓg#¢îÎΓg~è¢!˜GþÒÜÂÂ;õÇ?p»\·Ëå©;zô¯×—.=XÃzÖÇ V|*Y´¨xÛ–- ŽQ£lÁ`0`±X‚™™™ÖÏ6n|¥¯§gð  $"¢ÃHܤ‰ ÇŽ5Qí÷ùýö`0hµZ­þ'Í©Nçì°žºÜ&"Eä<à‘vÙ/"ÙCY,–Ñ! ±X,A@ Ã0 ÃHëYEÄ œæG€/àeà$p÷Q@n· È3MÓV‡ÝÞ–µwt\¬û>ÉÞ#Ño‡dq‹!-99åƒÒÒ‹® ‚×®õº].OÇ•+žKK›’““Çýó`CV9ªÚ:ØàÌPÕÆHDÄn±XŽ,\°àþ´ÜÜ$._½ÚVÛаîöíÛ]ƒuï~ÀˆS¾Çå°Xú8m¢°¶8i/?"²hUÕªx:a2€K€/ĸ(˜¤˜ªª[‡ Ó|BsxzPHl „%ª€iÀ¯€cX¿·ˆ| lºÈIð"ðgÈÛ¹„ŠADžöËTõÖ`‹ÓÓ%sþ|¶N™BŽÕе­Öš¶66jãà¤Î>~¾V†¼zØ <8BžDõ£ädž-/§9rÎØµ‹ëÓ§“?¼I~^‹%[±‚ïL3z QÅSVÆÁ¸Ÿê´ÜÜâôñã‹ÑN·û· Íͳ€Ëªz0–~V9"±ïJI!;–Õ²dñâ݇«ªºÃßþ¡ÊÊs \„úW„¾˜´z5§|>|^/¾>‚^/Ó¤W϶mÔE5¾âyó>újûö O%%¦iþ@Àæñx¼ï••}SSW÷;âñ@hq:Iª®¦$3k €ÅnÇ/‚Þ»‡¬\É×Q¡{>/o^DUÅçó9DDFe¤¥½ Üš UýcðÙåËeÇš5,+*" ©‰þòrŽUV²! Èa·'>´v8¼"¢©©©ªúEìLÀ¾}úŽÓ)?eeñªÍ†árQ×ܬG+*b4¾7o¶ya€0uuuµFêGÒÅ‹z8¹5@ž¨­ÝüÃîÝ‘û;÷ì鬩¯ÿx( xs ÊÎÊÊ™•Ÿ_ž™‘1AD¸ÞÑÑrúÌ™õmíí-#úé±2ŽƒÔIEND®B`‚app-2.8/src/images/distances.png000066400000000000000000000004341377436340000166720ustar00rootroot00000000000000‰PNG  IHDR szzôãIDATX…íWËC!ä”&, ¤Â”õš2—ĉ?Ø%¢—0ÃÉ…ÝáEzË_nƒ…,ߟ·âFbË“‰l‰£ZÓ¤WJ•;@¸%øUlDПkõxúe¹#÷HÀÁì(ù>ò=òx¹*€íÒ O9òãJÅž¹žrT€ןPãЇ®˜ºWÀ– t#Y'ÀCm‡"ó¼l ´Ø@´ýEl­W¶¡Æ„=HF›qþ ±b–ìø-–y¯@bX*†¹&=+ö0æB@m„×èIEND®B`‚app-2.8/src/images/dm.png000066400000000000000000000103201377436340000153100ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEØ  }:o"tEXtCommentCreated with GIMPW+IDATX  ßïÿÿÿÿækÿÿÿÿÿÿæZÿÿÿ?øÿ@ðÿÿÿÿÙôâQÿüåÿÿÿJß+PëÿøUÿÿÿÿÿÿmâ-KìEñÿÿÿÿÿÿÿÿÿÿÿÿâTÿÿë}ÿÿÿÿÿÿÿÿì»úÿiïïiÿÿÿtš°² ó44òÿÿÿÿÿÿÌCõ6Õ44Óÿÿÿÿÿÿ$èø¨ô„TùùRÿÿÿÿÿÿ|•Ê44õñ44ðÿÿÿÿÿÿÔ?>åð|‹ññŠÿÿÿÿÿ%üÿÿækÿÿÿÿÿ¥ÿ?øÿÿÿÿ+CÿÙÿÿÿ¬uÿJß+ÿÿÿÿÿÿÿþÿÿÿmâ-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÓÿÿÿÿ ˆÿÊ—@|:ÄIEND®B`‚app-2.8/src/images/download.png000066400000000000000000000046151377436340000165310ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÙ )!ûô IDATxÚ½W pTÕþÎ}îÞ½ûL6!!Oò0!`HØÓ‚ƒ¤/u:δ¾Û±£3vÆvhËhµ-Ú3ê Ôj§µ"¢õUÚ¤‚V‹@Ä$Â’BÈ&»ÙÍnöîÞÝ{ïéßUgÔB¡ãè·ûßsçž³çÿþïÿæì.Ãÿâ--D ¦™žŠÈãsà¬n½ÿyl^÷mˆÚ]u·u/½àÚ’ V#ˇôFƦ‘JgO~phh둃‘ÍÀ“ÇÐ|/Ùðy\C± MmëÊ«ªÊïZT÷Í‹»pl`‚ŸM03oÁÈÚˆÅfÀ‡, ¼iA=+X»¶x±w_ßíÀ&>Þç\ñ ø|w“²¿Ç¢Å¿ZuÙÊ=kÖ´·v.nB =ï² %™F^”pAG-R‰ dEbç¨n˜…KVÌoõü÷é íåÖ?ÜoßùXN‹ŸEEÝO¾wûmËŸóÜ<˰¶öŠ‚´QÀÀp&“àö¸ÑÒQ™‡‘œA&“ƒ‰h‹›y}cÅwúß/?=“üÍоÀ‰ó!pÞð=K~ðý¯½äñª<žÌ±9Ì‚ð cZðø=Ïòa^k9âI´4„áq p»e0& FÉI Ô׆˜¨H\ÅÕ'ŽWîr Û†ÏK•W®ÓÚZ[vuÕ²¡ÑKΘXue;*ãõ7â‰-ï‚«.ì;0 ÝãÂk{Ž£§÷RYÃcièA9H¨«ÔY ¤óÔt©ÐÛ|ß:'ÍÅ#+V´|ÅcÃäY€pȃ„a!šÊÄ„ñ© .¿l>DY‚1“A–<A@IЃXÒÀØX•^ôöŽaÇöÃl~[KM!˜ŒïþÛ9\­/Yºè%ES¸ÅÁ.îªCKk%&ÒyXœA”0EFÕì¸ÀIÄîý#pÀ!™Ñ‰8L=F8ìG:•…îuÃöQú¢H$ð[n2q Ô6w^çÖ¤2yÖÜF– HçŒp’؆, (-ÑaZ¶ï>JŠidB iÚÈ’KS|0EÙì V^½ Íå˜5§Í õ7€pV‚(^¿kϯ­ â@ïiLÄ €1äNƒHÁà÷©0M{"CÏÓy‚K Y†@#S$ÌdL¨nZ›ËBVTÔ6–óéLöšÿM@;ÚçU²MOÄX,ƒ”a)UÉl2Â%npJz82F `ÕÅ5•h TµT…FI‚¯OFÍ·Öz`åMV^¦wâ#üéÍi|ª*…â©,I)aÿ\d*¦à‰—ßE8 ¢±¾ =‘ÓÅÊ84’»¬TGÂt(§§`Ká–…_þh56½Á±c°Î,Ö}} }ÙüÑ›g÷]Ú,Íû`‰X]À¡¼e>’Ö‹œIîOd$#)èº ãé|š/‘‹çSÅÞ3Û!R  ·PðÀ¶q¼³i! Ÿ‚KnZßÛ†Ï@šN¾eç 7rY–XŽúê'£É$ý­îÀ]7\0ŽÈ©$NeôOä0E=ùE*1æœC&OdSã¢ui#OÂëQpøè‰3{À¶!U9œ®;žÂ–06lë)Ã}Qû†“°$ªNcâ‡}§€, (*4’ò3QÄbSˆ“ñbÄ&§`Ñá¿=N¯KL¸YP¥+¡,ª,gs<üJ/:šÊÐö"ì×04•ÅHÒ€Yp¨z†­ç¢SL2IÉŒì§0³ 4wF‚>ölÖ(c-øÈõ•¥ô %ð­îd‰Ä?"q\Ñ^…n:ÿ¯[Tƒ;—5¡½® ™Ò%J,Âò gÁHE©òÄgƒH"Ÿ‰é$ü9†<4»zå34›1–g ¢['iˉLKm ºêC  Ð|-yÅOf´èžÞX:ÇC‘IÌLNÓz7Òé,²F†‘¥È!oæðÞ@ÆÐëëÏøƒÄWv÷µesj¶ A&U$…ÈŒA/–.¬„írá»Kê‘4ò°Ž|dT…6®Ö¸@ÐulX÷Sh¾¤ ¦#PL”iR‡H̼Móx57ܲŒí¯¾ÊŽö¾EÄGØyÀñ‚È[¶\4·ê:=èõ%èƒ9ªÄ¡–™.`yƒ­!•*`7€hÊÄdÆ‚(ÉèykÊ+¡º<Ðu/…’y ý'\n¸*©ÚÅ[»w";½9öÁß7Šø ¦âo§ú¾ñ˜WžÛua]IC[c)o«ñ³«ÚÂj* ª°l ñLŽœTã$Ÿ…cû÷ŽÅeO9sD7lAE+ÈÚf²RTýT2‹\z‚GOôž²¢¿ª”͇ˆ3àÒ«Øï¼öÄÓ©xí?ŽL.I•õG3Pdçm‹õšÆàè4l#ÏO±ñ¾“*‡ú½âŸÛy‰*)c~¿~Ÿ$WU¥¨’ Ø|ÿÎg¡MÒ+Rùè{`8Ýì Êo«WB•kÕ€w¹ìqÕÉŠä6²…,+†Í”±;ÝjFTgw¯ÐtíµµwÜO²+ÐÜnP²â×xÖ´ð—'†ñááÛ­xß&œ7?‡óÂÚ狃j}ê¢oÜÅ×ÿùäå“|ã_Çø/žä+oyÀVµëL>”Ò¹ÈÇúA77?ð»Òö¶n&‘ñG"üá;.Ï3ê‰hË–•8Ž/ R°‚·ºYÐÃ|ý ƒÎ¯_ãZE g®Ð¦†ð¥©Áûæ,[Ã;ÖÞÁeãY ã 7KË!:~ ÿC|ÙÐJð1Ô@5Ά!ÜèJ„OQ¬IEND®B`‚app-2.8/src/images/eccentricity.png000066400000000000000000000004401377436340000173770ustar00rootroot00000000000000‰PNG  IHDR szzôçIDATX…íWK!å.\x©^©—êå覎?ÐG%mJòV ¼0o‰¾À8Ãl ’C>øä%ó+Aˆùus>œ ê#?L 6éÀ§ø•€i Ê+'èÝ5-7Òæ­$N¸„iA€ù‘Р±Ð¶Øn·#ÄNfÂS;~‚^s†ƒüÖhkÄ=?:S Ò¼È ~¤øÊݹv¥±²ÀÐvÞˆÅzñéí' Xi¡Ð€µ I ±b1EcˆF õ}jór>t£L0ï@app-2.8/src/images/edge_remove_48px.svg000066400000000000000000000007351377436340000201000ustar00rootroot00000000000000app-2.8/src/images/edgecolor.png000066400000000000000000000005611377436340000166610ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà )[Џ’tEXtCommentCreated with GIMPWÙIDATXÃí–Í1…ag˱K°O[Š%ØŠýăfĬI yàe¹n†ïñ³Ña‡EØ9¥Ú'ßù‹¹FG\ÚòO8¾=x‘~¬€8FÀ د ð¹ LF>'„Û€Á6NpGx[€f¤êáù]é“—á¨m‘ó{æ0Ñc»eÿ‰‹GÉ;åN§ë…å:v…KðïhÁQËè?»À¸ÃQðþEN¹÷KßÏ,â¯ý»08ô"ªuy îw+À®‘Ž0Ù+Õ2Äå{Ÿ]AO5öku;ýÒšIEND®B`‚app-2.8/src/images/edges_48px.svg000066400000000000000000000010441377436340000167000ustar00rootroot00000000000000app-2.8/src/images/edgeweight.png000066400000000000000000000011671377436340000170350ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà ;V$ÓtEXtCommentCreated with GIMPWßIDATXÃÍ×Ï‹MaÇñ×3× Í(?&™l$eÈŠdÔ`)þÅÊb,,¬Hbac¡(²²²°ÂÚdA 5+%$c̱yn9=÷ν÷œçò­gqê<ç}¾ßçûý|¿ÍÙÈ ›BCð€¢Ï÷ƒÿÀZMþE(E+>/`±[tZ¼*"þ`F1?¬hÛ"NàÖ`_ûÌ•m g"¬Àw\Âä0àp³o¯[X>W ø lÎ]z{b¨«ð§˜¨#VËÖ2Î&À¿q1“ð-±û x™ŒÊ 6â]<‡-™¥ßÁªÂv)µÆôÿ~&àç°2g–OàNüGs×÷'à÷0U£ÿ‡^6,µÓöZÀ>Ee´Cãëøµ¸’ðú N 8®­¨Ì C4…g ø‡(·um/¶—äyÉyìŒY]…¿¬nÀ&ño1‹ñr$vá[~½¯Äã+ðëc^„rC9„÷øñÔë|I¸f±*•#ØŠËø×¶¦¢ü ‡—+ñ˜G¦ñ$Âß`_¯bÑŠåR§yç¿‹Múd„8ÏÇqüjÉ™žd2ô((ÝÄëv”íÓ9½ìdãØóÈ0Ãþå…3ÔBDFI_dÑF¯ƒQIDATxc Œ5um@] $¯ª§o„è멨˜˜šc¦&ê@ÚFfæ†ZšZ:*ÌŒ´Á @lc]lfŒŠ`PŒ,‚Ñ=t+;'7¯€°˜¸„¤¤¤„¸˜°/7';+Hš‘™•ƒ‹‡_HTJFNAIEEYQ^VZBD›“…‰ŸÅc:%×IEND®B`‚app-2.8/src/images/ellipse.png000066400000000000000000000007401377436340000163520ustar00rootroot00000000000000‰PNG  IHDRVÎŽWbKGDÿÿÿ ½§“ pHYsÄÄ•+tIMEß 82ðGƒtEXtCommentCreated with GIMPWHIDAT8ËÕÓ?kÂPðóL &C6ÛL3ˆ,”âà"-tÌ÷’ZkÇBÈÐB·n%†‚àŸ/áé  ¦.í…3=øÁãž üµ‡¦Ó)'“ ¢(”J%˜¦‰b±(Ž’ú}VM“M£«(ô$‰ž$ÑM§YÐ4VM“ƒc‘N¥ÂŽ®ó $x£ëìÖjû1+“ák ð3À²aìb=×å{d›±¼kµ6Ø|>ç…¢$F¶9W.—K¦‚ @c½þõÚ]¾ï#uj@, '|íLQ¸Z­>·Í&ÇB$FÞ„à}»½»¹²aÐO€¼¼Ìf÷w©ë8¼Öu†1À'À+]g¯^o÷ÓpHǶ™SU6Òiz²LO–ÙPUæT•Žmóy4âÑG;›Í†!Â0Ü4ß²`Yòù¼À¿˜o7Ø*-ª/!IEND®B`‚app-2.8/src/images/erdos.png000066400000000000000000000034401377436340000160310ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYs¤¤¢ƒ<±tEXtSoftwarewww.inkscape.org›î<IDATX…WyPÓwÿ¼¡˜@Ê%b-‚Ãa‘xK©Ç®u—º+2ng[»t·Ýjo¦ö˜i;㺳C ãŠÔŠÇŠºµëº:c]ÛZdé.Õrˆ¶!¨ Á"DÎäõû#üã¾™ïLÞý~ïú~CÌ O@DÁÌ<$ÆKM¥T½OªTȾv ­uu8R[Ë{4è Ì,zò6+q×ÝÑ“—šwíÐ~²à¡ÅB>€€½{Ñ «ó´´``áB¬pgSìHÝ–¡–=·~u\ 9v;Û|æä?ö~·aâ…:ÉÉfgc€3Þ&@âŽê—$Ä#Â""'‰ÿ©zr9¢½uî1€ö˃ÿ°Ù~êÖŽO„8q£åMM¸-ÔΟǧ÷€ÛÚn\_°c{ú¾¿ˆß @ € ÌŒÂB¼ø0.ÁZSƒÞçŸÇ¿ßOx'ÌsÁ“¨™¡¡x`Õ*lMIÁ1ݨ(È7øþ1gž4GŒO÷CpÔ^ÉÌmš/5 3_Ó{$ÃwQÁrßʤ8Yÿ O|lœ¨|ÿøÈï…2¢=0k)óóé­ÌL28Ê4 À¥tãÌÜ`€ˆ2ˆh†«ÔIaRœ fL'©.Q²V¡ …Çž~š>úðCÔWUaë¡CøäÍ7鋘’%¢IA¤§Êä­˜ùÛÐP\FDéDtw´mvš´ÀFÇ0b·OnÜIõHOGö… °—‹ÍkAÞv”JÍÌzð¹§’þ]sâÑþ¾Ö_[–®lÊOø 0À\f†~¾ojIÑ´ÆÆÒ`ë‘âiæ-«ý^󸈲³Q0wîäTK$€ZJ°L¯xvû¶ùZ¢;ÄGWÄFDÈý¯mì-ëì¼ÕLDAD´€¤¶y\»0Åçç×,¶†3›]3>©W®à¿C"[¿·—?{ˆH1žl‰Ó¹24ÓõiЧYµðÐ@Só?Ûé3›CBH¶n½¢ÑPö݈èîæ:q{ÊÊpI8uu­©Á‡á«¢oþ0ök£tµçö "-€¯˜ÙìhÔ¸M›¨²ª MÇŽ¡¸ª 'ß}—ÎÆÆRä”1T*)Ò`ÀRR 5›Ñ^_½ ÜàäQZ¶V]ö^Ve\L¯“^²çbÛëïý'@ €Nf¾åä%&ÒœŠ |®Õâ"06oF©Û=àèx%3·ºÐƒDgjä³uiŠ_Éå ß¶ß4Ö6Z>0™ô1s¯P§°Jöíî>vîD«ÛÛ™G\ÇÎA"¢é_6Ý8 ठ°xVWç`6£Ùbd²Ét‹—Ý^F÷€qÇ&t:àÇÌß‹ Ÿ;‡Êòr\ÒÚÛ1^]W1©™ùkºÿª¥‘[áRÉñ3]ýýeæ.'ñbUòŠ%3 ¾ïºeþkyS3ßV©(zÑ"%'CßÓƒN£«®æ3÷º„Ôbôw^L<ÛýåJëõó¹Ö}Ö\T=îä忯ëÛ.ݘ)¶Ž[KK9åÉÇ}—`íò¨uù«bt>>wÁÊœˆ¸¼\Å'ÙUAB|XàLk×>œ••¥ÌrgO4"’ 3óÒ‹º·¶çœÍËSm#¢@ëuÊÚìŒþÁ±>'Þ×?dÚ2w Ü´XúÛÝ :ò“ÞÙù§eE3¦†‡'t/½z.ˆ¶0;Õ}x¥A±>dºÔïôg=5þÞU±ß¡»Ë®Ùñò4­6Ê`îºa4vU´µÝêó`ꃀôƒ’ܯG†^¶ Ï??ù¥Y¥šã”[ Ÿ½d¡,SDŸÌÑibTîûU7÷aÙLWºF¥48 ͽn¾)À7uM]n¿ZSzÀdú¡óó/Lu®ô#G¿m­6š‹‰Œ$ùêÕô‚RI*ÌÌ^9 ¾jºZ±»¼¹ÕÒ? «u û¶˜Œõ¦˜Ùæ*»y3í)-EÓñãxc÷n4<ó Þ.©{‚‡à³|é¬kÖÌynj©ÓaiGn /ÃCC°®Yƒ"o_Ş‚×ë±Q¥‚Ÿ—H` ´Zä(õ&ÿï]¸~ßÙíSé==èöÚˆ·©;!!;z—„oÈÚZXÒÒ ñÖ†Wÿ W þZÀ•j_4]åkcÅñüÌùÑO=$ÖG͜ڈ:¢&ðÌá ÜY~IØö­¶:|‡¯ÎÜÌ7]“.ìÂØ·ç¸½(†è6h`öjà}¾aëâ7Xl©Põùª¥ÍCàV®JpOÆùº»£š“JH Ò_‹@ # › ,{ZNJ9©2™UK;_Ÿ½¯Rûb[_ßïcà#¥ ø›p0‡¿«&@ƒ…Øv8å¥åp«Ò]†•ÚçðtÏæÄ}>ZŽ­NÂü4àT£¡"Û«@@Ã^׬ …8 î—Ë<&ûøØw_჆£Û¾­&@#•ÈÞN+_V£^e¯k6¹¦bœçî¨á뜃 |¼2  Ÿ:¬Aç9SYí»lüÛ®«¼}Í~ÏCàûœ÷Ôíäë¿õ60ð‰kÉxÐü–š… " «}¾¡X8†Þ¾îèäkóÍßàS7Ä6œª52ðÉêT$ 'iÀ$CaB)8aÈÌ ­¶4ØÂ×\·r8øxÅE´-ƒü>ÃÁ7ŒŒÀ½ãoª PŒ¢£œ«6[ ñ'ž¥Ðjí×vgóàfŸªËbó¦ãÓƒ0ÃÝÔÎÏÔ|&¯^æÛîªÏƒMìõ×ÂJ| ‹øø€šHF@#L¬Û;Ÿ{?ìªSO×€ GþÝ ý0½R€ô–d暊°¡ÿؘÎ|»B üAS '„öÀ7Ô(:Szõšd¶ûœÎ|ãç^ÄNËe¬€U7¬áÔ›i,·ÜÕ\}˜Z|TL”ÛŸ ‹P¯àÊV“½]v8¹Ïw¯HÎÏBóWU¡”ÅrÊí@éõE€›C|¡Õÿuu,W¾˜Pn§èLéU‘£¤LEeèÙT£ÁBÞNöJG=Ί^d°Hptæ´íôä ðÝߎ(·St¦ôJ’2Å(J2&hžÓH¥©Fƒ…¼ì•ŽL†úœZÔN‚£3ßÞvïà/ÅËéÿqý ¥·s•M]þ¿IEND®B`‚app-2.8/src/images/exit_24px.svg000066400000000000000000000005021377436340000165520ustar00rootroot00000000000000app-2.8/src/images/export_pdf_48px.svg000066400000000000000000000006241377436340000177660ustar00rootroot00000000000000app-2.8/src/images/export_photo_48px.svg000066400000000000000000000003411377436340000203420ustar00rootroot00000000000000app-2.8/src/images/file_download_48px.svg000066400000000000000000000002301377436340000204130ustar00rootroot00000000000000app-2.8/src/images/file_upload_48px.svg000066400000000000000000000002171377436340000200750ustar00rootroot00000000000000app-2.8/src/images/filter.png000066400000000000000000000043161377436340000162050ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEß65w jÙ&iTXtCommentCreated with GIMP on a Mac•ä_[)IDATXýV[lœÅþÎÌÛ«w×·ø’@JH‹í$ö†D¨ô…FÐJ¨*mÕªjûÈKy j+TÑVE´}@jU$$ŠzQ«Š*( ¥H¨Äö&Ž8Å$Äñ®í½_lïÿÿséÃþ,¡¥G:;gggÎùæ|gÎíÜs'N{C£i""šžW¸†ÜÿÕÌrµjù¾gºž`¾çs¥ 5ã`Œ)Û¶d8äø©TÜûóïÿä_ËÏðhš‚+]m2;vﱦÆyðÄSOŽrøóÅRy§ë¹Ÿ•B*%ÃJë4l"ئi¦i†Á”’B×õ„RڡɈ­qÎV ÃÌ:Ž=›L&¦ö¦Óo{êäyÚºõVDR)S Ñ+„xï[ßx€÷÷õ!_(P¡XD©TFµVG­ZC­Ñ€ëº¸žâ±âñq$ ¤’Iôôtëåå¼úëßqÃ0vd޾y hïÝûpôµW{îúBºR­ïÛßôÂ!Ç,•J48ØÞžnÝ××î®nÄãq˜¦‰fÓ¥•F]»ž!|ò<B-¥@½^G¥ZE©TÆâò2Åãhºž|ñ¥?p)åÞÙ©džv¥ €æñXù¥E íFæè[ÙÞþC3gfÜ{Çn!Íeiuu…ÖÖšÔl®‘”’LË")$VWW©Ñ¨S¹RF¡P@ni‘æççiþò<]^X l.GŽí ‘HúÏ¿ð¢ap~ßtfâõ¡Ñ4fNL`9—›žLOŽãs#»èÌÉ̤eY_yî7ÏÓ@¿×™ê‚ë¹ð=n žë…g¼¯†ƒq†ƒ`œ¡C¶nݪ~ýÛLÎù×N?úÊðXšÖSÆÛÆí»Æpf*ƒáônL?öïîÞ sÓgf¿~ßþ{ÔêÊ*4) 9!8¡B¶'‚”¾ïÁBá£@8ÂæÍ[ÄÁ§Ÿå®Ûüã/ ¦i:3¡—sÙ«´'—² Kó™Ìät,‘¬>;·ÿþ/ß+npŽp(;ä Ž@k÷ƒkëÃ÷}pƒcóM[ÔãO>Ã¥NŽ{î–mÛá8](,/]  -C»Æ0“™Ôãi:}2s4Ú‘ͽsþ®{¿¸OÖj5fYB¶p8Æx+ppj)%@„›·Ü¬}ìg¬P(=|jrü™¡Ñ4‹%“º¹º†³å  ‰žþô n4f2GȰû³‹‹éý÷ìSÍæY¶…P( Çqàû>„ðá €Ð××'ùñOy½¾òÌ©‰ãÝ>2Š®M}ú­#G_Ê]umù‡Ýçå\KÙ5’¾ƒÏNe^VÄG/\šß¶ßÝz­Ù$Û¶Ž„¡d‹­5z{{ôÃ?|„]¸xéàôäø†v±ÓS ǰ¼˜»f~½¦r˶í0m[÷ gNLþNhviš›¶oû ”VˆE£ "xž‡dGBøÑ£:—[üãLfâÁ¡Ñ4›91©|hp`×pþí³ð]PJŒ¤ï°šÍæ#/Í£\© Z©@Z­†\.‹ìbV- ¬3•<3™ uûÎQ|”°Zpvz ‚ÖZj­C¶cÁó\% •‚TR*0b`Äà i·÷ž>™ùï¬ “ð|Jj() 0" Ý^úF|Þ€Ð~†IÙ:¹RĈŸDØ…”ÒPJC*yE‰èÿ -J©Ö饄”2ÈþúÏOQë*¥„T-cí: Oh}ÆtÃ)øX©Nc퀈ZÏ2ù~¼OÁÿ>ôæ?_å•bÁt,«>ujÝÝÝ*‰B©ö;QÐ|ß7‚}FàŸ>. Z³   )…H]z÷|bîÌÌÙz½þôOŠ%“Ii;vë_P)(­Aj•r @€.RŸXà× âÖfðƒ, Y7väsS,÷¯cÇïò7oÞÄòËyŠE¢xíõ7¨XÈ.æóµ`ømf W²Ãƒ/æÆÚë7‡k•òEÛq&þþ×÷Tëè®#:™J©WaåbéP©Xȯ;Üõ(ЀjxEÌÄ^±E .yåbñ‚×lþåÜ»Êã“'îœ;wŽ]^Èb¥Q©\*^àêðÛ_§A×hv¤ÇXW<˜cë ‹ ÓdRL{Ûw¦è;µJåKÎÍͶg^]ÃÖŸ¤ Ò@²XGïßx“±~Q<‘üØwñ?™DÝ’þ“IEND®B`‚app-2.8/src/images/filter_list_48px.svg000066400000000000000000000002271377436340000201330ustar00rootroot00000000000000app-2.8/src/images/find.png000066400000000000000000000023761377436340000156440ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  šœtIMEÔ ;²[ñ¢‹IDATxÚ••[lTEÆ3ç¶—îö´´°¥>A/hAc&¢†ˆHd[y³‘‰ñ‹ÀCƒÄ”ˆáÒ- F(4Æ I´¼PÒBmK[ m·»Ë^Î9ãCi |ɼÌå—oæ?óàÖ,ÃÀÔu2™;cçÛt¨jŠ“sHÄã´þ±YèÃñ$BŒÜ:¿ÏB7t$ÎË#hY¤³Yt]G—)%J)Ï#•Jß[»kׯ÷vÈÞÞ[×\×EJÉÀà0¹lM×p=—T6‹¦iš†qש©ëôtu“Hd›G²¿gðÈõ¶v,ËÀq=\o¤¼ëÔÐ4,Ã@×uN40<ìîœHˆ§~?3˜Éäðû,4)Æ›O{[;š[(-}zp\pcãÞÁžÎDů‡’ÉæðùÌ‘ç) Ïóð<œëréŸV¾Ýþ=Ó§—ËÇkííÍ—‚¾)Z¯\|ËÎùŠ'MD×5¤#a"'ŽçÇý?1mÚK”–N¦Øº½ô³—¿ú`V ìÀŸÃõ‚ïÛ÷¼yŸØ¦™ÛP -.,”iš$W._oÌåŒ}³gÏmÎd2¶-ûbÓS‡Ë攇ho¥å†¿nYíŠÿÙ^¿ûŒz}ùNøx ýðaÉÀÍókUªe«ê?ôª:÷…­v.-¹/?$@Ó¹Ëôõô¤«¾>©ÊnUã“wXÝt¸«À&ðâ;Lõ,/LNE«–Ķæ©ùÑ]júü êQ®×½‰ú|†r{kUêbµê?8WýVV_¾‰ˆ-ûÏ«›·’ôߎS·q‘à ´êµH´|æÄØÇëV“éï&u¡žó§Zø¥9X';ºn3y/¿ûýf¬¬}¤ëígzêNí­Ø½~ÖÄR3ñüœgPÒˆÊd*3yÜÍŒÓg[èìèz,×±¿zëšÎõTìZ[ƒã/dï1–/GÌîR©TšÌÌ}éô¤ZQ>)æ÷˨þÜ2Ú{ÒGþV‡äô´ùÄIEND®B`‚app-2.8/src/images/force.png000066400000000000000000000034171377436340000160170ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEß4%ËSÈtEXtCommentCreated with GIMPWwIDATXý–kl\WÇçÞ»{½¿ëÚŽk' ÄI[¨HhÔVŠ%UK„T ©‚ôAˇFQ¨T U<  ÅuÔBJh•@Û#E u¤¦ò3o“Äö:Ùµ³¾»ëõî½ç Öv¬Äq–F0Òî‘v5çÿŸ™ÿÌÅiç?r Ejkµ&sÖÙ!« [¹"Êó r.“ ŽªÓÑtm¹œ/Üî>U.ðßê”Ý^¡Úk+í§c1û™PĮƱ@Y`Íœ 0Šb^ç³z÷5¡óƒ>oô)0·º×.ü(‘Õõê› µVG,®6ØaU1÷§Ì|ÉlH ;lUUT…*¾ÑTé ®H/½ úceà½(•kšøaeŒ;®R(–=/ru=–ºé4E‘äÕÂwš2¯±@&¬ÅÀ_U¸Ÿn`KÜåÇFhZƒ1 ƒ ÈLú¥ôûÜi°BJ5ÜíîZ۸Ƣ¾ØÌú˜ÃóŽÌð7 2))G˜¨0d]MÁ5›9r³D,‡Ps£ûrGÈj*[ÇkhltÙ^YÁƒ2 RŠ)l.6ÃFH‹OÁ (8…°3ˆ£pŒ…¥e^¡vÈŠ7†•¼œ,¾7ÇY| Ø .Æ6‰)åI Lú6§¦i`»‚ï’Âp1¤É„‹TDŠAK]4hÕUjÒ!¬À”ô`@)ª«q¶ÅÅ Ô°$nñ}Ç*eÓòbqƃ‘l€ïŠç]-ðN_þ¾,éöj¢4ò‰•5<¼´*¿¹"jˆ¦MIçV©Ú¡°j8·|"«Ý’@§C¸Úæ«1‡uF—D®”bdJ14aH‹Œ ;~>Ng®Ìúõåá/c|¼Õñ9úkýQCtNf D7V;«™Gà&¶Ei®qxÎDfR(.d!Q4©3?{1ÇŽùà7Xðô¿èHøêÀl'`4Ñ&œ›ÖËoÙ‡c¸ÍOE–™™/J1’WœÍÆáÍ_ÀôíæÇX¾ê³Àègøšn¾¥-Es]ˆg­™è- í+ÎæãFzϯœéy_1¨Æ˜ëãN ã‰uÅHlA{!´ÔgKÄ¡a¶´RŒgrZgáðp¼ÜÝÑä7—”ê¨EJjS„Ü‚%X»wÏŠ»7~é{äJ ¶< §§ 'òчð‹,•ù6ôHãzw:ÿ0šÒÔ2šbA38nŠE8wßîyÕY²qÓó•;÷ÅǺ°¿µ2–ÎçÌt¿'˾17ù"‰ˆ.Q6Î' 9aºÞ½Éa```uh-ï‹Îed¼û¸ì_ý€lƒî‡örÁ‡×Ö>Y\¥¦‚UHp¢?…îEö[èŸ@×ÂwÚÚÚ¬ÖÖÖJ«U@)´á’¯9í„3“ðV?œ)—@m6·]´T5§=N¥`Ò0u^¹±ŒÖÁƒ?‰D¿>¶ÙL†{zÑ+WõïŽrÁÄŸQE¿ƒ)í£bN¦0Ðý&¸iö÷÷U_Âó2cÐZ3:2B¦P˜˜¬®~=#倯۴Îçò?@KÔ]"ÐsòÂôQx‰ëÏ–ë>t^šSƒýL\'™J‘L&‰D"}»víú}¹Ñ¿íe¤pÚ 1¥jzyèõ0Wàý¿/$>ÀN$Æ mmmõ¯}æÜ™sòùõë­ÉÉÉÔ‘#G^êîî>QxWWWëŠm?Ýe?ô…»$ÄeКccš ÍÔŸáÛËe2îFÖ¶46ýéëO<¹ãĉîw=ú×rß‹žçýÊ÷ý| "ˆ^Zþ½íYù ÈVØ·¨óÒ–{œ¶%-ÏÍkT— ÞÓÓ³Ò÷ý AÈÜÇy}o§ì„Éåpïbþ–ˆÔäòùƒóÚC€Ér ´´´|׳D©ÒûÖ²,. 14šà|4Òq%pu<™¿6q–i©Tj…çyn¥ý¡5½½½„m'¹oÙ²_ÞÎß™.îÀR©ä˜ ‘uêëïB)…ïû$Æ;Rƒoçoq‡–N§þó]vÿz7§±m›ééé ¿+Çß¹S=öµq7²ßrìe_ÞðÇó¼5}}}¯Ø¶}•ÿµÅªêÔ²{Z7´67ß_S]B[·n}ˆ•{‡º-MÍQz-ztìJRóÿ¶†º:ûNïøò /9H 6IEND®B`‚app-2.8/src/images/format_color_fill_48px.svg000066400000000000000000000006231377436340000213070ustar00rootroot00000000000000app-2.8/src/images/format_color_text_48px.svg000066400000000000000000000003401377436340000213410ustar00rootroot00000000000000app-2.8/src/images/format_shapes_48px.svg000066400000000000000000000005451377436340000204510ustar00rootroot00000000000000app-2.8/src/images/format_textsize_48px.svg000066400000000000000000000002311377436340000210350ustar00rootroot00000000000000app-2.8/src/images/forward.png000066400000000000000000000031571377436340000163660ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs íÀ,tIMEÓ é3€rüIDATxÚÅ—]lWÇóµ»cÇkïfi;©“âV!‘ã T""ªJ}i¡X`É­úPU$¤ @H< JWN ¤BíC õ!%J•¶)•êIm·)_‹’¦ÎºñÇ;³;3÷ò°³ön½k§Ä•þZÍžsçüϹgÎ9þÏKù8Êã“;@1 !EÃ;äRL!¼³'3€H§Óò†|¸8 F@Õ„ Û¸å<Â?eÉñ€—N§ÅD`xtr/ŠúR|UƒHD’D¢&ÛâK|vk€¢ g.õã¸U¨Ì»Ò¤Cà>fñ›_n+"Ê:^ŸÀÜLdÃM<´o†Ý·là¶­Ä"êª=3óþònWþ o^2 t¹&ª/X<ý-`)NûëX6®E!~÷î}ŸcûS$6èm£%?òªW¦óüê¬Á³³àAxS–<ñU`®1Z[ãzÛ¶má±;ŠþLCWñMøî©"§'«Ü!² }©(!Wô¹4§€ 6eå®Oõ2ù˲|Û¶å*ãoíåE4ƒ¾¾>?*Ù¾)†çËU8þ\™ñ±$Çö™<:^às·j«ttMa÷v“kK‚+ òÖ¬·ãõ^õâ•=ȧ!€ø p‰wè”+²%ÆÇ’Ë›ÆÇ’üäy§¥žðCqnNÔ”#=Ç 7E`xÔ~ä·1{¹o_ŽýðÙ[SÑ&æÇö™|ï™ÙÓ¯¬Ò•Àö”à\¦¼¥-Y±3ß«L]°,«º)îM©.û;)¹bM´ZãcIžú³×R?Þiðõ¡(h±»€ÀP†¿óæà ±|i`?€r%Xzc±-‰g^mÖ-”|þ•-#Ñ ¶mh¢rd'«W‡jç“$_¢äˆë*ÍϾ<Ï=·§Z’xè—‹ìé-óÞ¢Â3}+ˆÎ,Ýû uC›»’š÷¯þÃX—Äé·›²™üm¡Y‰šTÔÈ `®Ðb$Ì*ÅÐû·Þ¶|ñÇYãcIü0L±°-i*µ†¶”/ÿX4t»æªíÍç¼–q£¡ã8n5lÑ• ^Íšð΢iP]@U·\—guë_Îüúª.„¿9Wg¸ó8sЇ©·‡ëë­mük?óH˜+¨ï5œ9þ´ež¾ 8õx „ •Ï/åçùd_g[Ï1€“Ç[Ê¿ù‹%±Ö{33¥ÚùûÅ—Ð(Ž`Û6G‡•é¬j}ů·¦qzÌ€˜ÞwZ« <òÔ|[}§"È^½Ró^ùíÏ€|ãŒå¸£™ç2—gøÂžO`h×ÿI~ÿäUº¢íòE!sy®öàfŸ$JÈÞr~Ú¶-Zz6+? „zÇ|¾Â@o„ˆ¡ahÊ*œ{»ÈÁÝ]üèwÙ–:†¦ âóÚT޲ëA5÷„eüþ òét:hÉ,Ë’½LNfå®B5P_™«ÒŸRé0£èš² ç/–8±ÔR¦k ®SæÌ…%'4®=÷<0 ÌUÛ¶›gBÛ¶±,+¨“ð„vøï3.žPÙ¶QÅÐUt•u¡HŸ‰–yý<ž/gk€S¿5µ¬Ò###*Ð1áß};‘žã(ÚpÔP¹ys»zÉd7šÖܪƒÀcq±À;WUÞ›-Sñ7û¤}ñB˜t×€rãXÞ¶M„$b@bBÞû Zì.m¨.7#ê®åð|ÁÇ©6lþ4~ñ%K{öa²Í¹V·£5¯f###J8½v=•#;1º÷£FQ°¥†µÚËT2TsvXdJ@!$PüV—Õ뺜†Ñ0ˆ˜!báêr1p'„»Þåôß»2F©V~LIEND®B`‚app-2.8/src/images/fullscreen_48px.svg000066400000000000000000000003001377436340000177450ustar00rootroot00000000000000app-2.8/src/images/gridlines.png000066400000000000000000000020341377436340000166730ustar00rootroot00000000000000‰PNG  IHDR M )sBIT|dˆ pHYsÊÊk²tEXtSoftwarewww.inkscape.org›î<™IDATH‰×YUEð_Ÿa™†aApdD ÜB Á0l‰‰Иè×òÙO Q#&ú¦/"HÀHBÐ e5À”ç\Ó·9wæ^+¹ÉíUuUWUWõI¡J)-ÀV gp…„q†pg£ÏÓlr)¥U˜ÄœÆÕˆ˜žA~#žÃ}|÷þ—)¥1ìÅoñýŒ^¶ëW؇¹ø´gD"â‘vb U¿]0 ¿Â¬lã?”Ò<Œˆ[N5ªo…Q¬T‡z —p·ñsyâ”Òn,Œˆ“=#€¥Ø×ã${ñVct^™EØ€w°¹…¿ Ûº°Œ¹ Z”Fñžž-™Î|¼Ô8’ Þ›ùrÆæÂ8֯᎜hqâPW 4Wg("Îf9›‹8·3¼Â«˜Vßûá&z7ðYD<(r¿cñm†M⯈ø¥ãQ[è·b²ÀváíÆàPÁ[ÒÔÈËŠÛƒã)°Ã:]Š[ò~° ^(ÃÙâøZœhÁß+Ö¯5)¶ -…2‘­ŸÅžr?Ç ì ¬ÏÖ#Ø_a<"®é¦' l'¾Ó'5up&¥´-ƒOa}&só*,Ì•›"{­ã\D<ì׆Ϋë¢C×q·©ºŒ5ô¤îÓ.Ÿ‡{êŽ ¢Ž{*e*u¾rº“+ªÛìA­7·ðÆb=Ò¿Õ7#_oÔ”Ò8.ð?Åúa…®ÜFÄ}¬ÉÖ—°nPš=Ne­ÁO…LUáJJiYéYÓ ;t>¥´^Ÿ”RQ÷ƒË<†‹…Ì=êJÝUÜÙe˜jéf­S°¥RÞüMpÃö:Wq]Ó‘:W1RZžaãpJiÏ '<¥ô>¾‰ˆ›k;.D÷ól<"nt†ÑŒFÄél³„#â£ÂÈ3êat ¿6a\ø#">/ä×bUD|a“¸ó°Áœ–qzL§cyÚ‚ÿŽztýï1ͦZ6é8±c€Y0„=xK Þü ¥âRìmÙ°ROÂw±bãêÁuëZø[°)ÇÚ¥»ÕË3Q0³§öŠ&ÿ·Ôý}K©›ø¿GöˆÉò>_vìq’Iõµ™möã±^5RÔ×ê6ÞL&#ØËÑ÷(ÎôS£?'">é)×Ël£•Ø­õi\)SSȯÅfuÝ|eÿÌlãùx^]¨Q›ÔÓu üqú/G¶Œ´±üIEND®B`‚app-2.8/src/images/hand-pointer.svg000066400000000000000000000032731377436340000173240ustar00rootroot00000000000000 app-2.8/src/images/heart.svg000066400000000000000000000046751377436340000160460ustar00rootroot00000000000000app-2.8/src/images/help-hint.png000066400000000000000000000031511377436340000166040ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<æIDATxÚµ—Yh×Çÿß™3Û«{-É’êØRdY8vIˆÓÍN]‘.¤”Ö]èCmpèB!}Ñ[i^ÒÓ6Ð6†Bi0”RüÒBš(KÚØÙÜÊVe)–,EWWWÒÜmÖó}e”W“ôÞZ¿9ßá,óãc8 ‰úbv\ÉcþqûÖ!ïM _Íѽ \9ZÍÃð§ÈôiåM„w—ƒ‚x5çîº8ù?­‘ʬºoaõŽ ðßÇ¿ÆMþ¥šzªBÎhè2@JP LÈÛµ‹Fž¤j=­OßúÕ0y1} {NPžQŠ  v‚H!¢Ðœ·ðÆHlsY?|+Œ@áCà¿<+Ýè‘lêL½r›¹ÞS벩u8¯uß›zmö+­|òËÅ'ò+ùOð!h|òæ‘1Ù ŸÉªÃkâZ1™u&&¢Û¼/»5Øs:ùÀ°Ñaã»|yê·êã‹WûàóÆ)¥4ñu1Ùõ¶•.”¯ ’Û(@L,,K¡šxTÉÊ«ƒ¨¯ýÀ} MNÈá¯4ÕÀ'ÚykÃD¯m+{Ä&+°ˆ*Š @„Ät ›FFÅlð€‹©{Rzû™#ø÷¤¾õôÈGÇÈp™?eC6»„Œ-ï°G$LÒÌHÚa'%ÙÎHZ9!1¤H,ïx`9žP˜¨áÏx(&nÓ©ž3 iôY¶u¢¼C›åŽÒ²Ÿœ¶ì`½í†·g`à¬j†Ã”%¶ „¡-Ã×íàÿ8A7fHy¢±½5Jþìm>ÞŸ¼¹ m¿Ea¸y®DK«–?4˜¤~9áëK*1çDÝN‰²,Âéõ?¢àNPà=¯Âp?8³–FQ[êýžä™¢ííýTò~‚;)€S«)Ïs%>¤Ô®Ön‚P1hr›: JÞÏñ?¢Ñ ¾ó25ÃáèTå 16bÒ®°EqäQ³¹´5‡O.7öDàõå‘‹ãjã[X޼ד[# B™Îœ$Ý<ö'Ÿ°'?~q¢¶v‹§žü*F‡<Ì_~ÌÇOÌà‹>ŽÇÎþPáá3—ŸØ+6f¥Õ‰ùí…59uùþ‡@ ”ÄÊ–Âv3AE·Ð ½@Èã$Aš¦ð=¿0‚¥5î¿ÿZÍ&êõ:Úí¶ƒ½ÊR“Š¢Iš`yyÚõJAK©"öže 8yž—•‚ ÿö¨î3cii ­†QsÏ?}êØNØ|騱cãA)€bZ#l6»¶mÿèÂ… Ïî©@ïûwÿ{ßyîÈôôØðð¹®»yîܹÙK—.½Óçí¸wοðü47ߨVí™™™ßßDü¹1OplIEND®B`‚app-2.8/src/images/help.png000066400000000000000000000042121377436340000156430ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®Îé pHYs × ×B(›xtIMEÜ\_ bKGDÿÿÿ ½§“ IDATXõ—{pÕÇ?¿Ý{oîåÞIHÈË‚A" ŠÃ£ `>Ð ‚ÊŒƒ£ÎP‹Ú:VmµÕV:h;B[«‚mÛK§u´Ú * %AÁT4‚„lÿ)ÄHÆõƒk+ÙÄ0ghÛ½ÀÿReÐÅ“--TUU‘H$°m›¹…d\ç â‰d K@–?ÑA³$L?*âóº®0$€ ‚Á ¶e°mÐ:9ŠáŒÔºø³g*ôgñû~[#xžàˇ1ÏÓƒ^6Dür0$O"@" ŒøùG0Jãi¹ó¥žÁÓåé3³³Ñ±$ù´’³SaŒ_¯>¥€{Ñ è¤ É©žI0r¦ý‰%@ªøÙЊ‹¥ ^RëÁÄ€Qæ €•\jHR™¤ò&Y þ=uQ hƒ§<”ë!¶œUhÏÀišPZUWÛÄ,‡Œá#Ñ}]X¡ ¸ à@‹Bé‹Qƒ« €§òl´arºÇѺCœð¢Líý”­Unz€Ö>—˜tãüñI>¿dÇŠ®çº‚¯Öþ¿Ò¯€F)/YtD ‹ž“GI«ûˆû[Þbä¬2¬«ïÁ™:ñ<Œe2?¢¤áñ-˩ɚL¼à‘ 0_-BWi<¥ý°,T_œ­ûX6ª –ýŽCõÇ/(ÆëíÅ ‡»c7fâD¢–3m%Yo-eþìXÁ²—G~!ãÎ œsŠ–ã$XùúMÔ/]“Ê`k%|¸Ÿ†Ý»88e º§‰F1¿ÿc¦ÏÄž2»äZÆ._mÒž_qä·e‡ŠÌ±CR~ú—¡Öƒ÷½±„üÒïp"¯Þy›S[¶°ãÃRúø`W½m'‘áéˆ{˜ß­cÚáCäuu! o”‘Ëž6KÖÝ»WÊ;rGˆwª PÊÃÓm„‚O^fv0æù·»s']ì®ÝBuq>3o&Ú&„Ûgã̇¹æÄF‚õ{0šüÔTdñbɸdÄΞõÏ^ûßRí˯=Íiâöʧ¨}ð%‚Gê(Ü·—ª£”_v ÍÓ`RA¶L¦J„¥hh¾†ÛLdû ŒÞ_H ¸Yxc«þº|÷î-ݹ¹ù«C™Oi‡”9}èß{þ3ä^ ”Ç•® Ï’¶t)ã×| @Û²ÐZsZY\& Ü£¶3lþCpª½n¿J´Ð¡º¡—}ä”§I8B(àÐØÛ˜Öù¿©©ã{O<Έï> fE} ÷Õ¬ç¡Î©ØŽ‹kX“ZMe¬”a ~Õ{Q«×ðÂÁMärÏ<~€§€– ÚŒ”öÍßk-MNçSYÁ¼Às¬Úw˜;ž|’++¶"‹—Àµ? ÌQ¨D+BbóéÛ~¶’£[+x©½ÙdMÐòâ¬ô •ý¹úKèDi´1¼^ŸŠ ›CsqŒëÔ/Ùðy;ßÞDéö§F™Z +ч‡éËÏÇ»ë.ž{åGáÒÉ–âžiÜ µ|´¿Æ GäxÇ)3»¬@z² øiÎÝÄÆÍ f 3-}>€mímWµµ· öEJÏ—$‚¿ÞF´nÉmKTïe3æ,Ózô“xÛ¸ÕËo¿u+ÐÞVõ'@ì`£ñÜAü.CüAÀC¿IEND®B`‚app-2.8/src/images/help_48px.svg000066400000000000000000000006001377436340000165360ustar00rootroot00000000000000app-2.8/src/images/hierarchical.png000066400000000000000000000002161377436340000173310ustar00rootroot00000000000000‰PNG  IHDR szzôUIDATX…íÓ; DAîi<€…ëÊÆæ‘X@ã„OáG /¸É|D÷Âé Ì38ÕŒÔûœàÌû°ýåÊ':ôéMÏ1ueýIEND®B`‚app-2.8/src/images/home.png000066400000000000000000000033101377436340000156410ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs íÀ,tIMEÓ  ¡3•VUIDATxÚÅ—ÛoÕÇ?sfç²»¶w×qbÇÆ‰b'M°=-R›„’ŠŠÚ¢¾ Ñ h­6}¨„òX‰þ¼‚–†ñÒ>ôÂB•¨„JxmQʦ6©³I|Ù]Û»3»3sNv½¯CHR©#}¥3çüÎù]Îïü.ðþ´;!ž<3{ÔiãÀ8J¶œ¡æPré_p8Ÿd:Vÿ&ÏÌ>< < Lv@zz­[>@o8êwç?NË»`òÌìšx %¿ÐÁL‚Ù‹iEîYç÷åØôཅý¸^*Ëà­‚òåz/8¼þ{ÀÛIí6ZŸ :€Ùµ‡Ÿ=´Ä±ƒ]Œo‚H”ÒN¸’K¾É† .Œí‹a™âžóýèLŒÜrY4‘ôÈ»6Q <¯ÿÝ#fUîÓyúTüŽÿê|s|½Ðth‚RÓb"ôN*ªMå6›s5žÞɩ߶ÿ_üMsüñb'}D€RKÕÊÏ2RBsh:%7ÀÐ#ýðÞ®ÁhÉ8®W­§èJ¿vÉÒ¿@u!D4qo¾ ‹&¨®Ö&«ù à €z ÷î2†æ´ÃÒƒ;fšŠú lch>¸Ë ƒy'ú·EÀݲ·Do øÖza…û‡Ú®ÛÔ›îÒòL­o›i¬”ívÐg—Jµû6ßA§¸ N«Z©>t‹×Oª67v¶@+Mª~éš |ÜⵚöúÞŠ€×ª–OèE¾Ÿ]\âäñÝzíI7|^}ÛoæÖ|<_Ñmµ ôêÛ+M«µä‡ÙÅåÚ—{‹Ph«2™ŒzÒ‰ärêkùPŠÇW  š˜†Ž¦Á¦'Ð40t­­4[s‚€Ìå){>Tó/9Æ_Þr@!N‡ÛÝ;p8ÿ ¡{v£\åÝé<~ÅÅ2Ä]Á¯¸¼;g£\­1×ÿø'à‚Žš0“Éà8N8ÈìlN-úRìó%_ †w Œˆ "¸-40}¹Ì¥O øležnîV×´cY>55%€ØtðãG0“/¢é“–!Ø7ãè ¤·7®·×~aè³¶Vä“k‚/®—©ød0—{Ù±Þš©k~(·–å·lLêBØ@jZ=ó<ºýš>¾µ5¡/Qóá•b€Ûúd0O°ùNÝÛ ÀJÝìÝÑ—¶fSSSZ=Äätå‰1ŒÄ „yaª§Ôz:ñ³„•,Õ|¦dJõ§V¨ƒšÕ¯ÔœÖ­aÔ-­Ã®Ï‰F0«•ÚàÖáÝ®9ý/ÐBŸ6zº:IEND®B`‚app-2.8/src/images/image.png000066400000000000000000000130041377436340000157740ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞsRGB®ÎébKGDÿÿÿ ½§“ pHYs7]7]€F]tIMEÚ 5ª­M…„IDATxÚíšip\×uç÷¾×{7ºh,H Hp•ÄM–l-QYƶ\rÊv”ÄŽ'qÍ$²LͤÊ5N%•=®¤ìÄ®Êâ8'Jä‰edE–eJ)‘â&qbGht£—÷ú½{çÃ{Ýhà¢|JbÞª.6º_¿ó¿çüÏÿœ{àëî®Ö%ÞKÀø!±[ P5LÓ4»Ç ù@üG_ŽišUÇqÆLÿƒ ã8­õ±×b0e6„B`nv„ðÜ@]s !oÐx7/–nöý-#ïÆ¿Ö|Ú5<˜ÛÞC¬u­‰Æãa@4 ¤HÓ$–Œƒ0ëñ nó°Ú‰†ktã*ÿe¢Ú­o¸U©[Dª¹â«¶‹ !€J©Ôh`…!ÑdŒÏWâù©^d¸—ª‘âüðI™<áõfP£e°N`Ë¡`¬ŒQ±vJÕZ¬•×B`HÁ¶ua~|g3¿ðÔ=|õÈ›4ýkÜ_ZSèÞ4rF y—™b ×1ìåx>ÀX!‹#ªþu‚  `H„ ›&#|lä½pv’hkbÔèäÌÂf^>7ÁRe£5™6©Ä"X2¸fº«že¥Û/c±:ÐÛ’¾#qióìÿ~šû?þ«,\:µ¦§ß€E3A´!z¾ƒ{'*J‹Äˆaï:)¼Ý&„¶ùO^ÛÏÁæ½´¬kå¥7\¾~i ;ÕE ¥…@{7Ó™ ¥±¬1|CVI ]áÆGoŒýšÙ¼.ÆÁ­ \eã8|ŒÊèqžø…Ïß( ÄMÓ J££4_oBŒXØñ&œô;Cßh‘9广0B"µÀaH:« ßÁµpžŽoš¼õ:ÝC2Ó‚crz>³*®Åj…'<Ô¨5ã~7BÞ]ââ8dòã¼öâ+,q®Ž)¶m= "ó‚Kb)̺§;hîŽe8r1Ãu'TS¥¬Ø¾5dný×êÆ „ð“h$QÿúhHðìñ2?©^æÕkM;6QÉl¢;a¢UõÎP®ÍºÍÞîÉ“˜Y›¾|‚€å`8† ª–`؆40£4÷âmÙl ³¥gíM&oŒF9™m©s}£Q |e؈¸£Z¢–þµWCÍŸ¸÷°Ô¡Êe߆(?ÒWA¹§ÜÞ´&IÓ}ï~f^>KqÂ"`)4‚¥p‚¹P•3)ÑS Ö\eëüy''‡xs¤Œ!ôª”×èþš@Ž£ùðþf¦ç*lMiÆ‹ŠçÞª.¡`îM]8®»ª3oZ¹îê¤å`€òlÔ”À0,RåI²‡ëSÌT’,Ù§H…®²^$©,”‰ b‰8FOŒÀ½e¶L¾?ÀV&‰d5ÚßÌ‘·Æ™²Ã8®âpŠûÛØÖ ÇFŠ˜Âó XSxY &‹5Íqƒû{#üék§ù?f;³“½›Lžyoe[‘È4S]£3oÕ4Ñ¡Á‡œ¥…?#6%)§‰‹9¢R²·x‰CU‰;×Á¥ø£-×9 qpn7Ñ`³/Lô‰iâëm®'9ﬧh9|j_ˆëÅõ0ž-2uÕ¢;äéû¸rö$g.ç1dàú¡WhåYþÎql‰qââ,×TŠÊÉçøÈ3?Çþ¾&l«H(¢´T"‹ÝY-°L ³c-ÿ¥Žíºƒ>ÕK4ÔBW0Ä ¾ÄûU‚î ©0£`ë±á_™F^æQÁó•¢É¿úx7ë;[Ù¾>Ì…o}ƒW_9KÜt(–K ªÈrŽWþç3Ca_´X+ „X™A‰0¹KçÙ'³a'ØLk2Âü\–‹§Ïpêè¡`½ º½H/ ¹:Åü© ¹W_ ¢_$¯ ¤'ÛÙ`oÂqÁÃ4Å"´Æ›)< ù© ¬¥ë|÷ÔN%7#›âüâÁ8SsS¼5nòW¿óÄúqëU6f—9;ºÄÑ™v–žù ‘©×¨¤÷x@i¯)³B1*Ï;¼: SÀß½6ǯ<¶‡ás—hênâÜ©s„T…‰“¯a¤Û(]†Çb•Ö¸9Ê/µFIfß*[7ðæ¹0G_y™Í[òDÛÎSZP0ÚD׺uvnDßg’›kâõì£Ì$[p00­*H”óGþ“—É<ñi¬ªMò=÷òÔÎ ïœÖüÉoý!Fÿ½D,vЉ©ôûAÛÕàZ£‘BQœª…V-]ª ~ëŸùøÁ-´—ÊD‚’+—Æ ­ëA9WÏúUåz€’Õˆ¼ÖD“m4oþ‰SyÎŽ]ãJ!JÀtnÝH˽’ëÙ̼¤j ¾®uáÞ¾&»Jzýf¦²8Åᦠ®:!Ư— Ç<õÐñžVþæø$û‚ÓÌŸýK®¸³l}äTí*R+´eCö*¯üÙgé8ˆ™Z‡Œ4 ÅXÊOò/å÷ð™O~€Å‰º»:™›ËÒ¹¥‡ÞíŸÇ.Û(Wß« Ó#žH±aëvF/žBU-?þq6Ä E@ \W¡…÷°Ê'±·ÆJ<4ÐDnq‰=f+3®¤?âríøëD7î#˜ŒðÆß}…áú.£|!Áš¾Âè _!;zްPìܽ‡k‹ÎôpèÿI’r§¹ö׿Äpµ‰]þ¡(–™ÄŒ·’Ÿždqì#Ç_axt˜_þÒ×qïÔ–[M5õ%QÀÒì(ëÒ&?ú¾½T¢½lض‡p4ŽÒ®çŽZûŽ®5)Tàï_ã‘=Cœ;ú=ÔØyΞgnIÝÿ>–^zž“ßú¿ö…¯ó;ÿí7ùÜ·§ˆEÁýmþöóÌÞ_™\>}‘—¾ÿÑL] #âi{p;/&?E0¨é ÎðßÈq¨Ç%ÓÚ‚Œ®CÅ‹ì>ð~ªŽƒãÞ) Þ(BT­ Kgž¥Õ¾ÀÞ®&œ¸äJµŒÒ ­4®uã ?‹»~ÊZß&‘H0žØM°ýÐpuÎe²û}lËÿ€¥Ÿú»ï»WþñÿÒ¦z™¹ Ö“}ýìýì—X¨h¾|¬Â¯ïßÀ‰I‡™È{è°¯qó¼žþ÷txjgˆÓ§ÆW/1átáÌ_çúkÿÂæ÷=Nn&‹ë8¸ŽZ!'o{â…‚D•½B¬pŽ©H5…é‰HWkOÆú^#´g|í§ ß|Ûâ¥Slj‹1tè~Š–f~n–jn¹ã¿ [7qöÌ%‚ªD×_†±S{îa:§(ÙA„r š‚ öó䃛è9þçäBë¸3”EŒcWKüò7Ø3„ÕÞAÌ´ˆe:é¸çafO}»R¤ê*ªŽón=@£”W‘GO031ËÙ‚ÍÈø÷ m@ D7ð…n(t¼RV2%G§B<ÒK%D9ËЮídóÓ\Ê&P†Í iúz· K·ã'(5ue@†‹ÎÏÓÑeë¡}ØGþŠÑuÛ‘mŠLTðÑýib©Rë71;:Âå×c45a¶PÌ/`ÛU*Võ]¡ºèð ©> ² ÇqÉ™«ìE„S¬jî)ÔÞ͵^Vqð°fh÷놰m ÷ÄWÙ’Iã*MXB,&½¡‡}©<›—Æþ6Úˆâ ÈU4?ÿ—ç¹^ޱ¡-Êæx™Žt W¹Ì—ª«Âé7NˆD‰´dÈôm'šn!œn%K`Ù6¶m¯è¬ÞÖ<2H!híßM’½rŒt÷MëfÐÏ­by×}ÞP5óéR¹>‡ë($­ŽSe!·„e[(×ñÏ®ë4 þñX‰Ÿ{l³kÏ óó[2=Ÿ®¥Û !n~QïÂ4´¤tÐ î¯}plWóp‚Ï/"¥w­…L-iúâë¹3~ìþxyòûä£üÍÑy2V‘ÿ¼·ƒgÏ#´"·XäÛ¿÷á[´®ÃâBŽ Î8|ÿ§€K>è[ „WB.Çxmgë˜øK]ëÙûÞsc#ãƒÛ£ô·B8âù·s(]»‡wúSSž¦apnÆá‰**–¤PÔd¯Žbt4“›™C¢q•²+”-S.w”R8Õ*–eQ)•ôl6;sàðý?\²iKÞŠk7¬¥A­µ·cµð=Ås÷åþ^-õ­ðí°··…·ÞΛ>A¹XFRú]ß™àqKÉvøÜ‹KÚÙA0âé òÈÁÉÅ®.‰rÚõz”75¾\Ö‹ùüž}ûžÞ¦o<~ºeBàú»¿ÜÅË%¥ðN‘t­yU÷cÅÎ;JóŸv¤¸rñ•ü,_;6޲F)·oÅ­–ˆ·wˆDJã Ï)5E[1Ø&;>ŽáT™›ãǶm#dg ä¦ÇbÆ[å²Î ;wíúp˜\uövSÄrÜzFù¬ ȰZøláqŸ@ú®ÝÂÁ VÁ¢«-JgSš@ï emຊüÌ$¹‰ëˆj‰@–m“Ëå.ö |8ï ûNÆån>!RËJžòŽ®µh(}5…¨˜j0´ð&4êÍËÚ[UÏbÕÌ—?’WMÅVLåÊŒ]£cë6JíûxÕÚDÙб¹|nÜl–¾;( LOOŸÝ>4ôß}ã‡ïÔø›%[-«<¿!©ý®ªF£]Ù0¥=æG‚vPZ,g?®…(¿aâÖZmÊ“ÄPãÆÅ®BÞ6Ù1˜aôò({ûÓlS6Wæ Ö…´õí hÙ¼ÿGâôéÓo<|ø}«ò.Vm3âÀ^­õ÷²Y¤!‰… L¥6()}__&EH`õ„ƒ¤|wO üÑWã*MÅv±ªWƒí88¶[uXªTXX,³¥·‹¹©±£C»výš/o§nGxwäJk”«(”Ô¿bRTÞá¤èj‚­…ˆRžÌUJã:.U×¥ê¸XvËr¨:ŽÞ=´E _ºð½]»wÿ/ßø™µDλY1à þw°Ç™ýâ¿ø๓IÛ;qÀ ”r«Rª üžqV>Á|u—_spø_€‚þÕ¿åqyí«ºê­ÔÝÝuwÝ]w×ÝuwÝ]w´þ?é€4½•N?§IEND®B`‚app-2.8/src/images/import.png000066400000000000000000000031621377436340000162300ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEß+EI×&iTXtCommentCreated with GIMP on a Mac•ä_[ÍIDATXÃÍ—ˆ\WÇ?ç¾7ï½ù±Y&ÛÄM4c iĨh±‚ÐPÅ ˆ’PH€(–@dQ4 &P°¤Æ?"Zÿ(ˆPÝ ˜6*AHƒÔZ°•’¦´M6qÅlÒTãv³³³3ó~Þã3;;™™] \îÛûfïýžï÷œsÏn70€òÞYÖ¹¿t¼†††îªV«Úñ^ØÂŽ;®_ºtifqÁíxéU«Õuªz*Œ"D@DšHE‘%äÝϪí÷ýLD »ÝDqÂÜÜ\óУµÉ ®Ãµµf­bŒÜFq¾PX”¹m˨6"ˆ˜å‡·<]¶Öš1 ‚ȹ†·fÌ×3\×c–@w™»œ¢æ0Æ,yÛÏûNT*|ëÇ&W*²u´È§ï\ÇÖ÷#Æ fyh™"õ<\D0­!Æ´˜ZzV…(Qv=2Á®{¶sï=Ûyû h† ˜®ˆïË€éò¬—·yä—ßœý;O?7Éáo‰ÊBD2¿@µ2jÓÚ+@{2 =¼ï$SÈû._}üy¦g¸ïËŸàÆ+4ÂZ­Îüü-Jyb¡@íTØó¹õ‰ŽÃû1°ø|å­ ß}ò%îß½…Z˜Q­Ö[ò Y–¦<4þ8Åâ0Ž?DP,1z÷7&Ǽ7þþøï.Dk€ÝVõdß4lA,=Žx•‹Óo³s[«Çq0ŽƒˆƒÇqðý6KIÓ˜4ŽhÔÙ™gÿê¼ðëGeEº‡µŠì;r†Í5p«ÒÀËåpÜŽÆhKoP›Q­6ÈÒ”4‰‰âˆF£æDaØ?éxõ3Üÿ_á'7ysãFÆÆ60rG™áá!<ÀªbÛÌLkk•4±ÄQFÆÔjQ´ÓCüóú-4º…“ËQ cÞü÷,ÿ™ðƒy†× Q,|Ïsqœæ¦Ö' µZù¹ 7oÜ`vvn@tJÑQxÄ}ñÃ<÷ËC”Êwà{b<Ï#Ÿps*†L i&Ä D±P•…zÆB-£º1¿Ð ³Ù`‹9ÛõÐ,6[7ðÆ©ï±sÛ(*`Äàº.nÎÅssx¹¾çù  Ïž`ŒK''Ñ` èW„Dp!I•§Ž~gÎü<õ'>²þ“8Æàæ\|ßÃóràæ;Ó¼pæwXqÉpˆSUGcùÔ]Ûùã+Ëû5ÀnU=Y«Õš% £ìÒf£9§™% c¾òÍc|裻(8¥R‰À÷pŒðòùçùù¡ûظa}g€ ÀÃã‡åÄGÕôÍ‚®ªHÙu Å‚Ï_&ãz…Ékoà{¹œKàûˆï333K'ÍÂÔ´’qräÈd`C²¸¹ª¶;£n¶xøâhÔ\¾|™¦)ÓÓÓé¹sç^?xðà@ºrCÒÇÓ~¦ªXkÛ24Âår™,˸víZrñâÅëû÷ïÿ :ŽÃª¨êJ½ým¿íQ©TÛ¼™«W¯¦.\ø×Þ½{wQ¡Pè¬+3°ëªª¤iF­^gÃ訞?þµ={öÜ Ô­×ë=÷0+y¸šõ¥,±Mcœ={ö·ûöí»ˆŠÅ⪿3‹Àçõ]˜µV£(ÒJ¥¢Ç?¼øõÓKóAmy¸eË– "R|—Ò(Ðw[_À=5ÿ¿³ÿ£˜¨¹^cÇIEND®B`‚app-2.8/src/images/invertmatrix.png000066400000000000000000000012061377436340000174470ustar00rootroot00000000000000‰PNG  IHDR22?ˆ±bKGDÿÿÿ ½§“ pHYs  šœtIMEà ±?ñ½IDAThÞ혿KAÇ?—óRF±1D°Œc'J$Ä@ÁÊZ8D$)L#¤´ÅtQ´cã?` Q ‰rX£1c3‘»7ë:;7 ó………yïÍûÌÎì¼ òL]À ô“*5#ÀÎ €T‚¬H%H;pŒO%LÊÀT©ÜXê`C˜ HÃ(=3@(`×ðõ£®D×ÈL¼ñ¤ øä«¥5’ÈÛˆÿŸ6A2ÀÞA–|é¼#„®ôníȼðºÐ6éÓ/·RXä&à¼DûðÈaÄW´Í‚`3ì È7!É!móJ°Ù÷¡ÑPÄgM°ÿP. !©Í>ƒ‚ϑţDde!©Ñ~5À…à÷²·Ò´j|—¿Ï®A¦ 'I}†E_ë â!ð+F‘õyç äu‚ Øv2›0ˆZ\L«‚ÄËûn ø ä’¬µzMß]1'¢ àTÅ1b® ñ“1Ë;bÄÌ ñÎõ“uMæt6FÌjàËëÕ ðCèðÓ=b/ q¿Øi5L«û,ÌÃ)³Þ&ȘÐYAï/q•3üDÞÛÙ:š³Ò‡’%(((((à¼Xî+­–IEND®B`‚app-2.8/src/images/laplacian.png000066400000000000000000000005131377436340000166370ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà  6+ÒÇÐvØIDATXÃíÖ±JCA…áïêE°´±Slµ³MK[m‰©ó‰–)¾‚`çSØ V–ʵ™"\BØ”90°svÙùw‡…%•Z²ªüsìOåëáýš&hZ±­,»  ™!º‹¨fXóñ¯Z0Øv´áý𮱆U\bÜáUUð0Qðá=EÑçÙÄŽÑÃkÉÛ<ÄÎT>@{<Äø1_ãª.ÜÒ¦5ÞÂ#^°÷Ý·$Àgkó7Œp‚wŒ1,ñrREõÇ)eš¦àIEND®B`‚app-2.8/src/images/layout_levels_24px.svg000066400000000000000000000002401377436340000204670ustar00rootroot00000000000000app-2.8/src/images/letters.png000066400000000000000000000016701377436340000164020ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆtEXtSoftwarewww.inkscape.org›î<JIDAT8•MheÇïÌìÌ~t»•˜dMWSi÷ÐCÐè­^4öR(XzHö"ô$Äo¥— zSO´…(–[!¡„¢´‚”Üf7Î6¡Ý™dg³™§‡Ý ³»`x˜wþó ÑhÐh4H$Äb±8uß÷I§ÓX­¹‹Åšñ‘Èç]÷bÛPJ½;???çû¾Ùìû8Àêò2¡ç}/"Qû“뺔ËeêõúYzbYV¼7x¸½²Rsáv§î8år™½½½C=›››C}Á€·Àù uê®ëR*•‚àPˆ|ÐÜ+\×Åó`±UµÆ ã'@ xTÞÈ‹È0¼ªú¥ª5õ‚E$äTuÍκºúXDGv¡uEä;ö³–õ¶À"ÒP¼öwTõ{-PÛ`ैÜ:Uõ[­P¸húªBD<À3àðµ(Øsüèžbã…-ÃbHÙ}° <75p.û¸£C†¼^Æ[Zp75јËqNóaeE§Ddø¡ªGu;½ê8`2%[þK&)„BÌNSN/w±«‹¡ÙÙëÐR¦Ó÷öòÎ8xd„+h)GGù 8L‚­­¸*ÇÀîb ;ídôWű³C¾’hy™°nœÉ093CÁJNsžHðQU-ÿk U%d"¹^Ùñ8‡á01 ÁtU_övðùokã®ÓIc>ÏŸTŠO««úÞ¨Óbü1_äz¾VuIEND®B`‚app-2.8/src/images/line_weight_48px.svg000066400000000000000000000002421377436340000201060ustar00rootroot00000000000000app-2.8/src/images/net.png000066400000000000000000000024361377436340000155070ustar00rootroot00000000000000‰PNG  IHDR ê‚£AsBIT|dˆ pHYsîîÖ²4tEXtSoftwarewww.inkscape.org›î<›IDATH‰Å–]P”eÇ©|ù ©Ä )³¬Ã@yQ~±:]„£‰_•©!!ÙMdÞT3 cZMy¡3IÎ4cMYâÚ 3 N¦°¦¶Úަà Ë¢œ.XŒÝeß­™Î̹xßçœó?Ï9çýŸW‰ÿ—úë¸`ndê,ý“ÓùÈÑÖöàDKKÇMÍADD³nÌMù¤ò̦;ý½{ìŽîÝöï¾Íi]»úé­q”Ö²g¿°õÀì}±³ÃGT­òìÍŽ¢÷*^ª®¶Tù+@2`0Ì\6`ñó±‘ó#Vk‰¥\4Açé,$Dçñì?olº[m³õyßÜt¿·åÆýÓ¾ÆQJ5ðê+Æn[vu>tì±?tì±_©Ù~/77e¿† X¥yà†D¯,^‘Öçxäl¼v¯üâEËI ·^ ˜ýúÔ€ÉÀY?}-"~“Ì€C«“Rj`Î e£Ë2™¶,_ºt5àCö)€Þ[ïV€‹_²L¦%%ÖÝhh°7ÖÖÚ÷›Câ8AŒ~GV ˆ3ÒÓó2—-‹fMNN²ÕjÝ x© ?Sjb°ßïˆH+@उg¶ ×+¥–0È"R=¬o‰Ào¾"*¥DdXl‘ÇôPW_ÿ½³¿ÿ±ñ›M.VU} d_F¥”A)ä2yBDz¼N›6wUvâ‰Â‚ ó»E‹®nÛº°Òh˜iÊF$­ÌÊzmajj&Ð[m6Ÿ>Ìùyiçö¼<Î] «µ[6n.=|þgK!hãö ˜ÁIwŠÈ©€6`SllÄûoîHÓ{ =IF/zÖ>HÎ)"µ"²CD6<5',$..Âk)â#§+¥¦À8¿QJ©@ŽäØXRP4Ô/wÒewÞjoïUQQ¡¸eéê:ÀËÍ õò–-T•–Ryê_––rdÃ>LNV&O>mmÝÇŽýØÜáé¼ß9@]ý6×§çþ.5•ÍÇsGûh=tKR&OLölÆ“Ÿ^®ÙfstÙ‡koW‘=GZsTTè¬Ç¬ç†Cóó¹êxHss©ñ¶µÒÒ¢wï*Ìhú©l­þòöž#‡_üseVBELÌ䤶£§Neñ… tz?z+?—‡†¥ÇÅE­渳3p11ôzñÂß „……‘üîÉFDú€*—º•1×Ü̳™^O—.ñWg'fo6¾Èp§“¦’<ŸÓ ee4Óþ-¸Û~ÅÄ0//»}d¯ÛÛéY¿ž`:ððpøxFëŽwK2·oË5¥Ôsׯóùüùć‡i³q¯®ŽÆŠ v‰ˆ(PJ5{~À¢õâ>-–a;ÙÝÙë®JÔ2ØÆro,¨܇䦋È]×zÍZDäêx~ì­N8_²IEND®B`‚app-2.8/src/images/net1.png000066400000000000000000000026131377436340000155650ustar00rootroot00000000000000‰PNG  IHDR M )sBIT|dˆ pHYs¥¥pÄÞUtEXtSoftwarewww.inkscape.org›î<IDATH‰µ—HÔwÇ_Ÿ;—‹ÓæèkQjuZ‰e-4üq‡6·¬ì‡b60ÒÀj`e4nLز ÒÝÆª,ûE›,jæá-+ûeäRÒ„R—c¨ßS¸ûúÙvúõ:Í÷î>ßçûyžÏûý<ï{NH)™ÌZºT„FG“Ltg'͵µœ³Zå’RzlÀ´¢"J‰â´¦&zV­b£ÇgM&õëÉR”‘àNËÍ匛dÅxgéÆBFág6‹=B?—}ƒÁ@”·7zUm&!„ÀhÆ´4‘ì9"~OJ…† Q‘Á‰²2šûúPΞ¥%#ƒ` °°QSC—ööv;Jb"Ç€¤ƒùEûÜá@Ù»—oÞHAT[ÚÛéq¾88ˆ­­˜vjývíâ«Ó§yl³¡Ü¸AGv6—ýüxGJIXÁUUt; h“8uŠFàíq8p€sš¬T•~)Qòò8ï†_Cl,Y ¬Ý·XØÜÑA¯ëUUt8n ¼|É UE¯Ó1¨Ó1¨Ýw¡O©¬”Ç››å_Ú}«• åå4èõ¨CEX­ÜxúT¶Ž[þþ̯¨ Ó™µ”(ׯӹlAžtJJ [ ±>{Foc#ݹ¹Ü]»‹«Ÿp"!Ä´À@>MIa^` ï·´ÐP[ËÏÕÕò×±:f¼%ûûùûî]nk¥”—ÇEØè'£o2Ö§¦r2>žÝkE¼ÄýÁ33ùéÖ-^: tu¡pÇd"È5 à3ÕÁ#"0?xÀ?ήpÖVz:E^BǶÈH6=ʲ¶6*ÂÃEI}½¼?ÎÝ­ðp¶…†â ÄPí ! ‹…”ÖVú´Ùò˜6U¬^MòóçCº08ˆÍÙaûöñ#‡sQj„GJ”Þ^” ødªÄþýÔØl#q®\¡cÅ b½üü˜í ™˜¦Š)¥B¬~ò„]K–°®¹™Îû÷ùüÑ#ù””òUu4••¼ ñLx<@#˜1üÝ×—™99\ª¬¤³·—þï¿§9=/§8èôÙ³Yôês‚öÙ°†…‰ù³fwõ*]RÊ Sýœ9bfRùX‚‚0^»ÆÍòrîÕ×ËCZ~\³æNÅÍ·oçk'½NŠKKi¼‡ãÙæ¹s1¥¥q4/Š­[9¬åm‚°ëOœà¶3¸ªµŸ¢ $&’åôórÝ¢E´åäðÇŽLp8°,_Î&!D””Ò>Aív#H£ÓëQív„ª20&RJvï¦X+R¢ôõ¡$$éÎßd"(6–,\d<9™/ìv»}D~‹‹¹ x‹€ÉÄb!02˜ ÈÅ‹Y |ëôBÌÈÎæ‡’"#"˜qþ<{22ÄÅ’™PVÆo6¡ññÌ›Ç{55ÔݼÉwRÊadÜ"šJ¾Ã1R<ªJK ŠÙ<º…’’øÌn=š×ÔÐÀJ`3ðªtÀ»nkÅݦ¯/3¢®µuh8­«£{çN.14¯>ÌÔÉWúîp0à´ädÊ'Z¬n)èé‘Ý@dLŒøpáBÖ44pòÎy¯´tÄGáÓÞNŸª¢©×£ØlÐßu‚…:¹FN‹Œ$¡©‰µñ~I_› =]f³ØM²ÑHðãÇÔUWSÜØ(ïMôýÿ¶_ïAhõIEND®B`‚app-2.8/src/images/net2.png000066400000000000000000000010251377436340000155620ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ ,ãµä¢IDAT8OíÔOˆaðß\Ƙh«qeþÕŒ‰tÝù3–j6ÊVŠ™ ²•…²RB”"1ØØÌb¦ÄYøW(¥!št-Îù¢›ë±›gó~ç¼çœ÷yžïý>¦1ÿ…žfƒ2®b]ÆK`fÃò©£†÷8ޝXE¿íh‚6ÌÀJ|ÀGl+6K šJXƒuùVìÁYœÁ¼Èõ™`Œ8±À,|Ëç6¬À)œÈÜlAw³w³°â ¢׊]¸ˆ,À²Ìíθ‚óØ™5‹q×Ñ.lxW -á-– †‡Q’ÇqýhÌVáSÖ]v¥…{)p ÷1„¹9›ðEXÓ-ü|Š£X#ˆuå^öã^×p»û¼Á„PòYøXÎõa>¶g~L°~€CÅàçØ*d^Òªâ²c-ÎáUÖõŠÛqZøzSX:‰×ê0ˆ+âmwb FqO(š—uÂ÷˜[?äWØ(,ȸEX3,|®þ”ÿc cŸN«Ýݰ£êÿwÄ ZÆ“xè ½YÁTñôÛU'­¨ì¯IEND®B`‚app-2.8/src/images/net3.png000066400000000000000000000032131377436340000155640ustar00rootroot00000000000000‰PNG  IHDR ›Sÿ4sBIT|dˆ pHYsrçntEXtSoftwarewww.inkscape.org›î<IDATH‰­•L•×Ç?Ï{ŠÀ…‚^~^@A¤)bEi éEÖ¦ZÛu³&í¶´iæÚÔ¸e]ܖئYÚЭYÆ6t?Rg³Iª¦©µZc­±¥"Ž‚ÀŠü¾p¹rßgôbàÛjw’“÷}Ïó=ßó=Ïó=çUåV›ˆ{ö°;/ò.ŸÿÍÐ2¨*))Q1u/ç7õž-óvÞàýõ³Ëö‡bŒi!Uëã7W•.̨\›ÿ@™kË\»ß´aÉŽŠu‰©€Z-Båú¤’ô”ˆe¡¸ª5ΚҢøT«U()ŒÝp_Aì,œU2¥áä–°èÞ~Ÿ/Õašjô]œ8Ý5#)âÂF"&ЬZï^Ø14â—¨H» hkÇè„gp¼Wܲ;˜Äg¹ûŽŽQï”1?ªŠJWßÄÈÙÿø$C*1y ˬú™6Åüa÷S‹æçD= ÐøŸ±ºîOÇ€pà’ªŽq‡~¹+õ=+6{ǯžûdðuÿˆžw¨^x&çž•wEWML]ml~ÕçÑV 5>9—³ÓGÈXà…Œ‡ÏôI¨€Ìà3âf~šó*VÕÉï~ _D"’îÖ/øó‰È"àZpþøMqÓǰ0[f³=!šå-=Ùw”}ª:5ƒPî/°l^šbTt\1Ïþý½@ À°ªŽˆˆmãjÛŽ¥)FÞ§í†Ãg¦Žªêèôü‡ÖÎ/^•ãxDD8Óäûócc'`ÆU¼:‹_l)b@q.ec>LàÅ–Çžª¶Õ:l`š–m 1’þÊ?¯?+" D$ñ‰ö'·—ÛŸ¶àóÛ¶Í ›|zz¾seþK?ŠÝWãp,г”8XV ŒÆ I—¤KA\4ßTS±ˆ ñN·ä‰[Ò$C*3Í6+UDqÅI•dK$ÜK&aIñR%‚¡ v–%ÉÆ·Ä-y’)+r³ìÛWd;’ƒüF®Ûž’»Äþ¸$‹ÓÐNíÑ6=3êå5„ŠörH/ëy½¬íÚªÿÕ ! ‚Š ¾INi³ŽêgÚ -Ú:1ÁiC0§9Fô4í4ÒÊùK­×ëÛüÝÁ˜ÙÔîï¹Ôvýt1|£Ç/ð‚ÏÏÕ„îºèáÝýïñûúfy÷ówóÂq 1” Œrþ\‹¹w¦™N5^T &Rò{ôø±sSµ3®Ý×Ìÿ§oêÓD>¼8Yß705ð?¿‰¥èÝIEND®B`‚app-2.8/src/images/network_three_48px.svg000066400000000000000000000007351377436340000204770ustar00rootroot00000000000000app-2.8/src/images/networkfile.png000066400000000000000000000023721377436340000172510ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEß* ü¡Óo&iTXtCommentCreated with GIMP on a Mac•ä_[UIDATXí—Ïk\UÇ¿ç¾wßÄiÒI'Ë4ÔEi&ЈÖÔ](” M¨Z#"dÑ? «€bp!âÂ¥AG H`ÑB0j±Á&˜4ÄZ¦‰¤LSi&ÉL~̽÷¸ˆï™“™‰3Þæ¾Ë;Ÿû=?îy„ÆÌ€0J7"2¥l¶ ¼mmm/õôô¼æ÷ûÿ ÃÌ f!ñììì:€l”D›ÇŽŽŽ~ßÒÒòÊòò²VJé=NÞöÍ;×X–…d2i-..Þéêê: sXkllìvCCÃó–e9ʓͰφ‘H ‘H  r2™œèìì,!ö.\ºt©¥¹¹9ºË93Ž>–ÛÛM•ÐWSðͦá<\ÁÑ_çA»r‡"‘È‹ñxügÕ%466žY]]UÞ‚a„?¹…õÓu€f€òÑÄÜ2Ö›CØ W£ú§?Q=þÐÓ“ˆ`Û6Ç¡¦¦¦sñxüÆAû¤”µÆÒŒðG7‘y!Œ• §@†A8zcÑ0ØXè{5?$P}{0ì*)%Ç‘Hä@‘§ô¶óJßþ(•×O{I§HøîýL´´¥"°´~ë j†’`[¸ñ |>ŸØ¡Ä‘‚ÆOúåîç°ÑBø½o­êþ"(•Æf¤~;îÌðÿöÁi<úð8§P[[‹d2‰™™ÌÏÏcaaKKK" ½zõê•RûÀŒ'WÎâØ×“xúã›xüþy½ž@¦-ä `ra /'ðè³7¼²¬««C}}}¾Æ†ššYP}Í…OÞ=‡gÃñAÞI!ûò3 Üvk M…ÇŸ¶D»ŠÚmXùž‚ ìÝàJŸ~³Vz©oÞm)sëTÐË‹…€]'Nä:¤R©TÞ¨€eYˆÅbB” `ŒÁÅ‹¡µ.~¹D„ááaX–U6€Öííí%߆ J) ¢R–J¥@Dÿ•y1lÛÆµk×*¦@___i!Ø™„@ b9°#ù¸¨D­5úûû+‚’«@J‰ŽŽض]¶s¥b±Øáª@kÞÞ^oÒ)ǘ9¯ó‚w1—/_®h„ÅC`ŒñŠh4Z± •"bf†R S@)å%wQ\ ƒpmDÆbfH)ÑÚÚ )eÙÎs¹FFF ”B6›•\R¥ÆÇÇ ~8“Éì;Q!ˆÍÍMX–¥Š¸¿V˜šš:° ™ápŽãF ADKEÜÉåäÉ“EïùRæ˲N§aÛ¶5==ý{Ie(„@6›Åêê r¹\ ÈÀï >_Unbbâ—ÉÉÉDA€µµ5¾ÿ¯œ’ÝÑZßÿœ„Û&ÒòîÝ÷º»»ß.zÍÍÍ'®_ÿî ­µí~¤ŒQÌ0c%™|pkxøÇjïž„¥8T*1:XIEND®B`‚app-2.8/src/images/new.png000066400000000000000000000015241377436340000155070ustar00rootroot00000000000000‰PNG  IHDR szzôgAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<æIDATXÃÕ—ÍNa†ë5”•{qáÒÄà¸â\¸ô\±0ê–&bX¸°‘X Ñ¿‰¤±R¤HE”þÐCÿ¦¦e€ãy{ú…QJ‚É!†IÞœ3§óÍûœóM›N„ˆ"ÿS‘sÀÇÕnÿ¥û¬Ç==d ©ð12<<¼j4::ºV<¯&“ÉV:v‰Dk`` Íknh¤8Òááq™ºï·É²,Úßß'†ñxÍëŠ@?ó°Ç¥Lf¹ Åb±:¯{pˆSww²lÛ¡ùùÏd×uéùÄDB&{Áɪ7JÎÍSøp]‹T‚@gOÎí¦Ggféõ›·Ÿœ¤©©iz÷þE£Ñe^AÀïí¶%†…šã”]ÍÑäô+z2þ”žÅ^ÐLb‹UÚõ <ÿjÍ67«”/nS‰c·…×~QðyÌÍ]µÇ{ÛîŸ;¾äˆ]¸½îâ”Ê3àuäÆuWbØ9êæ3DÔ§àô:­9"˜˜hr€˜kPS*-¹1âNSŒ òmû(¶|E›;ÛjüiŽûÅS(ðC¸fI÷kùR‡×¾TÈUºN­K¯…Þ·Â’=O¦³Þ£±qëÚÐõ˜³n©l4¤kÿàZSÔ Yoäî½â¥ÁËK|mŒu‡5¨ú·ÝeÙ<þžÏ7½Éºx&oFhòÿ™”žé?¿BÓôü½œž¥~Ql%¡’• wIEND®B`‚app-2.8/src/images/new_folder_48px.svg000066400000000000000000000003651377436340000177420ustar00rootroot00000000000000app-2.8/src/images/nextrelation.png000066400000000000000000000014521377436340000174320ustar00rootroot00000000000000‰PNG  IHDR szzôñIDATxí”ÝNêX†×ùœÏÉñJæšæ6L< ‚ TiNp„Äq´ücÔù)*" øcëúf}$%LwÙºÝOx’7Ui»žõ®OÈŒŸa†$H.Wü5“)ü‘N‘¤™:ét¾—Ï—àè(K3™¼9ÿwÚ¨V+pzzF™„ªIL] \. Ôë5¨T¾H..P«ÕXU“(˜£Ñy“D"A>ÊÁ­– Fƒ5Q‰œœœÑýýCõð0m^_÷“7I&b±ÄbIˆÇõIïFãôêê dYfM\ŒD:©ÑÝÝ„º·÷¹T²’ï"Š; ( 7 Ø‘Ti8UÙû͉„D&âó…yzzB|_‚/c vqA Û.z3L»Ý†ëëëa°ü]“Ö‚Ï %êóUAˆ˜ýþ(1„ã6Á^^^P„µððnül|qíçf³9L>_ çU77…y›ÍC¾auÕ¥^__õ"øB½J`ðxðs}Plt,˜l6K×Ö¸ 1biÉ:œ‹‰h»Õï¥ðX0ÚŒHR𮬨ÊĈåe+èx«MÈp÷ØÌøŒäry¶¸UµÙœó‹‹Œ1n£ 츈vÕæEŠÅµZªÕê4Y,.bˆÝÎÁGÑ‹`ý8#xDÇÇ'Ônw©ç6ñ|„LdkKXÀÿ§Óý®¸\xÝ‹ec¼ÀYÀ¯dvŸ²±á5…B“‰8Û䣸Ý^@TUÍæü¼L=ž-ÅëåM;;{ä³Àæþ7#˜Z­NÙßAˆ˜’Iž|*‚ ͦL! ˆ¢8Ç󫙊"Ë2åù€‡ç'™ ~? àÎ[ÃooG~óxx25B!1‹Å!ª‘È_Ó]<ŒD£¿ã•5@fÌøQþ÷eòä‡RtIEND®B`‚app-2.8/src/images/node.png000066400000000000000000000023331377436340000156420ustar00rootroot00000000000000‰PNG  IHDR Ö›^?sBIT|dˆ pHYsÞÞÝêƒjtEXtSoftwarewww.inkscape.org›î<XIDATH‰µVmL›U~îK¡¬0À"_([:+[Sp nš°16dÁ¸¹tLPg0õ†iܦn‹šE£?\ ~,bœscƒ4–Ý:ÊÆ÷¬”½…¶´ÇÊÖµ/í ™'9?ÞsÎóÜçÞ÷Þs/#"õJ%ËÓép¨¦)©© Æ€´4P]RKKñžRÉÖ{ˆÈÉÛæìþHíN׬h *wíB´Prÿ~ĨըòŽMÖþ…¶ŽÕê`æÓWD HIA¢¿‚¤$ß|{E÷á‘Î!ï¸ÃîFÕ­m\®+@âpÀïrÙíðYîþ~‹9))r«©ßzXµF¾*=eyT÷Õ©q½áv{ã¹Á2"òû‹`4âÚÜT{Ñnúú` L]PÈ ÀLD.±{Zte%:\®ïp§|y9ÚD,æÑ²X—ÑDB+4›qT£2+ Q—/ãŽ^ž¦&”‘UìLcÁ…+K¥RÉòÓ?ö#"[@×Å#° @Èbg²óùÌ_žÜÞa“ç§Þâ¿ûfGïÞ=k«á–|xZXXHÜ÷ßîÐlYã7´ŽØèÎlomnYð= ÆÔêøü§7¥ÆxÇ×=‘ Kü‘Mþ°EÀØudä®ÏRÚlNÜ·ÝúߘL“úcŸ.8÷Oáܜշ´Ÿû«ÿ”?lÀW±#"bŒ• XËUª¸|Žc¡mí£g¿~8PSúikSÒ*ôúIEND®B`‚app-2.8/src/images/node_48px.svg000066400000000000000000000002411377436340000165340ustar00rootroot00000000000000app-2.8/src/images/node_add_48px.svg000066400000000000000000000004211377436340000173440ustar00rootroot00000000000000app-2.8/src/images/node_add_outline_48px.svg000066400000000000000000000004231377436340000211050ustar00rootroot00000000000000app-2.8/src/images/node_properties_24px.svg000066400000000000000000000010671377436340000210110ustar00rootroot00000000000000app-2.8/src/images/node_remove_48px.svg000066400000000000000000000002731377436340000201160ustar00rootroot00000000000000app-2.8/src/images/node_size_48px.svg000066400000000000000000000005761377436340000176010ustar00rootroot00000000000000app-2.8/src/images/nodecolor.png000066400000000000000000000040301377436340000166750ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYsiZBtEXtSoftwarewww.inkscape.org›î<•IDATX…WkP”ç~ÎwY–…]..Â.‹( *A«Á(oxS;qšV›Ö™jš§3m¥mÌEM±Ž$Ú‰I'ÑéŒ&ÖššÎÔª©4Q£¥ˆT  r‘å~ÙÅe¹/»û}§?FØ ¶gæû±ï9çyžïýÞ=ç¼Ä̘̈H ð„`"ÓfGì›™¾$B/›Ýu ½s¸ý›ú¾ö®òIP DDÉóñFB2ÖN‰CœâÇÖ[S >n®ãÓDd\žsîàkó3R’ôâhžª2>ü¤ÎöùÕö?TT÷ú¿‘6}) s"#u1¤Ç}WÎbèÌ{¸ª‰ÿìøÒÕºPÉ'Žÿ±Þññ_¬[[þL€ßìÔÅ8vè3,šjñõ­Ý]¤Ï•ž ‘À+?š}£Øö«ƒ &.QÔâUXé|Ô¾µZ!ºO ˜Hˆ×‰H#‰´ˆˆÒˆÈ‡Ï碦"kÙƒ3ƒL³¸«Ù¾|HÓ²?Æzó{ÈH1ã#ÀÏ`æû×ÎÁªx pØ€ª2µìøézçD_‡ÍEqÚ>A#ªª(Àí/?64Ç‚L"|€µÛßù):ý‰èï彄ڪRuíɳ ;¶ýìv͹˭®¦ÖA*-w ïHù`Jìˆ*‰¿q {*1æŽÕ¸8}òœ9ÆÃ‹Á¤ÓÉá-­Îw“ù™ ^a᳈r» þûº‹ PVñ/lgf'0V%Ó’gèszû=V¯ËvhާMCÐ{¾½Gþ†g%X´(þÕ;þjïÞìXY+jhltbçÎÏëOn8äõp}¯·G‰G™½*= "cI-ºÓ¦aJ0•Mp0s½`4†ÍÛ¸1%7/oÕ8r˜>=/þ`VÚìißïu p"¹?cæîÛ5(qôƒ@Q!+ …( i…´^…BîwB­kÇeRSc¾õV¶) F#bÿþ•f³~ódä£VÝ‚û>Ž;…H$ɲ(в( ²( M]²pâ²A0PlH &A  €K—&f³~5€¿>¡†~?PUnäÛÔ©êtÃäU®áPž–¤\ݺo_/úuT¨ö–¤ÑˆºÉÐdY„V+ëŸ&}Øwö®X’±:)Ñ ÝCÃ"ŠÐ‡hÆæ¾KÍ7[ó%»}°ÀŒ‰ Å7­bÁ™R‰úÈåV1r¿35*œÒ{¸brá9Ë-cʨ õùGˆÁb7KÎÂ’’Ö™™–±æòÛÝ4&k½x ÑE xÜ c£:÷@.,4Ò§eÝüF0a²lЈb°€N–uBee×áÝ» J­Ö‡Õ÷Äû·ä öoÄ—]D¼^•‰U,€ó!&7;R£iK0àöþ†µRþÝz¹¤­Kp+Šß8ûÐp'1óÃ$=îýììÄuøÏ½„£óUaV•E°G$ŒCØ|µç¬¼`" iaÎø=f®\/&eÇË3…<¨âøÆðžDx¶Ì¶Œá”µw¹_:_ø®Zã+2Ѫ3kpAqC#ª†üwÄ,¢‰(–™»#'$d^ÄæãË 7AU½ªìŽ0“5)‡rk.h_éÙõÔtok_?r nÜ©´uçkÇf,É$BV$ëa°jÆ\“i w¸¥»£÷bælGÎþ§¡XRX¼U¢(’(€úS6Ñ‘ë%r]ëu{QsÛå¶]ÌÌãfB"Jþ0 7w¥ÂˆüæY8Ø?–½˜5¾l¦ÎÙ=ÀÅ…U Î[å¦Õç" 5øöVbUä¾vÆ™òØÑxpŒs‚Z—€Ò‚ ˜ë¼Ð¦¯g¬Ð<¿gK!ò¸n×ÕÚ«ÝýòW¨É8您 ¼}Ÿ|û<·Üyqôç¸vÌÌ\mÇ/òËᘘ7èþ,%É›÷l‚¨‘F&ú£¦êñö‘e‚©æh`vÅ ¨îqÃŽÏ<Ð<Ì·NÞÃξ@ýí.K½ÀGµ’ù“u Y!"Ÿm&0'$OERd§ßã!x±ÎæK/ù«k{ù]+±#'V‹µ*ÃÝcœy*kÖš@}C’êñ Ó, : ¡‘ãEzG€›ï•a¨§`RÀÌÃ.>z”f~­ÇÞŸcJœâwÐ TIT=ζ‚Qã¨g\~½-Û&^ïß,&˜µºãRÑåÊÜレ(F”¯£¹­W¶¶À`1CÖê1ÜcCos:«^gfŸñ‰0så¼ÌEë·>½.<Ò·¹ÀÙ®9Ûì{¸¹êË'Å |`üXÕÆÿòùc%u­ã 3ãOG®vÿýTñÉ–zÛ“AnLj„ätË›‰)±Y†(ÉãV†lm= Uù¶¶ž’ÿ À¸M=;å€cCIEND®B`‚app-2.8/src/images/nodein.png000066400000000000000000000027321377436340000161740ustar00rootroot00000000000000‰PNG  IHDR †VÏŒsBIT|dˆ pHYsÌÌëP)tEXtSoftwarewww.inkscape.org›î<WIDATH‰½W{LÓWþ” TD#âk¢Ò-ò’ªP`ÂTæŒ.꘢Y3ÇbÂæf¦n3f‰ÑðN¢™:c\– “M‡à|Œ‡c”‡P/ÅR @ƒ•J[(gŒb‹Z–y““ô÷s¾ûÝû»§çw‰™1™á=Çs†,*pOØ'VûL¯-)«¿~ýFÅ f´‹ˆ™'e{’ןîiÏЪ;2µêŽL­²åíÆ +öØËã0™ÕÑ´Ða4`¯¸8#$xé{¹&%ÀÕuŠ“@àè8†0§—" ¯¯¿»Xþw¡9¦× Rq?Û^.šì! Îú|à¿0F«Õi+÷oþrù¯£Ìlx)FˆÄÌ\eCœ3Y@D.\¿R°©§ç©º¨¸æLSS{­ Äf¾kÍáë·<,`·Ï«³c4Ou­••W².7`*‡ïŦ6ÖžÕ˜Ê*ç×C‘Ò€˜‰Ê€d<ÿ7‡’²MœêŽLmý³©Ôÿm‹2$"Y´xÍLÏ©ŽÏ•û»EFî˜`õ4ž?$Ä7<>.XjŽÍš9Õ1&J²Õôì"Ѽ%Â¥s½Fxzº¿:ÞÄÞ'¢1ëßh„^o|á½Æ uukný¡›13ªj\›@@5€ùc9ïÞmRývUnq@•ªž‚‚ªMÏÓâ²úT0óò?i{»ZS~§ñö•ìŠSD´ @€Hf>=Æ<¹ÌüÄ ¢D žf^ºóAW—&ÙO4/ª·Wû¸H^w¥¸´îºÅJÍÍ××[äáá1mÔAÛà!€½ÌŒY³àµ};.¦¥¡6#ÊÇQ‡/€$Mlé65Œab)€ã–¥¤ Äh„–ù¹µ·C³z5Òàek3²» ®]‹#:åä&»t Jóÿ×n(Aèì<¶/>¡¡H°‡ÏntÖ|}} þ~¨í@DŽkâC·¿»Eö¹"¢MD”LDGª«QÚÖãXdh¹w%ö@àë‹BÒÓ>ªlm¾ íVeh³.hJÚ»ÓìØ@`13cófdªTИ¿ÿÜ\´FEa€óÃñdËH#üw'n• M‚¢eboe[ÏN"Ê P @*3 3[Ôj|*#ÒÝÓT*(ËË‘^YÉr"ú}8¾ À¹‰6@°x‘WÐh0xÙ‰÷Oï6Uwù0Tcîgf&"É­[HbæÞQ¾V"JàDDN¾¾ 毒ú%?Óô%¥µÇ<èì´ÐÐÔV@h–ßi,oSu7XSMDûñoìËÏÌ…DD2™äؾ”Iáá"7fFAaÍŽuë"Rrr䙦X‡Ò²ú“çÎßPtu?Òëp5·¬¹¢òþ™q&'…ŽY‹ÁñoHÂÂÃEnÃyˆ–N òÙf±Õ5-ÕVFËÄëÜÝ\æf甞Ÿà³Šè™ùÂxˆhÆÏ?íŽÆ½¼fø‘€‡ï#Í(¿ *g2AâÖ7÷†…¾"ísﺆ‡¹ÒqG±¼Ji-‡™Õ_H,Š–¬1Ç›Ún³ÙåEðbê‹ã±Ÿ>uðKWW´ëà×'gH/OQÝ’~æûk3ãゃúû ƒyùUŠGÇG+ÐR¦ä³¡Bkn7¯lú.\`K~X˜0&ÀwQؘÿ¶ìÀ ŒÆFΠ·%¿´´>ϚϦ^ /©Îz¢é³øþ»]¤È{øHÕi-ÇÖaÓ½€ˆßJˆüpÕŠ µS\œ§+õy…V|Ûܬìú¯þK‰ú{˜KÃjIEND®B`‚app-2.8/src/images/nodelabel.png000066400000000000000000000011541377436340000166420ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà 5ÃðˆötEXtCommentCreated with GIMPWÔIDATXÃíÕÏ‹QðÁ„™BJJ¢F)jl,4¥Ð•RbïP~¬ldc§„ ŠõÄ4 %QÄŠšÂbÂb¬&©káÇÅLrï}mέ§Ó}oòð~ëÔyÞó=ÏyÎsžïóR¡B… Žkø‰sÿâð(Òh`}¿ú౿ïË2ûv¥7žÄ ÆÐÄTÆ-0ŒSØ€:Æñ¹ä¬1ìÅ>â^wWáUxßθæ»±=ØM´»ì9Üå’÷ºð œ‘nñ®„Çel]¯@->ˆç½¸8„‘ñ4âk—”çhã%žà{ø>%8˜2×ÁLÊÆüÀ]x"š äY´—ºdàtàדüÂ6¼܉À]šüÂ| ­Ínx¿GsØ’ñç²Zh{¤L†+‚ÝÈÖë=ÔJ…1æÃ™ÌeÌûh¶^ëÀÊ$¿VcM¨—Y| ëûË] ijcËqu5PǺäçvø>ŸÚôõŒ¿3Érw°OÒvk‘ex+µ95µØ/ZY‹%޾…ùÙä0:jvÙ3eøxIÃ*ð4÷à¾àÎcn¤_ðPâKú=t>•zÀ[œ ¼ü/zïS1>ÉT¨*TøÏñ÷ßÑ©Qbï IEND®B`‚app-2.8/src/images/nodelabelcolor.png000066400000000000000000000012421377436340000176770ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà  ¶Kç tEXtCommentCreated with GIMPW IDATXÃíÕÍ‹ÎQðÏ3yiŠHVŠ¢A!oÙ¤ü’…—Í(äȬ4;”÷ÍÔÈâ¡<±T¤Hy IVФãaæZÌ5~~æyæ™h6žïêž{¾¿{¾÷ÜsÎ:ê¨ãG;‹ qM¸¬Vÿ­€†aò·§à°J7Œ´€¦‚Ý<Òþ9."3±#ÕEÀÝ@gd/>Úù¾E÷a,Fá¶rè”Å â+–š°-èMÜ{8Rð¸6„Øã!‹!‰¬†gÊaF.øbܬÀ}Ü™Š+ý:ªbgY.‹×e1ˆ¥I¸ZåìÙ8•Ròïð ãóOTÝ)­“rm½k±crü÷èA&ã]#Öå÷±2ÞŒCC‚%©˜—ââ€wô÷­ØVøfj1 ]Åž ¼Mëñº€€=Ê¡;Ù—d±ú´§¥Ào6~ÄÇd¿¬ ³‹Æìó«Wãæ|ck™¹üN¬AÀ‚‚Ýú+ÙJßòJs /'dsä1âV vËâœÁÃß@AÑ…(Ú ÑŠÔ*´Z «Ñ6>’\7_`8Ü{ioÝdàrï9ç;3sæÌ|“@UÆ/?À‰É0¾ ðôä€ùUX3¼aÿ„¨¸Äñn`½î¸èR@èt° ,p °•6 ÀWà!ðº¼8è1÷[~N™ïÀJ3.%Ÿ=»|yÛçGÑ)ÞìsX6œ¤e¼x†­vMÆã pøárWJ@7ðøeæÛ€°C‘+KŸ¢Ñ Œ·Ú x‘ãíŸ1ø´¹’À  Ë`Û ¶Vúëò4Ç9áÝFF?êäBÑŒ›‚ʰތsÎz6¤‚ŠJD+yópÊ<äÀ€ïqÖÓ!LSù•e0ÛäËðͬo Rt΄©´S€ cÈ,0Ozn˜ù¼hú’ƒ_«²ln[Pm+\†×åÔb‘šå‹¢“GœPôÓ|“B«¨à³'ãDø`ayÀ ÜܾCÀI \V nn¿êw›ê¼Sðh58·‹^>*Ÿ‡”¨‘äŽÈjÒÄ `ÆŠ·ã8°\„ &‹%”Ä3}3€f‘OmTçºß¼úB—rc®ÉäO¢Ý¬Ú8*±÷Êþq$9.#Ÿ•`å°/1|Qc)Ê9dÜÞêé‹êÀ€%Õ4:5n4¬i½ÐÜ^Í÷HG½aÇqç@RïAà/Ðïƒy¥+’®uš ÷o•u$Éè4)'"6ÛµÖ¯;ß ùaÓv›£:p €½滵ï©3Ÿ3V"/Eµktúó:ùU`ª ªÔ:€}2Ö¡.¹ø -½ÕÿsU ’ÿ2ƒ,ƒÛ[ºËIEND®B`‚app-2.8/src/images/nodenumber.png000066400000000000000000000011231377436340000170470ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà 57œõþtEXtCommentCreated with GIMPW»IDATXÃíÖ=kQàg“¨`"Qa!•¨ébE+!)4`¥…Úˆ……?à?°,ôX~bR(Xˆ¢±P,-$©ƒ&ºÙ4oà2Ìfg²c±†ù|ï{æÜsÏ ]l1j›<^OrÜŒ­r}¸ˆ ìÇþ`3¸‰/U)x)yÓõ¶³Uø\@çª p4ið s!û‡¯ªRá6Ʊ;ñTNg4¶b…}ÊXwÙT]ÉñÏVK(quüÅG¼(Ñx×±7¹ö¨hñå.~í-jÚ¬€ïØQ”ÀH˜ª?¹vËñVe 4pu3æó¦6¨@·:â—xé—ç¥3XÁ Fq%GöQ¼)Úð0ŽEÆßÃ×0f¼Ë¨ð°Lq#S|wÓv!ÇŒ…r †ÞØ×BÞ“¸S’À¶Ìyo'&<’“f'ÚÔÌfxÒ‰ ë˜ÏÔüˆ´»×Éý!ÿPfŒq tEXtCommentCreated with GIMPWcIDATXÃí–KhQ†¿;‰‚%b”®ÔdBÅ .DZAmAºq¡"JW®ƒâc£ ’Yˆ+¡ w‚ QT\»ð3>ŠC#º‹ŠZ5&™{\dÐ1N̤vÔE\˜Çážÿþç?ç\èÚ?65››i0¯b€Äà$çÁn›€å@ ¨S@±gçÀ‹Xèó`Ÿ€´[†c áyþÚ1ëê°&à•@I (0à^\,œó`@Cï_SuHjlàýÕòPå ˆ0?#fóÏÓaɰV¦A) XÜ`”gŽ›ï -¦À1‹Œ\‹Ô,ÓFC|'<¯ºòIùp5„îðñ7˜>x°$ _£¤à6°­SŽ›WŽ›W ë´‘˜{d&:8¼#³LûŒeÚ·Z.¡Œþ©×åÿ*À°Àv=Ài~=ñˆ«"ÏË´W7D*½ ök5zóc÷€ÓA):@6(D¶µ­ßî7úŽñR'Áý€§\œ¶?j¾ÕÏï…AË´ßZ¦}¾Ã.¹·IÓ3a6SÈY¦-MÓpc4MÅ›‘Q‹ó¼„D³‚/K£Û]‘FÊ^JAZ`Ð×ÄÈñ¶²»ß0¼q~ R0ÔFÝcIk @)®Š$j”,Ó~ôX¦‚Ê,z"Òw]ÃÎH} ¦õ2‚­Jû>'7Ÿ|äæÜØÜ>ûu_óW¨Üõ`@ÁÖ¼‹õRªƒk¸"0•€/Ý{׺ößÛ7M3%.¼%IEND®B`‚app-2.8/src/images/nodenumbersize.png000066400000000000000000000011231377436340000177420ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà  !~†¯tEXtCommentCreated with GIMPW»IDATXÃíÖ=kQàg“¨`"Qa!•¨ébE+!)4`¥…Úˆ……?à?°,ôX~bR(Xˆ¢±P,-$©ƒ&ºÙ4oà2Ìfg²c±†ù|ï{æÜsÏ ]l1j›<^OrÜŒ­r}¸ˆ ìÇþ`3¸‰/U)x)yÓõ¶³Uø\@çª p4ið s!û‡¯ªRá6Ʊ;ñTNg4¶b…}ÊXwÙT]ÉñÏVK(quüÅG¼(Ñx×±7¹ö¨hñå.~í-jÚ¬€ïØQ”ÀH˜ª?¹vËñVe 4pu3æó¦6¨@·:â—xé—ç¥3XÁ Fq%GöQ¼)Úð0ŽEÆßÃ×0f¼Ë¨ð°Lq#S|wÓv!ÇŒ…r †ÞØ×BÞ“¸S’À¶Ìyo'&<’“f'ÚÔÌfxÒ‰ ë˜ÏÔüˆ´»×Éý!ÿPfŒqP÷ .²Ê‚ ² ƒÊˆ,£¢ýßttoþsk±ØoåÇWà3gèóyŠâæ†iÛ&8Ž7‰œ±L36/¯ÉÓ„®Îº6n†'¢qB!ÅÏœ‰…ÖÖ4]_Lo/Ôꮞ¾¡öÞîAÊËëÊ.^*.ÐÉÒ[•Wˆè8"¢Ï4>±˜ÓÒp='H$X—“ƒ¼¤$:åäDÎýä>&¢Œ’’ªÈ ‹òµqëZº¯^½ý«f,Ð|H¤ò`æPï°ÆFeÛÍ[•y.ÝÙÀÀïJˆh–§'Ô[·âë+࢙—ÂÛœŒƒD”Ò+cæïÝÝÝOÖÖ5§x{MŽ|úTU—_P~RRX~nP¦Úêééâeooo«sÙÀ4””„tf¨ôia!ZÀbÔÿ‚ŠŠ‡r¥RÙªsÌÌÕÌÌ®®rW4‡¢™¹ÓPŒ®˜ü·´àoC>…ÜЀ"SðL"@DŽR)Nåæâ©>ÿ¾}ÕÔ _ŸÏ ñ„ €/3cþ|¤;†}}ÏϾ½ªÍ›!ñö†Hc¬3ƒˆÌçÍ Znggå|43÷GfnïÏx;€W|ÃÌ%ÚÄÝÝIäé‰''Ø)¨((À~fî$¢×ð¼ l™Y6âøMwÚóÝG²ZÅUsC†*ë薪䱫ìpÀîzv&x˜]ûÀE£ª ,ÔgÍòeQBk+ ˜™¢£ü]¿{WÊÙCéë>ÿ9}ýíÕïÅmÔŽTT=”´ØÍ[•764WXÜÀW2( ‘€%‹gÇmÚ®éÞ„L#0—ˆöhÞ ³B©|ï¡Ãç‹7·ö©ÕÝ8}Vª(’U§ƒk Àˆ½BàŒ©aº­clŒ¿›Xì÷Žf,(-»_ ":Êÿ-kK×ì3…‡yø¶ÊÀfn‰ÀÝ{5Rfž£Ý=ç_/¯ËË+ÍÒŒê ‰H°|Ù¼³‚_³´k_ZVuYV,ÿVRp»~„yv›S—ž^µò G[³kùwždŸ•¦§üë‹ cJeÉâØO:žä«¸«h@·|úþoÆÌ@"‘WbbbÄ67gg7]¿À }- ù%XYY²E‰#½„n¯Ê+Ô7—Ÿ³Èê×!bÔ¿ ûºum==½½]jcæ'F(¸QšÕÚÖAÚ¶¼üâËj½(c/¡ù‚ùâg‡Ä[X޵+.–_νVôƒBQÿøE ü 9 ^¾‹IEND®B`‚app-2.8/src/images/nodes.png000066400000000000000000000020521377436340000160230ustar00rootroot00000000000000‰PNG  IHDR @ÈsBIT|dˆ pHYs"" •:tEXtSoftwarewww.inkscape.org›î<§IDATH‰í—ALÓwÇ??(mÀ¤jY'’•6Në”H× M¶Dœ<˜…]†„ËNæÁe ‡æ Kô°™h] ;xY²¨!›ƒ4¨Àm,&hh±-”¦bk)„·Ã¨ƒ„ÿ¿@êm/ùž~ïÿ>¿÷~/¿ßû+AÏ”RN'ŸÛíÔX,XS)#ÂÓÓ|Ⱥ뙈hÊfãͺ:ûúXH$H¬ÕåËkhÀæõ²c~ü~æïÞebhˆ3"2½Uè¦ÀJ)#°¿ªŠw…B<‘©í·~–õ‚øœ+3¼Š f³ª®¨à‹…×L& £Q³³Ü{ôˆ¯$ÓTÛyÒô´o—Οçñâ" ‘ÿÔÝMÌ륷¸˜ Ý÷x;r¹øøöm¢kk•L’ðx 9ƒ¥--ŒkA3êï'îrqaÝ+¥LÀ~§“wãqá0}"òx3çjµr´¹™×Ý‹ÁãÁ°{7Þ—`‡C}xâ­§N±Ëã¡hvñû™¯®VãÃÜ‘½€6Gkjô¡³XØ•iˆ¯¯]cn£¹*$yü8›LTfiª/ŸÏû¾ß—]vý˜ ñ ‚Áu‚ÏýþH¼› áW¯ïµ*ŒÖeÛ`[ 'ñ3ú"üÜ>vH~Š ¹¶Y2Ï­B9þDöQØÇæè‡†={öXSR®,Ê::NâåIÃÄ1·>Iù,`›]YSMM¶4ëû 1y`Å»ï¾?Å«¾ƒñ‰)LOÏ®’ï×¼tŸ¾ã²CO´3@‹{Í=.4ëì¨Wö ‡S…2^5C¼°ôÁ`\&¿K|áH$8AýÝês'ñ¹'H‡—Á×» "U7Ü7¢¸Nˆ,V>ÌVÛ:q,k,üÎç'‘6“•?F}¥þÄoôY1¶~ï Z{œà?0¡L(CF²ù&ÒùÅeÌ-,!4¿ˆ½{÷ZÉ;~ºm$~CF=5iu³€Ò?ËÄâšP¡Z´lñY ¶ ¦]/²sq·žÿ 1% ÍcbjƒC'äݼ-Ó°;’3&ÔSµÓûXOˆÅ!ˆ,CPºFÐí Bå›B›sü©ÏZî*tȼÉE §l“Ï›ˆ'§Ào¢ÖÖ%ÂQÛ(‡Æ¯‚/b”M‰$"êR-šB«èy¡ÅÏStö £Óá‡Ô<€½ ]Õ ‘• /yñƪ©Ô³s LÅ”ØÝï…Éb·¼¥å•KóË«#7ÙæÙ„—Ë<Ðhò£Ýî_'–;† ³x×}æ¤ø<3*6ªÜS3!ŒŽ3Ó&ä7 ñÁÅQs¯¾e`ü ##òΡ¾]¹¸QƒttM§ g€©¸ÝêƒØèB“ÊÌø|åË,ˆÚ$Q‰§g熆G`w8I‚:$|ø!®³ á™\€o ÐÞá,«Ä}ïÝaëÿŸÀ¹ö!HÌýO}îÔ"U„ªšºM>GäŽø<<„ÓݽÁˆÂâbœ~/=ô/ƒ„¼Åÿ-î:C(Q‘÷öñNÜ¿}„3iã ô3«àÉTh58ÑØÝ žX‰¬\ö3>Gªžœžeäî÷¢×lAC'bã éÖÀEÏR$¸†²žn´ÛPÑD…΋J~ m¾›['Ñ ™øe"î)ô(Èx5 ­>ªÜc“ð Ãf]ŽÄK—ÃÊÃÔcÀNzI1Nú‰”[;„|…%jJÕèò0£("øù3 t.¡¨¾¥| ²J*ðÏãqQåöFÑçò@«7 ýj’¿HƒÍ7'©Ú@Là{ˆREÝý qA—Å݈œAüùÍ·]„ël´CÐ(÷N#§ü²K«ñQêWHþoêúXQ¹G‚ãð ø`4™QÉ«ÂûgÏAþ@?én²¹oiÒ›*/råäuö¡ð¾•ùœÊ)ÂS¸Ý)Ü+vQÈÅ•ky8Ÿ€›·n3c‘Ûjs@"mÇ…„Pt» ó+€Ÿ4X÷$ |„JÓ8úa†œVýU³¬;¸:Ù!z/༺]&¾Áƒ”oØ8ŸœŠ=c¯™én*·F׃¤ËÉHÿ:‹ŒÕÇÑØ3ø81 †^›øÝûQOÔñ™îšª­“`+] 9õ¼ð¾g½êÌ=JUm”ÔñCáeS@ðÊ÷%`+njÇ©„DÄ9ŠÜ|6ZZÅ8ƒ/95Èj˜æ¢ÕÒ'õšzL¡Id·™ÛnF¹Ú¥v9,9=ùNìÙrÔG ;«Fˆcÿ9ßîû=ùœ¿€“ñp£­ÄOJénºX¨ï,ú”˜P(·@é<ùWì)*w-Ágû¶¹äD¾Ì²Züãä<ô7°ró°ïÀP Ñ!‰'e*¦K…>¿!ÕFä.[!° ®åÜâÒä‡o<¯<ç–5œiEåøû±X?‡7öïÇ:’«¤ !ípê5U!‹4[É7*4nÔI:ÃrsŽm•û‡$àJespäø)>|˜Œc.2*\"ÕÓ]~MÜ 6étv§wŒäˆ¶ùž„·•û"½Iý€ mô2JxLœKIõ¦.|Âme6Yñƒ\—šÁQ{ ó×Î%&6ÈýÇÈÕ‚ÜÎ^NEX…xö]\©UªûÀ–ÛP­uã®TA/Ê r¿ºÓj£·¢Qø¯CO¹-ˆËæášHª/š5æÇzó-Wx¬¨Ü¯½¬ÜÑâÝ\uÞ˯E¹v2g`õ§—©Ü÷Ò©Ü;øóÂñ‚ÊîP 9”¸ ÂÈ£‡Ã/"÷ÿÚmŠÝa™Õ¶Øk»^2¾/‰ÿf¹_ü€=ÏIEND®B`‚app-2.8/src/images/open_48px.svg000066400000000000000000000003411377436340000165510ustar00rootroot00000000000000app-2.8/src/images/pdf.png000066400000000000000000000100661377436340000154700ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞýIDATxíZiˆ\U5KÌb\‘QÔ .Š‹ŽqFg î˸(þqA(Šû. ?DÅ•ÄÅ ÑÌÀ„LÌ’˜Å˜ØöÒI/ÕK×¾/ÕÝUÕU]]ÝÉ7ç\Þ÷¸óF['¾tFðÁáV½®~ïžóï»Ë{MzüvüvLf³9¿2ÌfßÉaŸÉÏŸ?!Ú%À勵2°ÏKÈa_E88I|>&&öÈÿzìÙ#û|ƒá²Ç\àÁQÌçe°X”¡ÁA2<4dCJÃÃR*•¤\­Jmï^©‰”+)—Ë’Íd$K"3¿/ó·?4þ'Žßó3¯¯ðÞÛí—ÓOö™9˾ p‘ààEK¸¨C´¬­ƒt°::*©övÙõÞ{ÒòÊ+²õ¡‡dÕHÇÛoË(Âÿ¤’II§RFŒ3a~®ØpÎç³YI&üÌs®0ç¾n؃ƒ>ó ‡_,€QYoÆh«B‡kðiË›oJ²¹YF'&¤ Tk5‘-·Ý&;î»Ïˆ@ò™tZÒ@¬ŽŒŒ8­ýÈårÀd‹dîM!x d¹Fâ«J¼¤Ä-TëuÙúØc’Ú½[*Vç«ì4„ ÿ<ቭY#E\+ƒè‚ÂR,îÿy@טs¶;,!Ê*É+p]ßpmÆ)HÞ±pmlLþzÐA2!BÒnI†-X¿^V{¬Œ‹Ðþ6L‡k¬ üÁÿÏ9ð¼P14}* ÂoTaÛ‚!žK·µÉ»`N£HÒ5`”À÷qº`Þ<ÉíØaR&‹Ü&9‚Ÿ)Â(jˆý?lsŽ[x¾JaÔ%žtÑ`hÙgÿp”&lòåb($B€>*uÔ‚šE€ŸIl çÛx@v=òˆ4ð™iEòy9ˆÀsuüÖ€–`šòy½ï炤Õvšø'@ŪÔ.HÞ±+­¿üˆ#ä³3¤ÐÕ%5Ô„šC¤Nàsi[±B6wœpX¯ã7,‚® ÉÏ<Çß¼6Àsꊚº‹Ÿíª"ð»ïX„mÔ4†´½ð‚ümþ|ùÇâÅb: Â$© åpXVÃ)ã Æð(bÈÊ ’s@!ø×%ŠÂB©ŽPè½-Wh¿4%üÀXÌ*Pú™0dA°R_~¸üX{úé2Ž¡ÄÇHÞ!ËȯC*çÀ D(€4­®äÐò< ÞÃNuX°]¡"ø*€S•ˆ¼Úœ$“6ÈJ¤Á*ˆ°ñŒ3Äц%ÀæC•äÇKc|œç #Æ¢LâBPÞkÌI bTɳõ×~²Ï¾  U¨€Q%𙕾ãÙgeªýÔ„ öF0„Ñ $I¾?ç ¿ô’LÀ1A1”8ó#yäi_Øö(á¯Þ|·+äzñE#BÑÀK^ïÖýDZX¸‚BL½ÞÛßU€¾ÓN“èUWICY ÛeaÌâ ‰ôH!%Ò˜<–/—!¤]㽞÷~LË LŤ"@2qÓM¦.ðû0öpC¤á"…‘"sþùRûþ{3»l@< MKε &I{Xì>]2wÝåæ;Û:_¼ývIa¸LAˆ$€+bsçJöúë¥>0`„›D'I€›,ûSÅÏrŠ@¢3gJáÉ'IÜ­ ‚Ž(aÊœ:ë,ãˆDˆ1™3G2·Ü"µhTŽÞ~Ø!ÊåÊÀ:çDk€„0|í5³|¶mÍ¿rö‡¶øé§’ÀÆIÓè(DˆRÌ þ7E!úû€õÿBG«ý7ðªý³E@”+ØŽ"še9oãµtl¯Czÿ}Cž…’#Eb„ D„ˆ_{­”·o—qPâ¦üu€G€:ñc.øQ…¬Ä‚‡<¯²ãž¹?s—ä9´©c BˆF‡ ŠcB ý¢× ž{®?ÿ\®b;Fñ¶néêòG€ºN©®w¨Ÿ½kž“r?Ô‘Ë^ŠNôYÍù{Vóo*Äð_Hø¼ó¤×èÇhÒzjF.I`Ý1°l™t ÎìÆÆLåõ×ý@¡h(¬5W‚ 7Þ(ItŽŸ]g\¸¨ý•¸þCÁY “ÕÎNIaº5‚3ÊDèº Ñô\q—›þ¥€KÞþìÁëÂr@úä“9Ì‘EqÂ]±Pàæ§#š^Ãjò 2Ï?/½'dÖÝ@'W¨ pÉíX†WÞxÃß"h‹ ið£"xÜÀÜLÌš%¹Ë/§J”×sWwt‚I)ý:„¿ŠŸ|"!L£»±¹@äI¼ ó„À™gJôƒ$¹Bøw$€YfÄhŽ*o½µŸPØ"Lâá˜@‡‹x†HR*Œ½°1‚9ÄIºÆ'ÉOGýÄ©§2Ò&ªhÓ×I<âfr|ÆùÐý÷K:“‰ÃÚIDœ¤ù¼³èrÎVºÀK*À…¾N…öÌpR Â5ÌÈ¢\ÙÝ}·cÆbK—J5!Ä™†Fcy¿õVIƒ’FDq§Í3 ÇÉ#ÍVW|ú²E¢8êH$¢,æø+€“ˆÁh½û®DQÅ‹xo( ‚!%ñ ˆy¸#tÉ%’jiag5רëί>?t‰§=„cpKs„0êD ìEQ-ÃÀ¬_º->)q{¿P["f)E„ÃÁ]Ô0ò°~ãxꫯ$‹{èpF0Ê,j$¯WÒJ˜QVÂýX!öööJ'f‰MMMF¬•+W~Ç3|{. ídB´>ÉW»»Í’6„I¢À¹óƒÉM"eÑYZœÑælë’רÓâñxœdYéë듞ž鯵Ix×®]ÒÚÚ*;°Ÿ°víZ üm;ú0d|@[bRGÐ "’ǃÒ0ìΨò\ÓÃîñgž‘<Ÿýƒ`QçhÀ!‘×'yîè2ò$Ϩ3Ò$ù-æ›7o–M›6Éúõë ÙuëÖñ»ùákrßíÜÙ¾ÿ8˜î›*¤°è!’‰³Ï–¦¬&ò´=¶¿#X dÁ<¢œÅotë[_r Œ<]@Û3×y’ÿ©ƒ#«ÿ†µkCè÷Ÿj}_ðÂ~„ëñò×_KKSîึÇÄ&…!/ßÓæ$GÂz û­µ>sžùNÛÓÚ<ì­q}Æßç!T .Ù¾uk }¾ 8RÉïWÜó€ëý>’ÈFqC¹¢^(yüFwk½×Ðè«õ5ú¬ðÌù-[¶/ù2Éçr$¿·¹©)þ^ ,Ròû]›|áÕWÅÇD&LëcÒ»÷^³‹“Æ«²9ytº,Æ1F(€Sõ) «= sœÇ—Öææúzí´iÓ£©üÀÉÓÿX(°ÐÂr4ÂçP’Ï='‰àÁÇŠà>âÜBi À†ûS(ÚŸC«ýF<?D¾½µ5ƒ~þÅCÞF ë½W°2‹`–Ý£ˆz¦­MA¦‚Èõa5Ã{Âyt˜¤4ï]x='ÿIjØÙa1ãÛ¡ä7f‘ëQéyxÉw´·§ÑÇ«@þh?ÉOþŠŒå†šˆÄð2T6a»«ˆ&É1¾ýàƒF€ 6@uÑ¢®±_caôõõÖ!ŽûŽý“÷C!éÂ8¿Ã’E~W[[Œä)ù)€Ð”È 2!nQã©NEÄl àÖuÃ^¿%)%¯à5}©‘ïörqC‚ûã°°Ûà¤@ŽÃ"¦»m--ýèÛ2à(-xS*Á|NâGäÄeTÄ}s„ŸSXÂöb×&ÓÞNBú?^0ú®è àæ?ˆô÷3Ò² {I¾é»ï:ѯ? •ü” À¨qêšþì3É<ý´ySԤȕ1ëÁó¿f{9b~ÛES° ùOQ³Îš? ›snß„•c;„Äß¶mÞÜìÌðè oJ`”ì iWŽÓþ’wÜaÚ2ýx€ºôRÉ:ãº!KxßçóN~¬üOXù¿+ºâ_«WoCÎŽPòS*¡¹OË񨮮ÜÎÆü|ðË/eàøã%òÔS’Ãß•¼]< »ø1újoþÓþ-ˆ>\Áyÿ:ôå\{n?UüÑPKv–-Áü¯¢òÐæœ§;JÜ ½–[ýá&·úƒ<'?Œ~(ÜˉÏ+V,G?ΛJòapp-2.8/src/images/person.svg000066400000000000000000000117271377436340000162450ustar00rootroot00000000000000app-2.8/src/images/petersengraph.png000066400000000000000000000042261377436340000175670ustar00rootroot00000000000000‰PNG  IHDR@=ÕoÁbKGDÿÿÿ ½§“ pHYs  šœtIMEà +êR‚ŽtEXtCommentCreated with GIMPWþIDAThÞå›{pÕÇ?÷Þ̃F„%d°hZeFT24VtTR!”&± 6˜ªQ)AEiRA€…ÆbrOÿØsùË;înòÍììÝóþýöœóûý¾gç9|ÝÕñdè¿i‡ŸšaG?hYÕ ãˆéêç?B¿Lhœ&Óž‚–´ŸÓî>¨ BWD!¼ÝcñwG§# ^¿ÁeçÞ€êóI ‚çÃì¿8—µ ¶¨€ƒo€”Ÿ«ð2¡<˜¿ë¬ÜÏÌy¥@Vd+ÉN4ËpNãÀ{ù) 7°¸ù\Ü{›‹€±Û8ü¸xæ\zë©ÀRx3ÍéÀw@ÒÙ>Ê5ÇŽ¾ÀÛý è¯YKn;ÐøØ L´Q/´i?‹-úPô›êYƒ‰@g«V¯ÅŸ0ë› ó¾¤<Ñ`³ÏÞÒgÑÝ¿ ”Ù«R ®áO'‹Åé+PºËA$» XÝKàr¹.‹åöª> IiY$\>Š«F¶9Ì6Ÿ ]¥€û€¿Èy¯#õ=ô/Zƒáïrÿ¾ÿ:|UÀ(à-`Š—Ó=x(p¥µa• Ì<,˜Ó(Èxõ8̾î¶Õ‡€u^?8\ls5\?Æ<Ì€inµ;øöÃëâÎ6šÇ€õ(µNù½ßƒök{:Â.cYe‚¯dþÒåøKÆK{{æÁàzÊó–íÀ>m·+¿t9¾’!§P±å«(oT´ µ zÍî'k€+•çñ@žGÑN<½æh2U´ Ê›±å+uV`J2™Ù³ˆK€@üÈ_äÝä‘n—¦+„G=é)˜w-ù‹4™ˆK€ÌìÙ0%YQ€¿)©áô~±Ñ|y¿‹gXÆ'äñFXº9žÐ=7K%b PLƒ´ØT›¦Bš1µ}è:M)©šÌ§1*@êS‚¥Wö€ŒÛ" i¡t4¶K¦&‡¡YÆ{´ :þVpÜ¢î„öÜhVy ß®0ÊÕ ¦C†â •¡QlÕÀZè×nÑdZ,4SW€ŽsÝíÔÄ‹ëß!uèeùöµÕSáh•ª7àigûRÄz*¹3ø•Ì¥’/ ç÷…‚ 1n•fJ%B/þ äêÒÕúÄÃæqJæÀMàZé˜ý#Ü;ývln õÔ’.N¢áÀêë2¡¶=dæ"(™W€F“Up…òÜŒe{9(ýŸ²Ïø”Ý?C²D>ùBÂä {E7.¿â±úÃü%º5ði¡¹µºZ*v•5ºÂ´ÄRê*K`=AÉÿ¡Ý™€×H¨SÜ/S–À1Óà‘YÖ7Ë0öËCNÛT¶ó8'(ä+&' ‹Bƒš-&ñG,ðA'y1À«À‚B¨|š_†æ"ktYà ,“SÆ Æ_IaTô—©¾æL68íü$Áᘲl5<¥§W\§¤ýÑdú‡p“Nù ×£d¯6:©ä†¾¤vXŸ×äý àVÜî}N*®F» „QÀ¿¥?a¢'˜„v>a}4úÆäËi8ßBÙurÊg¸Ô÷Æh*owiï+aç–Nx…2Dçr„¸-šÊ+]‡S¥ç§Î¬z¹4Bȉ°NÝØƒ&KÞÐ1.—îi4(FFHÿ;°x¸?Bþ½2jŒÕ¸ðT ƒˆ„d¹9î3 Ú^вïÍæt9>8@È(1–TÛ`àWR "”»$ á+=Ǩ1R +áà]Z#0&BÙHGéóÑN‡`—Üo\uþ?œxWù}ÚaŠÑ ï/ÑŽ¼’•±cϵ7æ­V YýNp“,ÛfZòéØV°ˆÐñ«}Ðâƒ!šbÅ&½¨½JâõîööcvvÆÙó¿2wgsÝØ…?3;w7¿ßüÿ9,¥Dwó¡ú ÆøO×pÿf§‘M=p‡@X3¢ÍcÐP™¨'A:-ÃXÊ:•uÝ׃z/<ƒöþtMÀZJ™QïšÀéñõgÉPÊ@;(AcÊFÔ|»e ­Ñ<Ž®©ƒ¥¦øH\‚‡È\7Þ“6ÐA‚QOä`ÆÿùÐa†èàh59’“`ç{ü·ìÉà•ÇΆã³7È›7PlÕ€Dnïy— ¨ZP=$:é^—ãhA($âr©ßpÒ(x’ȂȌ³º§MbŽ]cåÕË|Êö©Wöiµ.pÒ¢=ÌÏžî;š"æŠOýévüí9oöÒ’<Í8À# ˆÈ¤`p«yfÿ†'2ÔzÑf¦ød1œüà÷ªZÿëè…ò»7mcVÍËQB4"€õ^°ŠRóµ}wŽmêøB-t,Ù©ç¾,œVë¾27Ý=˜N}ó‡[>dKq;bÍÍC#ý[ZÿµJ½ìŠË?=üy~ €l0OÓŸÃüßxh·Dàt_n}Ïø`ÿ9‡á¬ˆï_º”Ÿ«àrÞõò+î´ÐÝÚÿæS';!—Þ™ÿõXÉç€W\××ß.¬m°^Þ=z‚‡ôÉ2—öÅ|áØÕ•Ê2€Ú0ú¸lÞŒn2Fà©#£cO!Ññ‚í#qeyyòbþÆ\<àÞ-ÿ+ ñá‡v ÷ DåÓ³&†G÷¤H×[6£h¡è¾qþúÏ3uA箼à ̑ñì¶çÕ<­,óhvëpí}¿ÊÌŽ‚Í?þpaþ¸Zø Q ­–@-ÇË>¡OdöÑ~kÝ=¹Ì–3nÚTtÑ·çùî$D¸­éÎV Þ* ÛÇ šÙhå^¬øõ #Ãß/ßœ;árß« º†e¶]58¢Fícúp­¯ á\¹5?u‹•èìµ Þ2ݨVJQlÕÀðûÌÜ®N£»0­ºµ}·§Íú;ãDÏÐUéÍ›m_wïD Üí™ÉjX‰þ/ÐIpAS7!×ÞR Ý<ÕEO”æÿd?Îp‹ë÷íÕõͨVpú胾,eªaÁ‚TÄZ‹ó>hW†fÝ×]±ÓPˆ0hîkÆ´îÖfØ\ÂH´õ°QkM:…62­)É F®IjÖô!è.? 0¼%ÝñÊŸIEND®B`‚app-2.8/src/images/prestige.png000066400000000000000000000017151377436340000165420ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿ.l— pHYs  šœtIMEÞ &-4¶I&iTXtCommentCreated with GIMP on a Mac•ä_[(IDATXÃÅ—MHTQÇïYæ4•w¬>¡6é@c~ŒƒHd3ífãÞ ¬…AØÆ]-2(hQ0ТE;û€Q‹¢Å,§œL Á(èk£eƒfŽ·Ízμ¯ÉÉ<î{‡óîùßsÏùŸ{áïd-°"GW¢ôË"š…^/t¢þ³èËè«èÒj¬23: sj<¬ô+Ýx8K™fã\îkñ§ú“ÑéH‹?Håo‘ó\£Ÿ@/Ð`±ï:àJuÐ$Â*¢=‘*-c2ûìiÞðV-ò· [},B®¤û“ÑiÃÜ ýÉè$0eD\uîBp»á[v_ª.6™%¸×Õ™z«Þ3À×®ÎT¸fù´ŒMFZüwUÂÈ"$ lŽøÇzŸG>56UÜÎåy9`b´SÈ‚nI„&š²¯nÀI Ôª F€÷@7 òæ´ ]êle0®VX8·°(R±âoˆÈ£¸`Í*r‰¥ÒdÕ° ¥…Fà«*½ ’€l¬£›3M!ߠˬr`ÎPûefÄûúœQ]S>îÆ®©Û4©,óÍš´Âƒ—Ϻ««7£.“oÖ €fúøñS©>ƒjáÊñÔ‡ ú õ¯æbÞi dÎCš=¡°/1ðh¯l ˆàAÏ6—yæ-€f èY sAô`K‰î³kÅ?•ƒ£6$$—@6š•sYßàK<ŒLÕÔ‰WÀ7à€Õ(ŠÅ‰žsì,e8ö% e•i ˆ+¶úÎaèŽá˜ëŠ~ß]+²{³àÕnêaÓLäÍ™u¬1%ÝPñXG{ÒXÙ¶€x29åÈœòÕ £öG9%œçå@]H <~®®)îÅñÔ»ÜÃ¥žò¤Ï€ýI¤@¡²^µl})\ß L2Ú Ü†âx¶:õþ¢ˆDxãxø®àº×‰ç‘w¡°/œV,úÏhAôëm1š–±iˤel¦5 >wŠuÉt’}CQ¹»VlÊ*^MN6Õ?\mq~(úÝðKG{r¨8qìÅð¦Ð‰~c:`0Î;IEND®B`‚app-2.8/src/images/prevrelation.png000066400000000000000000000015021377436340000174240ustar00rootroot00000000000000‰PNG  IHDR szzô IDATxìÎ1 €af࢞Ñk6@”`Ú˜˜´µN<`r"ò,ÞÀ¡Lý/ðýlïŸö†adZ«ƒRöÚu·cÓ@=¼ïŒs}B¬í À¼«âR\¥qœ2¢+T7æ^ð³¦é™sä}¥€µm÷ÃçyÉÞ{BD ·Ò¬öÂ9¤eyåã *1†m¾í•ÛNâP†×{hŸdÐx=‰¨€£­pºA €Pãh|SÔi Þ µ¥köªYLÛ!q2\ù'vÙ=ì¯ÿ^‹6›=¸¸¸TNOÏ=Çqƒûû{FÈt|vvùa€^¯‰*kÐíž)NW.>&“ >==áÃÃC„Ç­–˜æ1̘æØÑó§xròc’”J©Ðn›J½Þò]wø¾¯¯¯HÏÏÏøøøˆœˆü-ý6qV‡t]ÇFÛ¶±ÙìÄ'×jiP.*ûû¾m»Ax{ A¼½½áËË -IƒLuAçIÓé4¼Æ»»; À¥ÅC!êQ€l¶…‚XÑ´¢{û3\œxô%õÁ Bó™ÌB’tÝ€½½¼"Óð¯¯oælG²¸N&“1µ/m‰ô{Ûîîæ’HµZr¹’¢ªy;"N››Ù€&¯›±¦‘ Òňås±T«0OívJ%]ÉçKôˆ…Èç‹”ªVa®ŽuÂL³m'!çþ·t= FcYˆ2}”f „8@X„vvT¨×ë˺^ñ\—  @*t¨V%!*á’Êå0„a4—*•ß4°V3ÎaÑ’ ©Ñ8üBcµZ…O}ê_õi0P³ÝGIEND®B`‚app-2.8/src/images/print.png000066400000000000000000000033041377436340000160500ustar00rootroot00000000000000‰PNG  IHDR szzôgAMAÙܲÚ{IDATXíWiLTWv!b•—¨%R Eª¤IµVMli°h£¶Å²Fý£,aQH$dÂÐ"BYD@@ö‘}S6 ²©$ bƒ‚ ©_Ϲ™1£€´ó’/óÞ¼{Î÷ÝsÎ=÷¾yóT.;»è/##.HÒÓ«%—.VGFÞ¬ML¬¨­ªj¿/•6A‰+WJÛ÷|I¦óçièZhoÿG¡üñsLÿ µ˜Óˆ‚íÛ¬=ë—‰ÞP ùå2Ç‘éç‹‚­mXyc÷8dÏñ¯àq[Èî+‚–FØØû¤ÉÐ:µ`çôœÌ~!,шKK¯¡—ªÐðÔB%3 -I÷¤þJ9 "’š §g(Õäj˜Gͨ(¿ý-Šº1' º€ÜN ¶t††;êÉÌTcu`gÄŽ³:€Œ6 ­ Hi’ꀄj ¦ÈlJõíãhjꀯ¯ï+25øßä‰D.•– «p©R”1qPózžŒahh½½½xøð!Åàà PPP€††xxxLnÚ´ÉiÎvmcc³+,,L„º££÷îÝ¿r¹\¸|ù²øïÕ«WÇØØFGG?IÞÝÝÊÊJ¹¶¶68p`ˆ¨¾™u•9r$O&“‰Ð©Š`C&âü–••}’œÅ666ŠÐs 0ykk«(Tö“¼Y{Um «VæOU;aB¾çœ*Éy¶÷ïß©àÚ`---‚ðÉ“'xüø1=z$Š´¦¦ÁÁÁ Zè ºïg¤‚ÂSÛÙÙ‰¹D´··‹™sñqJ¥R‹wL¦ ;“²`&äH0nÞ¼) ÔÝÝ[·n•Ýïìß¿¿– P8K?óÌùjΕK” ŽW×Lvv¶(d???899áØ±cðööæ°û¨i!&&ÎÎÎHIIŽUEp01Ï’CÍbÓÒÒpýúu±b’““qþüy®x¸ºº‚z 233áï ïªÃB„±±ñìöíÛt÷î]QdýýýHOO‡———XzL¦Á¡M† yuˆð21 â4•——‹”ù%ºÂ+çWîؼyóìöîÝ+a¢7oÞjž);c!"¯bžå¹sçD¤˜˜Ÿy‹ãœ«’³ çŸ{¡‘‘Ñì¨KxÉMOO ‚óÌ5Á…äéé‰ãÇÃÇÇG„—£ Ì¹*9§’ß]¸pT[\ùâÿ3gÎÐŽi8»€;wJXé»wïæÁÅÇME™óɹ~…PÚß·hÏMÎÍÍ ¦¦¦}Dç4CÀ† ôi‰dP?xËa›K©’s$N:jd }...s ç ‹myS£1ÃDçFÐqå}\WW×cË–-rvÊE÷±ˆÒÒÒ÷ä Úé`mm=+97-¶á«©© 'Ož%ÿ|hñ˜ëè6_¡Ì|Íš5Ñfff#þþþ‰Rw=eس³Ë ‘dQ|Þ“óæÅÝñåË—Â&55”ÞÁ¥K—Ö’ßTÂYÅ~°è“çAÂjžuëÖ•ìÚµkŠw@vxëÖ-±ò °²²¦¢ôÃŋմJŠ©[v‰Ùr¸IÌ4E²oñâÅ•äç*Á•ðµÂï"uÏ%<Ð@[[ÛqýúõmVVV -Û¶m=®ZµªYGG§ÒÄÄ|ˆ…¤¦æÂÁÁaBOOO¦¥¥U@¶N(Ή+þÏY‘+Öhùòåúúú ,(¢çD‚;áŽÔÊ•+ãV¯^Ý@÷Y„pÅ7‚¡"¥ 5òµ¤˜‡ñ ",Sœr´Ÿeß~TJ—¨óÅüÑÂG´còÉüIEND®B`‚app-2.8/src/images/print_48px.svg000066400000000000000000000004071377436340000167470ustar00rootroot00000000000000app-2.8/src/images/properties.png000066400000000000000000000020111377436340000171020ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs íÀ,tIMEß :ØÈ–IDAT8ËÕ•]h\E€¿™¹?»É&»Ù$IÓü4ZcÓ$¥¬†Ò¨X­D ©/ŠÐ' ŠO¢+"ˆú(¾‰QÔg-­QlŒJM›m“v›Ôº»É&ÙÝ»»wƇÞà¦I_v´{Ëð`~¯méý–Ò+ep¿„n¥hlŠSûà@ÌÊ.›ìŹBÙ×F\•Ƭ2¬µÈKv]dö@ØÕGŒÖŽÑÚm,BIÚªC²¹6"ÔEÕè};j^þ~|á Pâ°¿Îøëw]{x0{O$Tq]yw¬©^ÅÛ:U¬µGÄZnÑÆ&YUk[Æ/RÈÝ¢¾XޤNO-d¨r­qM•ïJ©‡ŒÑÛñÖvêÚ{Q¡„rÐÅ4Þâ3Þ˜ÖóËÄkåÀ]íÖŸÀT%tØVÚÙf@ Ý-„À®nǪڊQ áØØ­¥=U’"i⿾¡Öw*l7þÆ¥" -6! V5¨#`Œ°"i{ž­×Úìy`ÿîWélu™>îU²Ö€ÓK€ …Ü2‘˜Áø9ðs ÉdšŸFcǶHùà‘T©¤-´ö…lÝÓÑK,Ÿ™ûÀœ’ëÀÇÇâ…hUNnn64¶”ñóIü| á´1:zЧŸ{_ôt7¨Ó“™ÚW€úöãŸH€éö—³¦+?±¬OM†T}Üïoi¢FŠ"ÞJ–åt ?Ÿñ~üå’™H¤mŒ©jïÌgWäâüœ^ükªðÂG…¥³\¨Z…võízÛó¼/K…ü#«M|Ìnxòz7QÊäŒN¯ˆD®è¤Qőצ›Ó³óa].Y=}o½øÊeaŸ˜;ûû,0 8ÀAà%à=ëZp‹÷ÍK?§t~æ©}íӻʾy]¹®.½(Àå Ó©_}‘½r~BcŒ¹å‘×U‡€N m úäØ3±ôª¹º^ú¯3+®Æù >ò€DÉs£ã‰EçÀô¯}|ͶpåVåà¯[ùl`6ØUšŽÏOÞho4ÜÐlt‡,°ìbÀn`Ë¿½q*m.yÓKã?¿Ú=vŽ>+àIEND®B`‚app-2.8/src/images/qt.png000066400000000000000000000047161377436340000153500ustar00rootroot00000000000000‰PNG  IHDR szzôgAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe< `IDATXõ—iŒUÇç¼ï]çÎ>N;Ó)¥¥-LK¤@µU!mQjý€bEEƒKÜ>˜¸Æ¸%ãúÁЉ+®$@R(`Ù„ÓvZºÌzg¦sçÎÝç½÷ÝÎ{Žz%-6€Orr·÷=ÿÿyžÿyaŒA³lñ¹[¾Ñusš™ŸòfGuçBßL À cLÿð›ë›[;boXy©µµo½½up»`×éJ½¬ªózväoD1ä€c@ (cœÿ™€b“”\™îÐïp=ÅÅË»¹¼;E®âtT=¿ci}°aÝ6{— [¨Èf‡Õ¡Å‰0Ÿ=.!Žà$0mŒ1ÿ1þõñ/¥;­«êÅP*­èÉôÐgwÑÛæ€4hâ×§âxƒ…Þ`µá³äD~µÍÕ‹¦0þ¤:òôŸœ“BˆðbÓRycLõU d:åf;ÎÚ: „“¢¼Ç ãH!±¬–l¡/£éo×" ÇwÕ†{q¡æ^|Å6ë[>žÆ©ªêìQ5œ;e§^PsBˆ»Œ1S¯H@kã0ÚK¤‰t­#Œ0Dú¬U]©¤¤-iÓbI:ƒm wy/ðC%×o/^æî8Sr?æûÑÿ9`êÕDhc0B`0QĈT¤AAÜ’,ëJaiÁìlƒJÙièéI°bem{ø~Î4 öX¬éu;v$ôk}H a$ÐJ‰P’TÜb §…É1—‡*Q^ŠÀ@dÒrèˆqÝ®.:ûâä]üÀ"26D2¢×L@ˆ"P!(qÛ¢¯7Åð³.û“OwO?-Æ ˆ'¡Ñɸ{Âã¦[Zèl¥XRX2‰”ò?Šg‡¥¡Ñ(½¹é~k±ª•bu—׸âÚ©´Àw’MñÄ:jå€PøŒ<Üè–ÒºñMW_¿jõÀúžÕý뺯¹ê­+O !nº€ ð‡ ”$3Ç "?ÁìTž o;C¡áR^ŠJñä=й‘Íd³%6ßpŒK¶¥xæî-²ðøGYÉõ=ý曫b2&tdð>ïºù¶ ó“jà¯OïBô÷œ§KÚ¸ÑZƒŒÇ™ŸŠãÕõ¥WõW(WBÖ\côÑ:ýò"V¯I²u÷ˇÆ+3ÌÍôóØý³Äí|øíŸO[ñÊ 0$ðŽ;[ŽŸ~þšþe{ÆæŸ»Iž+@)$^TÃ ŠøºBa±ÄÂü"¹…Jé_Õ û|™½ŸI²lE+³s“¤{'馊ãç8ub’J¹Œ2>‰t†ÐUÔk.Š£BMàØ- ´6¬ìÚpÙ¦Õ;6½d,"Ò}™2ÌÌÌ£Eá†f|>¹ÓpéÚ4GF#Épè ­¬{ɸdf6ÏÎ >£ŸfòØ8ßûÙØÀW¿üm,;†R‚ú’Gd4FGH!¬{¬FPw—\!(äs„üã~Éš®6qèèq>üíIRËŽR+¥ùÞÛøó$‰tµWkº6çùêïo¬š}rbºxbÖw­ Úf|"‹Ö!B´VâÀÁâ‚bÓ5)d¢2à{5 bì;ÙÅû¿^c˵6_ø¥ÅÀÐ¥…Ž?ÓÁ•7T¹èu›Þ’âÚÙÓõ(ÿ‹¶.ûxÌn‘¾øš§F'NÑ·\_(] \ „Ô–B쎈݇ÓóÄM×AÁ·¾4ˆ‹ÍU]Üú1I¥¼€T u(”XRÐÒ!°ööwms¥\#“ìddøY&fŽ“Ld˜Ê>vúÌ3§ì—ÃKΦ&iÆÆK M>ïÓÑã§Ï¯à#Û³œu8îõÕœ!ï¼­µÚ9~J°jsœñSu¦Žøí7l»-*M‡á·÷ýˆRå }Ýú…©‡Ÿ2Æ› Š ¹ãú"Ãϯ᾽þøÃEÆOx CW¦xÏgØyKNÍg¨$ÉN60‘ 8£3-+zb’$“s㌜x‚Žön Î\¶áWî3Æü÷dt.ÖcãåJÀ®e¸ñ£ÝxK#ÀÎ@yÉå™á¾o¶D‡·¦iM-K&ív+)[ynòqB€„%¯PNS”bŸŸ”^¦‡fV+”}Êñ˜E,n0\O†Ñ ãF¤Ë†ÒbÕÌå'¢Õ½e­^ÔAè:…êL©T›[F^"`ŒxÕJV4?ÑZãyš†¯‘Z „D{’ùq¥+9½øw×}Ì«FÊ8€ûã‰Û£»îM·Ø2µµtIÇ­Õ¼°þcLñ%ÓGƒ{:ûìÃÒfÏ+’hZCÈisx¿ëÏŸýü”rÊój(ͽÑÝ6ð[wÜyY%,fú èjM"…BLcöاï\˜ v÷®¶^l|¹9–Š‘ÉO(½0©Ô‘Gõé#A (ÓÀ\sò͆¥ l^Û·ÕJź2[Öï$Pžý ¶-iIµ,Öx=°¿éã !Fœ¢yØ‚ìh0wd¿-L…‰ìhà®É7[´1`¼ V8°¸@ЬWiMä!“Sc¬kym …D‘Ššï÷¼z`­ë3㇂G‡ÿÖ˜Ù÷ÓÚ @H5sÀb³ ª&`DÆ}¾fDv¡app-2.8/src/images/random.png000066400000000000000000000145061377436340000162020ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞbKGDÿÿÿ ½§“ pHYs  šœtIMEà%0[ütEXtCommentCreated with GIMPW®IDATxÚÕ›yœœU•÷¿÷Y«z©ªîªê5[gé$„@„D@e“QGÄ—Í* ¨3£2.¸Ï¨18 ,.ŒƒA! ìHXBè¬t'½W×þ<Ï=óGUWºI Ý!ÌøÞϧ“Tå©Û÷œó;¿³Ý‚ÿ¥5pßê}ÞÛüÙÏ9OÏ?²ºó[ßnñœs?ùÜñKVýuFû3ëßsúO_¹ð¢ÖÊs×^÷¶K½Ý‚oþü?3ý»ßÚûúêkbé瞟¬=¯M…Bï †Óïõöì™d²¥ˆÆ:µß ‡?:o劇­Ææü¯€ôöâ&Û xäQêÞuò˜÷zîZÙ´çÎ;wu- †‡ç‹ÖGK [ƒô0xFk+fë$”¡ Æß¼… •ÂJ&q’·DO\üõ¶ï|gÇß4vÞx#­Ÿþtåu歹Swýû-§ûCCçx{zÛ0Œfá‹sWÜù=€¾ßýžø9gÿß)`ÓW†s/½µÏšYìê>¿ØÕý÷Å®îÉ¢5 exÏ#ÊÉþáC8‰zlË.+ÀÆq\×Á[õ;¿þMŒª*Dkªœÿ§Ø’%çMù—/õ¼ráEÌùåo6_{Ó¿ÿ½ÊëË—× ®~`†.ägkÏû» `‰.Û‚T Le˜?@¾@~Á|Š÷Ô¤IXõuXá0Ža`¥Ð¿üŇAÙvÅm¬db[¨mÚ§æýþž?(¥tǵ×1cÔ9ß²6žq³Wß ÀðK/Öo¿þ+';w.ö‡†WJå§3 R,@¾ˆQ_‡ ‡lŽ ¿eš(×5pù>bšS&#ñzT(„hŒ]»Í[J{A鸦¦‰©Í;M7Ï¿gå?š‰¯ã3×2ãß?tØsÅ•‰¾gžýn¶§w±/Ò€–˜‹{]¸*Œõ™«µÏÁr,,e`f3äÿø©ß¬DŠ–5±Óh å?J‚›Ö)'a·ÏÄÈ{Hg'…uÏRܶ³6"V¢~}ËÒË/j¹òÊç_<ë5ïÞßÉ[VÀš³ß_¥»ºVZÛw¼·Ê4ñ|@| Ÿ§xÝÕ˜§žŠcY8Že;¸Žƒc[èÍ[èûç/ámÙ †ñJ¡¿z=΢E%—°Ëa(Š=Bÿ÷–¡1ÂáBí±‹>=÷?WÜ °û¶[h¼ìããúû=]Ê+Öïîë{ï€Ã"˜€TV–‰lÜDfx˜l>O&“%›ÉÉdÈdsèövª?q9ª¶æ-1´Šls ™L–L6K.Ÿ'›Ë‘ó|¬³Ï$þoßÁˆÅйœ;´æÏ·¼pêi·ïøÆ7›F„O¿òòÁ)À˜4éÔ=ô# hÍhpËËÆ¹ÿ¿°~}…Ž2Ù,YÏ'ãydç¡‹E‚B{ölªŸ@á™gð;wd2g{½=Ó¯9ù]/^ñÂó½ßýõ :®ºz_œY_ÿ©|_ÿBq.d×Ü9ôÄ¢d ƒD?Rx”òQ ˜Já¢0ªXÄØ¼ß(N™Œï8h4  E  üuÏá­yjoŠ{9‚J #{ö›9¿¦ tïC¤éêÆ{îyDhèð`hè´«Þq|çM¯¾òÊòµk÷ºÀÎ5kh]¼˜Ÿ}Ìk[¶Ì ¹™ÜûÏa(N ‘¢yÃZ¶w¢Mp€¨aQ%’¬Á@E@‹F‰à9™93Áö<ŒuÏ•¢D¡€:©®¯#¿d1þ‰K0¦NÁ …± …ùçµè›‚ôô"£øŒpÈ·§Lþ—£}ø[êu¤þQˤž X¬¶fLǾäbRÃäR)†‡ÓèÛ™ºæ)L?¨|Æj E­2Ë$©°(£D• ªd/lG¬'J•H¶Ì(­Ùoé|€@.Ræ*­Ç×l´Tˆ\B ¥¿5B¸}Ö5c’õ‡¯¾úˆÀ÷ KÄ#œP‰T×%xñx)Η^-ø*À7 | ¢ \JJùB%þ"TŠà}០+ñˆãà47:f5^@æ®»úÉ­H.?îÉ(bÖjÄÚR¶<ESÈ ¤µ0¨}ßs,€t÷.jšZÈ .@a¢…Ý»‘-[H>Û4K(0 2®‹ÞL5¥Í}­Ñ(|2¨.£¡8%Êä™ßº•â¬Y¸"øŽƒ_(àår8®Kè ðººÉüú®ý¢`tN #BÃ^xK)Rù@^4i´h†DMJkRA@](´Æ¨ij ß?0WÊ5hMçoW1éݧÐÐØ€ ae²ä‡Óè7€¦y­ßÁÇ FƒÂ‡’KáeËI_±oÎÜlß÷±mß÷ñ, ilxS«€Fã +k_ /BF4)R#B‹0hòh|Qh-´Ö×ß_ ƒ[ïý}Ã+÷ün©öýʹ{a`€gŸeêÉ'ãVWÓÿØcô=ø Ê´x8OJ!QZU• ¦3„Ö<…twãW…ñ]ñ=tÁ#xâ ?¾ø¡wtþå/¿ñó…Ic¬èb‘pc#¹žL×·_š@U¹˜ªUЍQâ]>¸P¾žJ×߇@C(TŠ#–-?ï—ì—}:W¶ð©2¼3ZÈHIQ"¦YjÎxHÉEbɆ¾\:=µbÊtww›±}¢Œib†ÃÇ-üH¼Õ@F"Ðà+E2JU¥Û‚‘dHa/cøóˆÐ9¬HÙ§Ë‚ëÒë¼ÅrõYÉkj ¥#ÅL¥(nÜ€í:6M;,°Ößv;ó/»/“mSð† u¡ä“ZÀWO)|£TS”xa„°­JájtÌö‚i†E“%ô°äü²ÐòºÇÅÚgáD"Øá0áÖVü­[ÑÅ"µuu^ø…/­ù—]ŠˆX·~Ä4¿P8tC‘ _@Q C¢0´ÆWBÄ0°QÑe,‘ËD6T&²¼…”ÖäŠÐj?¥­ ÐŽMqv;¡ª*\Û&d[8–ø>Õ‘^>ÿÜ‘§ª-€‡®¾º^ûþ´RJõÖSS¥…\K 0X,Ò°y33·n'(  PxÜr=QD(dõ(+‹0¬R"duɧÕ8:ºJ4ÅY3±C¡Ê°ÅqÃèž=èB;9ÞÚº›uÏbô¬ß Z#õq\ÛÆ±mÜP×uH=ý …ž^ìPÛv^8é#d !œLN)¦ÓÖx௴f0™`ǤIl C¦I,#‘LOĉÇãÔ¶4ãMš„6K©GP†ø¶Àçeßgkà3$š‚LÐâû)“­ÀÇ W®©Æ6 ×®%½qc©UfýÑdrÀÚûîÅúãÇ—š©í;¦iÏ; ÿ+­ÉM›FßÑ *èìà°õˆ¤Óc Ù,5JQ›H”çv(„7e2òÊFT¡€_fþ¢@^kürzý–È0ÎhÇ!o;¤:; ²T¹hR†êyç9goøÁ“OpÜ™gaeúúl+Z""lO‰RX³Û‰MžŒ98ÈpÈe›³×­Ã.z ûûQÝÝ$Ž;×¶KuD8Dº¡, TbûHiªá¨Zþkøe…¨ 8µ±ºî¿ÿÜç¶Vô•éé±ü|aþˆ…=Û&0ŒŠŸÙر©M&I&$ âõqœ©SIµ´Œ©ã•i²çÁ?ÎfI64L$I64µ,”)\JeêHfwÈ—aì;•R*¨ojz`÷æŽÒc»þú4^6Û¦´&ÛÜÌŽ£òÚasé‹ÅöQ‚=jl›d2I</)¡±5i2ªµ¥L“TÇf6.¿‘jZgL§!Ù€ÙÑäreáËÕ[ùçc‰ÖÞä9sîhœ>ë˜O^yôówÜ–…ÿŽã°b1²Ã)¶Ç¢˜Ï¯'šJUü[û>æÐõµQlÃıœPˆ\sS)õý½Dèºt¯yŠ'/½œú#æ“íëgpݺJéìr`߯ú!_Z„úÆF¹æ–Ÿ¬”Í‹$0¢Qb“ZI&â$ëã„Z'10s:þ¨ ¯2 ÒëÖQ‹L&ihh ‘L‰Õí7UV†Aº³“í÷?@ïÚµèòtydÐ:bý‰@Ê3‰¶Ïjëëøá'®¨¼g ïê:|„ êbu„jªK¬íºdt@б3•ª@{ð¥—xèa¦\xn8„[UEø}ÿuP)û¿?*ןðÄ8im„âõ°iÞ¶íãÊ`ë››~põo•ÓxÅcPŠ``ˆSòïD"A2'6­ ©³‰U]ÍË7ÿ˜Î;ïÄìîÆÜ±ƒÂ_Ö"¹Ü8¡X†¾*E€‰Ø±ØÒ‚Ä|ì†$NM áÙíÞA7âÿ}ñú{^ÿ_VàûºÇ[¿¦#,¥Ž‹]È“¯­Ý—`]— 7ý˜Úûþ€r{öŒ»Zô˵ý êñJ&OFO›JضKù…ë@*uÀ*"Ô54¾:ý¸cöÿÄÅßùö^Yª ÷ŠÖ®KçoWQãy465‘L&iL&qüýƒÔt]2ÝÝä{z&T*ë1$XBÀøòûº¥¹”W8.®ë®®!×±ù€*@¬!y0Fxà ‡>U?sæ“"%–_{ÕÕÔ*ECK3U=½HÿÀ›¸ãÄFÞ{ý¿ÄÊþ¨Féòû ™À©ªÚ[à„Ãø»vQìêzÓ3`˜&ÓçÍ[ù†Óáço¾9ùü¿\1ðÚk§ê À´-/aàå—ÈíÞsÐÍ}P\nlŒ”»ÃZÈ‹ßgŸ‡ÓÒBÈu UUa{ý=N±·÷MPj%† ™Ü”Ë¿ùõÔi—]6¸øŽ¼âŠž¹gžunlzÛÏ­PˆÀóÙù§?RáKðWå`$Ž“´Æ,KÖ·,tOýO®¡ÐÓs`ŠàVU=6cáÿõÂW°òŒ³øàê{É÷õU¯x÷i—fûúè¥Óoí‚Ó~V~Tã2%‘<:P³®®4€$ÈæÆg¥˜¹àÈÏ~âßþuÙ¬c ö{G胫ïå¡Ï\K(ÏL=ýħžò®óhDDë ¨ÞÄ¢R†ãÞ"‚ñæJA¡@ÐÕE±«/ŒOxÂUUAn8ýôþ„sIêÔò%úisåÌŸÿlEóQGRL;ÛRŠáh„ÂÌû¥}©ä2áA¨šð… ò^ªŠFwŽû–ØÂ¥K8÷î•6-\xºm8Ð/U"ô,YLÇi§ñÌIïdËÔ)ûLt¹ú F…À·»¥p¡—/ùꗻƭ€lwgÿúW9òcžŽ×?`XÖþso­‘–fªæÎ¡©®Žd}œy‡ÑÛܼOüÊ$ø† ÐAä÷o¶lÇ¡:{á°“NÎLHUMI~ÿ‘ó9áË_^ß~úéÔ͘~“²,öáìhŒx¢\"ÇãÔ'“¤çÎÆ5Héýôõºén C!¨ª>” ØS‰ý` »{ü Yg¯ø«Þ.'/ûAÿôSßý¹¦ ¾âF£c|W3Ÿ§>%‘HH&hˆÇ ÏšE‹Ž-eo(R.¥tµ¾0g6êØcpN\‚5uê!™KTÕF¶}û¡Ÿ¨kjš¸νg‹¿ö•ì‡ÿëþ¯F'·~Ú‡²#E¬2 ;vµ­R!OÇI$“¨––Q(w€„½ÚqÈÍŸ‡ÙÒ‚®Â­ª&ÔЀšèEëŠDJ›¶]lš1ãî³?~éû”RùG ¹ÀþÖêóÏà‚ÇÿQëÑ‹–º‘ØžÒõ …ŸN“}úiH6$I&K?¡šZP£jÿŠ+”g‡¦I¡mV4Zº„áØ„B. Œ7‹”®Á8¡ÐPu$òlûÂ…Ë¾ð‹Ÿ¶ÿäùç>ø¡/|±÷[çÇ».¸àMû‡ã^½/¯'1w>|öóïyå·w¥˜Î¯}Óu9nÙ÷©]´ˆþÝ{ÎeÙöµÈ¿øÒÞHJ£­a]FC"Žno'  ‡ …ø†bàñ'(¼îÊë…‘ÒØ«ªê'ä>om}äÚÛn{¨yúôa€_û*ù—/«:áµúcsÆ/~ÆïÎûÈ´ŽŽ·n;S<ŸªæFæ\z)u'Ïö‡fË-·ág³dµ¢¬€@“6Mü™3°Z[ ».áp˜P(D¡£ƒ¿þ÷~£Ö”"Z_OM,öKËuïŠÆã¾~ÿ¶ŒÕé^jêÆ÷õºƒnÅýçéïåÃÜÏÓ?\Ýx÷=·ömÚô!],bØ6†c£=íyh`Xk††´fXk²"¥Õóq“IB®K¸ª éí¥÷¡GÐ_JzFå†iÒ2}zWUmí×=ëŒß<õ›»‡–­ýKqDð{—ßÈYW}ú Z轞¸þzÞyà üò'ÝÜ÷ê«KŒ¹Dà—¿s0$%è§ÊÓ sv;áé3¹6ÞÖm =ýÌÞW ¤h™Vº©­íÉ#–,ùæÒ.[;²ï¦çÖ1kÁÂC2CxKkõEãŒ;~ÀŠSN¹f`óÖ¯‡‡kG,˜¯LyuY ºB‚Ê41£Qôð0~6‹²,l×EÆÎH]ý6§:ü«Ïßzëï§/Zô¶|iò(``ã&êfÏà¾^ôO®ùF1“™-e¸žígDWîé0r¹Q ¦.TE#O†yÓ´iß°ú¾?ìûç?Ï¥ßýîß®FÖ}½ˆ3ÿãîùÀ‡ßóÒ‹·gz{MšTÅÿ…œh´"`Úµ±ØÖæ¶¶}]]wyÊI]WÝôãÝ#û½øøãÌ;ñÄ·µ^xÛæ»×<Ù°jé•÷tíÜuü°Ò BJÂ5U$'O¾oÚ¼yË;þ„'·½¸¡xÅòå•rõ_¼ž}ãþ¿]Ï,_^ù÷Ž?aåu rI}<¸|Vû«×.^ü©£¬òœîÿbý#QH" app-2.8/src/images/recent_48px.svg000066400000000000000000000005471377436340000171000ustar00rootroot00000000000000app-2.8/src/images/refresh_48px.svg000066400000000000000000000005431377436340000172520ustar00rootroot00000000000000app-2.8/src/images/relation_edit_48px.svg000066400000000000000000000003641377436340000204370ustar00rootroot00000000000000app-2.8/src/images/remove-old.png000066400000000000000000000040611377436340000167660ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs íÀ,tIMEØ#`ŸÐv±IDAT8¦Yø 6Q 6Q  6Q 6QcdgþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtijpU@åûûÿÿÿCgÿ ÒºõìàCgÿ§˜ÿÿÿðñõîðõáç웡ÿ¾šùùû©ºÌö÷ùûúüìïóùúüÏÖâûûüBfÿ˱üóéÿú÷ýñéþþù VmÞÜýáÝ÷Æ®üýöþùòýÝØ*óñÿõôúáÓÿýþùòüØÑûâÕÿýüöë÷Ì·ÿý5P‹7n2×½IEND®B`‚app-2.8/src/images/remove.png000066400000000000000000000040611377436340000162120ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs íÀ,tIMEØ0qÍ«±IDAT8¦Yøþÿ7*;aí/ þ-þ"ýÿúûïðòÚÐÚþЄ¡Ûäÿþüýþþþÿÿÿ®‰(L;÷?þIþMÿJ:(B5òôÿòáèøüýþÿÿÿPó åR þ…ÿþþÿþþÿÿþÿ  RÈ9, qÁÐðØûÿý³ŠL;ò4 üP3!+$ ÿýúøöú úý ã îÕÞðòýþ ýþ@ÿÿþ*- þúîùñ×äùÎþêô>)áâÿþÿ5þ(  "þ þÞâ  ý.ýÿûúþÿ   ÿÿ ÿÿÿ ÷îïññ÷÷ÿÿýöýöÿúÿûÀµÇ|©ññöÿýùûõ÷úû 5S###)ññdÿý  ÒËÙò¯ÖºÔÐÞáù÷ð÷ Ô«¾ôòo1>khBéàà!ÝÍüíuNôþÍÚý/ úùõòéäè´‹Â@Üî/ÝÝø×ïÆïý¿åÝb(C8Oõö#ÊÊíùùÏÿó¾ªÆöþêê'#0÷òõñêèþóéßôõïìÆàñ÷ó&+>ö÷÷ÇÇ­òò ÖãŸüÝ-ûïöÿþîõÛöúîÿÿ Êÿöèß÷"ÿ ýþòòöÝݲõõÿ›ÔÒ‡üÄó8ÿ¶Êúð¹Ð÷ç ñëÿæûüÙ ήåý2! îîúõõñüü©ø½•±ëý#$(*+dUUÛ×@@ÿÒÿÒÿÔÿ­üK³&ïøÿøûùõ"/s??|@óó'ðÊʾ÷÷Ú"öÛËÿðÿÿþ]))O/ëÅÅ»óóÿ¢ÕÁ½!ê+êõïÆýüìüìì üÝÝ8Úùù¨Û߇õ¡ðßÿ<è¿ÓÙ°ÿ× $|%¼T j/½`( 'º¡­œH}ÿ´IEND®B`‚app-2.8/src/images/reset.png000066400000000000000000000103201377436340000160320ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞbKGDÿÿÿ ½§“ pHYs  šœtIMEà %¢³¶ñtEXtCommentCreated with GIMPW8IDATxÚíš{p]ÕuÆkŸsî½’-?¥+]É’üŽHŒíðoãl %á Móš´M:Í™é¤Ó Ï™”ÐÇŒ10I !iœã'$ØXø[²…õ´d[–î=gïÕ?ι’b“PdÙ¡­—fÏ=W3gß½¾½ÖÚëûÎSvÊNÙ);eÿMNÔÄ_›:uÄ‘(ºPT/Uø ÂT \Aºvø­À¯EdÿîÞýúÿ ¾\WWªð wz"øÀxÆC©èSå *­ÎQ¢øVõDîTxì›»wwÿ¯àoëë¿æT¿n@fueeTM˜@ÉÈ‘ Š8ΡÎaîŽ:;;Ùi-»‘Ÿ}¡ºzaÝš5úžàîº:mŒérîåæžn-ﯫ£¢¦† “A5ñA5ÁÚþO¢Œ("ìí¥»«‹]]lV¥ Xò­={þò= ÀW'NÌôX»Þ‡™çg2Ìœ5‹’Ñ£qšd{ÑœCUçbç‹#ŠúQ„ Cº;;yíÐ!vªÁ#òÙoíÙóØ{€/ÔÖ¾âü%%Ì8ÿ|Ä0`Pø«µý»ŽµØ|[(à C\”0¤íÀÖ†!mñ,MÀEßnlüý{€/ÖÖþg7/()áý];žÀQô‡Rº[[inh`_¡@«stùdAiFã€\ò[ë€CªX ‚K¿±sgáÏ À]55e‘®3TÍ•óç”” ¾?B€¤¨sж};[ø]>O¯ V`™Õ{€PEÆ«ê … .I%"Œ:€ôøî½wþÙøÜ„ 3ðN?ÊéÓÁ÷ÏÃSÀtPþ£Ê¶U«øUSûÁ‡ežÈý÷56¾üŽ…¶¶ö …+#¸Öƒy ‚¨v |þ¾¦¦ÇO*Ÿ«­æT·}¸dñbL âyq ƒ@| XËú•+ùyW:=‘45mRÑ­¯ÛãÜw€£'~ësÉ·÷ì9ðnç󇲧z·QåŒÙ³ñÒið}ŒïÇ)àyq $æy¿}î9~ÚÕÐUjLîžÆÆ!çï¿ÄNþÀ]uuwZÕ{ÎRçnî{·ó™w{×êëÓVõ³§CE]]ì| ©˜T I¥0ÉØ³n/íÞÂA_东lãU-~ìý…)g¶ûeÀ Ê<ï:GÑ-0yìX‚# èÆ÷ãZP,‚αzãFÚEH«žýÀÞ½}Ãá|ùâÜç°ú|u~ïÔög÷d¨s½ëppm©sL™;7ÎûA쾤R˜tšß­\ÉŽ(ƒ‡Ú·ï÷ÃäüX¢\`€\Y¾(÷£“€U?1“aT6€`Š#À9Ç»vÑ+ÂÏ»{8œ¯¸¦z1Ê-WÊ/,¥æÒÑèD¸¾|QîùÀíÕÕ‹˜TVWù"ƒ"Á^&Ã}ûØEˆê£ßjlì=îÿXîÓªºÂ•+µóÇ36WF&•fÒürd" ,,_”[}BˆToåÙI“âã.9öƒ@*…WR¿ü%Q|D}ÿ¸wþc¹Ï‹ãûZ®L½¢šqÙQ¤¼4i/ƒpY3ÙœW¾8÷ó€Â£¢ˆªiÓú›ÔþŠça|ìݺß³õøœ¯¾ǃTÓ.¯e|åè~çSÉ(Éd˜rY%ÞpYù5¹§N²Ù²²˜ßûüäSõýGZ[é inîªóÙkkjV8m~=•5ãI›Øù"i/MÊOQR’aÚüjÌdƒ¯¸&÷ì°p[.7ÙA¦¶¢çܱёˆ.ŠÈ8ÀAc@õ7CÞùk«oT§+´RùÀÂä&dÉxiÒ~š”—JœOÅ×&éTŠÓÔ`&{€\UqMõêa TýT J¶¼< HrTû×(B  ‡Ñ‡ÿ¶!íüuÕŸÇ­‚y=“ª\ù1»žöRÉ÷ŒÀ H™ŸbúeÕøÓ<€ó*>V½êO6BŸÉåFFÎÕM_" ‘ƒOŽvŽ‘cÇö3;u±v€þN[(Å=CpkUÕd Þ†xHRWd¤ïo{ ©Éd¯¯¾˃Re˜wåLªªÊ•dz‰ÇaéŪňň`$ÞOIÁ”‹«Ø)û‰¶ÛU|¼zeÛòæEo @Ÿsû€QEÎ>XÅ‘£V›UeÔØ±ýÜ^ŒÁƒIî)¤RŒ±–Ï»ÉÂMÅhAämÙ×Á(Z ܘ½¾æã8&'ÌY0‹šêJ<ã!€$Ω*‡‡U‹u"’ü›IÃÔ‹«Ø.û±;ìÕ ùTeå“Àµs`p1;êÚνðBÆd³´½’°Àbû[lhܼ™M/¾xl½HÀ0"´ ªŽ[ž¿Ä+àxœœpÁÂÙä²xÆÃHà˜]:ÕØyç,¡‹ˆ\DäB .$´!¡-PpB[ åÙ±ªû¦Xݶ¼ù‚cÂð“UU?FõÊs=˧O'H§1ét?©)Š´·ØþÍãÂׯHQü C4 cÙ+Šhß·ç^y…UR–O¬œïõBþMj _5—Êòr<ñðŒ‡'^ÚI”áp§€ÕØùÐ…D6$t!W `<}…©{ùªU¼ øÊß¼0?hËó^ŒÔé”]APÀ,8ïbfNy_\Õa%Ò$ïmЄ~ÛÖ®yÙ8%™W‹Ù©hn[ðÒÛJb×g³Ï ,œc =ç‚Lf ÷÷þŨ(ÊàE ,éŠ*pÛþý<³v-ÛãûÔÒ––VÞP»ÕÿGè8Ç·ßL®¼u6Éû8÷CR°y ¶À¡¾Ã<»ì%\£}ÛVþ˜èm‘e­­½.›}a­µWè¦M\5{6Á`‰»¨ñ'ÅODpÅ–¸Ø%@´47³bÃvˆà‹Ü¶´¥å‡-7ÝTySí?£q$k²¾b©Sµú }\ÑÝsˆ‰Ù:œØäHLúuxêaÔ#Š,Úî@øù+QMÿ ‘æ?ª=ÙÚºàºŠŠ—Ö†á¥º~=WÏ™C*“‰oVwÞ9HŠ$GE€ªÒÑÑÁÓ6°]„þþñ––ï þ–%Mo¾ÓîW~r‚b 4UBÚOÙ¨ßùX±øÆÒÑÑ…ö‚ˆ<µÿM»Ž› <ÙÖvðâ:kY±f …¾¾#-i{]ò] …ø{ò¿®Ž–¯[Çv<¸û‰ÖÖoQ¶žŒãFŽ‹[]/…o<ãÇ'Äžç±wïþâÿzØØàSmm žÛ еk)ôõÅNçóNçó±ãÉ÷¶–~´v-ÛTñTo\ÖÚzßPœ¯ºµÎc5£ÔŽ«!ð|ãá%ݧˆÁ$ ÁÐÔ¼?Î Cë°ÒáåmmW£úãõαbãFÂÞÞ8 ’s¾ø©ù0ÒãT_=áÆžio_´hüøg7ÁUþöí´YËΤÉYÑÞþÃaylí¸Kæœ}6é oó(š8>@€"gÙ½¿‰°3x4íl¯žÐ(ÚÊŽŽ«ÖE;T1ª_|f˜œÏ}ºn¦*_ ¦e¸ð̹ôŽ·yòQžB‘îÚB ‚FlÙ³éäÝ?RL¸m¢o²òѦ/7þ#¼¶²£ãÃáü„¯L1¶9ü £ sæ…¢l>nç. Ò"Ò¾½÷Ö’¦‡OÎÓa§ Eå™ê[ëXïqWó¿7‹óî­°I©žpÁΚ<ƒ|Ô7 ¡¸D + !›wo£ï­>ðÒ‰3$Dø•"¯³°ÜW}ký×~jÒæŽ½ßùýÞñ«ùLý,µú’xRQuQ—œ9—|Ô‡ƒ3Ut@ qù(ÏÖm»âêvšC·šÛ&Þ€êý(E&#Žujx ÛÙ³åç¨?W­ÞÓëL¹Oí¼æL?ßÄÜ?îøá#I§‡cgsŸúêOšÿ£ñ#'€þ¼ýÌÄ{Uõ¯%%oŒ;âÐ>‡†33a5"  ("õ¨žr’F¼o$“ÞWC}¶ ƒùC9L} (ˆ:GO¾—Ÿýb îÍ SšmÜõg þÎ)õD¯ Ì5¥™ÙñÛ¡¶%ÄvG¸CÍ;T“1˜23Þ'S™fBvãG•቟ø}£8h¬S‡ü׺MüÍäžæGÿnÈÇ-Ãl5·Oœ†Ó€I~y@êÌ &s~­/0)Ïjø±î矠è¼x±ô…AŽ@ ¬ÛÒ@ǯÚAuGó£ÓŽg½ÞpphcWç¡MÝ÷š=f—;â>5†>6ƒ7Î/ˆÄymd@EJ$ ‡öÿóܪ÷½ûzØøÚ6º×vÒP6}ꌎŸß{'ìuùˆ¨Ë͘TzÆ‚\DºÏO¢!{ßøøâz§€'B!²´u¤iK+v{<ÖìûÞžó†¥ãä$XÝ—§ö [‰ÓËÕišTBº>MªT)-5EÉÛøx˜8÷1ô,í‡9ÔÜCak/@ˆÇ?îûîž{†km'€þÓ⎉£€[pÜ xâ ’2xã}¼2IÅV´×uEDÔVÁã~±òO{¿·ûðp®é¤p³€sP>„rÊDTÇ€8:¶k€_J†U{ÜÝÃ);e§ì”²SvʆÓþ§ü@áù¶­IEND®B`‚app-2.8/src/images/resize.png000066400000000000000000000023111377436340000162120ustar00rootroot00000000000000‰PNG  IHDR M )sBIT|dˆ pHYs--ÖÈ; tEXtSoftwarewww.inkscape.org›î<FIDATH‰µ–[LeÇÿgfwg˲»\Ê‚–-P e¡D¬¸ÕÁ6¾yMš-¥‰>Xk &M4‚—ÔŸ“ƶIÕ¦/Q,–´X¨¼TD(„rÛ¬ín—½Î.ìñ.A\Yè?™‡9ß÷ßΜœbf¬‡ˆèQI#Úôz-œ-ž›cI— –C‘N¯7Ô …™½ÿ9·VD´ÉhNY² OìÎÛP^¶‘ÊË2":š¯öÿ-ôþdÚ/Ž.tŒË^oè9fþ~Ý H’êE­Vllzk§ºzuŽWzíž翑.ùë™y/3Öd€ˆvo¶··>)™3t«JGP÷êù¹–/‡ÏÜr«ã6@D©z½f°£í)]A~jDÉÙp8‚’ûއFFo=ÃÌ­‚b:€ädíñC*´Já V øìÄ.­>Qs‚ˆ’ "Û+uÛIéÙ¨Š¬¦È®ª€Çã©@yiIºLqãçe«¼+Á ×<¤Ø€Z-ì¨Ü‘©[(»7=¢R 6Å zébkZ\½³XÛ R#O(Gq¢@pv`t̽V>Æ'<” S;ðû×~èœô¯ÕÀÏ¿:Q¤îxJÙÓÓk£7̈«{zí!§3Ø®Ø3O‚³7ÚΉ‘ÇÕ .WN~ú8W·[Þ[]ÓöxBLÅ£t_]›’çÞgæ!Eh^ù®‚³Ô¼Ü&+䟟¤ŽÎ‰Q¯/ü6 à[@D©Ò 23‘˜œ$5[²¶“Ǫ¤Üœ¤ù|a¼^ß1{ºù/§Û-?ÌÌc«6@D[¸˜Ù±$^(t¿>QÓ¸ÿ¥Ò„DZEÖ´ˆ$Í÷(30|ÍE—»§Å†Ã—‚ÁàÜQ÷Œ\ÏÌ¡…$̼ì À6â’x +!zoÐk>JIÖH’°d\EV“S«UŒFi"%Yû€²XŒe+@Dj&fž^ßÀ»4¾hýny‚ú˜Ù·bu£ˆÈT¾Ý|03SŸ+„)»w¬¯ÏÑ„'o¯dbæÙ` À&“̼êAEÌŒ²RóÓY ‡ßܹÙh”üþ0ß횸ØqýH÷•Éo„Vxj353__-xá¬É”PP»¯ì«ú74ÇÚpôã_ÜG>ì~vdÔu!X À3{”Â@(.JçàʘpfPÍ ¥Æ’â‡bÀ3d3ó`¼p,ÙF³(ÆçÑ)g±ÍD$Þ DT@fækñ‚£R©Ô‚ô›´Z•€@€‘™Ö ŽJ°Û}1k±¦¦<ÓÑÿxfv­„?o;Û28³Ü†Î®‰`_¿ãôzBK¼yÓÿ{mík™´ª{ Óþõ:¾;7âoz¯«ùò wÊÀ ڒ—R•¿Õ´?3+1ˆ‡Ãçr~Òÿ›ãÔ‚À?ÁŒþä_‹+¦IEND®B`‚app-2.8/src/images/resize.svg000066400000000000000000000003311377436340000162250ustar00rootroot00000000000000app-2.8/src/images/rotate_left_48px.svg000066400000000000000000000007421377436340000201250ustar00rootroot00000000000000app-2.8/src/images/rotate_right_48px.svg000066400000000000000000000007411377436340000203070ustar00rootroot00000000000000app-2.8/src/images/rotateleft.png000066400000000000000000000027261377436340000170740ustar00rootroot00000000000000‰PNG  IHDR szzôIDATxÚÅ—kL“WÇ5[¦Ù–-œR#زA@â,H++PªÀE‰QjËD¥0À‚P„q“KËm€l”D …m§83˜8uNݲÅÅOf™Qþ{OgHŒ¨´Õ­É/9=ç¹üûœ¾Ï9ï&!ò»à”à«¿ï«Sï¦b´CHHû \]Fz°þa_û Dà?̹dC%Ô‰t˜ÿ?Mý¡÷øë@Y-ùèb“xãÚ«&05qw–ý‡ Ó±^£/M–ÈG»ú• ˆß¤±q4}Ù‘c齊Ëço=…fàN7_‚ìØ âUH FÜFõ”ˆ£eš- ÞG ôÑܯ;zº‘ëЩoÌ‹Ö =ü‡ ¼Æ Ŭ¡Å&  Êz³©D͹i£Q)§ MÒ@À¢~ÈZ“ìcúG±•÷KaèÛI*¨ñ´Ÿ¸‘¯úo"¤ÿ@ø:-ÄE6íß…üÎv^y ŽS "‹)lACé™{†ê< p4Sd;L~ |ò“½rþ’¤vAÙvi–O,#à²|_´ßäh~/&´ÈÚSäŨ!䌚ÕXôƒ®VÂïÃ}%h­CwËEØ.Ù2Ûìi‘8, U2íø3G{ÑÙ¨ŸE!×!=XûXÄÕ1Íꄬâ7—î)^kÍ$IoÝ‚÷L#Zµ,,ÖÕv'šÊÕ8Y3>KeÖ(i\Sæ˜ÅöÃmž ‹€IºÅ&í\ëvK>ïãJÐ\>F1j ±T !W‘~µLhaá°,ôOéánÔ†Lú/é;Î"ÑGŸõÚœh»š"jQW¤Buþ ªŽ WÐCmƒvÐla®5¬P×ÚiÏeã²|OÌvo j †P•7€²ì³ÈOî¡ÎŽÁkf ‰'Æoƒ@ÆsÙ¸Ñ÷3¼SP-DÅýÇ*°wS¢<{Ì[õYhŽ€i(9ré»OâÀÖFìó—ƒïÑ9?\né¢ÍËÄo›ºn+»ƒ˜‰þiü6$loFL@=vºŸ¼ñR¥‚žE‚ f‹~”aÚ6IU{7Ÿ@v\'E}”ˆVƒ€p×&í ¨äùípT‡çg¶’(—'U0—•©ï»ÓS%‰óD§£0lŸS‹mnòãÏ@’‹©äɧ=¥‡zã£xò.¶Mv±Ö¯LnâûU¢8£ùIÝOªÐŽ`·/¶Vî1§€{ñ[‚ Zžà´gaš2¾TÜ×U–ÕÀ5ÅC6G¶Ì79“žÊuÊ)÷£4³…©Jä O!.Dç¢[ÏmÅ»XòÅ›³=³â:â¥{»Ê³ûQ[0¤’¦÷!`MñŒ'C\·Ž!~ïEegZ§5ø9çÍ¥õP¾*T,ª ðœrf‚\Jy/< ¸«r¹aì‚®ã‡Î 2çœJV8Œ¯Ê4h©Ð@Ú–MÖmFf±»UÚ¶õÖ+Ì)áîÖé%wâ¶Ö£¥\cð¡|I XTLðVå5¿ô8fY‰³íRî&FÈU¤{ÕKGÐZ¡E‡LeëKF‘+:…Ø`ªœŸ3,Cލ %jbCl‰Á—ÄHŒÄôv<öμî<çÛNô(-²õÅj´UjÑY¯GoÛe vMb¬ïôÃ7qeüŒÉY#6Ä–ø|I ‹ëœË5êJæãÅscÄüÊCV4d´âC|I ˤK)Ë^ü ¦íž™œ¤VCyÏuþuÏUœ¸ŽïF~&1™#kbK|ˆ/‰aö›‘×ljÁ.V;ÿ`;íÅ)N*ÑÑ ª{’@ÆdΰFlˆ-ñyåï†l»d?7ztËj«ÓŽ´{ö– dLæÈ±1öÕìå„võ…ì”;IEND®B`‚app-2.8/src/images/rotateright.png000066400000000000000000000027261377436340000172570ustar00rootroot00000000000000‰PNG  IHDR szzôIDATxÚÅW{LÓWÕl™f[¶DÿpJ`ËÄ)XVJ© C%VDA¨-ï‡ ,E:^òhy Wв‚ˆ@¡Ð"TÅ™ÁÄ©sê–-.þe–嬷Ùܘ/Ztkr’¯÷ûÎùÎïÞöÞû›ÀlıõÊ8ý ÐwÄÑ\ÙðÑ£ëä·H Ò?Œg_H nyã?5 ðÆw?cløGa0¤Kd]²~m„lÝ*¾—6#Úk°gŸgÿ¤˜»û-åcHà?²G£ Ês_™!KGñÔL$÷#/V ÙÑ!œn¸mÏ5\>k:¯"3|B–¶+vƒÖbVDŒ¾ù|¯¡ü8ÿþÇM¥zŒjnÌ ×Q-€­½/ô 4Ë@,{t Ÿ58!IÐB­š€öܤɨ/…a¹nšl€4úh~o9~ÑÐ|Üdô}5é!5"˜ªû{™½\“ ißÏÒNTähqVyå)ÔJû.hDx@XŽIOåòDú×"ØYÖºVA1ù7 ` ççDi j¾4 ­u#ˆ )„ƒEè=G ¯ÞyéÞÈ•aÓjÄÉmð¶ÏúÍÇ>7Ѭ3JO Ò=VÈG¡¬Ó?Aé‘NÐmySö‹CTv”ðÕÛ,Ú„öÆ‹hªB¨.–‚¯Ô4{s÷²±L”e âDåðÔ—hàb³+–l‰þ7™º qZ ÖXñ­\.a̽iöN(dëW 8jÔI5h(üCàˆa»èÓ®g‘© 6hi ünØ|°ÕcÖgA<[Ÿ‘ºý,d’~#jŽõCr¨öKB~µ±[Hj^þ1ýºÞl~Êô "·ÕjÄ…UÁ‘²³ä_»}ž½×r;Pœyå9=¨ÌëÃ6o1œ—îŽzq‹K%#Ä¥j’€Ä³6áÑñ`φjˆ¢(ý¬â^x9$Á•ºö,"i<6|$žµž»òÁ^9öo®Cê®(:|žö)³2`øÌ±n'nDm¬Aܶ¤ðšq8òKÒÅp]Îßeîø-½ÍáHçÍÈ@¨K½ŽH kÂÁˆ“ÈŒQbßqƒ„rs§—A=Bã6P¤üŽy/5°ÕU~ŒÇª2Nz”9ÂSF#nÔä—'¿ojsgÃÓb#Ž8Ñê‘ËWPøÏ1ñ÷”®‘»¹~nhÚb|úÜ„vèÏ· ë–'Ö›j€iYÁ•·IvÆæ%žö=ÇÄ´/þN·b‚åÈœB~² Òô.‹ºÁqÌž¢SCfÚÜÝúð¦€Õ…}ÅÝŠºÚòST±9üÓüÀJJ°è­çt–r¹ŽYS™1 HÒ:Q’Ùª<5 R:àë”3E·J©}Ñr¬¥‰Þ󠉪7®.œ’¤v¸}jƒÑjˈiõsÊôØÉÏáqÌ]‘Ó°…™‡cÏ ,ëdùýø¢X‹Æ-b6×`=íÀ7«Ô"ú²¤ÐuV–¸Y¦lu§¥2¬3n BêÑXª%Â%j¢eÐlã¬Èæ¼ô>àípô¦mÒÝø0™qG¬‘  ©T‡V™ª¦1Ôi%T"*Hÿ$G¶ðêŠI ©%Â5jćÉÕD“a)š?£+Ç)›Ã´>J ¯AM¡Íe:(kôèl¾ŒÞ¶q u]ƒ¾ÿ&® ß! 1#9RCj ÇÈ%D‹ë”Å2éRʶÏàºÒ¢~ a‰ +è3Ùá.Ñ Zf]Ëv¢w×(PÐmvOe%4§÷œòh:®â|Ïu\øž€ÄdŒäŒ5¤–p—hÌúÅÄë£ø gË¿0÷`˜ÇóUh­ÕBÝ>N@b2fÌ‘RK8¯üÕŒi›èëJl\e¹}Ò|ÏÎ"à!‰ÉÉ‘SOÃÿ‰¨õ„ŸH/ÝIEND®B`‚app-2.8/src/images/save.png000066400000000000000000000022431377436340000156530ustar00rootroot00000000000000‰PNG  IHDR szzôgAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<5IDATXÃå—ÍTEÅ·êÖ{¯ÛnÇù@‰FM4™DH˜ÄŒþL\ñ.M\kXÃŽÄ΂htA\ âÄÆ=`PQ`¢ ƒÓý^÷”‹ªîù`æ „QVR©Î{·ëž:÷Ô©zê½ç~6å>·>€]»v½ìýòN5ëػ」;vœ<:ÏÌ—7XœïÜS¦Ú òÜk¼¸g€‹|øî€Ûpüìß?0x.ý¸Àþ·ÎoËryš #–ÓŸdüÕ}›k@E°@xp,#˲m00–aP|#%"+ؑՕsç΃*¸Ò² Ò,ŒI IÞw:ç·¡Õ‚v+Žm(r²ú8Ö ã¯¼Ikñú欬°Fؽ{çk놮€Z¨VêmQ¯µ‚÷ ¦„g5‚¨ •,Ã# Àð/Þ#&|ˆ#Ä!Яª‹öØ'aa½%% ð4M£:ó˜‰lÚc V˜_)FüaÍra-Ñ€: TIO4/à…Ä!‰Ãg ’i؉â‡X‹ïv‘¼€¼íßj#íòŸwPEÕ x:ëTx›œ3x$¼þòówhè6h¤¾ñëÆüß±R^‚DM_„ ¥8ç¶ÅˆžK¹v³Rª¸PïZÅ0}±Ë@Űô&­WùU.ááÆÒ2õÌp}É„-éJœÀsåf—+7»¬QW?תd~Å'¢)¬Ü µw¦l¨Á© È5¯›5Y1žþ{éïñ‚ï›X0+WVU!ÑüçSßòë™,-Þ¸§úWj<õìNžya¨3[1|‡÷÷¿Á·nÝ¢( Dk-ÖZTUÅ9‡ªb­Å˜ðßååeò<ï÷#Íù¸ò-pVPÜ1:¶rO8~œééiŒ1x˜`rròŽYØ1:Ö߆®Ô Up6¬¢V¯÷k933ÃÐÐÖZ¼÷4›Í»P«×pш´Ôˆœ '\ *¨V«dYÖ§¸7Þis©AK­8ǽ#´×Œ1ˆDß:¸«›¯5¨ óö.=Žƒ)mãÕÛ©÷åVl­ô‘Ž ÃòïÛà¡‘aÔµ+—Yœ¿¾6øÀ`­ ÛÃÀP­ÂìÅKœýîΟœž¦6`$^J í‘'©= o8ÈfÇ':ñ*ç52(ºpùêUÎ4ŽÑüú‹¹ÙNoà³>à§Ó'W™éÚ£†Uæ»<_o|wîÙ_‡Æ_˜šþôãõsl}ò5â·ÚÿÝ×ñ?MðK¹èF‰¯IEND®B`‚app-2.8/src/images/saveas.png000066400000000000000000000021611377436340000161760ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÓ.*Ÿ½'#þIDATxœÕ“Ëk]U‡¿sî97›÷£¦éKiK+¶Z¡:+‚íÀþ ‚àÐgЍӂ8Ñ R,­RÅ–ÚUªÖ›>Ò¤Iš›{ï9gŸ½×~8HL“¶ º&k³øñ±Öoíq±ð>Õd<ýÀ ¼ùjsª}µ=u飹³Ç8ÉÜÚä~Àq7»ºv†ýÕ‘ëÑàÞW¶5ëÓogûNðÍ;N¢Ö¿>~îH\‰'¬xÒ$¦ÙÊÑÆâ¬#M*Xk1Æb­ã׎“§û׸ö)Qû] vÞÊ‹‡? /wÂëÁi5}wÇÎÍÄ•˜ÍcƒyïsŒ±<0ÚÇÄk/­ ƒd˜_^'ö1”@Ó³xu–Òƒ‡Ï7¦7ÖM§µP”†RNŸÿƒÙù&ª4kd[?JudZÝ ýøv/¿Û1÷à½;t—ÇF,Fa¥02ØÃîíyhëèªÈç7 #  ; ì¢¹På÷®§h¶ ŒÈ]ûHŒ±±DÑráàsûØ:>̆áÞ•f¾qЏ£Ùø .ëÃnzœùëc4› ¬u÷ F,Î{­‚Ã/>Aó‹mpŠþöWT{`±ºƒv^årm/K¹Cå¿-"ŽVVrî§:Ö:¬uˆõXçõxvÏ4¨A €Ô¸8-|©-‹y›RkœûŽUiëqËÙ:‚ä<6v†¤o\BÑpœº¹‘‘ÃRkÁû`ù6\bÄaÄá} ÖUŦËjméãG6m `z¶‡¿®ÍÐìÛMK¹™hmpÎûR  ¨&ÑZ0"ôötòÂþGpÎS–•5élž¦64 m¡FÞpÈ–ç9ôä3q|üÉ1ŠB!b*ÀÈ'ÒXV¾Û?Þæ¹"ÏK¤\¢#ªE@7ø!n5"ÚéTihµ2ò¼@êÞk-ˆ8ŠBS¯/07×FÄR± †ÇÇ@†!ªâlÌÏ×S=1ªœ¢³3A©c Þù;¹>1b)µÁCº»DIÖÂûíPÝ…“ê“ß2Û±"OšÆ„à#ˆ¼_;–^'Z í¬ Z‰É²’f³@ÄÒ±4O64@TD\>Sçì̃,%½ØLa­#McŒŒ¼÷h­@=L†D)M‘+|š bqÎá½#Ë ßwžîÊ$¶PÝ›ðbñÞáœ#ŠÎY¼³‚‡É|­±R¥ ei°Ö‚Ç{7–rú W̦w3!„Õ‘“¤Bµšâ¼G¬[kÅíå©¢$[ja’˜‰T*“JDLŒmJ›d ‹à½‚²âÑ9HÍ—Ã¥¯oàsëLJ¥õsÈ%-+_‡Öô¥B5z*@¬Z4ÌÎÛF@rJ‘t*`ÒœÄDp±à‚ĶÝz –Œ:õ¦-‡£õÚI çœw­æP”ù˜ÎÁ}Ÿlz7|»:Z›Ïû>ïåƒó´Êââi«ï¹g¿{æÌRHÿ§ÍZ·nÜ711" Ç CV?þøR€¦¦&õ_}çóQôÑ£7Ƕ¶ †aGÚ6ŽãÇSê6n4þ—ˆˆª™>Ý·d}ZšÌ™3gœRj~íès+kk5»w?ª†Àà”îÏËU}…!$1::8p– š€p¥º#QX8×-"1½ À „â˜@p°œN§«¨äÓë?*n_´ðÙ—~9Ö¡zE„=˜,  H1 ãcËêœ2x`iþØô°+“Íõï-söE"ƒ€Z¨a[ªÒDZ\rð“h©\½tqo¦ÀúiÆ×¤á”-]üd³oÿKZâD~Šî*-CD$¢7S @pBƒ¼Ȉ+Ò[Ó†T 6œ»£ÑbN¾i¯ï…yEK––^pu¨Áìª@ ÃØd¿òâÄír4Îol‡´ÆvÈáX‘Ÿ]'·V§µŸ‹¬ à.*R·ÈX‡·¥:ÿ@ðŒmÛí€ã·6»ª¨¤ß‰„xן"‚÷§ãQ±1‘´RSSZöìÙ»È× ì¿ýän–=Y.°Nƒ`9ð;pÕéè¡”8Œ¼áéezH©PÉyø×ÈÃ"¡À(¥”+{ŒÄ—OÜÛS§6¿9a‚T—”< ¸ôºt“™Ðr® zØYXX¨”RbšfzVV–Ã4M'0Fû[0ì²ËNƒX5mÚû ]uÉÉò~CÃàN %¨ ºé< \«ƒæÂn (裛ج¿UiõÂý¼^’.¿ü" R Îo–îý´üúyXìÒg¶¶¡;»§-11ñó11©åæÞòÒŒ­kËË'°¡¬,£nìX©KN–³g/¶…˜fv Hòö1P Ÿ«tIú#D@&´mRGŒ0粂‚9+œN)ߢ¤$i8r$ݼ5/ïéwÌçeš`e—eUÏ ÎPé@60E3î zïÞpà;WLŒcÇcÑÞ~—³³S)e~ÿ=ßíØ‘Å×--êvÍðŽÌÌ€ Í¥ºKIÝxýu·éÄÚÔÿVË€o¹ÿÞ{ßziäHY²8+«í\ è=n[G_5*âÕr ©xýÚo_)~âk`‚R*8ìáZ¸ËMÀÒMkªgÆ%ÛvîœÐàñ8Î V Ï·«aÓøo7‘†µƒ¥¬rŸ¿s€—/úE#0(¾ tzw^žÃï{þ8Îw„½±dŠÈYûâ¤öÝ_ê ðõ’RÒC« \ÿÚ.z}ÆÁú‹m9”`ËA—l[3ZæÍ›? ˆ Þœt:ÌAF¡4 «Ý«ÌªM:,z̓…ŠX¶Bc®ØËÀ°Ë€©@|@ʺ_€  œ¼`ön¬¼í@ý‘ã»Äo‰7Þ’Ÿ¬/6\jÇ'¦øÅV \Ðüù­j.h˜ÿ|¾hlÜ\áÙ×aÕÖõ7j?è¯j?è¯Ö×ôW‡¼!<=cÜ;@†aiÀv`½ö)Ä=I1MSMíg´vK\µ›ˆ3ÍÓcT`ûÀuqÛ‘Œ´¿èãû€€D ôÒääüCÍÍIÉ£GïinlTç*·³šÛíVJýóÓœœ ò%@Ž¡”Û‰XŸïŽ.+ß6|8¡³f¥6u«Õ—w˜SåæñzoÞ%¶€µ2?ÿw€ÒÙ³Uo¬åg².ÿà —ë€Ö½Xà8ÙÑñÀ‰Îξ¿Kn©¯7ÖÌûÔæÌL«&;û3ÏáÃñã{¸GþИ3: IEND®B`‚app-2.8/src/images/science_48px.svg000066400000000000000000000002671377436340000172300ustar00rootroot00000000000000app-2.8/src/images/search_48px.svg000066400000000000000000000004751377436340000170650ustar00rootroot00000000000000app-2.8/src/images/select_all_48px.svg000066400000000000000000000006711377436340000177250ustar00rootroot00000000000000app-2.8/src/images/selectall.png000066400000000000000000000017211377436340000166650ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEß ) ¹0tEXtCommentCreated with GIMPW9IDATXÃí–OHqÇ?³«•k”…[»éÒ V ý‘.zéÄRT¨%¥KÑ-$ˆ²ƒR§ˆ‚è„HF-[F¤Wii¡+YíÎì,ì6úmüœ™]ÇLèàƒÇ<~ï7ó¾óþÃýGdJœ.õ@?‘tqV/î¤%´;€q7 ¨â)Ï  û?£¸½X€ž@€­GŽp7d÷b…ÀŽÆ‹ÚÓÃd"AÊ0Ð }zšÔõë¼Öº´‘)d#-.dœ”ÇŽñ$‘ ¥ih2a8À —Ò…r  x¼pz3dSq±¹ßYYI³K¯€/ÀTî HRÞ Ý£ëhŠB™õÜ0PR)Þ»pÐòÙx.J%ê¤Üµ‹“>Ì®£ ’¬ªb«KQ <Í— ysð57sãñcŒñq2##dïß'©ª´Ï#Ñm6‹Ò)4r%xüëÖqTÓøl<øø‘ó Ë?d‘ä=‹Ômeo]¶ðKr|‘ÔKò+€II^¿#•ªÊÞÒRŠc1FDiçè«$¯oÎIÕÕ´uw3mšè¦‰‹¡mÙÂmÑÖs(Ç6ŠH?À­XÁj!¾Î“ôöi¸o§FGíïí[ªJõ冚ãq ¤Z[y”oâÊÃ(!1uu´ƒ³{¶iB(DñÎ4Y<|ȽÎNƤ„PúúÈF"œ³ØÐBÐ8«ïúð™#£¤³¤„ÍžþÞÑÁŽh”/R]^βÎN>ݼÉYÃà•dC6:…ê‡Ä45Ñ—LÚçÿè(Æþý„ç(Š @µÅ93bË*¼-_NUo/Sò£wu1¬YàÖ•q³_öz9XVÆWE±é®‡€+–-gF´Ûvç´¨yU´wÓíVì¶g³ }ûFÌ4m›ñÏ5 mÅEù6nö°DKä’~“Ž‹ä‰>hIEND®B`‚app-2.8/src/images/selectnone.png000066400000000000000000000007661377436340000170640ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEß 1Õ¨XtEXtCommentCreated with GIMPW^IDATXÃí–ÁJÃ@†¿˜€wA<‰ßÀæ*ôس¯Ò³O ŠŸÀ›ÏQÚƒ´ïÐk‰´Ð¢°%6^™nëZc7ÛC~X2d—2vhp@(Õ0À Ps…¼ËdQs@h3YÛRyê}8ú…T|V(&Úu¡‹@ œ£Š$þÕ_p¶`)•ú¬vcïDÅO"žØ#WàRçÐbiSùapp-2.8/src/images/similarity.png000066400000000000000000000036221377436340000171050ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà *‘0ûKtEXtCommentCreated with GIMPWúIDATXÃí—mLTéÇ30ã¼ÜÙaf¸Ã0¸8Ð qG·Ëîš®ØXÓv n¢[5HcÀbLlL ÛÚ4’˜&j6’»5iÒFtkÔ’]ˆ¥q]e+/Bë Å"‚å%‘—./2ÌL¿šYÆíöƒúaO2É=¿œç¹çž9Ïÿž _šØÙ³gµuuuñ­­­îÒÒR@ee¥þúõ뮆†5??_ PUUenjjòÔÖÖ¾°aà Àùóçm­­­)Ç_¶¸ß… œ·nÝJÞ´iS @uuuÌÕ«W“ÍbœvñÂáp8,ËL&ÓÇiii¯ØíöWEi0›Í'Ö®]ë°Ùl?PåŽÅby'77W‘µÇEép:¹'OžÔ:UUk,KËúõëWLOO{G³¢(æçç;£PUÕàp8V9“Çãù @||¼Óáp¤ÚíöD¯×ë¶">>>Îf³¥gffš\.×JUU ªªfú|> `IIIIWUÕèõz3fggÉÉÉÉñññqëÖ­[¾xߘŋ´´´¹™™™ö¹¹¹»»wï>711ÈÈÈ ƒòäÉ圜œ äóùz‚Á`OOOϹ­[·>X³fMG xPSSs¢¨¨h˜]½zõý¹¹¹öœœœ÷PIIÉè½{÷NMM]ÏËËûè {bóæÍš¥lÛ¶mQlçΚçÕ‡z`ð`f¾+|±ÁRï+#*ø °ˆl° ÀÀ$¾ ØlbŸ•€èþ¼-,|ÂÞvpûDØ$)30&ì;I†nI–d²ôCÀ´°iàžðya£@¯Ä…„õIÀ¸Ü P€9ñçûÀC¹W”ÅéÀ×#Jk¾dFÄÙl)÷¢¥HÉ ,Câ"6 xùjˆC‡E5ב#G´KYyyùóiÂêêj¥¾¾þ¿ßÿËòòòEá¼G›šš~\ZZP[[»¡­­í·—.]zkïÞ½z€ººº]~¿ÿÄ©S§|‡Öšk×®•úýþãÅÅÅ §OŸVoÞ¼y¬­­­TUUc”y<Gbbâ» Åßp»Ý¯8Ο9Î}YYY™Âv¸\®=N§³hãÆv€åË—ÿ<))©ÈívoÍÎÎÖÖ+VNJJÚ—••õM€@ ðÕÔÔÔâÄÄÄ}û÷ï_Õ„£££Š¢ü* ¹ÿ 066Ö‡ÏhµÚ¾n€Ç_Ðh4‰çûúú&eíï‚ÁàÆñññKóóó!`zxxø=“É”<22Ò Õjû=ztN«Õ>îèèx¥„ óz½¾Å`0|¼}ûö^½^ÿÄf³Ý˜™™¹YXX8&ºßk³Ù®vuuý¥¤¤dVªwGQ”Ë•••a ”žžÞ¬×ë¯ìÚµëÓÎÎÎ xò…=a±X¢šËjµF±„„„禄à QŽ Àk@3ð>,¬è~"‚ðkÑŠ7¥¯´@Ð!:©Àm &BÀ>c/A`ø¡°",]ÀzaÂ.ˆø ꕌl⇉ù†øƒ¢5ŸíQ¬»À‘Ù§Àc`¸\–ÿŒHUî˦÷`BÖv~à>\þøyƒvIRñµÏ`šÿ²îóXÌR¶TÙtf³Ùá‡ó²eË”Èt:] ‹ÜÇd2Ù—$¥W%nIâ“ɤ<óѯ\¹âjkkëþgEEE@}}ý·;;;§»»»?9vìØJ€†††_ô÷÷‡›››ß;zôh@KKËGÃÃÃ᪪ª]Z@÷îÝ¡¡¡§eeeoœ9sfÕƒB}}}ËËËK‰ª€ÍfÓ™ÍfWlllØív»„YŒF£I§Ó™RSSmrƒƒÁ––f8Õh4bµZ=^¯Wm6›Ýh4\.—Gtæ…¸¸8ÑhŒõù| QM822òéÔÔÔÅñññÆ-[¶œ˜œœì ×úûûÿ´cÇŽ&€p8Ü:;;{«½½ý÷{öì0™Lõccc-ÈÎι}ûöµÞÞÞ‹ÅÅÅ7ÿo>b–¼¯WÉä3$Cƒ¼¨2ˆ„€Dà ¹ž”µéÀZ9¶sÒp//ɹÊlñºL^"—ÿ˜GÎê<ð}aoKà=©Ž ;'‰!Š~*e’Ã2Ö¼*þ +Q#YèátJؤÊýˆÑj0âÖ%ê7qäü2!-®{ ôË^¡gýÑè—–èDªTgÑ–IŒ9òÝõŒQKd÷_x¿üŽ´ÊÑ]úa2IEND®B`‚app-2.8/src/images/size_select_24px.svg000066400000000000000000000005571377436340000201240ustar00rootroot00000000000000app-2.8/src/images/sm.png000066400000000000000000000103201377436340000153270ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEØ &G›?°tEXtCommentCreated with GIMPW+IDATX  ßïÿÿAççAÿÿÿÿÿÿÿËPPËÿÿÿÿÿÿÿÿøøÿÿÿÿÿÓIÓÿÿÿAèèAÿÿÿÿÿÿÿÿÿÿÿÿÿAççAÿÿÿÿÿÿËPPËÿÿÿÿÿÿÿÿøøÿÿÿÿÓIÓÿÿÿÿÿÿAèèAÿÿÿÿÿÿÿÿÿÿAççAAççAÿÿÿÿËPPËËPPËÿÿÿÿÿÿÿøøøøÿÿÿÓIÓÓIÓÿÿÿÿÿÿAèèAAèèAÿÿÿÿÿÿÿÓÓÿÿÿÿ ^ “ ',yÝIEND®B`‚app-2.8/src/images/socnetv-32px.png000066400000000000000000000027121377436340000171710ustar00rootroot00000000000000‰PNG  IHDR †VÏŒbKGDÿÿÿ ½§“ pHYsõÈS`tIMEÞ Ë[†zWIDATHǽ—[lTe…¿sæÒÎt:m§Ê4„KE ”ÚJ)„Ò¢@J¸‰1%<@¢h‚ „‰/ jLM`”b$R €-‚R¥%(€´PÁ^h›ÎL{|è8Á^HC8ÉÉ™3ÿÞÿ^{ïµ×?c0€ëåHèdtÉUΓ鼰ñ2?ó8¯]~ŽUXUÖ5ÝÇ Ÿã’—}&à²àzYfùÓú5qIÀâ§‚¬.²xˆï/¡©S{ÈÄþr;*} ]×Á§ùABGš©R@S·Ï×o¶qàF—€0P \º0M3#—ÄO¢|¼ÄÊ ŽÍH¤ãÜ­û1íõq{H+§›³æëzà€©ÀÀL BÀfåÀbàG "J“xJs- 6™/52·O˜NK äƒô2o€¡À¯ÀmàP xR`„ìoVcØÞtöÙß(öºÚ˜XîãøWÿ²L%ŸŒ¶Q`´2îî×€IÀyÝ+äwåÒd>‹k$¯ÞÙMy,/N!|®¾„’0êš(Z¿€ •ÿ7`&àßqrk–õò+W«Z€F 8Ý*ÃíýŒa]ˆl§€WÔËj-ŸRÖ?‰tÓB`ð½ªsBûžÒd,ªãÁà}é€Ã–A­˜›†00RÁþVù»Ô‡¾ðó@›€#†P¿%¢Å%"Ø0`ï³ñ'öôs òMx°Z¶Fo$4”ásjÁIe±H›DÔÏ eÙ)»ØÜ‡?¶ÈyË>CRka<þ«»‡Ó€f[ÿ3â‚FÏÐ{XÏ.Û6ƒU-‡Z0ðX™Åì¼ÇÒtÞÙïÅÚá£É“ˆxH– n©’ß4(+—‚ÛÛQ'íHîGDP70Á“ˆ¹ÝGË~/Öçé¬1G´°vP;dßÅÿ‘—Tà0pCºäØ*2Å6;Õ;ˆ ‘)[·€Ùì%9ç.¾Aí0¼…÷̆x*´8ak”ˆH—l nŠPNÔ¡ÖÄxµ0´ærÆHç’~ÌÞ%Úâ§ ñT¯¦ðçÊŽ2K:¿ ¸®ÿä(ÐYq#"bËÜR°t`¸z^ ŒÀT`/P4§˜ŠÌkä”^¤¼§Q\*h“4#ùª†C#ê²”N Q£—)¡Z/[·ö,y!úVÒ9LûEdtã¤| òuéöª=Ó5óàP#¿'t6ìëIxzºf y°U­°´Vh˾N@ jeZë°j}˜ Ä@•)Ð(mèÐ3Q"Ņ̃˧Fc2ìžÔóXO'oo0€"©ÞeÊ£Óp¢„€/lšV :?NHOÚ«J"A#Þ{Š º±¿šFѼ4J¤z êý›:Õöéû8ùG”ù ѧu–äÍdÑùF +W™ŸßÏi´=Àî°Î‚µ*•-ú!‚²(Q5Ɖe6V§È&[ÕÉÞ䧺¬°¶¥ña¿H “1ºÓ%$A™ QI7Jj—S€ÉÀ¯ÈîÕ,X0<…w§Ù _DÞž —åÞÿ¼pÂ#úó’¬ÊüÓf7W¶ùò±þ5«Æ43Ï´ l@°ÙàˆÓøgáNg†µ2¿ÍIôB€×5sq {ýxì;ñF§IEND®B`‚app-2.8/src/images/socnetv-logo.png000066400000000000000000000065741377436340000173470ustar00rootroot00000000000000‰PNG  IHDRP$¢½zbKGDÿÿÿ ½§“ pHYs  šœtIMEà(|–¢¯tEXtCommentCreated with GIMPW äIDAThÞíšypTu¶Ç?÷öšt:+d…†ÈvY•EF@˜Ç&Ï‚§ã” lŘ7¾F±œAä¡(:â6ŠDYÙIØÁl}뤻Ó[z»÷ýÁ/ÖÁªWõª §ªëþ~÷þ~§º¿÷{Î÷üN÷íÿÏ^I»Á=Û¢ôk×…éÿžHs‘ºkÙ¸H¿dñ›É„·q3Û"½ÎÀ†gê8ÕÒ‰¥ý…h·iãæ/úè€é-@¹i°²í¿^|ÚK°@Ëùj=ùùço~®.¹qþ܃-Åå©b0 y~'Rçu"æ6Ë"]#˜² šQ’žÁ@«¦‡)a×ÒA—‡1¯M"ëú®)-ĤQÈŸG1怞ÜíalXM'ÀDIÀ Ðˆ†½€Ä3iÄî4òéa¹_Fòr‹Ê“ 1C ŽòÓëç§à; V|j Zl3q@ P‘d§WD€©¦Dj1Ë~íjšÇ­F¿AÐ%ó]ÇŽo`ke€d 8#Œ. €y‚•m\!.ô@ã—‰«ˆä¥mn.µ(EY—„¦ŸŽnñ2Jé@ÐF4H¬Ó 6Žˆ5C€Øh £;i¾yè5ê>¾ÔzÁ–3×çÉ"×ù€S€ ÐÝš‰‰¨‚€(T¨U¬?ÒbÊ^€Q  ˆ<™*B6 ¨Ήð7 ¡1 _V‘M-%j57Í#€NB(B”Ö@ Èê'P"@í&DÄ/Âú0(›/N‡#¶_)€“ºvÉ‘m3à,ê(B5(€ÊbŸ$€ô‰ë !.>‘#hÐ$Ô7éóÓ(ÄÁVï¯,®‹§§Ùϸý! 7:¹ ”ã@Q²œá¬Þ|øa.Ø›$$‰£ ôžFšY¢¤^'};»Zý?ƒ¯Ë è0»6îÞ7ýQ…×¥»ÈJ»?©XVÜ{‹@ÛÆÍ†7½c äoŒáqlØD8z…W4جù Ê"W&U@(“õ¸¦èÒ×Á†ºÆHÕ ììõŸÁ„ú*_ªÙ¤ê_›kr6TU—=52±þ—xqm2ÉÓ­I›kŽ/)«·¯˜žTRÛ€šü…è£Âõ™²¤÷äø¼¾£¯óº¼ÙÏ[?™Ÿ¡½Î Ë ãïjLS¦F&:ÝAyßVp^\kQòRž­“ôF)Õn,8ðz÷Œ+“U(öh ¹dª¢`D5cX¨óT¢$ÆŠ{EØJ"ôÜA¢ü .LÐ'SavSÿðj÷𼋋…Ù¨ÏËž|ðÈÕù;Nj߀¸öwUº˜'îøÌm-M;WâŸb×·þþ#€¦âÃø¬åæ «nª·¾ä¯þ‚ÿo{üíöÏÏвSUåç¶x2{Ì) È–²ÈªÊ†)Þ:ÖªÕ;ç)¥¤rÈã–ý žõ–ò¾¢÷&ÁÉÖr~´ùý^=Kq+8è.³¿ 8µ€‰@…’.BHd{ˆøÍ¶×†óT½†y½+Î'Õ^ôWèg;úÂîòï§½·g×oÎ^lº!Ú»´ÖBõßÜ@ÇŽž§÷åxkróGd ºZ?,Àzð]›âªØtW¥ªE‹,’bñÝÉÇ(@‹4Hõ7¾ $p¦WeA‘õ¤ÉhÈ2Ééë ý$ERœ%Žâ‚ %Y ¸BÚä ‘@†4åZÞ³ F5q{°ë¦ØÄÆÍX!ÄÀVè#‚ïZ=øNíE?Ò“¶&k\ðýãõ'€’s_Ï<¯Ï\7 Ùé *¨?R Ù·WV÷ý·Ñ²^öï?þÅSû%I ô[èg¹\Ù¿s¦¢2¸MåúwŽe×ìYd÷¬—üsûØIOŒ5cå’ìÕÍ V¿c7F-( Ù+€"U Ô­ ©úÕO6LòÚ,™]·6tJ îÞôtêÞÞKœmc¢ä .gdûÈaûþÜèR¿Í«óþþŸ¨‡’Ð¥áj¼Îm­UK‹’eU.Êò–Ÿ: ¶£€Unþ&”k±O„q@”*W…*÷'¨û’Áäb! Aqßä‹\ Àóñ’ýÐiÇÙÄÌ'úí>|ÛêN©öšýWfýSV܇ù,:§Å=µ¾èô¢êã …¶Û»¤Ç,—Z?7Ð,ÎL»|âêRÇéÚò3›kvmûéao}í €¤ÑêeOÅñ¼Cû¯HÊè>¾Ï´/æ’ª1©‚Ø À¦…Ê_鎞s¯ª¥Ç¿¶FǵzËÐyéï4¶Ý^&Pí±—«® «¼ÁÂÕ€¾¬ÁýEJ§¬d¹cÐê F·ÁYÓ8Q)Z³WÔÃÈ7±YÊE›ªà<œ€Õ'€ßˆŽLP”5+}Z5«o°m›×¾õéæ­Èéóìr÷{{O·?»êóÊe óòÒZ…;~_›¦ÔnÛ²cÝÌU©íbJ”„™™ýg´·W+/:‹ö¸}•Û¶äí_öî¹#×»+¨L HƒQ.Ø:þ“¹5z?2j‘ñùcu ’úNU;Ɔ»ÆDš*ëÓâ¾ü¸|ÇìUÕõaµrxÖœò-õ‘ag%ü.¥v×N¥n×NÀspý—߯$F–Guš8A’äh¿ÛÛÝ[y!µ?‰ß{ÛŽtH„°¿™ºJ¢À–€Ý̶¢ˆ¾*üt ‡¸&ç×ÏØçUfu“pä¾dqù²ùÙÕð˜¾%¥‹'üvôà…$¾ì¬-±lÓjä ÅeÍ/1Tî%Ïá 럔Ð9è§Åû„Ê{¯¼}ï à …BH‚9_½¶$Ì´²çŒzcãÆ-ó’3»Щ+´u:B 6ßÀvjÜÑ÷Cu¾ì÷s’n‰’VDÒ(â»»È}Úã ýûŽN½†>Su¬K¦«.09`ùðˆ ÈÏG¹2Îjiɤ%E!:0í€GàÀ.Xéãsâú[Ѽ’‹öñx5pV7‰ªêûÏ.Ûö7Ëãlj½•»Ö)å›&_© æ£78úýæ(¯Fç4ÆÆ ^r1:ìv ._ÑÉf¹}”$ÒãVï õ}þ3€ª6%gTm¨¿thûs¥u¦¨ù &®ÐH ±àU$_SýîÏ¡+\–"½<ÛàøpiDbçª` ()ªz ›rKmŸvÈH6ö~úíÉ®Šò8¾;#ö3€k¯\›|ÍØÔrøC/og:ð“@;(Ô8U„©IÜz¡Ô¥Àå3Ã7¹Ø¼¼–5ǰ¶ùÙäD›ÜÎ<-!‘‰ÚÄU¿¾Çç‡\&œÇ¬Çvþà()3|ß±ÿ@¤GU5»·»º»ªŽ–Õ_ͶZ÷d ×Q£ÓFÿ‡ª?rPyáxÃ*@ÒëdÉhŽ“dS² `?9÷rαœç ëa-Pcã×Ëmç¼þ˜sÇQsúØK(öCîö=ê‹™÷˜ßsÉSQ£u†0µUŸ™é©Ó¯N ÍÒ¶…N8Uåâ¬*÷zÖ“—](ލÁ[ÎÂ]‡¡YÎÈX£‘1 ³so€b±8U„hHä>3Ð^”7±â|xÌLô/S®O|ÔäëJ{¨†³<¤Lè0¶‹ÚhXYìùïú+_WQ¹æ+WMÁa{ oIÐòhTÖ‹Ã_[ïxÒvu›!TòæW(îE9¶ƒš¤c¢³ŒÌ?bâ,Ëí¸8';jØÖ>v«qš¢mß= éèP=¥—ðù½ÞK­31Üç²wTª7Ké_,Nof©5A›òx?)eîX[Þ™ÎêåÉ;·úx3é®}HÝ]þ¬ÚôüNÅµî¦ Bºá*énZÝZFÒßο|‡Ëõûó€{<\κqÚõ6ÿq°9…Öû øs$Ôlº­Ÿ%7]ïÛ϶&‰Ö»äÐãÚnbå}DnOáÛÚ ÔŒµØÌ¬Fž¾Íßft»â½ÙÄ[omlu–ûvßîÛ}kÉö¿oÒ\PoIEND®B`‚app-2.8/src/images/socnetv.icns000066400000000000000000005523611377436340000165610ustar00rootroot00000000000000icnsÔñTOC ic08¥Œic09ùÍic075pic08¥Œ‰PNG  IHDR\r¨f pHYs  šœ@IDATx윕Åõ¿g+UAPŠ ]DŠ]±Š-¶$5‰-‰1»ÆDÆhbb7E£Fý©IÌß5vÔXر"‚ JS²,lùŸwïw^ïö½¸˜{ø<;óN9sæÌ™yß{÷î%„¼ä=÷@Þyä=÷@Þyä=÷@Þyä=÷@Þyä=÷@Þyä=÷@Þyä=÷ÀWÌ_±ùä§Ó ß%V‡’òÒPõÎCaeÜeÄΡCAQ¨¬aå›Bu\—Ïõaô¼°¢ßâPØeE(¨( aA»Pý^—PùTÐîÅuC(® w­UŽ˜òHXܨòÖ8ä€5nÉšfððq¡øõ ¡bä¸ð‚6úæÇ½*·ÿ4-ÐʯЦ/ÒC¾îþ‰TéºR¹ÎúÁ£áýCùe£B»uÊÃM/M GŒ¯ƒäÑüË‚o}5~怯Æ:fÅÆãBi(›,, ÏŸöJ¨Øwv(^˜ÙôY;¤ õÒ ôÒapÑÆ¡lBïPýÆã¡ÓÈ]BÁkç”«ÖØËü°Æ.]ý†k£®(•‡×o}Lwz½Š/næ[zt{ºg¨¾jD(xó1)yùÊx ¿˜_™¥üâD†ìª¯ŸB{ÝÅõ®‹¤R‘òŸ^!\;,¬¬¨íÞy"ÿÐ"‡¶‘ÎEmÄŽ¼­èÁ»…¢ ú…¥§¼Jú,­yßRõz5ÐõÒº¡rI»ÐaÞŒ çм¬é`]óòó@»Ê0@êÅ›~Ò:›ßîá%Ä•“CiYI8ÓeùtÍö@þ `Í^¿¬Ö¯70|rùÓ¡¨“>ÍÓÚR¦[†~eX¹h³Ðgþ{á¾Ö֟׷z=X½þÎùh£Æ…öüZ¯wyÈÉ‹tÞK8úíP¤Ï•óÉäȹò@Î]¼z¨(C{—…ª%9|¶+ӔʊBá6;…’Õ;»üh­íüÐÚýõ ß3è·}‡î7#É•,×á²ÖŠP¹´>g—5Úù`^¾U_0S¿ Œò™~í·jU«^¡{ðâPµ¼:¬ÕªŠóÊV»r'«}2ÿãTê3ýzá_Áïìs-UCoäàmÆ\[ž×{ ÄÞXsóÉ–_:?Ù”/¾Ù5äôÏ÷´ñÃ;]BÑZÕ·ò²{ ¬Á‹—1Ý÷û‚²Eú»žáŽ{û‡jîй’úda¹ÞÔç VäjŒ¼ÞÕãü°züœ«Q¼ÍYÇB½þ/XöIxç“v¡pí¾ X Ñ´ù«&OZõËDr5ɼÞÜy äη¹ÖÌæ¯aauu(üPîË{Ó;ÖT¶¶èþã°P¡[ÿo3ºmGk•×·<ààY Cå‡hE°ébñoý UQ¢§€>§mVº0nØÒ|7=Y<Ö'/=œSX”|m€mqÚÒ!òýW£òÀjtv+ ÅF3¬Ÿ×ý· >˜–•T†y÷ô Õ|éGk oþ¾cX^²"7sN(¬Ò{Ûâ|R˜ÿ±fx Ãc͘ðne¼Ù¼ùº® º"T¿61 ¼eÃP0·“¾Ý“š ›ÿ¶Á¡J_.2ë§Ã5ºäÀñؤÅ©ŠòÒÖ=ÀÂåeÍð@í—¹ÞxXN¹¯“¨÷ŽóßœºmÓÖIþ€')lêTñW€w ö UúV !U+“OzLox_3„Ëš:\¾ýjö@.^&®æ)üO çÍÏdÉ{ÝÒ›Îí’T¿\¾øƒðìÔmÃ!Õ•aåÎú.ÀEêÉ÷6$4á+Á:è5ÿ¹›…êÿö ¯½<1ôÑëþµ3Œµ0µÇw>©Èÿh» €òÒ¶=à †•ñ†óÞxqÛŠå‹Ã×–/ ÿzý¡Ppÿ€0q÷ñ!ü·{¨,Õöì(x€—¼³¯?"J6<ù®ªÓ¯ÃýBùþ»‡0yQ/Nob€^÷{s«Eí>¶…f\#´ÉKö@~ÚðâÈ4¯мSòÙPqÒ†è$®§@’<1 Ù* ©( ··ïF ¶ü$TÔgú»–ëkÁ¥m~ÇP=­s(šÒ+¾Û9„u–„Ë_~1œ_°<ì©‹ô¼ ¶zg!𧜯ݦ¦Gþg›òA”—¶çx]¼é}Wõµ¬w{êxož¿Õ¿\,z ù£ªâÒ°Ž~?S×˶Ü!|W§Âö Ú‡ÞË‹Cu\Ùie˜³Öò0û©á¬%‹Âi]{†[/+õß±áG þ³×ÅÁÅ<›!­ùAM>$nÉÿÈ{ axc³™¹só7÷üÙ-´„>æ“lJ6&è~ ¿¿l¸·ÂF¥žôhQ©OòmÐc@èÐwhhߥ_¢WÅ¡¸Kïй˺¡«ÿŸÕ5:8<ÖΰžÒÍåúoEA¯mÀ&lk'°»±|h)›—¼ò¨ËÙ6?‰Moþl@wµáÓylÐÉñáàM:MeÔ#Å:Š€|r] q ’MÌõr±¿ààð!ÂÀA€îø´£ýØäC[±9><7ç%ï¼Òð‰ïüÞü¾ë{“ÅŽ<ó/‚y»@(ïÐcuͺ¯`ãúÎìñ(c<6/O ôqÃeÔ²Ø&ìÍvøI€¹æ%ï¼2¨kó³‰Ò›ß‡ålºâ¡o W ¿iC¡ìYá ëÍŸm\?ml¤ö¼™8@ĉu«¸öè™icÛH±]à—ñ?䘼ä=mÖuç÷cs±)õ_u„{o¸ýFøqÜŸ”>Û 6?O Ü齕­ýmeÔ±YÙ´Ø@þä7­Ï‡’ªÂA;Þ`|Û˜>ÐéÃGEyÉ{àÓl~6ž7„ è;¿7š7²7iñ”`ó+8(7+yt\+xMݦäó_1R‡ ô/xÙÀAc½¶Áøª ¯ì#|Øfô¸­øÊ? ÈA_–¬ÑÎ>6è¬êä.¦ÿö^_Š©/ÅÒí°Zÿ#® õeù¶®qcŸ“á @H]žè (ï-î£oÔ=)Øàé>*J^ŸÏV:Qì#hg]¤ˆû‘ú@"Ňúÿ€ÂKb×Ìuº/oò”pØWžÉÇ_÷¡}úZEµb›j ÚBfø.¡H`Uðò¤/~ ÚãBÑr}Ãë±®-Ø^Ÿ œÄkœŒ:¼:1”ž¥ÿúºWr×Ù@QÃaË”ÎUx°ÓûÖ÷דy.Tê#¬™¿^[Ýóš †87º7©ëZí×lþbgñ†ˆ7¿û¨8y§þH¥lƯ 6j,qÛ¸ÜyžF.' }Õhò”¡¤V° ›h·Ÿ¸^Ü(~!x¹ÁÓƒÛcƇ€¯U\[OþK•"=«<n:L}*,Ó ŒÔÿ…°Çð]柔†¡ºÑtÐgÅZazqyx«]A˜ ƒ96´×b±k„4mjïJÞx,¬½S8uFçðëže¡c¿%z^¦çΕ¡º¼Pý¦Ιzž¥еW„kqêiཱུ{†¢¥ ôt ¿”[Í“Š}ç½ù1ÇyêÉ#äm+)l ýņb®`“!±®iÏ£÷ÑXœÕU3øL™ +oJÖ)ß~yb¸µFcÛýY;é¶jâÈq¡´ýaå¢ÙaþösB‡“^ øþ‹ª†Œ×A裺[Á%›„Ò.Ë÷?îŸõNí·Ù¢ÂKØT§Þ`ŒIžrC™Å›„ÇýI‚Gm~çŽÍÔ¹)zØhcÄ»6/ø5!‡›ÝýH§ŠÍ…ïRè@ЃØFÞà¿â$APÓ?*Núé:ì)þO<+Æ ì¶¸¯ççk§´#o‰ó.kiêù¢‡|2ÆZCñàÍ Ÿu^þL¨ê¿$òGS|ÓJc„?¦ªTû¶Ož{yBòFjcº~)mb'|)Ô7¨îüÅ…•¡´¼$,ýÙK¡b«y¡˜;}S¦O©Bíȱê[N™5#\¿àð¢ºj• «Ï”–ÔÙ\Òl(\‰Û9ÜØœ;û =d&¯Ã©u¡ƒk6Ô¦âÕLúˆÒCw|ë%åÁ‰‡¥XÒ:©{Il–I+å· DŒ…Ä›Øs ¼½ØFðˆrqˆñiEƶq_—Ç©šÖŠûÔ41ƒmÖÛ‰þC• »~I¿!á'ú^µ³ÿþ¤N<™%žecîúqúæaÑë„zsÿµII;¢Í9b×P8õ±P5h·PýÛçCµ^ƒ°‰½‚M5”¯Éî¨þßÚE·Âòpà‡ï…‰ g'OV‰/œwÚÔahoŸZŸ7¸ë|M}Úxl6ÿ¾âï‚×ð{ 6#O´ñ¦OëãîËæž 8 ºˆôFSQ2¶ÇâÚ‚MÖIÊ›Ý/#_÷¶ðŽ@ènX,8Ï‹¦·xY [ÖôóØÖá2§j’ˆ¯Ñ‰pÝ\±Žtÿ‚.½ô·ƒÂ˜êöáö;¯y YWãtçl×ôm'KÏØ*,›¹v¸ùÕÇÃ1ÙÚ}Ùe-™cÎm:>¼søÛaƒ]f…vlþ– gœèߨ]o>œ¼”ókÓ8¨â^>Ðg¦`®ãvnOZªªÕK¾%â¹§ÓÏêfóÏÇjjÌ—¦4dðÝ ûŽÓÉX†--Ó§>^û¦mCÝWK= Ûæd¤ÞAÝn‡°ÝºeaÐï·Îæg’,*O?IïæŽ /­ÓGÁ¨7Uå@wžæäÒÆ·G'°ÁÜße¾î¤ºˆàGØ$Ü-ØüO‹‚×ðñ{Oº¬Õ‰.Ä:éŠà©á8‘>:Ý^UIŸ®d2×Qd›ÈóHÏ wq sbL÷uJ_Ðïh[xŠÀ<yÄíGe&¾¦MSÄ:œ¢ ±NýÞ(víÖÞjÇpßQoÊÉòVkl~aòs4Ë ¦„ê9íÃËmmóc£B¾ÍÈkúõ‰~½òô¹ÿÕ;¯9°p빡pYIÙ±}¨Òïz½AøÁ£:pšê›¸yôÅú)CÊÄoÄJ.$l®sÄ…âßb'?Vó˜~ PQí&"æxRøŽxNð eFÙU¤TWG ÛãJÚÇÂ5ÉäL!¶Ùoî$™fIú²áiÇK‚™‚÷3x ˆû‡vi©(åëúÒt[_Çvó),ü8|¨xÛ{üìšD}J›ZÇ“ë0½|Õ›‰¥›Ž £GèEMÕ‘Ëö8ºM ÚbL¥ß§†~ËõæL:[ÃZ…Âõ¶T¯ÞẎk%oh98Hã¼Gs™¯6d]¬Ï}b]ôgƒ“r\&¸{ÿ]|S°™×ߣ<›'-Ô†f³ÒŸ§€´¸-)õ6´w¹²‰Pf>SþVA¼Ð¾¯X(ú¥çëBOÌ÷5°ÿñ–è#8ÐhÄz|ûÐí’Æúáqœº +y6ÂmvÇù¡za‡0zñ'É]8²tâÀs°¹,¾v_R6ý—[Ÿû±A¸CÞ,ŽdR^7#n‡7ö¼†ŽÅv¸ìWʰÑÐë¾qêvu¥nëz®9¤öèäàÉáQA0Çsc¾<¥P†ÄºÈ¯#6ü6à}ñßÌuì'òu¨‹Ç$·÷µS×Ñ×ý)K®uƒ)Ôcɾßz_côÈ´“×™J¶K^îå`„æ«äôms2¯}ØyܬPÀ×QçBXýÎú¼¹í“?ra£òZ!À-äijI3å‚ qîv/Šm–¾´u{Rëc="h{º¸Apçw[ekûvËä±Ë„uÑn' ÃkK6$6Äút¹ŠØ~º­ÓÄeª´N~µÇÀ†F˜óá%Í41NPf±Rûc¸ò‰Å2yÇ#íèÏ\°Ôþ"Åå^«x,Úqmâë$¿lI(XÐ!ôí±¬v(ouÑ ôá  6%vx›2J_G½ÅP}Ü’ßßçRúëøµnÔ><"g‰}äà#u[ÚqM ÆA8C×·‰XÜß:ÑÑKl,>»‹…u£Óù8EcYŸ²Éøl*6Î5Âuƒ•Ÿ)8”ê·ÍVëzÞØãAê¿/A=wõëý²éÕ 7)§N+W)}A *N„÷âÃëI]¯%¶lü»Å¾â^a=ÊÖ e±pm;ëx\—1f¶¹³fCÄjÙ ……¡Š?[m+²Z&ÝÔÉU…æ‡Î¼ƒš“7ek0‡X5X’‚Õôƒ;^oÁ¦'ßR™(ƒÄ†-UÔˆþ¼p¸˜)N“ESd”Ï|–Ÿ)ƒ^63qÉû KåCЛŸÔÐ&Žco¯tJ;˜U¤°}(ìTv[Q¶×ËBwZ¥Q+\ðè¡Ï„LI¡VÐØ:*bÇµŽÆVТyôùað6‡"GA+¨]E…nYÕzS†;ÂÝ·1kOÐŒ?q?ú[Gœ¾®rñã§÷yc­¿`˜èEÿ-âPa}N9®È“Ö]Êd„×âcÄïwgƲ>e½¤cyIA=?âyxlÒóú¹›óÀ\Ði½;ã—ÔSÆüyzÁôÁÕâ#qX_ì*Ðk¡¯Åcq—»}Ù„ò¸oèÒ%êoDÞÿ°S´!ÏP99¡ú³ÒUb G#5Mm:šÖ;­7ß9êÍ’ O÷ÐGs •|"ðSmÝþŸ…ÛwN>ÔÂ]_¬´M‰kŠÊÛ 0}ã<×¶~ʳ!¸æÀàÑ™Àf£ÎŒí £l²ùZ—8è9Äμ¬8CÄ›?Û|8Œz‰ÆˆÇ±§ŒÁ|¿!øÃ@aažÀMo€°Xs/¸&ï¹óxþ/αâyÁ´Ö‡OìcR_;ϵÛ*ûIÇuÁíÂÒåáÛûkA)ÉðGiÿVv[žü…dFh¾JœÖ¦DøS­ç°‡_ëŠ;9^û_¯ÿ9wþ¬P´¢,üDCì)ÖÜn iìi*ç|âšäIÊyûÔÁè§^ÏuwÇô”Ç3÷SšõQ¸í{b6…o¨ØîšššŸ¯)qÛ¸<çânìqHcÝ<ÏÒÇ…ç­l­/8ìwøúkφ÷žî©GtV2Ò]õøú¡T/ioæ›…r0D³Uâ°6%¯?ª'O KÊ‹ÂÝ÷ô +ÊZÙ]D =iý°|ö»á-}K¯=ïW‰£Ä梃 @–X¸‚ßù)€<–yp[vˆ˜.|GcÓrÐÔ%ñ¸ØCà"ÖYsUóó%Lk°`üÖ6/þH cYïç‚Oõq÷¼•Mž æ‘ÉH<'ŠÒ×n‡_Þ<¥¬-лHà3ë穃öö¹}c½¶Ó¾ãš†÷]öˆ#Å¥ú¶¨ÿ×~š~e3(T·ö¯ž¹û?Û=T}Ô1|øâ¤0UXߺ˜Õ+8´Í‰þ ¸PŸÇ>êz}™G/¶Z+J…Á…›é5–„?x®~—]ü‰í¥‚»ÎÕâ¯â÷bœ(¼`,RCê¹¶/]çt[Õ½ xÜÿ¾è(ê÷¥¿ûPîk>8ã;å±PŽ­lVžHr£líä[*±^ÆåšôZÁد M$žG:ϼ¡ŽÃ6nãq8øèÆÇK‡mñaOÁË(÷%M elzá-ÅÞâ qЏUpx>§›ÀØï„‚{ûÉ‘ÅúÖ¨lšÔ°9²®,ÿݦzY»ÎLÁ›¸ Fnßsˆ6iï°ÉŠvá™{Ñ ò6_:Ó\aüÌ?Ú.¬Ð›Í¾21ùæªËY?ìlÓ¢¿ xr£…a‹_¿:.“µÍ5˜µœÖ5Tþ|ëPôÞá c×PR¦ïwÓ׃y™I¹£8sÅ/Äy¢—àÑ}¢ø‡xC¼/´®‚€T¸¬¢‡`c¢ïƒL=µèò&P6‘oèç}‚ºú„ÍÅ|Ï­¯Q¦Îwdla“’òRŸqœ!˜ BêCqhZ<—*˜'¨§ œ'e ùg ˜íÅtÿA¿å¢“ØXð„°µØ's­$Yó•ò&$6Ó>«¸$ô:t]'|§¢$\}ó ªšæ¼$à ‚¯cóë?_½ïÕ ÉÁ£¡Úžà´6/ztzT¼Óž ¥ü.µÔKßËù4áÚZŒ›…Û0”.^¤Ï¡Êôc+ªj¾ˆ‚Ø\ó&ÖCÄqâdp7Ÿ"®Ô¿+: oòŸ*ÏF¸Vð˜éÇ\EÚOl þ#²Í{À›<ý4›ä‰äJ9Œ¾™¹F‡õ+›3ÙMš¶ ûÅ¡Jæ‹Øc®ÌTA¹lp·ãÚÐ&c™®_ƒÄ·ÄDá~ˆ#Åúb¨/vØòˆø£¸G°é9LlƒÇªgÝ>¡]ïþaÏ%Ã?Ï}!Tl1?/Ó ó'Âü©9é»à Û%qzÅK“jãF5mO˜ø!ú Ásç·çè;—ï7+´_ E!ò²=¦ñI«b}m‰ÿ®ª/ ôÆßó/=¶éÔMO õÅ UI°^Æ~À'1Üñ$6ø!âÇb'ôO‰»ÅŸÅÞâbq´¸Tœ'ÒÁA«ªDÒãSèñÉ3M®÷åàyMŒØç1²éRu«ÊîÒö¨À&6o² Jû 6èáP=ØF›ĕ‡ë=·tª¦I?R„úåâ 1Zœ(®<ô”í"‰Eâ|Á0>0Oviÿ °’|ÏAaín}ÃZUÅáÉA‹C¯#ßÕÛ- «7&ÍÔ’ÉÑCÂwO¾¦ãÿΡüᾡ¸{YØûÅ'’¿u¨iÐFfÌo£ÖEf ß*—.…å}¿V…=÷ú TꛂªõµàÅ|õϰD«¬\T=×#Tükh(£ß³Í §¿ýj¸F¿^¤i¥VŽ €d •"äíRÖ™GgT'k®”;Ár®8UPö‰¸Q ‹O0‡ŠÁ'ÂÚâqˆ@,öº^gþHü@ð<Ž^(xô|G°¡ ~Æs_eWÊ©/¸‹ÎHÜÞmhw™ø®àÎæ'6ÇæâÑ0¿_ˆóóoŠ`$›%J™#wáûÄ÷Å‹b¶@ìã8uyÒ óƒzt»yœ_Šß ó{‹¯ ZBìø±¸Að„Àû2øœÃzüÖIj‰ç‘ÎÓ¦Z±sbç9}†ê áò‚~¡jii¾²" ¯^:|º ëŽZ´<ÜÿÊ”ðÑÑ¡ý´ç’ñãq<^>møò.»¨½¾Ä£÷†ÉB[]O­?Ók­§_œ|+å§‹¾‚×êlR6A[”Ê‘uEGARúuÎ@)‚qxÌœÉÏSú€ø¶*‡öè²>뤽/pøpí–I{dRʨc¬Çm9ˆ<ÆA'Á ­!OJ ~G¼I˜ câCû“ߎ0‡OÅJaû™ƒç„ínÇœ±ìe“MË5ºÙüÃóºR<'˜;\+.ÊäRŠ0¾õZ'þAöBCJ=Æ žÃ‚>^ ìì.N·ˆênë‡uº®Ÿ¼¹HߣÅzZ¨>ÿÉPÔ¥g(U¾HŸ)hMßkˆ¼Øqð•ð8¯ ¬³?@)ÿÝw Ê;+ÏB"‹ÄÇâ@A°Q¿ˆ7ဠO@€ƒÓŠN˜õ¿«2’»ˆþN,ÔM× žz „‹õÒ‡².‚Í ì'èÇf:YpG‹œ 6ÌÃAèTEMúÆÂ5ºÁcáKæ€ílnì;Npphq€Rø›¹¥Ïþ ¶§ˆ;>„gÅ¡›– Š?žzìW·ÁNÖ»Ño¸¦œzÚÒuµŽ”ßI,—*æJu´9QðR»tƾO ó?ZÏ Oo΋Ƃô,AA”» êŽ ‹G{+ ˆ= Æ£}XºLÞ@7úÐKlt€)›|ÑÇýJiA;X¸Ol/6zó³ÖË´%Ï&BÇ'Â62l"v~„ú†ÄízN¤àqìæ`_±Ñ{ lœ'°»9ĘscM˜/¾´•Mò”(®³zxYu¡°0Vìcìàem?ˆõ{ ú€×8ž嶃¹ÄüX×— t3l·ßOTž'êûÝ~«)mã?qäWA¸XX Œ×Åäx±«`¡8Ñ +Þ(x|íÅÜ\åûê‘2Á¯“‚~¾ Œ1›qþ/Н‰ßˆˆ Aõœø»ØY°QtúÒ'-‹U@Ýv½ƒb[³Ùí:§Ì{á9Õ•¢—±ÜvΓf»fƒŸ!° üAxÃ([+èÿñšcñSq³¸M#X7üŽÝç Û[®<:ðÛí‚õY_|*–69}m¯²É<ðkoŸY/©…¾øš5ý¦àG¯çÇøq{]æ%×ðB²p,ï@•ß@°(¿œèœü†MÊ]…@ù–@O|ÊÓŽ;@÷wŠÆ¢?c}|d ÊŒíUQòîô•J­çå/ƒD/ÁXl ©?óOܰgKÑW` c67>c3l,È;øãírì}UàGú Ãú|·´¯ð#¶úîNÊaÐOð2à®Ì5år€.Úì ns¾˜*Øü¶1~]‚´‰ç¸•®ÑÃ!ÁÜ¥©g ±»IÜŽü£Ëä¬PоÁ‚yº=zN êlŇ^oÊÚ¼àÀ¯‚xâ¹Plü‡‹s¬ØM°XÌ~Y&Ïu iAw‚—t© ž ¥ì*ârRßñè÷¶8UY óñ®¸Q$\êé‹8€™ƒÇ¤ü=±XÐûãÔy—OÏÔ+IÚQo(£¾%¸Û¹Ÿõ¨(÷á°Á>ÚZ¸æ â®ùuiÏ:ô;‰Ÿ‰ËÄaâ;â^Á!6B\/hOèS—0à‹Ë6¼"† ú¿+þ)X3®æC,\{ŽÊ&Âõ‚±±ñ#.ô3^,Ö—­Qy&ûU/ˆÊ)öJ±à.ÆâSǦ#(}z§ƒAUI ¢—»îñ‰ àlAQSóÅŸŒg.ú¢w3Á‹Ít¾ØU\'¸^,6ØËÝ;ÿ%âq9X¸FÐç9ìNÉSOŠžl¸-¶Ð.†:@¸ãsb³vÒî·™ëŸ*&¾+˜‡ñéÙ_ ï(1G èEgcÄíŽWcleübC?=…íf,úL´w9©óŒ?\pHl 8Œ9|=–²µù¸Œò5N˜ôWUX™´ƒ8O°G ’;õ‚@!0Ò¨¨v¡¹#¾'æ -ÏHsÀý¹Û³,äwˆÃÅ“âiÁME{ÛêGê2Rt®y­Í5}Ùø¤i¨÷ÆwëPÕ*úщOiç¹RŽ-ÜõWŠÅ9âqµØS\(xiÃaü¨` ú±õ í|X¦Ûy\l`|6>çûâ]1X`+퀃Âãâ—SÆÑ[pp!O tfÏ=[ÝQÆ„¿ â…`!-, IyKï; 6‹Ï‰O›öÂâ@âz7ñ¬xUðúr§àîFÅ‚þº„1Ò,S¯W_Ç tŸ,6ÈQâ6Áºiï1ì%ýèËúvÿx\}Ò°ayJr?·uJ¹…±ØDRä©["x<@üLpX±a©ç f_A»Kvã+Ê©‡†„5|M¼¥¡û[¾Âå_Dc„}…ñ¼œgN[‹ÿˆí6§ïþ*JæAºÆ ÿª‰—Í¿§ð¦äŽuªà>RpÒø€…?D,ˆé`åï÷‹M‚~×ä??é»›öu uÆm¸FzÙP׉΂ ¼]0§ÇÄ3âfÁãæjA ~ìña†Ý>€Ø$Û–9ø ½¡­ç7JùsƒÅ/Ä‚yèÌ3Ûæ§nû¨Æ*¸Û_/Þè_(ð7v!øš¶>¤<'6íèÃ!Ã}@а®ÀÐÍoxú{Í^Ê(cý9ÿ(Xß)‚ö¬?v?Øy— .Ž#òèÈr—!õ~L ±!X,C¿©‚#ê¯ •´§ŒvO àÁÝ€¦â@¯¹jÙOÏÅÆ›Å6o.õl¨%bžàà" >ôG¸Æ.`£¡á`cÎÌe¹xPì-ÐÏAçÀyeWdÊ©£>)8Dè‚GÄÖñFÆ&c;ã9­¯ú 1Q0/ »é‡='e›-ŠzÚÇ1¶²ÖøòlÁús˜16å?ÌÀ‡M6»×„”kÚ¢ï$ñoA{„yp˜1nkÌEjò{À Š“ 2 âà|åY`ÊX,`á 8îj;AÏÂM{ ÷MAPì$ØLôó‚*›¼v$m©Ô56û Âž½¶°Y¼¹˜/s¥ƒ›9r Üå6󹂹ÂBÁ#îA?Úá|Â‡ï† Æä.8_Ðo8^0ÂæÀ&t`WlyæÀfd¶ñZå™O± ]ôçš´9›Æ‡³º'ÒÏ¥èƒ8^°…92oæƒ=ŸˆÅÄÇâ#t΀­Î;eî÷ üƒ0ÇKsæ’(Éÿ¨Û8•” bQXxâ|Ap²)÷â±è@Oý¯Ä2±›`óóX¸•`#l6/¨²ÉÝÔ’>—“ÖWσÍB0˜¶»ÿm±R ‹kÚ’Ú6e“<>ëˆõ0ç^+nø ^?ŒÇc0›àRñ‚p›ë”!Æ™)Ããbl›mä¥Ü›ŽÇ{ž.ž3)²µÀ/ÐXAïF©Æ¬—Åú/“µ'8ô¾/˜k¹8"“ßN)u´3øˆ<þ$eŽ'‹{ý‘üP㇜ýdAÙ ^P d£~O°Ï åŒ=³ŸØdôe°ù ,t§€8¸TÆÙ„ •Maègœ80±û¹CaÓÏe´Ú»×Þ„Ø lÌøðF%ånŒ.ätÁã/cüeò\¿,ެ—”@÷xŒoÛÒöÑÖ6±ÝŒAžzúÓfoOÀ>R¶^¡ïæõ´@8^°±›ø`^ä¿)°É¾`]¨w 9O{÷CÀ=‚~Èyàð5]˜ÃàÌ$Xð8N¹ÍËÅb±D°pß³O±¸e•qE&¿«RÚ¤åœtAæš`y_ðèI?l%@I=‡'hë99¥-ýÜGÙDŒô’“’šš§‰2å9ž·‰™‚»ôîáPàý/~æìÒºioaüئ8O{Ûçv§¨Œy(¨§=2Q8OÛº$›¹p Ô'ñøq;ÆöøÌó[™Jôs &Ûâ4m#>¶ oOh3Êãü÷tÍz²à0p?ekó-wªlÂ<Í<å/lö‘âañ„8[<#vŒ±‡xG\/f‹{ÃôeŽ´Ao]ãÆv²9Êâ¹½ kt³^¾c*›4´‹ÛRž–sRØÓ\Á‡ÄO‚?Ì(Á_Š>[úr·ßBÜ!ž× e9 ÐÇ“ zžû úþD ŸŠóºØØ¾[z,Õ :y4ÿ¯à`æ±íøÿ<ÁÆë›)÷ühçu™íÌäJ<žSôY°›õ"8àð÷¶‚9Þ*Þ¯ê,‹”±ÖI}ï|\GY›—Ø1mÞØŒ8±³Ö”Öl°¸Ì‹DwTЃĿÅ:bk±T qòÍ‘X~æ:NÉ;¨ÈO$—‰U¶±™ÏgâÁFBØü´]!8Ôx­z–¸[<'¾!xOÐ× ÆCèCRÛôå‰Q‚òTMSÄ`ÁZ.°{ìcRÊ·¶Í~ eœgc½*Ž”Ç~à:÷gÓÒ·)bÝŒgéý‡ˆ~b¿. Þ`æ}°Àfdæ…ÄöÅ:íÏšVùŸ­î/&w‘v‚ÇhŽÒW°ÿß=åܵ:e (i3C l6ô —Eþ@|M Ô‡㱸ñÂë²N¡Ðnt°1щÝÜÁ»àe l)Ø8ÝõÌ9ОyиËÚFæ`ì©ü¿ó6Ù¹‚z;ÛOÊü-l†×„uãëá™JlÃ&À>ûÛ™ƒçêy¡2|>R0Ï“¾±ßñÛTSRÿOÚÛïÌý-m¶ n.¾-æ‰úІ9÷ æz–@¼N¼d ?‡â‚6ãà_ÆEW^ZÙÞLÞH,&CPq’³—ÈFÒî;‚ú炚E#è¶Ô+®õŒ‹Æ ‹ å†rÁà±lt"ØÍlz쮹3} <7ì'ذ‘¾Þôä=s¿\ÌÌx}Û_sU÷OÆúiTíù8µíè=Wx,Æe“0ØçØŒí\{¾ôe® îªo 6V¶CßÓw†ÀÞX¶GEYÅóÅ]”.ŽÌc€ ܇ü`q“ þÁ\¼^Øuª¸CP0ýXlÌK+z‡²°8'Œ,ˆ7‘€‹UÖ[h‹ýÁ"Ý#ôLèbÁ½ð´áNÁK˜1è?Zp÷cóÑ׋»Ÿòˆm#…ñ¢¯@/è"øx2™ z b=±¾¨Ôc¿–þŒÏ5‚]¼TxQ`7Ü)öí±µ)‚íØ›M<7RæƒïÆ»M؆G”ß[à_ÚàGRæÂZù.Ê|¯”ãêxR ód¶‡µôBly‹óe™úûàÅgè… ÄbøH èulŒ?TüA0¿û:°O· êÚãÛi[¨ËK <àÅf¤,AÂô,ÄÅ‚€ bá'(¿A èb‘½Ð,ºHŽ˜ÅfQ½‘¼°¾>MuHZ6!èuðtØËFÀß ±|Ÿr>o~Ƨ¯KåoŸæEðþLXܺ„àl©`û¦ÿ’÷z(›´3•b\!8˜æËü™'ó/x*ã`¡ßцCƒy3|ˆí^'ûÂ~'Å_³Níé‡Ö2>Ðõ÷v´uRÆÀ—ô(~+8œŸ´e}N· t ´ÅVÛ‰ìÊK <^XœzgFAC`q7‰Ÿ¸{²ð7 ç" +&^hÚí'‹A‚Œ•€ ¸|XŠË¨gñ°<ôxóàØL°{ócOÁا oÆF?u§‰©‚6p•à‰Áyÿ°¡F™zo ºšUsD¼>¤Ìa^¿¶õmå¿/hƒ­Ìw#Á¼& ÚñdD?æîC€MÆAà5°_ñqz-T”ˆ7.õŒEú³†ºãÅ ‚±bûŒ×•±X;bêLQ!¦ „5¹M ƒþØŠ—1Ða½Ê楩ðbàD‚’Åp±|8ñÀB<*8±"èï…°NÕ¯u£ÿ\±X|]x“¦ƒŽ¡/ ï s_lñÏöb;6y‚þ"±\`§vGåïKAö„À.Äsr>)¬çóÞ§žzWÑn_Á|ê;-ö+)þô±‡òØØä·›jw±Lœ#ðýßþ&ÐCžÍ‹OÓkøëçb#Á5ÐÏk@_ë`-ö» ž>H™v[Èã_ÊYcôóTÂu²ÀÞYâGâvÁ¼h‹nÆr¬àëŽõ«8/ y‡¥‚ÍÅb°˜k î wÒdÁâ%/„²«ˆõSÏBÓ–ÍÈ‚^'ÐË¢2ãR^`ì‰q©ƒŽþØJpûî^ì6´eÌ ‚»úù‚—”qÇù½`|„àNý#̱1r`cÕÑ&^7òøÇöž¥ü\Á¼ˆ_ |ðª Ìþ Å_› üç5ÀOéuðZ°ÑœÇÿ´ó°~üPüG0&ýØì¶[ÙÚ<å´·nÖî8ÁÍ…þ·gRڱΎl¡Ì±ëWq^êó€ŸÂ’7Ábøà‘’7uXß-Vžð"(›¼iF`¡ñ8,0íyJ,ccxa—Àr²È€]Æçà³­>Ð7V`?v0?+„ƒ‰yüGì%û¡æ*÷? Ú–Š7¾mG§ý»•òÿÌžtcĺ‚?á|oÿãSðà»Øïä݆:|ÖÁŒÄÆgâ8`£eN&Cl¸üñLºÑÁMâáC€907Ö”ñh‡-”9þâƒFÅy©ËÞ”èG:Þ‹ÊB(HoAÞµ›ò,ºÀÎWQVñÀffñ t](›…t2>ÁñfwPÒŸöÁŸ•g=ñ;w[o^ûŸêY®IÝÆ}ìw|OÞãݤ<ã½*8h(·}Ê&yÛì:ÖÂñÇ!ÅAr¢øT k° Ü6ÐÖ‡saÞèÂèn3‚AmEì/†å4®Çé‹Ådñ‘˜&‹Ä뢒 í§5W5 ±ƒ.Ž,uóÒa†X$Ž{ Ö‹‡-Î/Ë䕬4,þýÂÎØôA7wþâ%Ác>ÙF0× Ï·\y§±B¿Æ›å›ÂsnLŸtætkº0sÍÝ|FTG[„x¯ãBOn}ÅÏr¬˜-žû îÔ´ƒXׇæÏ›r¯‘ó\ÃAÌì+n£ëŒ^|§º¬M=ð´ÆáÐE_‘^oU­^Á/Sp€€-äÓÎ!(xÜFp6ds}  w©@hkIë¶^ÒôbS†°¸ŒÁõᢇ`³/iq[Ú{ÜÅx}ø˜X ÎSÄ8N6t…`ü;v?.8l8 ÒÂ’kñÜÓãLWÁÿK¶Â5þJƒ ø 0gR6Û ‚ wáž‚výD7Óy½Ç£íhó ýƒÀÆÊúþT|"Ð93“[@IDATÿHé±®H÷Gó"­onØF°öÉ´%|WP¶Q¬©}nVìnHX§Õ)Ž Çë„*Øü£…»Y#|‹ ׎!âA ‡ò;Åó‚õ„+ÄP?Žâ<)úˆbˆØ ±Ør’ .ч=ôÃVì¡ÐÁšOp@»´C§Ç¢O<7Ö6>t™;a \ ‹O޼¯mm—‰çodýRàlÚÅ‹÷¥Âbà@_SF¾J,{ž*¸æŽqM&¥l;ñŒàîó;q‡ØHô[ î*#w ëg±m?eÙ H±«LÌÜe>Ø€P×ù­ûnÓØ~‡©áücûw…7aÍ[­šùÖMó¶ ¬çxÑ_ؿʮ’çl*üJ»­ÅÁ|NÉ\³Q9Ü Þ‹Ä1‚±"ôdí¸£ eˆÇt—sØã÷‹#×Ä ¶ ´µÎtÞú’†kâOˆEdÂl†v‚S/ÞÌ,:8ø¦+φ9Aà,púùdæTæÎÖ[°pEw?ħ-ºè0æ_ HÿgÅw‚½ŒI{ƒÏbn(èÔsòܸ¶­”}3S×O)cìš¹ŽÕ$äÿ—ÄÁï§|‰ïOÒ=þf­ìoüê¼}ÍúO°Á?úÁk¿…ò¬=kÄþ!¹K;&‰%îüÄ0&7%â¾ØC[ .7ŽʉUbë#AŸ¡‚8µÍîãùc«}Ã>ʉ0@®£!ž„ó¤®c|ò8ñ31[ôGˆë„Û‘fËÏW9E܆PL<|_\!8D¶w ,Ú€¤\ƒ)mHm)‹8X¬ÿ"‰øŽ~d¸è!è×XaÌ—„íhl?Úœ¹lË&éùyþ´%là¿MÜ ð™ûz¾ŒAË•Md/ýdc±^<½±©¸û¢›»=ý_'ú+Øðw Úœ%Øœ¬“2£l­P† Ðe|­¢ÄvÆ¿Oœ*ÞÊ0B)1‚XG:µ>Ò5Bl0ar,&››€ãtda89‰ ÎFXµ»xD ôI÷c±8‘aØ@°W‰A‚rŸq8½6Ø Â ù„òû[ ¦Øv®m?D}§ ÆóÉmûËyב2>ýé{¶ Ìc)[+ôÅW«C˜ Aϼs%fia~ŒâÃþv¬p}½ÀoÄþÅwö1)òž`®cÍŽïË]™Œ×˜ÔëÎú Ä1䃟z¸°M=•ÇbùDñG}ô%ž³Ù‰­ØG=ùÃÄs‚¾<PŽ!ôaã2|­Ù|§â¶#±SãÍÄb'°9§áP&Ïbáø=Ž@ÜÇÎÁîÇ#á‰g²xý åÍÄi>OPω~²°#½ð*ZEÒó`!˜Ã¢\ì-°;;E×”a30ÆïýºÓ6Õ´ÊýÏu›9˜–¿¦ hÃæ`®iaCÅBÛ]…ƒuÃgøŽrn · —Ùç¤>Üíobƒ§¸oü½—@6›UœÄ~÷z{³Q7VLÄ Ü)F t—'‰+uÄñÇöÇñË5vb#ÿ!Aÿ_ æG|û¡/íð >‰ãfuÇŠ†oXìD õæg8‡É31„X8òë îl~îü; ;Š4çe;dtàÈ벟xTPã¦A¢>GÆóa<ô=,âÅê¯kæätP’ÒgR¦Î ©ËVt(ê›—µ|ÝØt@ª!cqà¦Åp\N[:lÞWðŽÚ7ì÷ üGÜØ·qÿÚ×è?B¼*èõQ¶NqRü‚ŒƒG‰:I¹©+.”Ñ6ÛÚ§ã;™m‰§o‹¿ tpˆâ³xnøÝøÄ”íUQÛ‚vCaI‡P\\J ‹c1œ 0i&Å¤Ùø†Í?@0ù2±•Àa^P/jÚ‰8}ÖÓGyƒž'2)ù×ĉÁÙ8°©Ž£}”Œó3±Xl#˜“má)ÀAà@Åß¾ûoª|cl`C] ïËæž–cUÀbiîabÌ@ž@úŠ8HðžËÂqDjðµý=Pùo âé‡IÛZSÚðO¯;-fšo§ô.A|Áû™TIí¯Y¿qÜÆqíø%VˆCÅÕ}÷æNl3?ÚÒ×O‰_ŠÛ…¢Ž]Ba·>Íž›T¶‚ Ý19™bM, F:p :wOîL–GvÃæßZ0é¹b3/dzósN;á—ǽÿ ž"Ðw³X_ ´o‰80I9@˜cÀ-‚dn€MÎ{ñ°‘¶2s¸@ «¹A©®«UnÌ2>È…àâ‡uf md¤xOàG¥´¯Yëï‹Ûmy @šâkÆ=MŒØÁZ1&ÂZ¿¬9r©˜-*ãýF\%n´£8n‰Ç91ÂhÇ“À…=ÏüÐ]ø èÔ¥gr­¢ºeÄ.IŒÖÝ µj6S³Á·Ù10l—p×€ÝÃâ¾{†ê¾{„ê vWª¼ÊÊ†Ž ¶Ú)Y”PZš<îwÓr°ù÷Lö³qÎ;‹2œc‡¿¤¼e27 tÀÄLz‰Ò zX8ô NœZŸ¤…….èç ä@žKÄ΂…òÁFꃀ,*E?á N£ª…ñ›#ŒEpaSC’mŒ ujåzìÅß>p9¸æ®ÎSÔþ‚µe>i_sýM±X\+Æøº®6”ãÛBGŒOÝI‚MOü‘®Èäg(å)aͽÉ×ô'¶|POÝ!â§}ï&}×Ó¾YW×aÛÃáÚOi_-õ^c¿eöÚâa»†{Fuðþ¤_«Êðq5'Ì–c¹½ö ÕnªÿØ+”O, •ÚÉUËx˜.ž* U×ô+Ý4¬ìùµP½ÅöÉïLCQIòYm&ù´`ƒØ!¤€3¼ñítäTñ¶ ÿ|qŽÀ‘ eíiO‹BÄA‰î^‚͂юàã àäwž~Øô™À>ëIÛ¢ªåº[´¼Á°,*² YšµZ¾‰\üÆúî*æ6Ù@q˜ð!€ÿɳù/øœ'@üÝ_ß­v±ÐÇͰöÄ'rŒø`<ê=Äý‚2ø—øš@ˆ âÛ:ÈûÀ~ô(Ž+tÌSZ²ùá öÏw6 W÷ åO–†*ö×ÇÞÕ¾{BeVÝÁ›…ÊžÚ—[ïœü…gÙˆ'‚Æ8Hvè´q¡¤¼:t_^ÞÙù£PzÂëz­_ *¤%…š®•1ó*]éGñçŠ ¥ þ£tKÃCÕÕÉëó¹jÂFJº(EÈ›q‘ØAàl^+!,wú§‹Ðž¶ç‹"ÖëWU"˜K9Bбq?.K¦¥ë* .£Íû‚E%xp?e“—7Ó”ò„CPL]q;]¶ ang‰_ Ï÷Ë2Ì>öAÀ56-. ÖÚëKºT&þ ¨ë#è¯òwµ!caL±=¾fÈ*ÄΦ©š†Ö=Õ|•K¯‹íuÊøØÂ\fŠ#ö$Š¿ ÚÞ*ŠÏT[Ôµwè<`XØ·C¯ðó{Ðã­f滾Ú4J„§ï.å¡ð.í×%Åá¾w‹Âî›î\óò=­ë”w…3ôë½¥áÙ&*ZÕº´.×Ö©åó QKÇÿM’aÃûëéQ§¯ZüVà[á'ær¾ø(“çôGhGÎÇÚ7^(C(‡\ˆÇ°nî6c¼ ¸›€t)³ÏñC,Ôeƒ›}‹³Do1V¼¢Ö¿ê=$¼øÛçBõ i+iÁæ—¾DКŸ¾*–‡É¯?öÅÃàÍ*zô/ÞlL³^Y¹ÛÇ¡„¥µÃFÉjË Û,¹Ë¯­—?—~œéàÉ6t¶“|5L/F¶¾Í)Co¬›~H\ F ©)Û_°Ðcó Ì‡˜²uÊ×U³¾ˆÇ¨³qª‚C'í:×Vm±©¹+z®úÚFô°! I¶5¥ZWõSÇ&çÝq|ú`ú{?]‡]¬;âyPWŸß“Æú ôáP™²ÞÀðM¶G •ÆQ CA‰µ©²¥Â¾;7÷\úÞ)ì9B/écuûú„P±¢(œqä´P± íÖXC3ó 5ò)¯†ÊÅ=ѨÐo˜v åm"©ó´YGô赤šò¸cèLJ>RÊDzT™ôF£mŽŒq“ Ý›‚:K0:¯l£…××Í·ÙñaÅÓ O1M‘OÕ]±¤¯ãº\äñ[ìwn<p8ž)XÏó2×JjýÌü{Ñ{ãõ§ýb†°ÿÒ6¨*„˜AX{|F{ʼæäM\W¨Ê·Ûjë°×.³kÞP»œ†^¤×JÕúœAõçO±6|•AåÂ¥%aû‹CÙJOa•­sÁï7Gê„)¨ £Š8wk¯í(§XðOÁ»¿Hì@ò`q¯§+ó’ðBR~ ?R2)º¦í,ÁëN^RX’8P<.íy x_tßk íèçö”“¯™1Wu AÞ\‰çŒüÀÁÔXÁæ9"öAcû¶´þ±¿œÚ‡ÄòÓâØÌ Üuµ}’5v[RÚ7fó«Yò'â¤úÖ%ñnC™Ë)sìÆ)ë_'y}2¶]uû0~ħ¡šß²åJ>“îÁ‹e@u!Ckc/ëÌD¿,^k%/Ís'|Špå¡ZFt«\™8¡±É©óiçQŸC)‹…MàSÚåW:¥c¢|:ëq\ž¾ÆE0]p§º^ÌvíígÛDZYT®lV¹3K©}’¥ªÁ"!ݸ‡ .I¦‘TuN.cûâ<ƒáSʶ~c½L™ýB}Œ.”ËlñÅ^wjþ‹¦É Ø¥"Tè3ë80 C²Š>æW NÕ¹4ŠO–'Ÿ(,S¶Æ)5‹Ì]ÇN˶ù)㉀>î§lÒÇe\ÇsåiîÍöÔhùü§Ç¯"õyôä%Dº½]IÊ©¿hHЕž4š#øƒ×³¶%Ö1W§Å-ÌÇ/Ÿ«Êv‘:÷Å~Öo€8Að>/ó6ø¤®¹©*Ö qZsÕðOÛ’¶Ëñå˜sêv¤”9ëµ» UºVë7â:6£ù-x@¿~/ÔgqÊd„í®ÓÕ*Ã[³:…NXœ+)•3t/Ômf†²8‰dA'6“‡·I×Z¯—)›H|M¾¥b}qŠNœ<þ_,xÒ%ú 6)s±¤ûÒçÑœ;ì{êGàg“†^2`ÇC»[KЙ^ÖîÌhÚð„±[TFžÆ|'§Švö y@ìkòÜñ9y§þA,Ü'vk ÷#vœW6tó”æ1jJ³ÿôØqj}¤ÖOoò¾vÞ©ÛÒÎýÉ#•+Be»åaÊûC17Ä\I'Eç,½Ñ®'€é¦v$Œü¢„Šgž ÷¾ÝUŸ¶¨múÅf--!Z_Ô2–­ Ë—%F±0Ÿ‰q‚ eçÅÎ%pºˆM„ŽUœêöqJÞ(Û$¡â´æªæ'eÜyöG‰‰â#}|˜…MŽÄcÇùl¯OÓ©Fê?ê2~S,®åÞŽ]âMÌg‹Êx¿$Ö€(â 㑨Œ6'ŠO2eø†õŸ.È[â<}Æ‹óìmâf±±xRèm®Dâ>™¢Úr§ºêiב‰ã¶Þ”Ú»ûÒ–¼ÅåË> åsŸ ŸÔ[§—µ¹U+¦l˜×µŸõkÆ—ådü˜†~A¦>–<úWÏmfÏiªX•\ˆ>îXýž¶ðë“ôò¥µwslâÝØa o§‘G˜SìhÊ–~mä¶q½ËœÒ¾1âö¤ÖG?–ëCÁ ›™ƒ€ÇÓãÅ1\¸¯²µv‘¯KöP]ŸÔ&¼!V»¸Y”Ô×7KóUŠ˜K6Û8pxš‰…¡}Sýƒ„ýFÊÝž'=lgm¹ãóë{b¦˜(¨?[Äc¬ƒ<âù;­)ýügº¯94ƒõw àglAÜÎiMiÝ?ÑÁS\IUEØê½ê0N¸³tn©^ ¡•…ÉN—ç>Ó÷¥'‡ú,@í–Æ¬²éØPÜwI¸ôú Cek~2©v0Mô}¥Qûé¡Z¿¸Cå¿„ž ’\×Ì6Ç©ó?Vßwn¤,[JYŒ.³Jº ×¾«»þÏâMÁk^Æü›è ®4´óžÖ«¢ZyG¹æ¼v¶‚ôFtyœ6F?-› ?}Èø!-ž{º<Ûµ}BÆö5þÜFŒŒ»«øn&«R>ˆ5C¼,N´ñº£ƒMJZŸx,§ôgƒäIõþyòŸÀòôEÂ@1^ “§×úæÍø‹—‰ãÄAâ(} öú#ÿN÷õ U¹øo¶ßÝ?,ןŸþÖ Ï*/O Sž —<Þ'”èsÄÉ_%emØŒÂÄCúqí} ý­pXeeòŸ7þTª&‹ß‹#EO}ÜXYì` –•"âÅsÚy¡”­íë°.—ñ*ÅyîaáC€òa9Bp÷!ਿZŒz°KÆ$€NH£¢U„˧˜l‚- I|¬«íꪈÊ9HÓ±ß÷Ù^ºÄk©ir¿ÔÞ2ù)JŸü~µ#n´c¾/Š ñqŒ ŒõÇ_7ˆØçö;iÇó{G°Þ.[[ùᢻ°3•Ÿ$Êw*®ü‚ìn/¶¼\b .éWm×}8-üßF²[=[ó)Š¥óî¡ý “ÂùS_õý£ô"«ùç2j—Ðaƒ¥a¯“¶×W®ÈÝžÕç-šžCï+œ©s½Û’ð«µÖMœØGŃÁA,Ü}‚À„ó‹EZ˜£…v\3 àøçÁÁ\àˆOxÊhKŠøšEßGpW¡Ý»‹Ža£¾$8õ_cÓžÍÎ]â$A9cÒßv)»Jžk$þš’Ï4-Æÿ¨Jxm]Ÿ-PѤ&öK:E ~„=Å^‚»þõÂëåCzšÊ.¹8Ù]Ôà,°Ù>à‚ ÷ÃU ãÖÈMe;¬DjYàÜܬ nf¯Å“ý9ðÄöÚöºü^P×–;Æ[`Y´‹ÍomQŽ‚î ñŽc_…¯AܹíÒX>êJßn¨p1Ëœ‹ë¢äÝE5hÆúð…iŽCx­Í®Y_8¼ËŽËÂß'oìè{7¡ëÕº€:¼Ž5Œ”¢¢ý-¤®WÞÆuŽv¦J¤uWuvi›b*ÞŒ·­@{¿¾ü9 …€‡Ç~ð(t§çìÞN=rúÙ¤ªôòÙ¤šn¡™K"NÚoâúñFéc>ÔwßÿH—ŽWÌsuå“Z 2.ž¾'íóz÷4á„-Ò<ïÞ~¨`qÅ×! ?–æÿk4å±qÅæ¨*éÑ^ÓS[çß{ƒå§Ázp1Ü :Ó“S'»à F,6§íÔ%êw!<á½v6˶Œö4D¿H)*‚ï2Ò™pM隤^lûxXy@¸àÑ?O)^àSƒ^7&êÂäL*=H%ÎkQ²(¿.ªÿâÖë“<ïú¹>ƒ 'èÏíà—`ìX/®©ñ£Ïÿ¾9~¶—õ×ÎÆ™©m¢Ùú¼eÖ•‹ã:¦âÍDŸ­ {Âðø¸þƒ“Oº‚ë¤ýöSâÓêžÏÝ—Ž{lù4ýŒÒ÷‹ûfqÅýÙ¾?Ú8Íyf¥ôî“ãÒèÆtдiòÔ?ÒfouKøâö¼ë’jº0€-Ì>ëü(qgÚÎî˜j¿Â»¯sšûô]Åçó7§Zç;qZÔ;¢èƵÔ y.¬Càfø \WÊŸDêØlHÔåMÛ©×1ccÚÆ24ÊLmSÞßrÐÞCÉà³}²Åû–Ÿ¦JØ©eÚ¿n©ÎëÆÄ»ÔÂä‡Tz -+ ¿5e|ƒ_ÉýPž¯kQwSØ„‹ïÁ,˜yÌØÏ»îÝàÁ  퉵‹Ôqź¼>lvêÌÅõ‹5t#k² e_†ïÀ5àš³‚º€·ÀX±:”Ðë¼|êõFtô„Ò /—9`;&Ö9Õº×æaY¹Q´­ëÜk¶Ö…/ìÍ=Óͼæ÷PjTœp“åéqé(¾cl«¯LŸ´yª}a¥T½ ú[߸ÈñÝÌ•áÍRõ©§š·ç#ÿíÒ^/œðƒž Àþ Bœ‹ví\*ÐažüoÃñ`ý¡àéiÐß €Ë¹h—/–õöUÔs¼uI\[Ÿ‹¶þç[A`DZg“,£@JØ`ªî'à%°_Cíª+/kLGÞ®)w÷°5ï·°¼óØ 6ýT.§— bn¦Žiš—Ù¬'ècÓ=@ÉçézøTóox|Ÿè&ÐÿÚ庸±6‚8hcëÕ×dëÇ7¯XxmL‰öö帀 `Mð`¿\Ñï1·Ðgªý±N ¿ìôÜi$ìtè¨Ôî”MùuyÏTí~â“| ì3÷œeîÉ+¦êïo–jÞ6Íâ¦;jü=Å«B]ã5Þ¢¬fØ˜Ô‰Ï ÌÝxt:€'€c&wNÛoÄ«wþp¨†?íå§™>씺¼Ñ#uxjenãÕéa>ç| ¯A~;h³Ôuâ£Åé­o€½àWàk¡«À®MÖ»ØÞé˃ÑzÛM|0|\èwàPçdðÐè–ié²HÔi¹iŽ•Iˆv¹˜ïÃðEðnäâúdòG0Ør.ðk ­Úè8ê ¼È~Jœ¿ú%1‡Eµ[T½z\ ïhÚ•‹u§ÃY0=¯XÌü–´ô­¸FÃ?K×$…ûÞp* ¯c=´E,ÓÇËÃêp8ýÀ'ý¬øDé¡ /sª£¡yR\ˆã¹n¾ÙëSho£Jt&½.ïþ¾à¡çå¶R´ÀX\nxvP³CI7„ 0Ÿ¯ðê·øÌÞxT:”ÌÃïñ·Þˆ¨î?#Íï9§˜KšÒ9u}½Gjï^ã;ïã{7/âÝþ+ýJðgÊÞñGç§ÄÉ/‘ð7ÅUÏ­»û‘6ÆÀÍyLé‹'ýF“I|‹ð#Ü_,ròÛ…ü‚‘Ž]R;¾,®Ó/†¯Ááfø+èPÍÇz7—bÛ°ÙÔ6Їmt$®(ä.~Þ êœƒ›7‚l!¹¾È»pæƒß¾¤§½Áö1x¸¸ØûÀµà ¨Ã~ûÂe°%¼¶u^ê2xmý”ìNÉ“àáf»ÆÄÇËæz ° ºôåÂÆkÌŽ¦”;oç~öÚuwãèsË]/˼I\ 7„°)Ö&t¸Iõëþp¸öG€åÑÇ1ÍdëëÌ+ÑÖ~®ïŠÐ†ÃÖð…Òõ,Òq°+è{cËFQ‡ö)yº-3n´u hïÐØâ…Hê„›n7]Û§m·M›ó™þ-¸ùöòš¯é{¯Kuzäþ‹ïôT±ñ‹¶Ö/J¸Eµ[h½œi¶GÙ[?ãùÏ6lˆcŠÎoÁ/àn¸ b©*$סÃÔð$uA1ˆÄ²®¥ôdRh ÐfÛ)¦Þi"ø-[RY0(Ö¢=Ã@»—¥Äº;÷ÜW±úH1.>†ÿ…Ü¿ág}ïÚÞµsÍ\›ÿº¿ãçB×ÔµõÚöŽe_}"®Á‘°%ƒÀõþ Š}¯ë= ¾ ®¹:%æצö5ÎŽ…½ÀÃ_;çòJ Ññ:ncЙapÁtV82œ7’2Ñ:1¯óìû.t,†ý•sá °Œ…CÁ¶¡zÄëÕÁ ‰ÍïõF`¿ÁÅT´Éàq|Ç‹ÍïµÄ¢‡}>¡ØÇòèg0»yõC.᛼li󎻨{YI¬»svîúM_„¯bMGSvès7¶„ÿös}í@?ò‡ÁÃ`ß>ÐÐ`ÇÔïëwø»àQ°Ÿwâs (êwíσë8lkܔǫíÄ1Äñls<ì>åüãf™HyÐ}–Fè8Åt< ýa xâï :0‚Õv>:yÂkwl(Š ±]<©×…4ØNƒÃög€Š¿‡Wáw0 ìkŸoAŒãlO™òmˆõ:êµ)ˆrSuÚæ:pᕼyëë•Á°hûÂÄ~Mƒî6Ц%•¥ Ö˜§iø!ò1×o-ðipL«Àrû(å~³¯óòŽª¯Ïãe °.êõ§wÞua¸!φ` ØGpž?„JyãHýŽ[Jø1÷IŒ•×›÷Ðú*L†¿À$X´GÝÿg%¦cu¤'¥Ž5XuØš`™§¯©'²'®'²wkqCÚçX–ïÞat°ºMc,ïŠAp1ØW^„ÂhP§wɱƃÁ nÇíñÚ¶ê4 ,›mO§’_©TgÃy{WÑNm\éMã߀ókY%Û4¢HÛön¤®±âÜ.ûÇZ¸6®‘þ ?Åš®EÙwÁÃX_ég}¾4|œÇƒí]³#àep]]uÛ~ð`y^ëÝôǃk h—¶j»v:¶:b<ËÕ,Øßñ¼»GL8ÛFŸ¾äm{4xx<оX¦²Ì `ö:P1õ„÷ÔÖù¦€ÁǸ°ÕÓÛzQ,|yºm§¶ùÙ`¿Wà°ßA0΀[JlKj0¸àŽö¸au‡~ûÇØÖE>lóú9˜ß;¯3”ë¢j¡ò>µ¯þj™ˆ’Qä|·†˜_4Ó?‡ƒë”‹åýJ1/SËo¤‘·Î1FÃÉð1Ì×ΨDã ÄúXóÆÑ88”1°.ü|º¼ ¶€±° x \áCÇÌÅ1C´5¿¶¼ü:lq~ÞDöíy6ûhë2'ÓD‡)¦nN7™§¨p¸€ý Ú‘-‹Á×Ö7$±0‘(bÿa¸`—ƒ~=ÜÿŸHN‡ŽÜŠ {\ÔÀ2çâ]Å2ïÃJ×»”Rí5H¢iÈ¢æíššêÏsšÐX”Ï-ïvJõnœËàݼ!yËõ™s‰ùèk}sÎço›N¥:ýåÓ†í¢oä}oæ ÐΰÕÍ$^{Àz0N×à÷p'|ìûŸ ®c`B)ïS@è"Û „Í‘F£ü:tXÖ v„Õàl¸ F‚íê®*? „##,œ¥‹¶9ø8èãœ\nVÔÓU‡þ †ƒmÎ_?¸†®eopc¿®§môO¬Iø[]¢Ïõ›iø|uò£áxÔãÓÂàŠúòØ( ³áó°Y;C[´ÙX9<@Ôï“kk½6jÓºp8üls!´:qaZ“è¨ÀMï"yŠ> çÁn°)Ø&—¸vá\4ç•/bÞ¶±|èp³+‰w‡_w²a\ïÀE0´3Æ2¯¨Ë 1¸Õ“Ëpa°Ø×ùi«¸™ÅqL£ÜT‰1ê®ê®ÏŒ‹V”†yšÏÅ´¸‰lcÆütÐw}aðU,‹õ1ÕoúHqm\³µá`p£ßß…g@ywêw¬x![ˆcçöZuùšDYØ}µCÖƒ-aG8N†¯CEÃ:Ù…pC+·‚ÿì ž´+w7™u7ÀÆà)m )ç×%õ Yº,®Õ-”ipØÏ»‰8¾zŸ‚ØÈCÈ_P*·îyø¨ã^Ðnu„ž¸k¸éÃ^ûMm·Ì:õ‹óÒƒ_ÚiÁG¶Á×ã–—Kl¤òòüÚqþ¹þ¼~ayû䄽êtã8~ÜEŸ>ñptî>ŸaO¸ô>qõ‰káÚÄú¨C±¯åÿ®ƒýÞ„“@sw¸¬óf’ûWÛâ "[ˆýÔ»*D\hÃpXVí8¾êuÚêõh_¼AŽ«Mû‚z/mÍ×p4×JøQ»·ŸBô¿mµy;X ´Wý>m\ޝýŽ·|ËGAÄ.ÙŠ,‰\Ñ…QN{-œn0±Ì`sp“<.Š‹åFq!Ô }yÅ…¶­¢>eL-ru?´!ôìA~·R:>ZjÇGðkX ‚|XÛlçSwšoAíÁe[ëµÅàÓ6mÇ—ðIÌÇë€ì§ä«Ÿ*i¸@ ‰å;Â¥Ê+Òð‹©¾í–X}>p}bó÷"/›ÁŸ@¿¼n4û8oýà&Û®Y`»Wà q<×Çñíçµ~ÜÆ‚}uKC›ßM%Îù(øÅþˆn¤s!6–>=VÅ1b¾¦a[n‹óÿxpáS˵+æ©s±NÝ®ë¡-®öÚÿøhϰ¼Sºöé!|C¶"KëXØ27€Îv/åo"õp‘\X7W¾Øö"@ÔåBÛÎ@Uló8LÑkSƒ«\òà3oàˆb€ÐÚ9®†àx‰å?‡> Íq„ý ØG´{½RÞq´Í`3à›¼6¿(qî Iè1 ݦù˜Ž½hKøR;•|ó;Ÿ|9_qSé‹“À—EýáP¸ªA?Ý û‚¢þØ`åó [µQ_i“íOƒ§@]Šs¶N»míÍÇ\lc •ÛnùáàÍA}*3KùÐ[nÕYèP=ÇàÚ \€×K©A²%x7uÓDºyòÅÎÙà°Þ…³ Þ Ô{>D°5e3i£ØV쫸©Ï\½q7O~.8®AlëF1èÊŸ´Sœ—ºõE`ÇÆÔOaGØDQ£‡Á¯©ÓÞ\¢_ÌEÒÐFÑþÜÚý6ÄÓLl çótó{íZÓá#¸ |´×O^ë7MÅukŠhsØ‡Ò ÊõîÚ¬/s?F\Ä\lã\œGn»±rœêóÉÅÔþöÕwmBʽµíÒùŠy7ÒèJl0Ýÿ¦.FqF\çu±h3hø¸x¨ÃÅU"­»úôOës|Äw,u~ ÊmáP6ƒæHp<ûÚF {"ïÆ:Ëc^^Ç\"2SŸ.ܰQG¶è¯ŽœŸp:£­õæó4t[yÛØ7Ú’-ÖÁGtïÞ¹LáÂ9ÚÖ:×Ë æë¡æá{< CÀº“à1pÌyИ¯CÄG´‹õ˜Hý'ÁßAÆÜœKN”çs¢I!Ö9_×ÍÃ[ÑÇ–«#ÊÈV¤9= sûà à"{ÒzB{GñnâB{9,[ ¼“D—míç©o>'ž,ó¤W—O±¨ Õ‹û‹zÿ ÇR´ë»àKÇ‹ƒÇ9PÎÉ;§›Â»˜e¹sœKø"îfñ¤cªÝŽÁíuäÃ6SõFy´±L”xÊPgÜMßaiø56´ótÖ½PªH: n€÷ÁùËá¢ÝÚ¦DZwµèŸ¶y¨G»µk0~k8h[Ø›Ï!âAßÇÓ‹±kòuòÇCØm{õè/Ç]\{éR‘Ey@§¤^,ª‹$ÃÓÙê.²mÄE²¯i”ÇbGz0un¶Á`à8¦‹º´âËÇ7”6øh¹ x÷÷À‰9:š*¦â£¤r#øh÷kØ (9Är±®-•Yž/Ú ®ÿàMPoŒAöSbƒª\ÜÌåò(¹>çáKÑàÙÔçJnˆß‡™ö ÅziH,×VŸ(¯]ß æ׎ùH£O´õqcX¯Dj;ýÚ„¿ÂÛì—þý!8önàft }8´=÷—õâxGÀ* îÅíÒÏïÀ5°.ô/õ® Î[qž«Á6àx­Íû‡Á¯à8*òy NƒÁSÝÅs“»p.ð-0ÜÀ.ÜÉàæññÍ@Otû»–›Šu.°z Nïr¶sñí±ÏWÀv¹4Ö/æ`}Ü•œƒD{½Kz@)g¹`àiÓûðS0ˆÇÎïÄúA®†¸«EYž:žý<ô…ù†ôä}ÊónZ7²¶Dó¶óÚù … áÐ~µ€>W´ÿðÀþ (úZ5$–;ncõyŸÐc[cÃF»´é 8|¹x;Xçúë3S}¢mâuÄÈñä¯çñ'ð³?87×ÌùÇÁÙi^‘Åñ€Nuaur¾ :ë\ˆ÷`Gps=Þa ²a‹k ëâÆ\³À€u1 Ç7Í­`›\¼å‡ÄÂÀ:uª_;#p´ÿ0˜ F¥{]R¼ þ{ò¶‘q°(¶±½6ê m1µ¬1 nÝúöÆÚ†~SQ·ö¯n*±LñùxÂÖËÉ%æfûÕá¯`;uè‹…ùêEÖG›X§r_[¯í_‚kÁ±½óûT¡íÆBúÃkåcÁ'2ŸNLuØ&Ö1Š*Òœp1: |Å…ø; Ñ…öÚòÿÖrÕÅö5]`l¶= FQGù榨^¦ú‚&d¢º= VƒçhðRÜ âÜÜn6·sο¸Öq–§¶‘ü PŸzÕ¯-öñ Öþ­á˜Úô ü?PìcÛ°+úBÙ ¸ôÅÚÐ\rIQ~ècívND>ø’ã$ˆ8ÈçïË€CáZp^ÿ _%¿O©¼ràˆ––|ãäOùpFl.²åÖàâý6ÖwÓ¶}>„¾` ¸9uÐd‰$3æ¡m” Á £MÚaàºÉ Îk]8œÇ¡ _,×_ƒá`x œãî`ßС {õ>)¸N–;æâ̇æiŠbAur,ªÁv:XçBÄ€w±Ø<.ŒøšÍ6Ã0È]tƒÓÍ36ľq8^s.¨ºþJ>ÇôÚ;úq ÚfyC€›LÜPÎ7ô}›ü›à\?€3¡Üq8oqÃK\Gj;±NúÁyð¨÷ßàÝ_ÛÛ¸&¢MúN›wÛXï2uþ‡A ¬Ú]¾iôÃÒŠã¨W›´G;ôSnÚr¼ÎÉ:ç¼ÁòM@Û¡˜‡öyì ¶Ñ¿•'´¤Ä‚êü|QcLÂ ß \ÌØø¦.¾j;°Ý+0Üê‡õa\ >ŽVÃY° è“ óá#û4¦Û;g¹hÛuå…¥kõ¸¡ÊÅ>»‚6l'n ë~ dض™†NŸ Ühow87À!Юõœ nò)`½e2|œ½¦Á°œ _}áÐçaØv†MêÕ§Ún2|Ìî;BÌ‹l½¸NSë¯?ãX¢}á—ÐREf/p®®¹ñ`Û·ávpÃÿâIHÿhsC>³.æ×P»JY3y@'»  wš8\;ÀSÜ à©-ÖŒÞÜ<`°½ l: J~y°­ý¼³:ŽcŸJùbkÓ¯@›ímLbNù|S›>‚¸k9Q´Ïò˜›vz-Ú-JôuÎÞÕ•~ð3P¿¼ ǃ}¾/BÔ9ß! ¬ÚúÕí¸¶DYØ¡½Ž­½aûúäïÇP—¾)ߤ5êKër)÷møÓ6$JøÉñ´cð¥sr½¿®_ÌÛÔ5·½s¶船9îW`w°½oÚ¦±ùPÕ:EçÞ$ß æ½#Ž…ÙàbÓÁƒ ‚ätòn8O{ÛØ/‚ÉÔädp£Žl!ÞiNï( IŒÑP]y™º º‘`ºÂ×ÊkÇËËb -S´ÿ2p~¶õz&üÞYÏÛ:†óýxç¿Ü_ëOƒ÷@;æ€SˆcÎÓÀ>±¢ÌT±ïM0 |Ôþ_ЦƤ1_–·¿0+p|qL7°¶¸)£œl‘„ÔyëÓið „ØÞCk#1²Ÿ}Û¦ea“k‹sñrqœãh0Ë 0ƒÚ;˜ò'Øbƒ[©í#©íëWBGž†ŽGÉh÷­àÆ »,Ëá²u„˜? >,èb¿8ä<°n†¡àõsxzÂíðC°í㉄lá;ýç¡~Û8^¾ñó¼uŽ«¿ï¶¦'–R’%u¯ÔÛ| =B¢NÛ½!h·› +A?(k€7‹'ÀöIÔ©¿MŠ‹óy’XˆÕ±&dÐïuHèó:ƈº6“ê¤Ï“¸ÎI"`Í»X¹øØ÷7˜ ƒ`œ ?7 ý"¸ÌG™ù¨‹”¢…ŠR }‘ÚtÍê,sƒ\ \×@ŒM¶û+nò ‹Ü'nÕù»QÏŸPnïðG€mß±<$LõË·Áú]à-¸ôß ?¸I´ÑÃÓrÛjŸ©(Ö›7|òÚuŒûæõÑ.tx(߉íB‡ù/ò‘Úßvöû´7 ~–òk’þ ¾Šö(ê ÉóQVI?#è|Å@5ÜTn7â…­¼@ ^Å;œÁ*nˆåa °½§¿º `å0@~+ƒmí·ìUÊûHìØöÑ–²…DàåÁòûR]Øo?ûÇÁ NíÔ>Çr£úzß×ÍÞœç*%V"µ>l;”|Ì͹(–=ÎQ.€Aq,íhëm·¢ÎSámP×›ðMpâ“À íڦߴէ é »ƒ‡ŒŒr8¨_ôƒ¨Ë±ÂGÚ`>ühDy´µŸ¾T_Ä„þ̉CVßœ ÎåVP§¶†À§ƒãÁöú5t˜´õ˰¨§8®åaÙŠ4§\¨òÅvÂ`¸‰7Q,Z¤ê¹¦ÃºàBGðMu]´:ÄÀ@qLÍ>¼ýÉÿ®T¦þ ‚aõR[$ú˜æ«}ŽãÆvS¹‰ ¾yð#ðêæ’8lo[e\ Ú.ƒAªh‡Aï˜aÙFÅ6ö7—ý”Íàzˆ1¼C„à&ûòÀ9|j`}p~úÕr}þt Çý›)|v‡mÖ›·Ÿ~,÷%Eõ›×qÔã!ø? ý¿ÅñE]‡Âs`½ë«7¦®¿©6êÛÁ¶ýAa³º*ÒÌÈÞptº‹«¸ã «!Ü0¶½Ô£Ž<øÇÍb››a5P‡ŠQ¸Úap)¸‘ªß|Œ‘««í¡Ó1ÜÐŽã»éxòÞ•bsYîÕqåð.h¯›ìP·â¸ŠsŠ|QЄöí7uŽŽr$™WÀq=¤~Úíœú‚öj§›ä °v9GçšûQÛÄú˜—¾ 1h~TÇÏ`U]áKÛ:ž©öhÿ!ðg°ü¿@Q㹩µkpcëÇ]ÁÃÊqʱŸO c@}ýAûµM?9^EšÙ„:Ù…uátº ¬¸ãÀ;’肱€.ôñ0¶…aCPWXú7(Sç}àÝ×`°¿A©c—®Á‘£n1¸£­ý$V}ê×^Sí«ÁàÛzƒ²+Ü Ú'—‚õŠsuÊÒ¢zÖ.4}rX¦¯´UéçAØòùc@Ñ^7ÔØœ[ø‘l¡'|k™ûh#Ú8WÇ Ÿ†?Mõ›ícM¼} ˜7^†›@¿Šå¢ ×Çëcá%Ðfý§®ˆ£HµÅyíê\ ´ÃþúgiýŽŠŠ”{@§ê\l@±ød‹…GêàÆ‰`‹Es!Õá‚=Þ¡ú€Á£ÎÐc{€íŸƒ9‚7-RuĦ֦rbóG›úœYJµéEˆ`V¿þŠsiÑÞ9C_ø(_íõ°Röm .&»F%IDAT¯ï¼^œWÌogò£ ÷…þ _êCçåfËý™ûPņÏSu8ŽqâF½´á Pòyx}ØVÝ{Â÷ÁöëAÄ’ci¿©6ù°Øn-ÐÆÊ€ZJòEË^q!î…íÀÇd+ˆ@ÙŸ2ï¦.²â¦‰Í¯þ| Ë­÷ .u¿ÁÀʃÁ¼¸aµ%èfS¥¡zë (_n(¶ ]íÉï p hûE0ç§úö{@SÄ»öáMiHuç’ûȱ |7ŽvûÚ_ß;§ŸÀdÐwÓàd?ØÆööµlaä>3/®A¬Cø?Ò(W·›ôYІíAiàµëlÛ˜‹©Å˜J¬¯ë>÷` êêPWŒA¶õ‹Æ¶uqB\X%8®m3®€ýàB{guc)¡Ç4òÖy§=¼ƒ= ýAQ· œ@^n‰r}¬ž;`„Md‹qËö_ìcpƒü´ï¯`û=§Â$pÓø˜š‹c4EÞ ÑeMiH›êR;m,í’í±½s÷ ú(WÁéàÜôý¥|ø0txè7ñÚ±#üý¼¶íÞð Zâ~Ò°·<Õæ(s çÂ¥0ƒúBb¯s»£¾M¥:¸­‹ b>æ”—»€€”§Âµ N¶~!cA- qÓ^n8}¤qp8žúoó ©hƒ×Þáwï”1F¤Ö+P‡€w¬WÀ;Ìà˜#Á»Òæ`pz0(ù½ö ¡%Ä¹ì ¦¹Äø‘:'ý✶ƒáVøØÆùé+ýiÛŸA/ÐÇn¼ÐçxúÎëð§iŽuº}Ø .¾Ð^…Æ|FU},„ýRöøäòЯ1Ù"oÚæEG¶eqQr‰EŠòH§Òèðîÿ¸’hu^nÎDïÿ„íÁºhóF)ïu¨ùåJûˆ‡‘ïêB°ì|x `;σÏÃçfp“L„84Ô_E‹-¹ uÖ¶ oySëSq}v8¸ù´ÝƒwÄÎaäÏ‹°/ø„àSDøZŸ­ JøÔ¹›Wg¾1Õ¿ôDJU± Üôï `þaؾ ¶õN{hc%6^ÝUÝϰáî¼°‘¼mÇCCzò.Ö?1Gûå’_›?7ôdp¾âft^3à{ðuØÞ‚ßãüÃtpÎnÄði¤Ž¡o.mR§ï?Œ+@±‡‚ãšÏᲸ6Í%üÐ…ÂÀƒÄÃÿÛ íå:¢½åiaèdÀÅödwq»‚Rq1¼3½À»Œ¶½Ü´€‹k™zD¡×kO{ë•7ê’ú7¹KÊpL7²›Ò1Å@”•Á¶¦Ö+>Ök£ýä,èJÜíN$ïøa“öÄ\ɇҤFÎßùXo;ûD0v&ßÜ¢îKC|BÑ~SíÐ'n¼5J×a—ýËÂ~²…^û½ úf œúO¿¯ú5üÜ›¼u^o ߀èç¡¢îЯÍJ¬o¬}¬”;'ýi?çq$<êu-)Ñž¯ÀH°n0Ø/ôÅPT‘æò€NÍ7…ÁgÀy‡P\ˆÁðWg±XˆuƒÔ`p¡]àXøH-ÀvAÇûºðn<ƒÁ>ÿïD€m H1Pû€².ü´AîÅ>Ž›Â²˜«ãhsÌwyïŒöÉÜùˆý”Hë®ÿ¹8Áj[7›öˆ>Ò†ÜGqý:8WEûµÅ¾åX.1ÇXËá”]á³±ä¥/¸¾áë­Èû4á:¼Šua“6ê;×*Ö×4Öß±Í[–ÏÉ5Þ¾>ý} Œ%uÇàA£kƒãäså²õKSƒ¤õÏä ó`sq†Ùp)xm*y0F šÆ&²ÞàV¢yuÔ€Áº=Ü›Ã9`;§Ãð"<ïy ¾]àF0X´ËàR§äeêŒ:Ç= Ôm!ŸkÞμm›"¡Û^˜„~ý©n¯ƒèçõà]œcÌ£¡”êzQ§›ØÍúê; V„ËA§€‡ë°>¸Iÿ ž…!à­û*úH;¼[›æk×Ñ–êB¼öåß}°:<WƒëªDûð]\×ÕV~¶ˆtr,¦›Ô@‰;²ºÁÎàãá*íÚ§²åÏ€é]Á@·¯›(°LÜ ýÀÍwïüã‰õb°©ë2˜‚¿cÊ0 ”Ðé˜qŠ \4(ýȃ)æì|í§¦nõk¯vš†ÞÆ‚š& •S«= ‰vˆºc,mÉ}dÞ9>ÚV ÚÔÐ).$[õê}zõ³waŸ¦¬ï€›[ýÁÕ¥¼ªcÅú˜ÆšE¶j¿8NŒ©eÖÙǹ8þ>ðŸÄR-lKÞ5Wö†-ðJmU§âC]©ùäkX4n­?[ðÖjoaŽnß¾Cjß}ÅTÕ±Ka¯§öû%f´kŸ¾L[7æÉä- e@åDp›zxø˜g}ùBxâ´ÞÔ€ôî£/÷ƒáû`à0>SèÊ7„e9\úcìècyˆuk‚l5ˆ6¡7ôQU<.—µ±¬1‰ƒ+¯Ïu…~ÇÍËc|Ó]`8œ n}RÞÇëëA†nÛF;ËŸþuCN…SÀͪ?†½@±ÿö0bÚ?—ÃTôcø¹¼®¸îÔ­8”î¡Ý{+÷NS×X']»Î˜tà1é©£Ò/Ö™Î[m-žÿ‡›µÒJ©Ý }RûZÐFDg·a·¯­IíúM]ßy.ÍØd›ôå9UiÏ·»§—¯J«tc;VóþËÊê?ßíåÔûÃÉiöGSÒ:+ôN=¦½_<ÂåÁ‘ûÁÅÏÛämmç]b2ì‡À ü¼ãÿÆÃVà]Éþ€(Êbœ<}ˆú-À±r´+Õö³à%ØÜ ¡Ã±ÄkÅpô`\”¸ò kûС1¶ºÌ{§TÌG»KÉï±¹r;¢ Õõ}l§äúÕ'úÉå.°Þþ¾¼ Gƒãœ“àë0<,~ ÿ ¯¹}쫎\r{òòh7wði³N]Òi3{¤¶~“×4¦šA3SM·êÔŽ‰µ›Ö%Õ¼Ü5U=Ë(rTõÿ(]2y^úkSÍK¶äz+ù¥õÀ€M‹Í—6™ÎZc—T»÷&©ö¢>i.Ïœóy±]ûB vEí}SõYýRÍè©¶/m7Ù²’TÕ9õä©ÀÇ8ïÊ9åeñ8飣¤°ÌÅ·é<âÿ0P¼3y§ò`°î-°oOp³Ü>æÚFŽÛ.ŸSèŒùä:“íµ=×éoNV‚XÛí7þýXæÜ•ápxˆY?ð«ºl6‡Þúkâb…®=‹'ÀªõG¥[7Ø>ÕþºOšglcµ·OµÿÊø'yËž¤ÎØûñÀ4o­‰µQÅ¿Kë©_O†m§]봮̪F¥­¦vNÿÜó4çÐRçn,óTB©#DZæª+Nj>?ªaÂá¶ÃÙ¦ôZôÁ󯥡ݦ¥ö3§¤9µµõ]ìÝÍ{çrygõ®r8øø9”ÿï4SÀàw#:¬b¿5áðWkuêq©[q<ñº¼Œ¢ÄþNÑT”Ià¦yv‚Ð:c#xÇv,J¼ãªÛÍ’ãÛαܬa‹ó? ~ ½A¿Y¦/r¿rYHèò"òê2¯˜F?S–Á1¶¯ÂubûM¡\ _‚aKP®×ìqð EuJØŸØüíºõL{®—–ï¼\záËÓÜÃ&¦N“ÑÜ™–6^˜¨h?–Ã3g Os]5Myá®ÔgةıŸz²Z˜ªÏ´®ÞŸé¨K0ØÆ£ÒO¦wJ§þÏ}ì8¡ý¢V$£†Yvgaî]5Íû馩c×éiû)ÓËÓÞKs(þÉ 0 ¨oÁ üþ¼“+Þµr _šºÑ½;½nÓTb¬<źú³l^²E†nm3¯Á¹ðmð p¼Ð™ë{™òþ:È6(_¦ôvø0«µOà˜1v¤Žg~0< n|_ê̆ûaxøD;²õú´W„®¨3U,·Ÿ‰›ÿtð.¿+Ü Î1Ú-Æ4õ.¯­Óa5øQÊ»¿…ÿÅCLR×åSç•ÖIÝkWI/^pªYóc^Ë3º,ŽhP':ÝÞ7͹p!=ÒrëNKµÏß]È/Žºo«½­^6•NœÙ!{ýØÔ~*KëÂ,‰Ì£o;îúûíœÚÍ›”†Ïx5MþøC§¶Ø°Œþ8|¼\ÞæTðnfк ö-`&(áGÓÀC@SÁ@[|"ðŽ305óÞ=7[ÀC ƒZQ¿6xí!àÝñbøI)¯žÐ«nmô%Hl² Šú¢o4ˆ¹˜Z/nèÐe¹ù#àlèÎÑòüS¯}-Ñæ¯>Uò:¯íãAâæ÷ik-à9®þsýÖ+ö‹¼c䢟¬ÓGÛ‚Oêscá"ø è÷î”æ]|oªé>/µ¯ T,‰Tcɸ¾iî¥CÒŒï® ¹$ªZ´O¹Ó[t°ÅU>Œ×Pç§uguHÏÜx7ÑŽµKk°OÓ;¦Ú£FIwA©Ycà›ð^ 7Â`<øZÑ 0ï]ÉMUnŽ×Qf0Úgoø¬ [Ák›#×iÞrƒÕÃ&êLsæÕm[w9Ô¾–õ Q´Q”Ë|è4¿0ÉdzIqÜ5á/JÒ‹ôNðÀŒ»oŒ©úB§©‡`Ž¥qíÔëz~ŽƒAxsùÒa#ÓÈ#_NƒF½“Ú‡ã¬[ñ)õÌÓÇÏ÷L¿|æžâ%äÒ¨k‘¾áœQÞJð¦Ê%÷Ý,Ý’ÞùËípan%œ®î›&=ýhZ‰ß,¯€w±ËÁ0˜#ØÈ6pÖå>5uÃøÚ¿ÔïæÙž‚rÝtµãǵi®“ËÂ67Å¿Á;èÛð%y_óødëõ›/môPQòñœCÌE[N‡S  íÈÿ <è%æQwU÷3tx:cœhçu_ “à ðIÊ;´~ô0SÒ_WóÉÏ\wäMÛþâÃxíÿÝ.ÝÓŽkožV¹|,ïîÓ¢)ÊéÛ$Ye»)šv«jŸf?{g³ªo’ k¤3Z¥¬=&µßptúÚVï§»²Uškó;Yy·x¢X%õ"{5A°Ńà:pCIù¥h±ÄÀ5–Ôå]§<7ÁÎÐ B LŠë’Sf?º‘ÿÔïc²ù7ˆ:B›GXÔ:»ùïmÉõäúΡ›?äL2Îñq°/K¢½©ck‡DÞ4oÃe!·•)Þ‡¸ŠÔL¿¸ù}ia½4Eò¶ÑG}Opo,×#»ÞÆéS±Þ7ñ¢aShJ›wñÔNo§Y]jÒ1µüú°)}>Ë6‹ ŒÏÒ–Æz‰7MxT¿øØgÓr\²fûû¤ùŒLëuíQÜm¼ã»™-ƒ<ß+l¯Žá%=^»1{‚w¶‡ábØB·ð\ðÐÉ7—EÐ8yð¸ù〲|¨ÃC,Är7ªwÒ'êÊSÛm™ûvUêÔ5®¹V=F@ÉXƒÊàs‹óßPðÐäSÒP»Khå£k¾ Ìësïrg·Á@?W=¡Ë4/‹º (÷qÛë8XÔ»3Ü„sñZôyJ“EŠ:7ÿ xÔã¸CÀ×ý×€cùè×¶y»ÁgÁ>¢ø~ÆÑðøl ÊŒº¤þç¬ú\3df~ˆa]ÒA¼žjˆ¹ðe3h^P…Îè;3Õ¾Û5­Åûo/X»l¯ZåÀ'æöûBöÕ—SÍÒþ:faîõywëÉü}nôçé3ê?w1ð› kgð+±"ïµ›éKàæònïêŠcǵíw…;ÊÚ9†ÇöuÓ< !ÖÙ?RïΡÏ6–Ç8^+–I§Rj{cÅ;òtPâ‰a#ò!tæó´]ˆõöUçòí´YYÜàÖ½ñ~DC¶Q½€¨³)ó)ÚòÌ_;mJj×sÎb}¬¤)ã,Ðf.žä‰¶¦¶[Z¥ªºÞO ´YV­òÐ|’oàÊ,ŒQØRâ-5BŽÏ{cÛp?hpqÊ_7fŽíÜ ILÅÔGv‡ŸÂnàæ÷¥!Úº!Ü8±¹È_mfÛÛZïÓ†2^‚õJ)I!¡#¿;[±:¼ a».ÆtNa‹iŒåAàÝ\ûnçc¿Ðm)*$t¨×¶Š±·2ŒÞp<¡#æCQË@þ˜ì0ŒÈacË VÒZ[~œµèh‹VÞj~U7u&ÖEÔ.z*‹ßµ˜Æ†§ƒîº»Øˆ‹¯¤yzl‰}·€u ÖEuwÃØnpK­íìcëÍ{˜F{S ¡ƒ¢BB—©mBg\×µª+?Œ‹¯‚‡‡‡„ÒyððŽ}rvGY®O…p|ìv9~sKŒ]èí¹ZêÒ¹sÚ’— ‚ψµŒøÉÀ¹™Õ¦Éó;|6MSgÒNnêØ¶ãO.;.Wžš¸Bšï¯ìZJÜU¸ßVý;=Ûmù"à­¥Ñçb°»A5Ã;ßP>»¡â½ [7ÝÅGý Êl£¾àSÄà†ºÅqb,ÇvŽ‘æó Û"¥Yý¦tˆú¾J8ŽõÓ¢¤îñ ±,p>2¯”zpœ¾ü9 <8ìãaø¤ÐÜøÄRÀ¯~g-¿ZšŽS.}z%þ¼¼¥v?úZ‰×ÿíxª}'´àH ´˜âB·:áĬí5'½8~ÅÔµ³aÑBbD¾ÃÃìmO§™ü& GZ`Žc˜FÞ  |ýëo#¬7PÝ´Ê–Ð}åÁ4vß7é‘ÐDÓÈ7ÔÎ2‡–*£½¶ø2eˆþŽç{åb½š©ö«Ï;ÿù°| ¼û{0D²…xÝܨ¸Ðé߀¼öhš÷Ø?Òu·ô«; ‹Q[à‡˜Äqþø½éµg[Ù¯[åPS›æOãWh¯r?[©¡°o†E2 ÞìžjzÍ*6^hlî€kHŸcY®˜z—t–q‡t“(n Å»üïÀ;|H´‰4ƹˆ¾¯ð.üÔݬFឥŠÜ–ò¶Úu äm|Ôצ‚ã¶<ÙB¼ƒ?öw~ýà/°ì¿?ÅþJÌ¥îªyÆ(Ñ.lwó;žO ¾æ÷å‡mĺhG¶õȳws@ÎOï°ñW?aDjkÿ4o9,5nb‚‹²Ö¯¡3Æ~±AšsÑzé#â¶ÝrÓÒüÖúm@ÎÇhÕ¢ó†ŽIUÏM]n[3=ýõiÞlŽuvSÛuaQ^ç3{á¯d:¦SŸøGñ'²­uÞ±AL_œgÂàðŽ1j¸¶ÊMðߺ£uæóÁÇö(³ÜCap¼þà!¢ä:êJêôÛî«à'û^_RDœÅøµ>yîî4gù9i2ï µ»v­ôÏGñ+ŒURu/¬ö~$âÎØ3ïá©¶mÆ®‘æùŸÃ^9]AÜ®¼//ùÃÃV+.b›¾ðÄ)ÓÏw~+ÍãËA«†ÏHíü´_î‚(x½" B’[1ÍãÉ¡ãË˧Éüiæíç§ñOÞ[¼6-Ú¶â±6¦â&ò‘þ8þ n4ÞÇ®ßÀd‹¶¦Š}Î…ƒÁ;¶›1îÂx¨Hm+ŠiŒëõ«à~}A·*ö‹¾‘W÷7ÀêYðuE=Ù¶#ÄÚŽs;¤ÿæÓB›íòVJON³VŸ™:v©æBþ!®^ï’çñ:¿ÛÝxfÕYé:^¦~­]§ôáãw~nõ“ kõ††Ã·OUãïIÕ›o—NàµÚ)|‚«×P~Y6„‡Sþºo Ónr§Tõ\O>èÁ[`Lð±Ug¦Sº?Ýįûªžkå'r̳”æëÔ;òÎàö~ ù!o\óöû~ ¿7°U‰4úDûüZý…½ÁñBbSÇ! Þ“áøŒõ(ѦîªüÌïà[n“ŸÒ5m9§CÚŠôúE2ÞÙßâ åûÙø÷ß÷@ñM"Æ:cú¢MH,P›067’ï ìoò©Âvþk[œ],R[´½°™Sº]¸óW¹ò£†Sz>¾˜?ᮬÍNô“ •½™Ï«0VëõC@¶80Þ =n…ò€¢¢ŸiôS‡Æ}p\næõøÒâ—pøÒä[ Ŷ©x âfò€ËéËé ~6 6%ÙâNí¯ÝxT +‘ö‚ÉÀ ¥"o½wôÛç}lãÝÜÇYÇŠ>2¾—àÓ„e¾xh¬JcëoŸÁñàAà†¶¿‰âr ‡½àˆ×À•Í3ÚŠ4mÅþÿËvÆkè¦t¾ƒa¼ 3Á m;ïèÁê0Ÿ?NiÏÿ¶ï |;Nâ=”Úù|uÕÜÙ©CMuñÞ‡´Š€›[]O ¾ÁXÙü8¡-JO[´½bó'O®£¸9ÝŒOÃú°;¸Y}°Î;ø|ÆãlöÍùVÜþk­–ÖíÚ5 æèÁ§3æÌM¯=ÿRÄ!àïòWéÀ7Ùp(ø+/7¿ï7L_ ×À:¥rç¯óC›úY9ÚÔr5hl¬añ$p7­w€cà:¨á ;ð] ßY}ítb§iŸ¡è²ÎTnå3ØÙÕ©†ßq·çcÒ鹕91ØÎ]椫ߟŽÿhjñO9ú Ã§ _fxÐø£²ñqBE*X–ˆ»¿›_üœ€rÌæŽ†÷I¿ï³Gªýö€T{7ðò&›—ëÕ>Ä·ã>Ø¡.}†ë·áþ\úô5Óìþ_HµlWüêp>zb³ûFb8ª®Hõ@Ü=Ú¨ù³3ĆŒÃ X[6í9<ƳæèÔe×7Òœï>Ÿ:¿Ï‹_,ì{ðü¬»ôåEÃÅ<-\±Qê0´OZçñÛÒ›“^KsùŠJÅEåGÛó@$mÏìŠÅxÀõ Ú/×3Uõ˜úÔ®–&žó`ª0­¨k¤kÃÅ*óo,fó ÃCGñËÿê´+Êãï¨ÿ@Ã+¥mÂÞ*òùñ€wd)~e÷ñ¾Ca¥4ñÂ{ø½ßôÅßüºEe>)tâ­¾kîâ èn7¿ø«@«+ÒÆ=P9Úø6`¾{6 áoÚ×ß:Íúñã|ßt¬{do yÓŠüM*þÝ}Å¿ ¨ÈçÀ•às°ˆ L¡¶]×thoþÝ >Ô\ÿZÝ—ü¶ íüfšÏß`ø ÁŠ´q¸¦ùœy`½íS~P}Õ=|)_ ¬ð < ì>†?$˜“ªåÛ›?gîû?5ÊÀçp¹kjÒÊ|áéüiœ-!“‰šï§ê‹ÿ@ÔCTt~F¨Ÿ‘£?ËaÚU¥ë¿ù\j¿´¯û³Ùÿ©pÈ‹Å]ÙX›JyÛð@åhë´XVò‘Þ-MM| å¤7Ÿô·l_|¤ åªhnQT€uïg¯|Í1©³}/?¤ÛÂâо¦øäa TQßR¨-åÙe¤×¯RüCÿ–ö~ñÁþ¾ %4Zr Ýx r|ÎÂ`üÝþUoÝ¿äiÉ©yÀ8¿\ÊO´¤•Ý‹ò@åX”‡Ú`=ߘøË¿ŠÝ¿xz\ýŸ·èxå-ãÊÐ2~]¦Zù¯Ê?y°7_ŠÚ‚çot/þöËt¢•Á—Ú-"Km[EÁRx ÿΩú¯wñÏéÍ,þãËãF¦jþiÆ@þ¥Ö›Í¬¾¢î3ô@å à3tög9ÿütæ›~oo H7ž.ªø”!_P‘¶ìÊЖWo!¶ó/®×ÿÁ–i®›µ9ÅGÆ£·Is¸ûïóê]•7›Ó·ËBWåX^ÿ Æä«¿Þ©ªI7œ¼ ÿ¿²™•ôשfVUz{N»tãg0Ê-ìÊ{-ìàe­~ØöéÅ1ï¤G½ªø×iK,þ®ï¾>©æ·CS{þ‹îRhZb*[À•'€pjkR9áž4d\ŸôÜé¥YËó$࿸^ñ7 +ÓïêÁiö¥ë¦Yn~¾#°r,Ž[qÛÅ ‡V<“Šiz`ü¸4ü…žéÒÝvâv÷LÕÝK/ üßöå;Ùk¿Lü³_¾%xþ^£Sº}4–€Ù}/¤vOßÛ¿[hÔüJE z |ý[p¨ŠêeéõwæowªSïYÒ Ýç¥Í·{/Õìøvª]zjϧùŠ· ?á÷J×4lßT{×ê©ê½né½Uf§]ŸWüe9…ÊØ-àÊÐNm­*‡íÚs¯Ùxû4 cu:„ͽóŒŽiDïYl|¾Ö£šï˜Ì·þógÄVŸ™nŸ×>ý‰ÿýªèÇ9Q‘Š*hóXoLj¿á¨…ÿ½Ð&£R¾ö«…¾R¤Í»°2Š>_2úSo|¾&X™MÅTÛòy+ª>nDŠˆ¨#Ä @¬hjô@w<0iº Ó |…Æó·¶iÙß§ÝÚ´Zãi+-7íi³£4èk|Aã½T¯÷ݦ{ü‡m¶Æ÷ßg¹,/=à—¬ð{hðwéa@õpÑcf«[’gZ5/˜6bœMk-ØÏÖ-,+oûýºaZ%I“–þì|ð^ËkG M¯½}ÊL;­’ ÑÑ5á¸P‡!=еŽÑ·ý5˜ž¯üüžÿ·ÿj¦ýT¾à×uîýÛG“ïLÔW‚Ov(n.¶›~=À:æÇoìŸkHy ѵæÝóoZxƒnóíËëu'ý-óKøé©ûì­önPïòÏ™õk³¢&-ÓrÍ6Áʬýò¢ÂÞjf,7z n='u{è¢á‹æé‡}ôôÿź·~Ô¤µÖ1m…¾¾WŸðâÀÍêaÞñ åô,@‡~‚øç3=ûƒåäí¬iÄ ±¦O4î`÷À‘ç–VËÚõÿ²Vü­¯zT?ó«÷Øë…&¬7{Î*+Êþ¡zà¢ã-€z9tÑÎÜqp€àؼúöÀâëôÿv®¾²3X¯Ö5µN+è:j¯&öÖÏŸ¶Âu ƒ!|W™M8 ='ôá;< ÿ"ÝCo:~•5 f­3Âdý±õêÐ[›4eºYgMˆæFàç¾#EDÔ Ê_ýkѽô“Øö×ÏúÚ¶´sg&ñÖÂh®*Ê/ígCœª<‹w–/ÆGDì]Ä€½ëßXzôÀn{@»ç\ŸCÅOhÒŸÉúº~{^±[Ô3ôð"?U,zôvC¶ë#EÔ¿âXÿÇ0¶àõ@ƒ–ʺ÷˜~Ù¯aʪún$;üX‘~²˜gN¡5Å|œÔ÷QÖ×»â Þ`´ÿ€ô€~ì'¯ŸÑmÓhy"ŸÖ Õ-ÕN@=;GlN>D&Oi9=ûŸz<˜ÑæÆñ<`elÈä¼_™E›Âà9N¯Òµ±#P§ÄoðæâÈMVìÈÛЃ“†Ôq‹êô@D³£x7¨¢==°¿=жµüþÎóK´üg­ç“Í‹ú* í¶DŸ2¨çÆìï$Ö=Ј€pb,"z §=°msi¬××ó†ð½>§[ÓßýïNû™ôQ;hφ!6¤¡#™Ót'kL==°<'{Á©±Èè=ô@NËd_·sÓü@Y-3øCú@PÇÒ¦R‹âßèúó@œÔß1‹ØH†ÈB2ì'[þk¸Hõ-}ͺ&¶¡ÜŽU‹mµžs¨ƒ_4¨k—G㣺ô@œtéž=°O=à«~kÝR¾`¶Š~שç9ͺfËÓžQ“õ,@±®›³OOŒXYôÀÞð@œì ¯Æ2£vÝ>øÃs[ÖY±©·5jˆ¼·YOÍ=<ÐL‹çº&^c|ªo2‘Y¦g˜áð<`¤èèýä8ØOŽÕFðÁßU¹kô¤ühkê(Øüòßâþ¥ €ßC÷„õÂiÃRµA¿ÍŸ?Çô|cܨ—ãí<0='æq­ª„ƒ²úÇô¢~A¯U¯¶oµÕͶqÞ›o•ž÷éë‘zÉèÛG˜µh ÿ·Ñ†|“©•‘¢¢ö—â`y>Ö=ì‚'nð¿ߺÁ kž¶Uóï%Õ}C´ P§€ÙÛ+ê¡[„܃Ž;‰7âŸèýä8ØOŽÕôWþ8# 'òæµV\½Ô6hÌü{£þ¿j¦gëŽxèoîý¯k1=÷g+Ú;lAÒˆ\§6×]»¢ÁÑõî8¨÷#í¯Gø`wЗáyn’‹š¶m³Ÿˆ·/dm+êp@'s‡Vÿü &3w,ºÉ–K•+¯ÿ½ÍREŠˆØ—ˆ€}éíXWôÀö•>ä`ÈK1š4¶XÓ£·ÙZ:/YÞ×róô,@i^àIj›ó±ŸÚþ?$¹ßß ‡¿Xì>@ÊA’(FDì-Ä ÀÞòl,7z`G¤¹0Œìaxrmv´&º¾z ð-z€®ñ¿&XÛü@P­žþ¿k¸Ù¢Ö¨[,ºÑf;Òx$ l«7ÅuŽ~ŸÍߪ;Ú+V§ýàÞ~G==°<wö‚Sc‘Ñe„™Ë!OË„]ç“sÂy šÅµËmé16&—·ã°ÂÙOYŽŸ ö„µâuæ%|¶ø&½µð‡Ñú|AÎÚ[7Ù«žYbkô‰cÈÛ˜‚p¨eOyô@ô@z ÖúŽlZ,*z`¿z À\†wïi]îµðF{›ÆÖ• Yá{ǘõ×ÒºÖž`ÛÿÙÞf_Ÿlm…¼5··ÛeÜiiâ‚Ãûo[Ƚpˆ¸HÑÑ{É~1î¥âc±Ñ¥ÂËeç8ÙÃi9Œ÷4è œžè»ú1;µw»5ÎevõQšÔÐ÷ôXùoj2{ÏéúfAQß-j·/뵿Ÿöhý‚Ÿ8ö~'Ývo¯ó¤ÍIËãŸèè÷@¼Ðã.äȼ|  9nòp({þÌA²m«6¯·Ö¾#ìöÞ öêùƒ­Mï6œ¨ßØ¢«™îìbÕÏà¿FÏ÷ìd+´æõc³¹ çÚ› ³ý¶Óo[hb¨ëŽærô@ôÀx NöÀy1kô@ÊY:×wGfàï2Zó„=ÜïP»½¥Ñ^ûàk[ÜÏf=mÆÇvöõ$€Á?ùÁ¢f>Å ÚÈ v낹vŽÚÒ _þ §%î‹Ðu¡Îeçž.v}äÑÑ»é8ØMÇÅlÑ)„”Ë!GN‡)¢»zO›ð&} hÕ㶤©Ùžî×ÏΪŸåÿ6Ôì9«ÌjéÝ®R½22ì bTçMꙫÛÿqBòc?¹ö¢]§•ÿùµÛ6V~ð'mNWasîæ§Ã®÷ÛaŸY4×.ëÕÏúmZü€aV•ør^ u{œó¬4®‹°Rµê!C»_ÏðõÁåý¬ms£5õÖ3ýº ñÃ-ìêÇþn(éÿ ' Å}H¸JÀFÂáêÝ廬¤IZÃò´L8Rô@ô@7<àT7’Æ$ÑÑe„×M8»>­ õQ-Æ•«ê4Q˜%¥>ÿcÿ&0¨2V/¸PúÆžˆF­ø5Pke~ÌÐÑöq=Œ÷RÝ“O bUÏסá[ÍÆlÐB« ™olÖ€¿Q›÷Oi¥¿Tyx«€×û’7 Äõ“ÞçÿéSKíck—Ø&ýZaCû¶d€Þ¬"oNxåO9ícÂßx˜Š©ËðP&AZ—ïyœ‡#ˆ؉¼#ÚI²==Pö@xÍ {8äÕdŠãÊEVtYa¶>ÂY‰Âût ª·  Lt!âŠùkÑmimö]…‡5õ²†1ϱïÕbGË€)ÂÈŽ¼^À}‚1Ø3è7ò ¡ÙŠMl ¾=Ð[ïòçôÐáEJò›¾mˆ¾ðWܶ©²ÅÏvÿJágÂ+ÝpHl¼Rü »n£Ä{×a¿ËÎC]({|’A<ÎÑGDtáòeßEŠ==€Ò× a×…¼š–áiÐAé°ëXE3Ð?O`ðNÁÓò -;N €€4#„· ï {„ç Gœ`ƒ4!èר`½•xpCÞ(ÇpÉ Ê¸IÃôÚö{¦­ÍZu_ó“ ìZõƒBD×54ÙË5± ê n.ØW…·Ú[°fá‡ÂeB/Áí ófÉ®ƒ§å0¬èJ|Z&)z z Š¸`#EDtíð:q9ä¡LI„³tÉÓ†:9mÄÛ aºð/·ìyºþOÂó}z's@e¥}¦ðuá(‚ó„¿jà/èsÂ9mÝç¸USÍZÅ·®·‚îéç›-ßÐKhÐÖ¾¾1Ø®· ¶n´áŠû«òë%ÄNÒßÅ«ü´íLZô]BûŒðAvæ'ÂÛò@>ˆ—BÃ.ÏóxÙ‘GDdx€‹5Rô@ô@u„œË!OË„C%{8-ÉÓ1€éÑ»äÞþ‹Åß(0p2ø_-\,ŸL}àcbð á…; „Ú íÖ±uƒ7ëÓ¼ú™áÂæµVhÓ=|Ý.HFZ ú¶mC¢ï`{_q}y­ýI‚>õ“´…zþ ¤ïíK•LHxð‡×  zÚ Ù}8^ü&2ÞV‰ …a—Ó¼;iIãù<}äÑшJ†S¢*z ìðúp9äÕd²‡qîP×y8‹³Zgå¶ð:á=ƒí—„÷ ÜO‡|Àe[Ý7 L  L( ó•4íÅN·{ØQ',(Ÿ­ü Ÿ„HL(,›[ìTüFðÛ“L›xhÑoHL(Ì‹ÂÃ!GN‡³Ò†:äHÑÑ)pqGŠˆØÑ>ãrÈ«ÉYéw,=[À8X8_x­À@‰ŽûSÂ{Vׂ¡Ì@ͽÿ¡“ÝmÂw¿ÖÃ|Rï@§pv ¿ü¡Ï k©ú&*f < qûà: ìu?Jì$‡átÃC9Ì“– GŠˆ<àB ŠbôÀAïTp„Ë!OË„³:Òó0 ¦A:ú‘Âé›Vזּ¹FYÍB`rÀö9ùÒòáŠ`ÕÍêœA÷g[ø„=-¼Õ)alë'|ˆ€ˆ‰ÅÅÂ!qaY VˆÝêý«pZYËÊÿ8a¶0V ,·KbÅßÈû-Ë¿¡ÎÓº.Éä÷päÑÑeÄ @<¢:{À´.‡ÚÉ-6¡+?z|W¡, yŽ2H1ø,œ#|D&0!`Ðgk|•–ÊŠÚ({¸À`ú&˜Ÿoöóðñé2ÜEUÈÓÀ]öH×9g°î%̰IË÷Vò]QX¯ïlüLF§ LdüVÀ-’)ÏÓIìd—Û’¥G…ípÙy:>ÉÿDLˆ€ƒéhǶºª èA¸š eò{Þ‡2iBòAÎÇ@=]ø¨Àjš¼ Qß—¸C=èªÑŠ ¼‘e¼[¸Kàú¦^‡Äª¶!-vP«wl¾L`"\/<#x^‰;%Òr›ã„¾iØaàŸ+x½;É„C ëÍ’³täõayQŽ8à='ü!Ž <@gï~È‘} 'y:‡³8髃%DÙ l¬ü_*¼_`uÎõw“pvYö‡à¨Ç)”]çœr)ƒUóÒ²:µÀÀJyÝ%¯'äÈa˜²¨Ú(Œ¸@]Ï ¬Ú=½Än~aÒò;{ü¹gBó¿» Lž¼l·+V’JdÈÓ¸L˜ãâe„ú0-úH=àÉçXnØ8Ëg¹á`¬Â,wˆ¦­Ï<ÖÄ"vÛñ„ßm×õlÆ)ºHô‹kúS*7ùyVuµ ®O:«ž­ìà*-ëw]u¡Œ×Òá,O–`帑‡-ùs„ó„w ¢èÿ(¼@`…Òå{¸»ã_VÉã„Ï L O WÔÁ  ¥m uÈa=.äñß9eOøæ?“ÚÈV>d§tÝNÇS.qL^þ³,S?Ïp;£ŸÐÕ„ÆËMs|äD\ï2ñ¡¦w9ònz`êtËòú¡)?EŽ ~Sbþí;ú÷èóõãSmúõiy^‰‹ófwºÝÓÍÚb²=ñ3ïHûГg%'}îþ›:Ÿì--·e5é·ØYíðûìm½’Ž”R'šr®å×?iÅeóv¼¨:%vÌ”6Xá¾*?·kb°9·ÍVóúÄtQŸ”Î%ÀòŠhBÊŸWšâ‚’cèêÈ{ØŒHûÀSfZSC‡´*Ó,y|GÎf¨=NÕתSû©Ü_帴êï6õFë„¥êHwËü9v«8”›pš5oXmm+ŠIÉ%;üMŸßaÙÃY²ÇQ¨ËÎÓ…ƒ†Ë¤eP{žðáUÂVÁñ*á_Ž5iHë¸ÓúHÃà8^x“ð®rø&qJV䶸=iž$HýÁ(än—ëP!¬ôÙÁà>m£žkè_–Å2ëw[ˆ‡<ìu1‘a·äÏ«~Úó pfYæAÇjäeeñP—%S¦ë½ütØõ/ç(É+ÃŽ´ÜÐáÖ´èŽÒ S¦ÛÉÐ/i(Ú‰ÚÉ«3ä°6-€™[²Pö;@gSA×@ΖI\¬…ÏmJþígÛ2’NšaÍ ætÚQ*—YOx€CiyàèóìȆöäƒ/oÔ¹ÏJ†^Tb¹Ç•€{TŸÓ·Õ;ì“[ÖÛM+–X‡~Ú•$aVŠ]·Û¡¨ÚD~Vú' ¿† ìlN– L¢Â2½N©+új6¤õž‡2ÂÉE˜Ž4%iTÎq¯^«þ†g[ë¸Ómhï^öOê¢>º¥Ñú+ª]ƒ}ñ¨uÖxÜj˽V_¦Ò ÏÉ@Ùs8(oráöÖk%ƒÌîb…GZûcý¬IýdN}ÜÃzžàÓí½í'‹~o“fY^“?>ÊiO<ÀqˆÔÃÐ}ó®·vmû¿KÝÐÔKºQ]Õ­xÁ2íù¯²Ü]€“ß/ŽtOÃÁ¡‡õ^vƒ¯×вLwÒ®mÅÛ‡[®†¢mTÿ—Ûdo˜w§m<ÊšÖ,?¨/’ôyíáwGö3ÃÓzÎárrÎjõ\á]•ºÁdBðJñ_ ¾šõ2áY²Ô=rHÔ3Yø©0Zà4š)Ü)p:yI:·Mb'™p…¶ïö¹žÓò0ƒ0¯ðÝ(Œ ¶ïï°Ãëy(+IB®#àeÃ?Æ s„QÄ„êá !¬CÁ …å¹ w0-»y¾Rhǰë$î¾O·©8r’5?¹À¶s¶]Ö·)ááôGÓ—[ñEK-w˜žd¨þˆÂI¨[œ ÄŠ.t®nT¶­V«ÌÇué—ã¬xÿPõ•šväu 4gošƒÝ:yº5ÏŸo T¹BÖqÙƒâbV<0i¦M”cÿ¢-­1œÛFm¶¦×>¬'µViU¯.ŒdP¯€n¸Žä~­·®²g4”\s´Ù}C­À–š.¢|¡Ã.~òûõúåÖÞÐdùºÏikÞ±€ÑT;C}–ŒÎõ]É;k¦ûN¿ÇêûùÂ[…S§J˜Sx‚ÕlãÊÙ*Ìà÷Jó¯ŠyÂÙ+f?âÔÖ…ŒÀoH̤Ð.¸\ï6¸ž:¯Øá€˜è\*¨+OƱªƒmh#é²{5$$>œ(i’Ùq`â¶IL(,×exwd ðtIaa×è<—×ôRýJaÔT0p¸]£ùKÔ¯µÛ`ï¾_[3:ëè8Òï1q+ê£3õM-¿1Õlu‹µ©è&Uòí°ë©<¾hz Èƒ³ˆ gXî‘[u?l¦}P·|fº¾)«­ðâÇ-?}eiÉÂEÂÒNO&*k€° Ÿ^–kʇYGŸŽäÙš_l^o—?ö7[™Ë+L—ß¹C MP …º²ªæ˜ÛšeXÚ~€<.“Á+Ôy>çéxò8yý!'[îÚÀLVþtN R Š \˜x¾Ð©+§„ëÝ– ŠÓM d õ¼Ôu‚ð èCÂê#§u™òÇ ç×É"M<$¯×ui{°Ëì>ÐVʾC@Ϥdœ€ýØëöHL& ðP—¶“2ÐÁ!8g1“€_ üLv¨‡^ÔUÔ%–çõ²QzÕt¤¡¯Ÿ°§ Ë ãIyÙ¥PíýuûÓ–y[Bîiнú[ÃÖ ¶uâ6­©Å~ÕÚ`#´ÒoÅbkœñ´ú5åbÀî‰ß+uN׬£a¿cö³ñÖ*'7+|oGƒ¿ð:[9y†åõ`4Ç,ÒnxßFÚCð„£îÍ·o¶?èá—óu‘ØëÒÐåzÌ[Ýl¸¶‡Uefgû¬]Gr‰¦ùã­ØÖ`9Õù¬Vÿg¬ZfO®yB¯Ò wMµÞaýÎÎ×t¼‡C^MË÷4Õ<†¯Ü_t>#„éÂÂáÄ“ì' L*œÂ²‘ćq üÈ‘À¢,Âߨ[¯¦5Rò>Ÿ¼¸mΤ}³ø×ìeA´¹-!wÙÛCX3ØÿTx¹1P#ó¬Bè¯j²’U|Šìäu:gÐÿ‰p¾'SYÇ$ {H¶]Á„\W͆jñžß¹§óp½s÷m§vôê§÷ùõ ß‘§ØóìOì>N\gö^­úûkÊGŸ³¯ˆ7ôì“}òÄÒmPÕ»IÕOÑÛï+ÄzüB>Û¶ÏÚ$'6kð¿EÄùº'_ü¨6%uO,YŠïíÁŸFzGê‰Û¯Ýj9=`ˆnPC£=4h”MÑàß®.×4B¥ãj1Ú‹Ü•œÛħy¨KË]•G\H”Ë`3T¸PøÁ­L]Uò*éœÂ2ºˆ÷8×ë(&ŸÛ}BœU6Īý•?å²âÿ°¬,‹%–å:Ò_#Œt¦dÞCõºÓù]¯l;‘Üÿ]8]``þœµ ÿ °+àrX¦—r%Í<žèCb*ûáe%™ï ïx@Ð}–½3߯y(‡:ôÐÎÊ«·øÚD±u£m{’]˜o´?éÉýÖYËõÅ©»ÕÙé,Ü—ƒ?ÆQßUß½9ÙYå–›3£òö É"í¢ü„ÞÅl1¹{à„iÖ»µÙîЀ{웬ãý÷ZÃ!ê’¶ªÛØÛb^o÷eI“„oO2›3ÊÚõžmcÛ6;ó¡[’{¥dócîÉ³Šª'·ÇmöpÈi+9„>Œs]ÈÝ7žŽ8×!C r£… xŽÁn‘ðbáq{àPXŽËðjr’IV Øí«häiÂÏì¡üÃv˜ s;KUÑS6º/ˆ ‰<¡MÄyØeÂ^·:~#°"¿B`~­€O / ø†Ý ·'ä.û$0ëT°B®gg÷â*á­‚Ö£É.Ä×Äß)à&Yäufñ´.´ËËò4†»î6º.L·7d¯/,;´}Vš0}:Mbû„3mz¯fûök}õ#ÖüªÇôýgyžw÷÷± ¡ïØW¦èýБÚam·õšŽ×ņ¯ïrGk™\ÓõvçĨéì/ãôe«†|AÙ5Øï´Þ WÞ©Pg㾺ò»j{‹Œø’.’ÛIRµé[G­yÊÖéA.§]1uWÒzù=ÁwvކñY2:×ïŒc¯§qÛÃv»¬£œ¬ng‰_)0GÞË…ï M‚§—˜ñ^~ÈC™„N2é—Ý_2; ¯*G0x½À‹Mi½^çIÄnüq;²8:À.„×ÉÄŸü·€ü¨pžÀnF5ûB]v®l yý\†s.ãÿw KÔù ábÁ'M+–›–=Ü]^)T‚ç uµ&»ßªÙUŠ×ßì¹ÿh:h=¤…Mî:Š þÜï¯ÂÙôo_›¬EŽ&zN`õüÙ6ôx½"x_|Ep—L¤]ôÀÔs¬aÞëÐी9O³Ðâèñ'ñšé 6i¦þ~=~ÒJ-•¬±Ï`»EƒÿV=ÑË1çRÞÒ^ÙYú½¿;vxïÌ6êð4Yõ¹ŽòXi.X:Ý-áí­‚¼_!/×9.§¹ÇU2—Êc€ã^ÿ4ÁVØ}„ôšŒr¡°ü’¦{=_˜:]&qž¿@Øu‹ð €M㞃€< yÂãâ2iˆcŸç¼lx5Ò]áäÍ&”ñy…ðkÉAxìT¦—_Mïñ!w[]G^ÈÃi^ŠÝ»ÓuV »Õâ}S³5¬ƶh×m•÷ž÷”Ù?>VZù{û›c(;o™o¦ï ð¡SfÙïãà¿ëG&}ìz aŽ•‹­¨îµêí®êU°öOÞm ìÐûqrÖqûa‹º«Ó5X8ØŠ«zÙÀaclÊ3K’ÕYsÙÆtg€:­ópWqžfoòtçK]¡Î宸ǥó¦í¦­i" ƒŠÖöÂ9+`¶Á_)襾k¾}à!–Úê‘«ÅQýk„óÒR×WÚðÔ#ò²K¡ía×wÅÓyÒå…yÓõ3€³#Øu†À®E¿rX,!ÊpBÆzÄÌîð3“ü’çqNùìŠ\'<)¼HàvÇèáÛõb#y+²—òt:Oï<6Ô§ózxóô¹•c¯¯?fº]]l°ézà¯ø¾ûõ%JÉCxµD<ø =GßT™=J_,ÚÄ!ãlÞѶ`ÕÒR\ü»spDÚE}޶‹öYÝjÿLJ­ñpº<WP- 7Ï·|‹v)šröÒñg”5N[{²Õáf{عëá®Ëâaºž’Óõ„åz:—»ÃÃôÈÞ­‘7¤0ìiX]2°°½ü>Á•_I¾´î+Î@’—å<çzça¼ËlŸS÷tWˆ³ÍνÈó:/iKÑí.¼œ¬r=.Íyð¿„g|6Jx¡Àªʲ=m/Œ²nŸH]!?&(ÈÇNÀ„K5›„â ‡Ú€> {[Ó:Âô—a-|Vð§ÄNäu¡Döp5ÙÓÁ°#t„p­pœ}Hø‚ÀÄ{ Ò†Ç-”“Ál  <ÇÀ`Énhµ<¡íJ–ÙïpI‹‡  ø•ù6ÁÓI¬ØìõÂÉO›\'±“LØí et”͉‰ÝŸÊav$Ø8KxVó*¸eÕ뺻L.;÷BÓa×ï)ïª Yq¡n™_ßiêe ‡N´ñý‡ÚÚ5,^;Çr寋{=?ýÛÊÞf859—›õÚóàE7ÙÚ½^ñPAx1ÍÙ{MжR~ÞlkÕ6Ó×ü/ZbÅä‰ÿðrÚ{ÕïQÉlá1Sf@3úÖ†fû¢ l×ýøÓа%é0õgéÐCaÞ’¦gþ†åV“½þÐ>OëÜÓ¸U ª x èo JS! 11øD -éðÙg„O ÕE%ÄÚ€ÒÃΓ„ÞÞ—•ìIÂÁ×Vµ>À8—*¡tØõ!g`|k´3mO˜Öå®Ê%Û˜PÀW<A¹´ÿ!û9'±BaÝ]ÕáHãéBN;¨ë>á ¡|sÎH¾K˜ °#Ö§`' ã\Îâ® 3géÂøž»ª#GØu¡Œ½¶ÍZ·Øª¾CìªêÛ.yXý„ŽÒ¾~ywCÿ6JGúägŒ7  úþ ×k¤nx ëbìF¶ƒ/‰¶• £gh˵h— lµÖóžHÓäg/ëÁ\Ì´¥wü*­ÎrÖrôYöýº¤w iN³\61KçiÃt{K¦~ÈíîOsOƒbÐà>á\aµ¡ƒà€kƒ7«üóV¶ï>,ôp²ˆúØ'—Kº´a8LÊÔÃm&€Éƒéôÿ-p‹²_/°€C }ÊžÆËÃèè)Ÿ°sŽÝá¹Âß&;ý…ÙÂLAWBæDDê„B;\NsféJ%ìÛ¿Øá¶Pså0Îõ¹öVk?ôh;J;g ßj­Ç?[rfX k™¸0ÎzÚrêçÀ…G?ÏzOšÞÉ'µlþ~³-NvÁõrö.z™cW[£.”º™!ÓD.fÞ=uE©ãkl°—IÕ«üZ I ðš¯&—Rîß¿¡m¡Ý®wÞ••  T‡ ƒÈ‚ðUát-×ë„ï ¬(ÃØóÃ!׫¶ž7Õ®3O¯$ y9°A••ìñDY¿HçƒÄ$rdÈËËâ´é a™€}YiÐ…ÔUl{·À€Cì¿G¸U€ÿ“à“Nt×A|Wäu‡iÐù1ór<¾c×AÃY2@¯âä#J|Gç<­Ä¨+{ˆóø4ß¡  …×òŒd{¬rÛ((Ëf¯`ëÀCìµx®‰™ÕËêßЦ֡«Moe1˜ÒÔhSòúZ ÇGžíè l¿djõàß ä°ÂsŸÕÓ±J^]™jLÙ*ƒÏ~ºd·ìŸ2zªmlîÔ1`qجjr:]w[vx¡\-?iº"ì mÌJ›.Ãëõ´ïýiÂ7v K 7&>èHÜ(‡ô÷ Ö£#(¬£¤©þ—:Øæ?S.¾C`P ë§L‡ÄŠÜº¸þwÖ„eS¾SX>ö°Â¾R¸T`âC<÷ã¿+PòÒrÌÂü VìFNSµ´®‡‡ ?“0nP߉Âß?W®•üJÝ .åt^+*!ÏçÜõ]qÊÉËLë=M_-§ yhrÓ¹ÆçôÉ_¨QoMC8A}[˜]=ã@Ÿý¤ª®P…OoÞj=¸]ÍÙg.ßÙÅ¿Ï ©åŠôð_^­{Mã5 Ȥ5Ë¿z#.ašÆkT÷ø†´ôµ#µí—ç! Qx¡tGN2ªuZèAòN¢Ç;ï™ °j§ÓÞòrÓ— sÎàNGø-abY¿Qüá—B¿²Žô!Òåò|Á7…×¾ÝLü ‡BžÎOÙœbã…— L(¸V?'Ðöju£O—ÕSa¯“ò¼žPGÛ– |‰ãÖ ÂÝÛð·OÖ$Rg”Uû S¦÷QÞ¯; »ÎíaÂÕWà íLÛ8ÆÄ_øÀd…:H9/…J³ta|–^Céü„ÓH—ƇqYe…ñ.‡õ‡r§x~2üÐ 6R£þx¿zûlÕÖÇv¾T8M &ê§_r~U_kÝÕ¾¢šÛc6úÅÕcˆéŠãü­§MºÕr#Õ5ûoV×[{Ù=c¥.]$-6cÛfkßó Ýe¹¢Zô!Òy«åótÍ.”Ë ‚™bº®0ìƒÅ áápA‚âYÂmƒº,Û¼ÇùËÂ;êx¯À®Òêðò%f’×Öß•ì…x>wÅ=-6{;Ð…aò{œóPçéC](ó)°¢^ÿklî£_÷m°æÉuºú§QVýÉÂlôëx²Ÿõñ2ý^ËšNçH)aü[ñ'|¤.< ÷JṟÂÔv¢~Ó&h†©Á²råu‘µ&£˜ÝߢýXu}ú’ºi†Séø¼Óð”Ž%”‰[ºÄ{](w'LˆóÂó{‡w™Á„U56p Òõ{Zt/Kb"‡i‰s ?WøªàÛÔI~™À†ÉÁ} 1“¼^"‘³ÂèÂv¦Ó¸½è‘_(ŒðËcÂS‘§D:|ÂêUge²Š Ë”ªÛD>A'ê窱ta×Á½®Ãs&ƒï2:}…Oî‰Éà|²8yXÍ‘ÖÛ¦“:“¨;«ã_ ”ÇÄ‚2™hL># Ç¯é:¼<×Ó&ÒB®s^M—$Ný! NŽ5ä~+…v géÓy8içK¸"s›×Z~Ë:›Ö[{U¬žÝÁÖIÀ{¸ú·ejÏе6õÁ9É+¡uÒ‚}o&'I¤.< Wåü¼ÍW±Fi Zë_Çê¢9É,™øC¶Xq]ïd½xÛ¦deÄj($Ú]­Có8ça>—üèÒaOrʃà.'Š Œž‡ã^è$v¢t]^–ëCNYŸè€éÔ™\|Q8MÓ)؉¼Lçéá4'Þu.{ùvΠ8SðánÉg n£Ä„°÷TáRá}“ò:Q>yºCn 62°µVÉD¼·Ã9I]9ezxžd|Ëä ßÎ(‡iä·6J¡’ïïTLܾ0Þåjœ“0ŸÛ‚OV L˜èAl…ÿE ]˜OÁ„Ðc§—“•&­ó°óRIÛÿRVŸr0LãulO¹£T- íc²LyÕÒ©=+ê5ÀcŠÊÑ_=€Œ«ªMß¶R7§o¶ ««÷¥q°¿k•ìÐ îû÷+­œ+éN²×\´7¦W»å6”Žþ‡tƒ&ì|²ìΊ§¤²òìŽngeÒ„,[ºªËWuYiü>±””ÍJòzÜm%mIßU™ž.‹§Ë Ód]ôÉ•¦u™¼]åŠæØJûiÓ¿ÕˆøÎ ; à "}9OOË©oÓò¿ž'DN0ÿ@[±#Nvv"gu8;ËsPÅ{Ï F`ÐW IOzûÚ©~‘è}Æ#xÀaÈÉ=(¢SÖÀåô{ L/×yXðv0!ÈJ¦GîNšt¾BïÎ)„M>1É* vÔËuì>ÛY›ªµuoèCßq`~w[½NvDؽ8p¨|Æk—Òx©_Õ«ãÖùAó´5©ar© „'Éb”<°‰cè'L¿”U÷¤ü(òJ£~ འ¬PeÜ•¶ÑaîÙôaÇÚØÆa_Ê;æèZC^î‘Ó`+Û­Ç ƒ§{%<ë2/wI(”QP&ÏTë`ˆÏ²™3gŒ€ÜÓO9¸ÿOÙ\Ÿß*s÷sÚEW%lX+ìJžª…ëƒ2Óí à ¦ÞflxZxÀ½}ˆÛ?ØM¡íé6¦ÃJÒ©-ÕÚ…ÞÛÚƒÞ'®‡ƒ±Â{n æv8¯ .Ž.þ \/„“·´ aØeçÊÚ‰Ðüõ‡”‡„e{Ûðef哺Dºîsü‹†é›íöþÍ: ]fðŒ5̱¿ü »´«kØÔš0-NºÖñ”éF]$ƒªÝí~Yû-¥÷z °ÐÑštdÇœ÷Nküó—ªfûÍÖ½P1÷çî›ûû}Éoj迾\6„kó·Â[Ëázf ¦·o˜lýBЕ”<ƒr¸Þ­© d~ŠJµÏi¶jdçÙì{»p¡ðSáó÷Õ9oÒãdh³ËΕ¼Ò¶P&ÞËñ›ø4yô¡œö¼¤ ËôºB›øšY®I¯ë5ÀÚ5eX£‡3/¬Þøú&žoNÎH]x NºpQ:‘ü¢yºQÒ ­Çh­R¯ïÊÒ &2Ïè׿ôa#¦2ùÛ~’¼ú¶»Ó÷OèÉj:®Ë¬8ôN.;GÊa˜•à<ör«ñ¡JóM!\]3ø€æW°R2uSTÍôO†½3Nç'­vO^-Py>(ô@ÝoŸT;Cú}Aa[¶R··×ùX醺z’Ÿç½SüLápöþ@€Â6z›ài6Œ' ¹ÙíÃv!Þ'p¼9¶ø4´ùo‰Â\a”0V¸O˜.,(ÇÇH/_ª„ºQxØy¨K2i< O—ÛU\WiÃ|™rG›{õ³ñeút®=¬½f=õJ8š¾m©®~ hál[pì kÖ[\»Û·Õ«+ºm·_ ÝÎp°%ÔT²0yFrQÞÇ2Ñë%\$áU]O>áY§;™}%‹ÕXƒnбíMòŽ'¨‘é¸BP·‡]†CÞÉy|ž¦x¶âÇ–Óùa;¨ïájÁŸ0>?`PrûÂ!ÜçäõS12>„CÎ]Ç-˜G›…kˆðÇnùøÀ*±yY”å€ÇÁ]öt®£ˆ7)¨‹¯âSˆ4n£çgÕx†Àí §[%œ.lüXyœsôaœË¡Þåt\Zïe†Üó„:d·;­Ãî‹4·BÁ íí¶¶±`[–õ ³ÔŸÌ²Ÿ.פF€dû¿njûÈýq°GëW9¯òúóP³ºŠyºsÌ(V¯DO^›@IDATæ« -êu±Ü¯väµ  ‘î(º g¹"ìÐ8Çd¿'Ü"Ðù¦ãIƒîç¤ËžVQ;÷Ñ¿ °š ;uOH›Xeþ·p¾ÀàC¹¯X ¶ >PºÔ‹î ÂÛ„ÐïŒÈï¦O×CÛÇ ”3`÷î‹· YyvV¦çÁ¶®!×õ/•¸½Ìtù´Áu¬À¼Nô‡ ¯˜ÈA¤sª&3Ypÿ†iÈçõ„ýl‰„O©Ûó†i±u¹pžÀÀ¯©rr~ýAüubâò¼;v9Âs8”‰O‡=O5Þ©’rÀmíeÚèít½ë^еߺÙV(r)ƒå-pØé¬GâÙ¬´¿C;´Ð¹©ÜÚ©Š8 #íÄzš´(,Õ,yµ>0Q\#¯Õ£ã¸® n¡IŒ.½þóǾƒ­Qß¼á…st»K”Áªkð×ïè$î ‡îõt!'å.®è°‰GÁÁá©å0v®&§/…Jaeè5‚6Gr]9Ø%s[ˆ¥z}¡ÞëFÇ9yº0.”±qð"á/‚oú]#ùµç—%±rþ¢K£«øjqèw—¼ÞFÊIë*áÖ­ÖñļäáÌ…$¼O‹?é× Ñ ú¶›Wo¦ð¯§ž£MÎBeaP/MÙ§v†í>­¸ž*ÓÉTÜ0Ô–èõ’Ç8ÑÐÝÌzœ%Ó31Ò3Knn·Ö•‹­ÿÖ6F·|%ì³eš œ<ê<®+î¡ÖÉêëûâÔ…“Oç<Œ#­ë‘!œ´¡]ÈtÐÇ ÿ%0¨úÀÍ.ÀïlÛª`BÞNÃév•ÜÆ®òQv .)'Ä6l„Òe¤ÛYJ•ý×}3KÑì|àžÞ¼¢·)Ëw®s®äIz|JŸh¯¦¤ÉO÷2‰òÝpŽÃ¡Â}“:ÈãÝŽ’6ûo˜Æå´m®çRawèbáêÁßÞ!—Õ’Î!±"{Zsîç:i<Þò68÷<„Ãóå·ê`äCÖ¯KnÑÐ7øÕÐz#Læ€üMg’v7Ûµc{¯ú5[8·r~Ö[“ö‰½~Rî“Êêµ’º °ìgú=€œÍf{é¶Cêó¤ÒgíFÍyOvë6[»öik.´'¿ð|›sÔ¢“óNÂ; Â]Aшë@pò2 `’Æqþyʤñ°ÇKÕ)¯—vp®cpø•À ·E`E÷ÕƒOÖj³ˆ +}VZêï¹/=íéΨ‡ÉÊ«(íté¼è²ˆtä¿E8EX$¤X©v™8^!¸ÏœS˜ÛçÜuÎÑã§Çvg ŽÉ%”®|ˆo|2¶V2ùv•B[ü¡ÞeêÛ$¼^ø®Àº¾&p­ø¤ »ÓÏaE'ç!6û9 ‡Èçi½Œ$¢çrȱÑ>”9î~îç2e“ޱ?rÞ½@¸ðñ{ìE ´ÂÓºJý]zÅÕ5«U7¦ÏWj·V¶ß5¶=PàåæH]zÀOÀ.ÅÈ’ôkS_ÓE’›?ÈÚVéÎ`½]$´‚-2hõÓ6B³ÿïê=`žŽž ŒÎþA8BàÜà¢Ã /¤PVT—D‡ã”– WCWyˆó²œ»ŽŽî,áçÂá(EØK{è| ‡ùìÔ>»C”™UvXñÀ‰< oÐÓ)ÿL cÆV(§¤íþ_&@<ëÑ*„uw¿„Î)Ù­xð²Îêʦ.÷ I½n¸H¿‘Ìí!t/F Þn‰ˆ²È·L˜,p<™œ9¿7ˆr¹Þ+|I`2‰— ì0ñœ†Shr\Si‡Óe w etîKÃi™0öÒÊÑ&ò|ÃËÅÇ-úúz3;uí ÛºYoicºÝYz—NµN4°M-“ÝÉ9õËœW¶ðúüTëMÙçöq2Gꆦ̲¼vÖg®êecÆh]0¶|駯În·Ï“°sñ¤Ö/ÿ5>ùeÃÜê×À2d¤0C8Ið{ÝtÂv¸¾Xm±:EOsÑ9yó£w9‹{'˜æ¤u²çEy8Í=ŽÕí‹…ÿ† ظBàÀÓm­F^fµxׇé\¦\à¾Aï Ÿ§C =6ž*¼[`w„ÁšõQ!‹B¿gÅWÓÑù÷±"þ±ð'{Ó¶8oçøÎ?üŽþº²ü-‡aÏ‹.Ô‡²§qîqpìtÎ ÿo L„ˆã:¹]À~vs<¯ÄäÜôs¾P¸Oð ™§Íâi²u¢´_Ã0×ÿXá„sn‡a?v¼QøžðNÝ&îš~¨qõ¶¬Y_üÒqúJ¥Ô8±°é'o~c²õ\V³v4ÿ“'Ïìtj¼ûÏ |=HຫÒa×ׯf§ëá.7Iæ8Ý#0xBW i»¯ º¿CÙQ¨C†<ÎyI[ú»³²Â´.“‡óŠÉ×Ä/&Çq¾@{8®é:½½LÞ.píøuãiÝ&çJR•¼<ç”E½„µNû‹ÅϸžÑaã„ï L´¸¹†)çÇÑœbgæÚmÓƒƒ­ã–aÊì5(²©¿Zþ‹±É»ÿ¹†‚=´`¶ý`ò,kÒ.@[^ÞìÎÉV–ÖÇœc“›:ìný,pãGï±ÆIkjw'€¬gì]šçoR÷¤«ý­‹ï²«5SεnJ:*IˆUètÁìuDç@§=&ÐYß,|ö^½ö|Õ mÖþùÛ­‘¯êñ´Z#ð°ŽÆ§žkبþxº&Þ–Z3·&íÁ‡‘vÓfj¥’³ lµÂçnÓ÷´upÖ¹9ßœ¢/šbí²ï~ÍŒ¹H§çÑ¡É舣Cy›ðaA{•GbBë/÷= Ä €÷ O ä‡üÜJOˆë#qNÚ´Œ®+ mÃ&‰<Íï6’×eO†]G^dVÙ?؃*Ÿ#p-xZ‰ÈÛƒ2m¦,OëzçÄõÜBš¡À?nqäÞE»&0åTŽÇà‘Ö°æIÛ¦-ô›µãyæÄµúmì»å ¥àÇÐj…Øúo“M:ÅŠÏô¶œú¶ïΛmo®ûêÅ?ðõboMØ9e†5j¦ùaƒ7¯Öi|ù8ýœv ]8‰ûþ-²IÛþvãH+èiïØZy½,ËZttÂç…ÁÂù ܿt:RÂG„ï/X±Íž/Й{ÇÓ&òr]¦ÃeðcÁ¯¥í$ìø”dV@Є— lw2ÀBÔëu‡rYþ“Ž÷p˜Ùõ^NV8Ôy~O†‘_,èè$íDüz¡EH·YªL.íÙå¯73á.(}~²d&­Ö×;'3}çÖ×ÿ“dvs¶Ïå#§‘d,ÿ ãPQ'çç+a&cYuIPX ÂØ ¿Yxµ°¬fGí~á8a«@]{JÔƒ½”Íu3V¸@`geOG o®-sv*œô¿"ŒØeú“@{)¸ßMƒÇá“­ÿ¢…v®VýOòûWéêfSI¤ û“èkù<ë•'XQ¿ÍÂÖÿ_ü™^9†ûÓ¼ºª›ƒi7<0U“½gÚ¢ïé/л§£Ÿû¬n°i ̬tO¸PûèÏÿhxùÁÑVÐ'2ó›¾ðF»qÀkZ¿²2@vuMÓ©p~°x–ð+á«Â,aº1ÀÓ‘0a ¼]˜+¬(û {£t^Í‚Ÿop‡ÛàqŠêÔ×x<œº°;^#Ð!Rî§Ë >LÊŠªÄ1éÀöt"|^ þ°ƒô:¥Þˆƒà¡ìº#C´‡‰vbóû&1ø ›,\*àKo«ÄŠÍn;º}E]ù`wlÀÇ´Ãý–ŬNþ p<9VÓ†d(ô…Ÿgî38õpn@ž'ÝÂNn‡Û†Þã»ÃÃ4ÈØÄ¤Œã{¶Àí)î _† \7!yÝpÒc7lV÷ãÒ£Ÿ*'à“!çí…– × ?æ»Øã×:§Ðnta9Aïþ–ß²ÁŠãO³c{÷¶ßk0ü°ÍVø—–Ÿª'ëufïíÅNa»¿Zrë=µxŒ67Z^ƒÿ-Zù¿lþuɤ´!ÒnxÀündYð€žÈÍ›cE=9;R×ÃlÝ8ª_›å.[`¹Ó4ÔmÑ%ÊWµö&1;gð_£®æóÚÐ|¼ŸµëÂlÐ{½/Zt³ý¹Wkغ¡2È%×TÙd§PvŸÎÑ#;ÈCÇM§uˆ0Kø”p„àõÈ-IÆ ƒp£@§ÄJX›ŠIçE~ÊôœNÓ½FzV¦§Þñ1ñø“8¥ª a›©:ZxJXC Eaò|„]¹Ëa|˜— ƒù¯…¹ÂVÉ=“!ÊpŸ¹ εOÈmØÓÊ8^Øþuù½Ç‘cJÕÀéJU3Çùzá‰%äþÈâœg›Ú;„ÿ<õ¥)Ô…2é°ËÎÃx×¥ù0%ú’ðB6cÛׄw îˆ|ØÈµÀ€=I+àÎâΦ \_œëä…C· Ÿ.s&×uAäõö'ŠàOÚ^¡Îù|ƒ¾¬§W‡™h#†n·j þõgÿð¸5¼fqéá@ÊÝ ú5Êådúæd=1{˜µªŸkÖàÿó vñ²¿è§Úµ‹¯üqvü ï^â‰ç&ïØ7ëJ¾A'é™ÕÝ¿Tu_¢éºæïôfœÐ=íp¾à±YWÈmê®>¦4Pó”ÿW?t‹Ý¦þäõÅ?:‚ØíadˆpšBs]†§e::x:':ü„³„Áä7òu;ƒ ƒß#‚¼Uy/þ’é)Îôƒ66Ò¡¾\˜-@;kGØ.—±59$I ¥?¨*¢·…ËÕx%SJÀ/Øþ]áMå8&5ÜΠ­Äy[Ü犪+¿Ð-¿ Z´Çýæ\(|_$hiÓ…G…Ð.§9e,~'¼Qð:%VÈëwEeâ» ‡q.;'/öNx¥àçþ$¿EÀ?ØÎD—©©AÎv?¸f˜øxZv]š›~:¼iÓ[{Ôöüæfûˆ¾ŠvÆFýàÎä5VŸÖ5ìïéáÁäH2q%d‘"=Š™1X§áówcÌæh¨dõ¯vÐ>ѶÙ^öÈIçÓC<9=ÄCV 3ò0òp)´½*{{» w™š!×eéé¼è¬ôþQø°@§‡Þ;h:±Ç…ÿèàè(~,¨+Hh“þž!,)+nñ¶”Õ;°Ý/yu{[)ØÛçíõÊ\ïaç^7ñƒ÷ Ÿ ? LŒˆ£3ó´ðPV°®É}Oƒ†ágOãç«æï”ã8O¸çÝKà²qÊò:?×)“°sÏçÜëôpïNòe¥sçú;…> `Ò·Pàü~ŽÀy «99ïÅ*ׯ³’?-üL`‹ÿ›Â›„#…åy{ik5Ÿ(*!·‰€Ëp‡ë=œæßÑ{ õ9Éf4÷ÑŽVNǧ¨·ôÓÂ#õq±™OêKIš ÒYÍÁK SInœÂ䀃žì ø6{”õju‘_õS|ƒâîo/ÚëVÙ¼ù÷'Å‘=Ry€©ç<ÀÀk}‡ZnØ8kyì.Û2qºMmÌÛguOþ }©j·†mµÈTMôP ŸçµÞº”yVÀ¿¸Å\(½mU—¸©QO‘5[ñAuƒ÷µâ¼ÁÚYPÚæV+nÚlm›ÖÛŸ^h¿q¤Ý¸n…5jÕ_¹îÔ´äúKqZìz—á_§á¹árÈé|˜°“}RUttìjEiDüµÂ…I¢<Ò¬~*¼\&@¬^&Ü"†>#$·9Ô퉌ÿÆ/~)<#xû$&”ö8¸ÛÇ^‚?<È]"üD =Þ‰;'—-=A”ç6õDyÝ-ƒz!oOÈÑã›P‡F <+1E ž‰Ó×|H<íð¶TãJRIƒ¼·ÛµîM&(ÈN7l %<_x»@ˆãš Ÿ†¾Ê¹/1ÙâçÙ{v ¸Æú´›¼3„Ÿ ä÷s#í÷‰’d’Ûrdc7ä<ŒKÒhШ/‰nëh·S=Ê^®×‹§77Øá¹f°µÉŠ›õ¥ÑCÔºÃ7Zá0ñ![-×GW8ý; -§U³åÚÇô“>¾P´T;™÷já³ çØ­žkÊZ|ìï•ö&‰âŸ=÷€ð=/)–€Bæ4 çW.N.n›8Í&74Ù û—jå’f¸üvµé¢hÓCƒ!¯Ï ÝbS·B›¶×ôÄk“MšÙ&ãžYõ„µ­[i#7¯KVWè^ÿ Ýë¿QY— ta§@ç¹Î¹ëBŽ’·É9å]&|K ƒ"ÎãÓ²¢vˆÛ,«Þc„YeùÀ(±BnçåÒq;€[˜ ɪaNJv4Õ¢6õm·ë·|ÿ¢ÆÞÑÖj÷?r[²Ãa‡kkŸ¶ŽMÏ*ÿö×—KåÄ¿{ì?${\P, ñ@èO—“íøÆæÒ-ÿv]¾}'XïчًrMörmsÆ…T¾RyÐ¥”i;mÒÉÿ žÆýîÁýâ‰ì1E±šøB9Éñk…UÂõ‚_´te…u.‡ò6¸LX“qû«p‚pžp·@}žÊ V%Òae޾/œ) ó2¡9«Bçõ÷A— NžÞÃΫéwÏ DÝtºéö‘7,7”‰s›á Y`57M€>!|J`òäiBÎq{ðU·?ö„|@>-`3á½IÔá¾ ¹ëáørNû9¿¾-L-‡'ÎvùEÂCùÜþq%­¤E†$×g•ΉcÄñÖ7 \‹£… Âa—³HˆrÉÏùp0V8D€8w_$0‰`‡ƒkÕ혇áÝ‘Éäé’ʼ Yƒäi¹\Dr}q¼˜Ø`ëVa†p™`Zм³ïû‘Vù ê£ W'ñ\—ç œ÷ó…¹Bõq놱q›×ê9ÀÛ²e½&ÜÔºýù¥° ¡¬D‘öÔ\h‘ö‘Z7ëá–ÍPt‚ëâhÎë>˜.–|ë+>rgòpÛ³)k$?-´5µØÎü¢º͘;t‘@Ä_+\N@Dòsa¬pŸÒÿ³ïàvVÕ¾þyîUP©Ò{B¤ "Ò¥X°€ˆ‚HGÀcCÅ‚**ÖƒëÄ‚¨Hï(Òk½ "zîÿÞÿû®µ~‹‘/ßÚÙIvBؾ +ÀpØ×3Áµ;’ؾ*é§¶ªWŸª7óñkÆ«Ý3ÄŽs®ŸýßìëÃüZù«§íœ3³Ž/ûÿ…m!}2¨w„¯ÿ{xRg<= ÌßíAGþ™Õ#à&Êì?ÿñÌ¿ý÷3<þÞ[ôÏn¦´Æ¹é´üTöù§ƒÿGžÿÇÆqëÈÂp(~Yn æY ”Ì­TÝÃÙƒº·õ¦Øðú´‰öš–¸aÊJz¶r´¥ÝÑ ½ü탗¿‡Šò+ð 7m2|¼ô×ǧ@YV‚½Á‡ÏžŸˆ_îøØ&ëõ`I¿Q§Ð’Ú'õÄ£'4¿cìXÇ]ûšðJÐ׋㇯<ÔŽ4ËÕ¨ÍùOyÚ"µÞØFÖ1ÿ ¿~]O^xzNÆ#mí«6õ*Æ›ýÔv\T?ˆîÅì8+µõÐIl¤ÇÖ ë˜¨‹ûéc0±wýˆó1¶‡­Á‹~!x üÞ«‚—¿âZõb÷¡`¾ãà.pœ{ÂM øØ8¶ƒìÔ©Ä>V©}ŽŸÄ›þÆ“ßø4ã±'Ì­ŽÁ~8îÍùÁ9ôÑv'tËá¼âËž?Xz9sùcQ¦U_×køw–Œ@sÓÍ’J†…ÎÔdÃ%LaÙRGcÈeìáá†ôÀñËæRÜôì¦ÔÎæM¼†#¥é§$ìÆžý›6*†¶Û×þßáóð9°}Ú¾öAÝ ÅÆÀ¬KY|(œöÙƒ¿9¦µîª[‡ñ¦¤î¤%®_lÍ<3¯uDÏC¦–4mÑk¨ÞD_Ç$çQÆÏðp\ýÊV¾;À¢FzRûlÙÉo²ñšݰêúFFy%L/wç×ù[v÷×| 8¶[ŸÏ·á!ðB´nӬnjqÅxÚuº¿||üº>¾>BÜî´µ/É_Ã~bOI}Õ®-öV]ÿÄ“7m¶=ãzÆ¿ú(rï)?…{Á¯ýÊ>9çðfþK5/§†ê5n‡j\ÝCkSØ¿„› Zó 0þ(¼ V†àVx¼ˆ -[?Ë8ܘÆS?jGŒ·ab|…úX®£õÔ¯¢Sˆí: vO×àQËß¶É IM÷’?Ž„+a",Ї’‡è–ðZ°LmÇÉ6Úë÷`O™ 1u$ñ„Õ•Z¶“_‚e·m{‚‡º‡ÿ=àAoõ/eâ2EùÆ•šžx›-ŒN¦™ücùÍ:Úê®~M½éŸöéÝ1ÉÜ<îº0í)ð}¸ÆÆÁ/@{mWÕëøF7ŒŽÚ×cw-¼÷“éE`ax¼|„, ^‚Ê_á:8v‚sÁ¹¶ŠmJµ}U·÷ƒûö۸†¶׿ÅàX´IÊ©aÕÍc|I)¬i¶Õ1Yì—çÓ'Á13í­`ßË*öÇý8œKH»ƒ6ûø%°Í|˜†2+GÀÉÊœ19(jkr8µ¥é§Ý/cÀKÎMè‹ü*˜“ e¸¹| z hÏÁ ®$T7MIXõjÓî_þ?„}À¶()Ó/$L¡xØ×ß»á[à¡êaI¾Ä›¡õZ†uýºÇ}„Ãc°(/‚=ÁÃÙÕ5p.L+wÂ_à…Ðì¦Òô5¿í²í^ÿöɾ{Iäòoæ#©#é³ý‰n˜xÕ»9Úÿ:.Jòuc3÷·Ùfã¶'ö„ÖÒÔOŸÄ-g¸þ ö/CEŸ«á<Ø œï=àsàbÞ¦˜'cPÓô5͆såt~\»+²à#À¼ë€¿¬ ñE팩ëç88®ÅvY¾ûqzÄõò¿Á½s¸÷¨GÀ °Ìm}²?éj_׿ü+êUÌ£Ô¼ÕÖM}öoÊ·­Ár·—„^üw„÷€ÿÔ›zº©S¶'õ&mÎÆpÑeÎl˜´(ñl(í±ÅÇëçàÅ·¬ãÀWö‚ज़ünàæ†KIS¥iSšyºÖgÿz€Yÿ;àB0n¹¢z°ù•þ2Èå¿;ú! j?ú´ÄríSQCì'Á6{° ¼PôõÂX ô±­—Á‰àåãƒÁƒÝÐö›ß<#õ½™žÚþmË2 Î/NóE,_’·YoWÝü)G»bêÎùi`“†:Ý’¼†’òSPì5ÞÔk¦%OìÚœÿ[Á ×Gš—bÿ<£î×Õ¦0ì>ó+jG2͸vq^¿æO÷ŽëÁÇškLJ¢ó¶Ì ¶1ym‡ûì“p8ÇJúaþ‘$mK™ñ5î¸:7ƒ_ÃÀ`ž}aQØl¿õ´•¹ãŸöTõf\Åz#É›x3tÿx¾äWŽÑÍo; >~ôQÒßnløwŽÔPÆv²‰j¨^ãÖXãê›À x9= m›sGÌëɃڑf<ö„–SEmÖŸCÉ/½Sá0¸<èŃ_Y^ëƒíŸ^Šóác8Òø‘ÜSuëöò8,_ù8ÜÎMS<0ÇÁ_ íNßj8-=åê7\?~Q[¦¶€:PâSCW‡-àzp,2Öú)ñ¯zlͰú¨Û>ÅqxêL{Ö—âk`AðB¼îEŸø ³.VÁ×ÇÑÊàCiIX v…ýÀtÅ~šçjø:¼~ ƒmv]Ø·Ô‡:Pô«’x3´\÷ü]àƒy#°|Çp-Ûf¿¼ïy Šå¥Lí‰»ßæ‡'!y’†©ï§IzÊóÒßÇ磰œ/×S$uoÓG²Õ4×ñØlwÕ]Ë'À§@߯Á2à¯^ÎoígÍûOÒ,ÇK~Ex!¼ÆÁ¿ƒ—뢠X®}6ü,ü |ôW’æ˜L¤mµ?êÚ]/>4\[Ö㞸|t¸íÛ‚ÿÌåeé<ü| œ”‹Ú*KauìnûL+¯~¶Ó<+Aî‚ý;Z·ÝÎ…k¶è;š2qëÈôø&Ï0£pñ eθˆ¬‰’Èç jkt7¥Œ‡`}Är”ħ'ìæœòoÚ¥Õ6y8ßãÁ¸Ùzp ¸ör°¢öû¤>#RëN~m©ÃúävøL€ïÃó º_7^཰)ì þTì d>šuZþ3°'¨›~LÛáøF’÷n Áý ->†3¢›Ç>‰RËéZ¦ý7yì§ýØ\_^N®©¤*‰·é‡žOô‘ÂŒ‹>U÷2¼îë[ ü§i·ø*¶÷åð6X†ÍÁKôÓ° , Š}õöFx ^þ)µ3‡)ÛøôŠy“?¡ëâð—ÇÚv¸Î|ðü„ìßW¢~_ÛÙ&™„áä8ý¬C’†:M±M!yŸFwO¤]G¡ûËÌh¥Ö­>”çhrH,DÊ :º©í“ÇÐvüÖ/?ÛÙåZð`¶¾šè˜HʬKlVàX)~Ñ Â÷`x#lãâÅp5Ü„Gà xì‹uY‡_r+Â[À‹r^ø<ä‹KŸf[¼ø½ôM›S¦¾Í|InÜþÄŽÚ)¿ÆÙ´$ÎcÚê—eÚ?ÃØ«ö¤Fª®Í¸kR±íéKÊL¦'oú8 ÛOÁ ÓGÛGáTpìŒ;æ//Óezú Wÿ™g Pì‡bù×éñp#()/íëZ§ÿoíKÍ>;¶×¯y˜îדé¦Ý ö×v‹ÁFp+ØÇGÁ¶Ž$¶AŸmÁ2ýE0ãiZÄ:«$®¿å{‰®ýÝÁ2´\ޤæmÚŒ›Þæßa8›F`ø˜M=Õdä(ãÍ |¶Óa7ðÒñ‚¿&€‡›@d”oÃ^ðp3+)³­¾®Ç³›×xü åiðKÅCÕ6ü<¨ä3ð˜U—?E÷%íÒ>©k÷ðú;L0/²/Á·Àƒîmp(lk÷xŠÐ1þ-\öÕÃø!p/Ù×wã,ÎÅÍà—§u6ŶX·¨ºDj›cK˜´äÝxÒb3¬åVûHºå¤,çK±­Jì걩+5_×òìߤùïØêöÛ°¶;zÚœ¸õ8Ηƒã²à¥¹)ü–€5Á õ¿Á_^ë@¾òµ{ÁÊ àÅ{8¯JúâCbf%eÙ¿AsïÚ²OËQ×L| ÓgÛú |V€•ábØn‡ÌêscÝ‘POÛ’f˜4õŒyæÀõëøº¾-gcÐÿI8lwmå<”9q†€9kV²álU6RÂÚÒøe£š¦îKß/Î=ÀÃîmp¬·’¼'¢¿ŽƒE µD7´Ék\IÜÐtÎ-Á:s=€>–‡ÉàáSóå’úlcÛ›‡˜é=àÛÁ'auð13?¬^<ÁïÀ/È»àjPsJT/ç"mHˆ©/±Ù–èýÄž’vOµ5ýg6>¨ì6{›­Y||xý\+»kPIŸº±gÿšÏ4çÆ9º |, Žñ¾à{I=¼øß «‚’uèœÜ_ƒ¯ƒsbÝ®û´-¾˜fZ|ÜlçŒP’ý²nµîôÙ¬®Q(ŽÙÓðix ¸]{ÖáÚkžåéIýþ©+5­kéþmÎu;n®yÇË:|LÅõäãA[ú¤>HRÏh|•1´Á4Í9,bŒ@Ý(Ù<©¦¦yÞgÁ6=‡W^ ~yi)9¾‰îåïáj_Rv³Î¾ŠĩíÁƒ*e倲]“Áõf]J[™–£Ä§»¿µ?¶1mH{=„sÐy9üN…uàM°3¬^>Þ^6WÁàA¸ xpúòa µ_ßHzÚ¢O$íN¼éã¸êcÛ›iæ1-}5>-±Œ¶rÌ7Èž6¦žA~^ †U >:‰vÚh>çÁ1L»µ‰ýó÷ây#˜¾L‚õ`}p›ß5”uùô“à\PæçÊòÒ^Ô1•ý(íËàzqOë¯ã`?µ%4Ÿú“ð[Ð÷ X|DýÜw®UÍþ˜W1Œžò; þèã8:_ËãéCåPˆ|E¿Ñ”—<†úGҦćáláàý`gLk¨ž¸›’0iFϘˆü'\ ^@Ï@¤nžØR¶¡—íÒp#xX<ËððBjÛ°)w¤°¦E÷ð Ú¾ жkáeþÅÓ€qE¿øT½“8ÆÒ&‹^Ã:7êöǶyŽƒ“a<(¹hô/cå àcÈÃß¹M™–“~¢N%¶czIJüª¶üúؘž2š¾¶ÁòÆò!–>'ôrv¼"µßÑ Å<ú{™® öwØ ×¹ãnyÉs'ºå™à—³>ö)õ¶é˜ÇDlÏ&p>¤¨EŸHô¦ .o€wÃæÉ@èCôwPûhžZÑŽÄ–¸aÛXxfl K‚¦Gáp=ß/Bò¦Ü„$õë7ÿkÀ5z œ /רÂ`¿¬/eÔŽ$-ña8“#Ãj&‹f£¨ <›ÈP{B«rºqrà×tõûálØÜ\~y!/ƒ‡DSR~3lúY¾(–ãaüNø/ÈA|º‡¼nj¢ý¼êU,ó4°o;Ô„Y¤[_³¯Æ/’ŒQtÓlÛí0…÷ÀNà¥äÃ+ãbè!í¡7~“À±r<¼Ì,?þ¨}‰-mé' 4mî_ÎÛàfx,3sq[¿ËyìóôHÚ?(}höCßä3Í1w-y¹ØŽñ°<øëŠã¾l æÑß±4Ï}p!þÊàús^ôq¬ôiJêmÚ§·Ì‘òšv¤mÓ*/}Ñ/z µ[§së>q-)›ƒkê§°?|·61$ºu(ÆSŸqÇêŰ˜æû X¿cúp}'?êPþÕFÀM1”½ðËz ø:dã¢vÄ êæ=¶/›á:X ÎmƒÄË2›s£ëçÏþ‡ÁÁ=†“`gðbÊ!…Ú‘f±'œŒrC"Ó›ýž†û %Û^û™v{))ŒŽÏáð%X<€—ÇÞ<~­~5ÝÁïวú€s.̣ا6iÚ·]þí¡à¯0yZÿŒÊ_Éx$ì ›ÃMÐV^Æ„äŽ$Þ6/IšÇ:\»öÇKßµê/YžQËÂf°LÅñO]†¿€}À® ÇÕqI½¨c&Ó*ӾخÌíh*¶Ló(Ñk¨Ýò´öí!ðë_ý;°®QÇR±Ì¸á ±|eð’wÝçÄ´;à pŒSê ‰íÊs4n®¡Ì9#7¨E^^ºß¿„Ž_ÿ¹˜P;â¦òçþ»aiX~–o¾'¡¹ñLÓ–uD±^/¿Á<^þÇÁ^`;›mšVßÈÒùÿî G#£)o4å4Ça¤<£ôÍЋH»—¿vûîá¼ (Ž·lûÁ¯ÀŸ@}œ÷‚y””ßÔ;‰½?µ½úˉ ÝG—R}º–Ñÿõ?.;®‡À‹¹Y^³¸ô}’f¨$ìÆº›6㎣kuMp¼´-ï€ÕzñÚ×ß…°ØÆUÀqtN\^À£Ël¶g¤|Öaû~^¼຦§\ŠéˆyÒÏè5ì¹uúxnÏ×±Ø ôû4,„ùAÛHÒV—}” àXzæì úÊgÀǦs6Z±iKÂÑæúÍ¢>fÑÀŽA±Ù˜ -ÒƒÆM½6,~íÔ9Œ¯âð]8«|!Ü„®o݈æqCÿ¼^ ÚªOt_ýGÃ{ÀKÏ6‡‚?»Nï—?Y:’úROìÍÐÃçMpLn&Ž2ž±Š{3{BÛ¤O³mŽÙÇÀ¹q‡C`9ØÞާâØx¡É$X®…óÁ¹ôëÛòžmí©¶ª;n‘jmzBë¾nõZvúnm:æVI›’G§<¢ü‰y)p¼Ô]×®ïWC.sÔÎxL&tÎ…ßÁV°,¼Ö€á 7í÷ƒu*Ƈò/<õòøîÆ\Õt7¸ q¤ù{1éGÁ¡àå´!œ«‚v_÷U<.…ǪÝ.æñrÿ9xûWy©yà|lÏŒ^þdª=ÚÚľ>B>Ðæ0Àfûë¡•C°é>ÈnÞ¤eLÖÁ¶yÏî8ûÈZüiöpø¬;ÂûÁñòPï†'àø \I𘬧Jê×VõÄ«­êµŒÑè®±¶õÑlqëiÚköÅukßÕ‡Õag‚ã–ü¨}i¶³Æ«ÞÏÐPì£\+€}M>ܦ[2®fŒ^C펩_êWããÚù¸ß ^Ô¯×cÚŠ:ź±ýJÊVw¯Ï€sáœøp=üî×ÇŒŠuEœ·¡,\KÕŸh§ñ–û)ð%p3Ø.Û;Iûj¿ÛÆ.e¥ìꓺ’¶Î~©*ǃ‡pÄÃÓÃøø|v‚ƒa"8? ‡­¿øð:®[ájðà¶Ýµ DûñØ›¡>U’^mƒtû(5O³ß¦Å'i–{tû¿ìÛÃË`<86^â^T›’òÌãåóm8‡\8/Dx±½ ÌçmŽëdˆÔöŒdKÚhÃ<0jßG›w´~);}0l׳ÇÀvùÐæƒÊ_˜^¦i%e5ëpì—eúº¿ÿçì³àº^I=Í|iGÓ>ŒÏ†>fà OGu“dc$´õøT{­"v7¼‡æñp¸y=|kÂíà…®XfÊÎá×EDÝ‹êg°1x)y¼ NëéUÒÎj+Ý6zù»~k;ÛÊO¿Ö%ñ0x< µ}ú( »±‘ÿ:vŽÁ;{n¼'€ãP÷•ó Úô÷— y)Ø–­!ÿDà¡»?xû•é¡~ø øø@°œZ>Ñ~»kû«®Òfë¦<û7k!–Œ“y›zµÅßP»}Î¥±!ºs¶ ¬«ÂF0ûëx:/~aþ|ˆ:·^ø®c}ËÓî‡CÁÇàn Ï7as˜Ò^Ô©Ä2’^õ8j³MƒÖ—ý›]b;k£gÿÚIñ²÷áäZñ°(L%ÙžûS×e) ÃÕÁ±tþ6ËVN…Éà:4.$MSRç4‡³vêB˜µ5 KŸU#ÍdØÔÝăÎîà%¾üÆÃ]à!b¾&U7}8<¸½èæõËÀtmU’¿ÚÆR·Êh!Ûbÿ^ o„ ðg°ÝŠiJ3ìZ»MÓ¿Öçeµ/,”wÀYàCK©c î\ˆ{ÎËðFø|×wÃ^ Ø?ÇwØ|ü&÷pÞ<”-˶9þ–©¯ÒŒ×´6½úgìôkÓkk>B^ |hºæ~^JG€v/õŒ‰ãz? ú=Ž“vÇÒ1OÙ†µNÇýK°#l/‚ÁÇÞ `JòwcS–QˬºõX·ãšõ–üÏEh{ÒuÛåC\qœl£¶Ûáàšø0,ãàbx 6}Sj_´¹OÄ:£Àqp­ΧãÞ–ó@©þu<«}`æa¬³¦ôa©Ó;nºH݃ô6ߨ -ϼ?7±ñmÀ ¸2Ôù¯u“ÔɧŸ_~AxI)ªì×€—P3_3ŽËs.¶é|p,¼üÓoã¦Õñ%Ú‰W›}v"æ‘€­ðA<¦$Ž«e:~΋—ÕÞ ÍC{x‰ÚÎ`Sø6| Þ{ÀŠàÃÌŸÁ-£MÒž¶´6›¯õ´ÚßÌkÌ3/,ÛöXð-ðC8V/ŇéY°,®ÑÇÁ>{ÙØö´#!¦)DñÒ;ÌçîÎË É¸Ô:bKž-Qî¿vçq2ιãúuðBŽØÿÉðk8|P)®ÇÙ~=Íþ&îüù뉡e, ŽëéàƒÀúã‹:• JK»Í0ÈgªÂ††Y;ÿkÖ?,}&F n‹©›F=˜æ}¼PªhÇæA{=xh¸±W_óú§®º¹=$^ þZ°Tñ±ŒÇÀ|üJÍßµÌym£ãã©Ø~¥†ê‰wùã¸î ^2ÃÿŽÍÎ0ÜCÀ©àhëÌØT{Õ-Ï1õb|||l½¾“!mò?|| \þ\îm;,KɾMýÝÝ¿ÖûyHy5”_W¢@IDAT­ê¶Gl·­íz¼l÷Žð^øøp}*Žý™°7¬ ¯ƒ«À~+–UÇ«©7ãæ±l×öåàãE±ÎåÀ´ŒjGOÜ gÁp-Ì öé8Ø\ÖI¿_‚Á2ÿ ““Hx<ü²ÆôOԩƶ™®O•Ìe-£¦õÙ0ÍÅ?ªV1ÂdSè’Qç¨mS™îfý(< ‘ä·L/úƒ _œ{¢{±MÓã‹Ú‰{0ø•>,Š_gôÛ_µ½U'iޝfkß›6ÍKýd8 .ÇÛƒô]`Y^j€xsL0µJꬡ)±¹µaK8¬Ë:ýç6€%`øøßc¬ô¶Ã²S¾ydØ'÷íÁ†}ÖIÞÚÖµÀËþe`݇ÀW`WXò@´Ý‡ÃÒ°ü…yÀµ–yA•¤:«[Ï9ps/¾á¶`¹‡Œ¦þ¸h3M3]ù-l ÷™â¸;‹N£Nû¶?õ"7›}²Û~| þ Ú=|¬:×OAÄòÆkÛòëÅmË]ðp3ž¨iÆcŸVh[”¶±ï¦ ÿÎòh.œY^á°‚æ&©ñPâ6àµ,x-ÕÇM}Ü +ƒ—º‹—…?åGÌãÃ`CÐßͯx°¾rÐjËá®ÞËÉo¦=ñ:µþ´ÓÃR‰_ÂØ<(? ~+ް~²—ôôA}ŽÝC6‡(êˆb›Ä:®çxiø0¼VÓÝÃÛõøáïá½Ðv»>,Cß´u ±]óÀÁà:²úÆßð¿Á‡ÁB`;VMš¶myÐ/c÷ºkëp,(®K}R¾¶ˆùL$)7cXýLsN#®ÛÏ·ÁöÚö¦¤¾„¦GOØÌ3«ãÖ뾺Æ{Õ¾Œ´×œ×Aby…³ÁƒëÖ5q<, ÇÀ ÍG›cå¯oÅõõ]ðו¬ÔþX©G¬¯J⣙ךo¨Ï¦p#elG ‹¾†ê5n5®î&÷gTwÅCÿ^¸ ü:KÍ|nÐ;ÀMþcðR/jG·lï;aGPŒû•ér¸<Üø~5xðX¶—É–àâákÙÙÐUÇÜ˱>Ë_'á9üÓ3›’ƒ3aƬú¦Éöãù`šcð X”ãá$¨åhWR¦yåplüsœ’ŽÚÑjèoÜGœóók8Î/xmËv¾–øÍ`Kð—nËÐW±,ûÓ†udÎâgóÃ:0V¿w®YÓí¿yn†#àø\–;ØÆZ>ÑNó)†M: ü±|Ç~¸íSêD팶`QÈz¿Ýuœ:P;ºíЖP{S,ï¹;¨ÔÇ“º¦Ô~4Ç2¾u µù¨Çå%àXn ÎË/` XSo‡ƒsoY¯ Íúj=iWµ‘¥#Ú,×µãZ}vÏ*÷Ágz¡ëc(³qÜC™3G mC57`Zî&¾ªI¾¤šOüwÒÛÁƒ|5ð—9iÓ=~ŠkãBx ¸iݨ©µ#5®rc7šÇƒæ¹”Œ‡aÕm“qûàáÔ–Ž¹Ÿ'}õ+voðKÉ<߀ô=å%$©/æwžÖ:óä‚Ñ_IØ=û×¼bº±ù½äãððŽùbà%ècÀÃÜþ¸œÿZѾd®LW,cm°<Ûûjp=xp§=ñý ¶OƒuØOí¶Uq¬ª¿ý¨q}bS¯¢]qŽlßµp%ØgÇ/chø0G‚æÃzñZvôfˆkGbOü¹ÝwÌ`Ŷ_q|EÑö$œ ®ÇÑKÝððü ô×oPœçÃÁ¹¯e©×zˆöãêmÿ¶´¡í9áà9ø¬6›(›Ñbb«Ej«›Ô¸›ýð ~¸¹_Þ‹/M¸)x™ùµàWÂi°èç—@¤Ö›¡å›ö]ð äpîÆ¦üënzÚ:eêì‰ ;ko¦Ù^N=¬—æÞ¹n¿´›}­ùÅZQü"ŽüšaDZñ§Î©ù½ì½lý7ÝŸ€ó¹l~å)óÃÞ°+\—Àép8׶;Êâ=\>W¿ª}¸(é‹íµ¬Sà$¸×NÊúú^0X‡—ùÒ„˜ú6õ6±¿–½<¶Ý‹ÉòÄòáfXüEÄ_*~¦+ÖYŸíRô©óÒôí8ÍÄŸô=E4ã±g\Ʋ~ûå>¾|ývçj_p½ ®åM@9´™'RÛ­^¥7Í~¤Oñ˾¥Ìa8#à2”±l€ª×¸5ÖxÒ7ÀîW–âWÎ=p¸ùâÓÌGR¿¬èÕG›¢Íî øx¬?/ˆ‚¡¾­â! LkÃ&ÝKá–NŽÁ\w–;<ˆf•4ÇÁK\ÛHØ–äSØ^ÿÍô8°¯Ï‡­ _·¨©lóä"jú™¹Ö;HïTÒóµàާóöxÙ9§> ¬o]Plÿ ðJxxÈ;ß®­¿Á2ðò^h¿öƒí@¿%Á~ZëæHp }.ËñQc{¬'káVtH—€m‰¤ïÆÛôØj˜¼W£Ü >|LWâ§þwXì·óm½ŽI›¤½¿#ÑKνažHÊO|,Â5)ä\¸ î›k›Òïé ¿ûáð“W€k`EX ܯË<~ Šc5šºõ­íIܵcùÎÇC° 8æÚ?Ó ]GC™#ð¼ÙX×°ª©G *fÝuÅ/%4ã#Ù½ œgëðpsóŸ;ƒòqX<¸õ;€yÁÃ"mCJlÇHéSeÀ`lÓ½pmBmm³B2VmaÓfým6í^€{Àü ÏùàXÖ=”¼£“øRDGšñØ -¯™î|9fÚ=DCUÆý3ì ï…OÀî°(x¿¶€W~§Ã=0Þ>,ß¹R\wÀ—áxðòë÷âWÒç„Ú&Ám`›,O±½ñiê¦'M½Š¾Šõ©'žÐ4çÂ~\o†aðgî'À‹ÎòÍ“z–E_–ƒÙ!Öí?©økÆÌJúžpPyµÏúzÎœÛ ]×eux)˜®Ÿç„ó–±BbìŒGµA{ͯ›-å ÃÙ8õðšÕ«*#à!ëÁäK;nIî«uƒ57Tß©§˜îeàO¥k€?÷zèk_ƽüÝð®…¯‚—…—¿mj“ºyÍ_ãmþM›y”7Á8xÊÈ’òwÂgüb![ÇG?°\ñ×®$Œn¼Ú´;'²؆¤n¹¹„Q;bÿôO?»Ö)Ûœ2jZôAa³Ú>Ÿ„­À þ-`œ÷µzxè¯d=\î?ý¼Tí×à˜ê?-©cSÛŸþ§½–“tëPW׿ú&3L¿®/‡m@9Þ¶]±ýæuþ}öQcZ$u¦ÜØg4´N\Ë@Ú0£e§ï#µ%eë[û’y¹»càtŒ\C¦9WÂoÁu’rP;2¨îAöä†sØd³ÍaÍúßœºQ<ü2[<ˆréÓæV{-SŸ§Á¯ «`/ðÀv#»áï‚k yÜøÿG—@Ú€:…Ä_cÕ§pšF$‡‰¡—Ë´ÄöúÕw$Êh×­ýðòóKÐ>Úæ ÑNYM[[ÜùY,ó&¸}•¶°­œê—ôn SÿÍXVlƒº_ÁÁຢiÁÃ<—¹cð$x¨;&^¢¿cš:´{9ß/T¿Xý’·<Ëo 1w솑¤'^ÃÚÿªë“¸¡Ò½†ê…¨ ŽÍ~àxÙ%e©»¶ì»—ÒQ;RëŽmf”ïx+‰wc3ö×6ZNÚ:R)©ÏÐ6x&8¯·‚ÿ¤›¶,[l¨¥Ž§N‰§M kƒø×´¡>›F`´éljÎ\Ysàµ"|šs’ÍDRGÚ6SÒjè!~ xÉ^æóàöµÿ3X² ½ übZ}ù“Ô‘zØÔ¶T=¾czá<?Qú °#‰m±ÿ«Àp x˜W1=$n¨¤†ÎÅÎàÅï’¢íTxÄHOjžèI«¡i¡ÚÛôÆ «eXÿnp8ÔG›þŽã¦} Øô}|¾ þÔ1ÏPÛ¶)ñSàðrp ì{Êv혯¶/zì qQR¯á ©iÑÛBmŽÍcàcÇ_³lÇ¡àÈô ÑN¿ª-ºiêsš4Û”xÂf{ë<¨+Îãxp˜o¾^HÐÙ;þ³È`yð¡0¨ìjW¯q¢}©iƒ|úÎCeöŒ€‡äPž›¨ƒu~¨#z3¬­ÍfÖVuã–yì¾òÝà‡3ÀËÔMíæ7Ÿ—Á’àåï—¢~Uô±5¬é#¥U¿™Ñ"s. ‘ÊÉxyÀÝ>tn¿ô¼´,#>†ÑcO},ȧÁqÜ|Dx¡Ž“å™?yP§ÐWq\ýÒÌC¤¦ Ò-»JâÖ}6ØîâΟöñàÅïœo‚s¼ (i¯ÁcààXmÛÁ^ 8†ö[à$¸œgÀþ(iW7Öý›±­¶iéƒòd­Ú&%~†Ñc÷\» .=މûà ðT¢IûMËø˜Pí5®Þ&Éß–6³¶f»›ñi•_ûâ­–áºÜãYçË¡Ÿo†KÁ3ªJêo†Õ'zõQ¯cCÓœ¯¡Ì†ððÊœ1lÙ ¶ÈÐÜ4ÚÛ¤æ3ݸxѸi=¤Ýä^„iÆýL;¢»ñ<šåҤ¾¿ºRÓº–Y÷wZCÚb(öߟ7½<Ì[×»züÛìúûHZü…Fñòÿ*xѦÔ~9ÑS®qÅòå@Xšé˜¦Ì_‡ØtŒn¿ì£kÇ/þUà-à…¿œŸ„mÀË?å]޾5,†‹áaø5ì>–Þ “@1Ÿù_?€K೰̹”Ó.LIÜpfÄñò›z2~†Ñ-?ºcò 8÷—ñea[p’/þ†±£öˉ?ã#‰~öuç^øRBëKI[¦ìÄÆ>(t<³&–D_«çøÂÂä^|QÂsÀ±sOLï\Úž´)!¦Ž­–UýLÊlýPfßä©ÁÚÛ|Ư†êUÜPuS™æÅ°L7ºâWÐ.ðïð4øâ÷bZÆCÚ‰:Pj»tJ<áÀŒ³(!õ&L›Œmö­öO]©>‰{àùoÿ[ã¨|üß3Ω/¡>J-ϯ-_€·Ã?!õ¢ö¥mþª-uZ†åz@¯{Ânà~ì>þRÏuèGÃxœæO»-3mÖîãaeX¾·Ad1”÷Á™ðSx/¬^v>šrQ[ŽMÛPûAtTb{·Â}p(–—vw ½xÚo]ó€ÿËàà<*¶Sݹý3>ÑSvÊÂ¥#Æ•¦½kíþµ^Çàbø!øÏ*ÚÆBR³¬Aöø¥~CçâoàÚY  û;Üûúœ¶ÿ+p X¾>§ÁëÁó¢Yg|Hê¶»åÇfÝêJÂnlÊx3->Ãp FÀ :”çf²ÚŠlÖ´(i ›éñkæÍFó0rc_>”GàuàÁøðB:Ü»‚ÿ‚y„ˆõ§ÌlÜÄkÿÙÚ¶HÆÉxôæ°=ì“'i)wG ~+߃¶°“Èó8U´yàz¡>ÏÇQiú×xô´ÅððŸÖùÁËÿÍà¾4(Öçèþþx¨{Z·¢Ý²ÿa¤ˆ6Ûåå¨õý þ|h¬ {€ëDñ²ß¼ÇC„—Àà20îx½¶…ùÀ²­#’~%^äY‡²/üœ³ZÑN; #Éû$†3á­à¯>æügŽÁ=qøX±ÌÌêTqmU¬?u4Ûby÷Àî5Ãë©»-´=ö§)Ωëd øLÛú8ì޳ëò à:9ï3à5`ºëèÃð°8f¶Á‡…kιÎxhŽ:…¤ÝSKd¤¼Åm¨Îì¸É‡2ëG`Z Þ4}OX}š«ÆÕÅ ¿<ÜÐÓ :_Qnæ;aS8~·Âʰ ,þà‹ß‹/eÛõ„¨iÆcŸÕaÛ¸Äf8ÛåYÓµ%®®x¾v5‚8ž§‚‡^Xó´ æ¾dü }TÕxߩصÅ'ºuú˜óÝÆõ؉Ð9Óß¶(ÄþŒ{|´»ÏÓn}=øÛ$e˜¦¿’¾>~!œ ^ÆÂàÅjÛ–€íáÍp;œ®¥‹À2,/ýJ=Í8.}IŸ íÓI½”ئœ^R'¨6/­ÀuþžNê¿ýÛ× ÇÃY° øPrnm£e§µÌý1Ný†Š~Ñ;†Ùô'íK˜v%^›¡Í‡£!÷½Aý×ua~P\ßww´î£à6ô¥À¼Ûy|ýdø |œûyÁú¼ðÛêÅÜ*ÖuŸcò|#ÈyðEø=øKÂb`{¡x8V®ÝEá>ð¿¥°Oƒ>Ú1uæNûOÁ‡Ôrà˜<‡ƒãâ]ðz°,ã—Á]½¸J÷€u8ž¯×Ö™`¹ÚL»†´µ¯k[ ,[çÀ_lÇgÁú]C™#0|Œý`gñ6±¶Ø\øñU÷€]òð`õð¿Ü|ñÍÁlXu¢7ãÖp¶äyp%l ™sëô'>Ë}¼\<=ο &A=$ÓÌ1nv­cÿ×òCJO<¡öŒil‰*‰×tí‰'tŒ…ßÂ|à~ ®ïéSHæ¡«­êúÔxôÔmÜCØÃrý^ønB/öWÁêàœØñ ¾v€ÁݲêÅŸzHQÌ«¯uór4£iÚ3–·£Ÿ?„ `˜Ê Á‹Á ãí°<‚}t\Ë´Þ*5Þ¦·ÙlSÄtyüuÂ6¿¬Ë9=R?êÒVv’–0ã>hOZòÌlXˬºuÛ/Ç÷\°?g‚s§Ôv¤}ŽÅDðLñ¼ÙÌçZÿNOwÝX†ûÀ÷‚çÊ=°1xQ®¿?8~@­3zÚ¼2éÖeyoÀ>|¬ËzÛ1oÇ> Ç`ü¡ÌšÈâ·ôlÇ;zj­ñºÐ½üßT~E ’šç)œÜX§CòüýµÍ©òøzwã]—ƒ‡‰_q~m¾^ A›ØnË1T…ÝÔÿ›rS‚ñØj˜µœô„±7Ç>é S¾áß`'Xìã“px`UÉ8Æ–x £ë£žxtÃ|ùxè®»Ã[á 8v†ÅÀ‹ßCò!ø¸FÞ[‚e8Ÿö'RëˆmZ¡yÞ+€zSše:®®!×ÞY°, ßלþúxñì çÁù°Œלm·_)u*]›¢Ï iKóKõ˜^<ŽÏàcÄË(ó?(Ä¥ïc?”øªÛ~©6í͸¶Ëi“Ø{Çý[ððqÓ&úÛþ!ã´qÏFÐùç"ÓÿL¯ýr½]ž_ÇMÙŽ}µÙ–¦¤ ­CIº±ÁõKÞÁ^ÔLÆ efšj²Pk]ç,úØ £7ç·‡ü'Á‹AIþ„]k÷bðÒúüüºz!œ¯¿ºÌÓÄ«ÍCòp›wwð@¶,ÿé@Ÿ´³¶96óU;Ñ–”YË‹>(ÍÊâã8F·]LÕFtŠx|k¨ÏûÁ‹Éü{X.êŒ#¦ÎØ$Œ½-t¬#icíÜ®kƒ—¹ìÁðØRçÓèǼåÁùò§Ùëzñ]3JêïÆ¦ï¯ ¿éeKY SZâ ½8ì£/Ú÷Â2ðf8ÒC˜ÿ çÂà­àãÊ/I%㣞ò6m‰*ú9—U| _ ׂéþÅv==u%ÄÜÉo¼’2ãg¸œ{ƒÒ\Cƒlçéøc]®“fùµM®×:Fo\‰Ý~/ æó¡¿)¸ÏoçB‰o õw} ¿‡Ïƒé®_×ã/Àöik“´Ó4}ìOÓƒöè ãk|(c8͉âçÚ¢ÚirB§ú©77ó˜ n®HõS7ïcð9ø2ä>ýíàá§èOhù^$¸‡ñðUØ<܉õ×tã¢$ìÆºÛlñMZÂØ³Nµ·‘ô„ú¨Û_ÛVóhOµµbxù{ðm BÊDíˆå+ƒÆµ›ÚM¯~^æ/€Mà5°øØ:^ÀyT¬w7ð2}'œ ¦Ùëõ¾¼Xk;ˆÎXŸ_ð¶«MÚêˆÍ0íÈ8Û^Û½ì÷‚m·>ª¼ü×Ûxpí×>íô­†êŠu* «žvù¼¼¬­Û6~þ®}mŠaÚ­Þ´×´ç‘î¾s^||' µ#5Ó–rcÆÏñ°­‰ë=¡þ*±;êæ÷áeßÝ›€âØ žŽyƬ†ÚõóCâ|¸>©c3ô aÞæ$Éc™‘ØŒ«7ã±e GÀE;”Y3Yȃs3ݹhnœÌOÓnÜ dúSà/—¶ãáí½x6šyÚÀÜ9æ'ü”Ä<¯Ëó;¥Ù9üPâÓ »©Ïö±¦GOʱlÛâ׊¡~ñžÐ~G7ŸâõqûtC%a7Ö;õíÁÃÌñ2ÜVÇCÉ8¶é±%Ô×ÜC×6ø°8¼€>?‚à q¾ý§°,ú¤îôÓí0>b{m«mIêį͖yñâ±ãaM°“AÑgpü#uüÔó)>vN‡»áSà#HY®Ï×Ï ±¼iõ¹Ùÿê_õAu í£÷PÆf²h-­¹H“VÃê×ô¯-JZsSz1ý~ï7±›yðpʦMÞlæÄqé–Ú<ÝÄ®‹‰°,øÕrP;’6ZÏJ0ŒÔþ8hË—–íý XŸy#ÉWCûäAu¼Œ×ôÎ MSË÷'ÏE æQWvcÝ¿^ ¶ÉŸ¯­Ë¯&6^þ·i–Û†c¤½Ž•º‡¡¯ãè£êÛð*øìÃSá­ðr°ß—ƒm²?>RGêÇ4æbÙÖeÿG#iKBóD7tœm¿ci˜_›ìßzðføØOźëcá"8ôu¼Sj§ž„µNmJlê΃ß ¶Çuø"H;-;å×0ö„öCŒûå=oOOº¡’xÕ«­ãTüši)ÇÇË÷`aðaÖôÃÔ±¶‰s¹68¶–õP^‡t´gÇ*cÖ uÓ¦8v¿ƒëápðÁª,—Â*¶£ö¥ÚR–‰éOÂØšaò'4}(31.â¡ÌüÔÝpÖXýÚZ`z.“ºY²1'ýxx(x€Ÿ„äoB’ú›¸ê¦{H<ùš±ŒÍÀrWëPj»sÁ½ûW{i–¥T¿®¥{àøUkZM®_Ms}þ<¸ü‰ØKÒô¾Ñ1õói³ ¶û‹ðö^ܶš¦$ìÆºµ9¦~*Öá|< ^\–›þEOHR?ͺdEØlÃÑp ¬ó:î_€…ÁÁÏá°=â¸[V­‡è+¶³Šm÷òMûíc+®·ÓÀ5ìcícàå’2–Gß|4^ ®í¥Àò꘤ìfˆ[G´;–7€uúÈõÒvÌß®/%cÝÐvÆ]»¢=uÆÇPI¼êIdkÚ-Û5oÿ}0úõm"µŽØ k=Æ€ÅU,›‚ã'_Au<‰vÄú•ô1¡6Çì|¸ …;@Y |°ƒf;0õm)[[S’/ýK¼é7ŒÏäÔÅ4“E͵Ùë⌞ÐAQ÷Ëà »atÔŽiSdóå: ÿ-»Y;‡ÄèƒF|{Éý å'4¡êŠß¿<ˆ-_ÛŠ`¹ƒdl—Rû–>zð?á+ÐìoÍGr'Ý2ͳxp{©GªtCó$n›Í“8jG7T´GýßÖi9€—DÇf˜±3t~ͯÿKàà…e_‚!þþlúkx+Ì^|®ëŒjGšñØ…¶ÁržK±Ía_t5K@Ƶ#ñqŽþ|ùØüUÀ‡PÄñù \ÇÀë`!ðR4oæ2e&Ô®î¼þ î„?‚bÚÎàè¸)Ú*ÚꚊ^Ç86}›y7Miƫʹ*‰Û¶´ÏôØ›aÍ]Ÿ¥ÀŸæ‡W¢`›Ý뎟k×´6‰Ý°¢¯qè÷sw=(Îǰ!¸—ÒNÔŽ´Åµ ÂLÉÓ ;ÿÌØÔE¤›vã¯fI­mÖ®X¿ëau°¯žâÅücpÝeŒRO Iî¯ßªÇÇ:·ÛÀ_¹.çÈô_Á›¡Î ÑN›M ê—v¥¦'^Cõ¡ÌÀdQÏ@Ö¹>K]œŒØŒ«;¾~ÑxÀxiç"Ë‚ÆÔmuSôzŠÙËÉl…žÍ`W8¬Ëœ2bšJjšzêV_>ÕËááðj°+‚mh®ó*éSW{Ç©åOò5}c7L{c³ mz-ÃôÄ{ꈜ·CÐ}ø8Šmp|ƒq/¥­Á é8 Þ!–íÏaXN‚{ }@í”my2£â¶øpùXVÆõ9“Q³_ðJ³éw ûó0xioKÃwÁ…s Ï8x7x¡ûHØœ3שóf™Ùg¡Ô³Çß2ÇË_˜¬³ŽYôZžñ2¯¾¸õý¢*Õ_ÝrÜß¶¹™VËlÓÉÒ*Žƒkb‘^ªkÜuèØÜ }V¬·M´'­†ê[õ‹á.ø\¶óÿÀ`'pþ“?}ÀÔ—jkê‰VÝ̉÷ *£,ÞÑçz:m‹.6Ã莯_ƒ/†çFIz®µý¯>n$@¹q0øEõV8|ÙWÉFk³%ͰêñõÐP¾)÷5è ‡EÚ]êÛoãµsÀiobZõkê5Ý´HÓ/ñ„ú5ý“·†ŽmZÖí%\Gx58#ç@| -~Ù _¿Bý¢O}–÷kðrZv„kÀys Ôr‰öëPŸQ±~Eß÷Ïh!|éOÃ<]QËpígͬžxÂ6»ãíXÍ Âþ°ì ®{/2Ë×ï•ð¸\»Û€cí>±×®ó¢íAð±f>Áqî–ëŠ¤ï µë?žËЍW¿Äê]¿èÚ[îÏàð -åE7bØÇ'`o°ÿîëA©ãŸ¸¶6{MWWâçÚ¾\ǯÁy÷âÿ|| g-ØŽ´Ûp´à:E¾WÊtŒ@]HÓ‘m®vÍ¢u¢·-ÞŒ­Ú¯%~ÝØÔ³™âkÜÃa"üƇÞã°ü<Ø<¸ô ¨IÜPIØ=û7vC7¦m?<8lóËÁ‹o-ðPQÒ—šÏ¸Ò´w­SÿMýÛôjkúXZ³¾Ôú«OÒ í«8~öó} h;¼$ ãöyØ v‡“áØ|88GоŸ‚åÀùñàóàM[œ»”©¯x ¦¨3,–ëºØ~Ð+ÅògTl“ù½T] þz5#bŸfþ´/aÒW7׸se?mÓn0Þƒ¢Ï’°øðöÑu¸v}(®§GÀyñҬˇܣ d>šGù \€mÊÚDíÏq|“×°Úšzü¾ŸF/LmÉÓõµÕ²ôuœ–Àñ_ÖåN8 ì{Ç¡Jæ ¶š^Óԭϵ|Ü¿€¿„™~0¼þ>@š¢OíkÕõMßbO<å4ã±ÃF ‹q—aRºÈ¢V]÷¶xl).ñ„±'Ôî¦Z.âáõ¸ÜpŠ›'’òª-i ›iæ©6ëý3\Ô³/O(+‡†þ©'z[¼¦F§Ø©ÊÖ¦¤üª×2;N=¿ê{ ›}]˜DÇÔ///.Çw"ì “à‹°äBü+úe°-86G‚‰c˜ö¡v¤Ö«AW>ÆJšuÎh¹i«ã`_›Y)Öj=Õ–6ùP¼Ä ^tŽãéð(^2+ÀÁ0œ;¿†ÝG^²·ƒûÊ‹Üþm /íéS‰uÿŽ‚ËÁ¹Sêx«+5IO^¿žÏ†_‚íŽ}¤·¾Ÿz$õÙÞ•{FçnW°Ÿžùëña d\»±gÛŸ¸éñIhš{ÅdzbÙŠ{æj¸ ìÓAÑwsxØžø£v¤ÞEéCí¿N‰ws´Ç“6 G1ÙH£pë]²(ˆè ]¼YœYÈÕVÓ¢'oB7„_Ø^$Êa>øR/t®î€·ÂŸÀòÍ“ ™°n¬”[_bK˜ã±þü÷Ö@ñuC[¾ôuŠ|Æ•:¶-þ©§†ú×ø´ôê_uó%žP[ƪ“Øû“ñò²>V/u¿Ž^ûÀ‡agØLw,ófpnLÿÜ9¼=X-?u N%–e9“à)8¯'˜)©Îé-ØqóÒókî¹’Ìi³~ûé¥bºã~7ØÖàFX&‚âZörß|è¹–¯ó-+À}—µ‹Ú)?áƒ(¿ƒ…À}PÛ½†M½§ˆŽdoÚ¯Ô¯M3bÇÔ·ÕòíϺàz] <7´9Nרã™Öº©e'¿*¸Oî1Çõ~0äûàà˜- þw KÂ" ûÇþÔömÛÙÖ–8›–ô؆á€Ô€i˜ë8EOX7«¶PíÙ¬IK˜jŒû%âõŸ=㵄/\· {ßú+n(7D0î#â 𠻚’¼ÚÓŽ¶ÐƒãoàæõqS >>.†Ô‰Úã‘i¥Åϰ¶g=>maµU½–í¸ØŸ´1íÓîðgpœíã;áM°5ø•ïÜ韲…~øór. Ô)$õLalD,O¿åÀ1¶-C<ÿx$žP{tÇÕ/ôáàW§âg_ªû%ï<îúß›óê©d.k¨žx|o†¦+±wc#ÿM?ôŠÞ kšz-_}"xéúø±O{òeø0¸ÇAI~/ò“áEðj°^ÓjýÉûì–ëÃÂýcYõ¬3¾¬«ÂÁr²§P;ñE Ý{ÿëªmjÓ«-ín†Óï“úPZF ›¡%ihê@¾Ñè#…¦Õôè½âZƒ,Þ$_²™®@ßn†l¾lLS,ô•‰{ùõ>3bÜØžnNÅw€?—/¶-íKŸÛBÜúã½úi«’´ØŒ+ »±©ã±×ÐC|;8·Œ™¡iöÉ ß~ÚG<ß^öÏÔñ? ì·åù8óÀË\ vÄ1kÎe/iª ~’b=C™¾ÈX'4wÆÔñT Þ®}á€â×z—¤9^‘[Q®óºêø«k󌻬)ðÑàìÖç~ÔW[@íè–ÛhÂä3TÌ3”#à"JûÔ…½†ê’1LÜÒâ§iÚ²Yb7>.΄n˜ß€‡Õ= _ÝdêM±ŒÑHÊIÝ Í[u+ãG›€xhx!.ªŠé5OuÓÆ/yâ2¢d¬ªS³¯Mã^|'€_)†ËÀà 8 ¼àýùQI[¼ÞóÃ'á:°ýé—lênÖIÒ)iïsݸ¶5;­6yÈý°#džPûóPõ:'Öç%fècö…0\>bׂ3Á2#~©w‚ÿQž¨ùŹdL›aÒk˜òŸÁøf8ê%˜>6l–ò mãšàz÷-Ï÷±â¯!€_Ó³¦“Ót‰g”s£ *Ãöxnø¨ø<ƒmsLŸ›À_óàpLS^Úˆ©#Úƒ†ª'^æn|(½hîp`º#Åg,zšc[×ûÙEØ´×üê9,\üÇÂõàŠñOƒ_Šåœ'‚›¢­\Ì%õ4RÎy$ìnNmMmµóÿôú5ä†Ý¼X_y è›qI¾„YcÆ „*ñ­¡éæõ«û°ÍâͼͶãÒi{lð þcðmø¼ûŸü?B_<ð=øÍg{,'>5ŒNò˜KÆjf v¼ì‡‡ýÛz…UÙ½â¦+ø,ÞÛÃô´Áõï#íZhûù¸ÎCtCq~çÉàüÿœOñ1p'¼v†ŸƒbÛL·^‹Á¥àåé£á °×aÖvêÅÔ_+êU,×¹p¿ Ü÷J‹è†mèo•šÇvûfû_>h¼¤Ï†a)°/îaý«¤¬j­Þ㱺ï­Ó:î…‡zzìþ’°øëˆ¶ˆyëXÄ[[<ýH¨OÕ“g®ë@ÏõƒÑX$uÁ¨Ç)i±ÕxÒc3®d!«»ù‚·À=p"ìnÈ* ;?^²ÕN´U¬/uVI[´{8xˆ­TLÅÜQm/t$¶×Ÿ_O‚¯v/hœfÒˈîᩯ‡al¨}©6ûí%ö3Xž”Õ¬K»6óx8lÇuc°­ú8žŠºb[lÓnàx[ŸeX—b{j›¢§þŽÓþ±í^\c!–c_¾ ·ŒE3Q†‡¿_ðò¿œŽrïûáÕ#äiÎIæÆðA8Î…¬ÇÅ<Æç‡Ë`IØ–óÙ^Eß•á0ðòþ3ßýüùÚ5”2Qû’vÅàCìàž±nóGÔ¯aÕã[äks®®cç}kHúÐ=wÜ·öÍtÅtÛYCíJ³ýÚR^Ò×7þ±UÇH¬Ûž*®yÏ•óa;8lkÊCíKÊ6-z?±¡Ä'åè½á:wFsνÜ뺰ԛqsÆž´„ƒKí¦¸÷à0ÈåÚ±*)/¡ùBÇa„?æ©ÄU››‡Ð'ÀCIÑmßàmÚ‚p¸Q=ÄÞÚ—ÙX)˰ræÕö-8 ÌçAU%eÄfÜ<¿…Ãzz®øÁðøØ×EÀ¶Øö‡ÁŸAiÃÇÐm‹mÔ–²QG¥ë7VâÃhoXf ôÀõÁ³?ü©W^í[Ï4[Ç~ð€ŸqLKÒ·„ú»Æ]ÇW€sœ4CçÚrý W‚¢íHðçú«á)P¼¤Üß/Ò¯Àâàºq_ùP,;õt ½?úY–~µ?mº¶Øk{BÜ:~®ïEá-à%ëúñl;®û¦n ›ù1õëSWâcØ&ƒìñÍ$ŒÝ€ã—ü7÷—ßÀöà£<é¨ý¶Œ¤'ÍPIþ„ÕÖq˜Ûÿ¸h†2õâʘ¸p²x¢7Ãø&Œâ†Ù ]ènÒŸÂNà!-B|ÚÊ!yD”§ÙfëëJžøx@ؾ}àXP¼à}°œÉ» ú’°¸×óZŽëªY®që{îêé}I¿c0.ÉçAž;e{©øÅ>Þ ?†ãá°&DîDùðpô ï\X£€}Ì埶$$iŠyYŒø÷aEÆXÞDyÇ€ã9VRçcfËôu윃ÌÃhˬðhóįÎElmaüúrÆfÝ9wM_ç€ûPßOÂ…p+œ Âà¼åFðÒ:VדëÒõêø¤ÔŽX¶u6¥Žg›[BóGw~-wgð±·l¶A9þi‹ù”äž°Úµ)Õ–üÝ”©ÿÖñM õ¶½ÙkÆ? ¿‡Ì“EïÿÉǹ©R뎞¶5CóUŸ”[âsmXóÜ:u14uãM§øµ…m¶Œ­inJ¿òX¿XO$õž°iKÜPi–Óµv×ÿyD¬ÿC`Ÿô}5¼ §7Û¹_^­?ö6[Ò¦=ñm†úYï*0<¶É3DßÂÍ=}5BÇô5ðbø¸g›Rë4-ñÑ›eÏ5q³¡tG ¹€šñŒ“ö -~ ã—Ð…¯˜î—éàBWü"þœ¦7¥Ynâ ãߌÇnhZÒkXõø*^²9@샛ò>8<ؼW7ñ;À´ôµ#Íò5ºæ²îâß ;™Ëë¶=æóËÞ ÜCÃyOð‹Ç4ýî„oÁ8ðþ#8®¶Ù‡—á~à!þwøØNãŠm ‰Fô›ì_ëÕ,Å5‘öŒe¹cQ–?¥ûXýâX6 ËȜԹ̘jkê®õÇáÄ^›ž!t_úËÒWÀùv z¹~\3»ƒëK›âÚz9œÚ\§ãÁú\k®Ïì ÔÒô©ñè -ÄõýR°O@>"¼8?úfÏ%Ÿa˜§°WâÛuÿ¦¬¤×´6½ÎEÕÏÀÙñrÜĽj_€£ÁþµÕ[ £“eª<¦ J×®“,й®ãt¸.†æ¢HÚ 0ãeº Ù/ˆ*u«{xxa^ Aq³ºÈOœJ¨ý «Þ”´·†ƒÚ:½vë2O.ÝÄm›‡˜¤º>^° ÃK 6Ôþ&Ó§)ŽEí§q%ã•x.tÇm1Ø~ ^ØGÀ+Á4Åã»ðØÞÛf?üÃò…]Á‡€vï+Àv6ëÇ4Pô·nˬ}˜a:l‡—ÑX—;MÑÕÃØCÙGÊœ.YK¶3zÂØŒ‹sz+økÛyàeîº:²Î\3ÎÍ|àCÞ}»lï…À³@1¿ëôÏð[ø(¸-ßu£ŸõViÆ›û'ñfèE¿Ø>ëÝÒfçʲb¾6jšm‹OµW½-½Úô¤O†úDb7îXœîÏ_ƒxÛáX¿Ü÷Ž›óÑ””Yô¥š7¶”“|‰ÏU¡ƒ<7Jô¦>(®½™æØis³y0*ua'î—ÀÒ‘{ÁÅýshæÁ4•m¤úõWâÓ ÛÒ:zy’n8Òšx>é'ƒû»xହ°¬{Z’þÖ0ºy-Ëvø5u$øh:¼à‡ø>ˆ¾xÀ}ôóÐðŸ$l‡ó_ÿyb'ø<ø °/æ©¿É‹Û{ú8F—ÃÁ‡yÞ çAÍ“¼˜ûåÄfئë«Ôô¶xÇinø3Òá77ô?‹¤¹¦µ@j>¿„¼œ®…\N.TQüj^ üpA+~i¼ü2¨R7Ȧ$xˆ¸‘«¤îÚÆªW_uÓ2Ïͼ5ß´;ñª{Á~ rÀíŒn_ –WóBjZSÏÊÃl/ø œ ^ÒËCäÇ{sð =š_–Ý,߯¶Ãz¾^þ׃sÿäIHÒ%ö3s6– ›eŽeûƪ¬Ì³åeŽjs-\÷ÃM`ÚÚàÅž=„Ú‘ä3 á-à×¾_³wNE¿íàFð¼Ø ^ OÂß ó«Ÿsb1n6†×»qíãÀ¼ÖÿrP¬ó$°Œf9˜:ù2ï5lêÆ›ÔüÕ?vÃARÛmž}¶ÿàƒÊñ?´[ÇfàxúàŠ µ/míˆM'õPãê‘êÛÿè°¹¨ÿGw–Î5@úûHaõnèeò ø1øeœEäØºPݬ炋\ß+áƒp˜î—„iÍq 67±ö”YÛ‡¹ßŸØ­³IòjWO˜<5$¹_¦z¤¶Íƒíhð¶}ëÃð—´5þÍ—~?ÓŽ‹žÿ„‹ÁŸ_ÚŽ—6ý<8/ù@Û’qDí‹õ}žû¯¼…â¯ÎoÊ3­êÆÝΑ™ëmk¸ üUÀðcAÑg%p{6œûÃcðøÉ:Eí‹kõPð¼ù;8g/€qàY²¬ú] AÊÉüVr$4­êÕ7úHédJÂnìÙ¿¶·)Ž‹cìéCì>8ìã¹\ AÛ^H]5ToóvãJòvcÿÃÿ:‘s£ÔIÎâÈ8Œ6-þ^@¾Zµ‡‚xl§ã,ú} Üð.ô¦˜/âA²´ jWÚ_ÓSFò%-aì5¬yFÒsÀ}'ûnÿöÎήªÜÛ{ÒBï-…Þ‘ª ½"Ø+M¯b»zíÝOP±`ï½^lX¹zíÒ›J“†„"½§Ì÷<{ÎXÙ9gJ2“ÌÄûþòÌZë]ïêuï3gò4˜ÍE]¶'mÑ&ý´!~âÓá x¬ŠåøTô1ðÒq0œwÃ$˜É§]9ÑéÚî=aW°ßÝ௄¤Ç;¢Åú¯ >)>Êq$8h1½‡ÌÀ[©—4ÏV6#ÞɼHEÖ×éøxÈzðìÛ‚kZIšžPÏÏèâšö)àà…ÏBæñŸñ'//±ÎÍ/ƒsþSàü¶|êÌ3<ÿ:°XÆÖà<ÖïYæ£[JsŒ.ݦßpóŒ®/¿q‘ôKÂ¥k^‰×u^ çƒsÞ·¥é¯Íð_ëƒbÚR.ÝÒ¯­ávºä“¸„—[×Iöï ¼©ÏÀGŸ°}Tú›}æÌb³O½é¿ ~Õò›ötx;œÚšf ’ºhÛô[VY/ÃMÚ¥KšN®i”,Êø [o~_/ÚNóØ\Aª¶Éß:™îQpÃóû*Ø'~œ°¤?¼øü^nrï7aÓ™>ùน˜æe…µùû¶&›«QÖW”¸=¡¾¦aæ†5 ,s(Åùrü|cbýR.ÞA‹éÍçY`½•ôOhéül?9”¶½iWŽ«þ„KWÿ=àÜþ.Lbßze¾ãíSÌÃtW€o™\ÖÝ9| xào @úA»ÿ‚Ùð386„qæy?¬Î]/ 3À±\\‡Ú9W\Ÿ–ÝNšý¢ºèãO¸Ì#qI“1I8¶Ú5ãÊüÒç±×ι¨íupئ7Ãí`ž¶Õµ¶øPæK°7½nIlÊøèt•Äõ„–ÓŸå-§M\h 3¨¥«¿铨Žmâ⺠]È.ÆwÀWÀ í"ü9¸Ø/m´mJY†q)§ëF ‡ñŽc§±Tß.uJܞТ?mCfü.È9àÛ ÅƒzSpS‹ Þ:oÃöÉFða8¾Gšàb·Ž³à(0ãNû*°eZÎmp8d“ÀÛQ,Û<Ü(ëá“Ò—@½ñJÜžÐà~¦Œ;IæÆìK}:¸z6@Óìh%^’:·²¨¿övKKÙuÌ]í+ב‡ç7` m×n p¬-/R¦¿t'ö‹ëÕ¬ãS`'˜$y¿—WŶê·a6|†mà‹ ^q®ï _€³á Ønó1om¶×sÚù­<¼rIH=P-$éwÝ Aüq­oë©N%þ¤QçºUJ]¶®ÓGR_]ÇípÜm·³AYÔïe^kI¹©cévò›0qMéòö£ìøå­mÍöd`›®vÑÅß '¯è3I£7,>!¼>nÚ{øv3qq¶“Nùj›8]q™+@txkI¸t§«¾”f8qi_é6ý¦ýV+ ð-ÿƸ>©»`}åùtø1\oãsy¹ÿ/`;Ø´ó ÇøØ¤\ó[®A}âð."ÆYGËø|+ÖËñð ¯ÓÜï+ÏV6‹8ÖíbØÌ»¯C‚èÅ–Nu^ì —aÂ)ûÁ5Ñi–ÕsmùÖÆ±ˆ˜ïÊpü#”vã]\çÞ•à¼þ˜Î¹s4´;lPH’¿nòô ¿Þ ŽïÁÕ`{ò§ÂŸÀ¹u$xvý{Ú®i0Lo^gƒm(Ë#ØÖ¯4ûÝpSW?¼ˆ­–ky±OZuʺð4HXÝ`ĺ‹ùÞ¶ß1}/\Šu9ö€ì·x’ÔKe³®Ñ•úèt•Äõ„–³Ÿ‹;8£¡úø jlšnÚ×´SŸE7o»Ì_ƒ÷O.ÀïÂqp)8y³yXvXꇪþN¼‡©†‹°i‡ªÖ5ëž°ñ$mÓ-Ñ>aýi³‹òï­° ~+pÓõiéøü¼Ø?ŠßÿÂËa{x\“A™ ©G­hý0›òîà¦n¸“$½õt“| x)QÞ–U¶G}Òè_±óá”ÁÌŸá¬G»¼m{_c’4™‡^¼09_¢‹M'÷…D?@{ç‘Oï‡Às@q½DÚæ€OÔ®á+à2xßUÁµÜNÊv$¯vveÙÖɵáâaÿðBìšùÜ™WúŸ…‹àûp ˜~'˜ ʉ`»m¿’²šõ‰¾Çªç§ûeÙŽ&Æ9¾O×áÆ N^¬ô­@IDAT»¸xëðƒ¸ß†ý•øônYëëÞI´õ퇗1ûé£p˜¯}÷G8 'uMi¶ÅøèÚù£ÓURïžÐrô3k9jRÝ”rÀâÈ€ÇÖLâ«®œ´ ;!À.È#@Ÿ¾_†ëÀ…ãæ]æE°u¡¥êÕ›WÒÄuqíºYX¥ê^Iš^EÃS¶'¶Ñ%OÝÒ¯¸ñ¸a½\€¶ïhðP·~™[ê­§ý䓸gàNpƒ2ûJ7€”w1Î<”Ôµ'´ðÏ27ò÷‚ý/yúŸ„_I>–¯”i{4ÿiÚ¾6±ç4ú,íǶ=}ìÚø8+Éc é<(šbúŒ}ò2ì±>ßçÈ1°\ ÎkŸ¼Ÿ ®uçtÒâ]D̯¯ø2vÎA]Ëuíü|sázÙ>»€6®ר¾p4˜Öµä|ÿ)¤ì²üÒI[±íæÕé‚c"ëäEì0OËRtãw_x˜ŸuRWÚÙ‡ÊwÁ½árp JI½µýüž_/iûƒ6ßÓþ26I‹ª.¿¬oþÄ'ë¿ù-’X.ÓjD&šÁøËI×ô—v­,J]Óu2ˆ“Ð'Ú#ÀE(_ƒ¯ò\Ì©Þ^i§ëlyR׌“a'ùÅpU˳PþÉ׺ůM’öh§_i§So7+7ŒÀ:® OkùqêKÊù¸¯ëý&¸ì“­Á¿ן ¤-©·yYŸ5àÙZâ˜Xg믬ÛÔ¾áûa½R÷%­AæÈ’æ3Øô¶Í~?~ÝJ<ö&]+ÉRqÊùa†Ëº&<ýê­ø›pm›ýëö1ðPköw™Ñ‹-ÎÍá°J+— qw… á‹p=hg™ãa XwuÀ:ð ¸75ë‰j1×óÛàp'qi›eÜ¿ý‘ÒF¿i}OÕ5íËðÄ»û»D𯽿נ|Ü{Íß±ù¼ l·RÖ+á¸ÆõçO|åýð¨w3È£¾!­dP Æ_N‚vþèJ·LßÊz!ljèkd'ø)phËoøÓà&áBñ`saIÙ×–¥”eF׳pýÕ™^Ë–„“..Qõçƒ+ãº(ú“ä×]¿nüæa[ »±øZï­àâs1®ÆEîÂó.Øö„ïÀTÐÎEî]ä²îµ²ñ£Œ×Ÿp܆y4Î'§­Áöx¹¬§}g=¾ƒõr#I¿öU×%-7c³¤ù,nzÛö¸~Ø/ŽÁÒË-%óV½ë×¹q<ʸ| `?;÷_®ƒNc}\LûíR/ûd8\†­£—ûà ðv°Ž®=/Ó/´®É³à§p<®7ߨ6Ë)!X‹óÿrøxxkÓ¬¿áú<oœ:ëœýǰeëFRÃûÀ¯` {éìûß‚ãõc8¬¿ítÿýDËoߤ)[·„`o¸?:]%ùô„FùOey‘r`âÏ@ÛÆ¾ü‰O_$}ÂM×xu7†=ÀIædÿ| nœðzp1> n É»¬ê^½~Åø,š¤‰>nôqÍß§“wÂm0Ê ‚`¯” °WÙòq!»Ð,ãIð°M¶upÑZÏÌ%¡OÚ†+Á§/Kæ#Yçà7¿9`þJÚ® øÃ6»I|¸eo>?ë–ö¸aŽŸ¶ÃPJêþ\2µ¿l·e‡Ø~¼ñOXk8 è#ÏÌ£¯bó¦–]9úHºL£Ê:6ýŽ“óZ½kY÷DpŽ»Îߥ8ÖÉ#ãÞήL[úÍc"Ø—>…ߎmæ¬ñ뀗çÔuÑ&ù¯‡ÿyà“º¯Íÿ’‡®DÌÓ°mþ¼\ê%’¼ÖU}Ó_ÚÅ›ìÑ{±QW數v®¶®åß´Ü3pÝwí;å`رrOì$©ñ}ùŸ|RÏ„G­ÛˆÑÚr@âïk@KÛ\†ãoö…‹Á8Ëjp5l Š‹ìàÂuÑÚ¯Ú™ÆEåí¶<ˆË2ô—a‚µ T{]Ës!Ÿ.‚l xk1>Xý|º©oêãE¸íÀ ï ðÐ~1ä"cºÓÀÿ`.§†-?N¿n)–Óî@Lù±M?4õ‰k]".ü­ag°Áç!åi;¯¥?7eàr±?n7eë5\òc2þ2 g}ÕÝ>-ûI9WÊ6E×8Ûõwð­Ö5àÚ žÎwãK»‚o K)ý…K[ýÚO†²ê½ l)ãüÚÞö¾« eúæísà!ûmx6¬® çJ¹– .2†©C\m”äßZôgêXºe:û®”ØE—üãF¯k]Lo¿ÿÜ/׸q¶ëUð=°})«,#ùêfÞ6ý±/mK?IG·¤á£¹Û¿î@ý±}§¾0ÞÉ´6\ÓÁþ»>ß„{@;'¡‹Õ úZpsÎ[–×ôc¶˜OÆHÛH'¿ö–s*¸¼%« xk1ü4¸<(] ¦³}ƒñ¯€ÓÁ¼ÞÛƒíQlç{Á'øÃÀ æõ`]…ç€åLÈâÃÛ+Öß2Ì/m‰#ÃÑÅ5.þ¸±«ÞÍí[`þn¤ÇAS´3Þ:[—¡–äù#2ÞnêŠülËOÀv:6K*Î 7QDZS_Õ+¶·ÝX÷Œ@OÆÈªÅßt]7€‡Ì9 Ø?Gƒ}c¼ŽOÙ?ƒ5Ab¿•}W†K}m\ü°½|+Ö'uò­„IÓ΄M[þ¿â^ ÖÏu¸7ì?€  XG/ï'‡¥%®éÁòl‹â¾U–©.åëo'Ív•m+ã’¶©ëd}_ö¶Ë=ê¸Þö¿íx.g8otðöŽI™¯û@Ê‹?ñqM«Ä®©ï‰E?mèh– „mˆ¿”úÒ.È0œLŠ›ú±ðCðà,û3eOAŸÅªº¾†#¥¿©K\ÜÄwr]ô–«dñ6õ½Ä¹`.Ó±îÓàÓàbú&¸‰LÅ‹ÏÁà&§ÝlPV€Ûà¾ñIcup³ìTïèKWÂx{ýítÆ7Å aØl“á/€ulöªa?´ší±Ì¡–ŒñPäkŸ¹y¾¾^ 2ñ.wÒnN¤?çzr¸^î/Ž/‚•Àþr­½|8pØÍùÚ c²ˆºHÊ.ݵˆtMYþž`ÙÊI`½ [Wý—À‘­ð;qoÓ)ëÂ!àÅax1pÝZo¼(釞ÐÂávqeMS†õ'¬[¦wPš6ÑÕ‘øRg^g}+¼Ü•á2HÞeJ¿¶±Ñ¯$>þf¸6Í?2Fcšƒ‘Á‹Þ¶5ý†K}âÓ~Ã%ê½=zø»àÜ ^¤¨…°b¾¡V4Â)7nlÚ¹Úx0o®9÷ŸAo»¾^Ò¬³ã'®Aí\ÇàùðYðÌ¡»þ¯Âá`ZóµÜØ¥?Põî7úK)mší2NÔKüx{÷䤉MÖ®$/ã΂ëÀ·5ï‡;A™^l«}ãx5óN˜¨Þ:öåO\Ü2½ºQ!vÄh”²³õ'Úe¸ô7íl{Ò$N]D ÛÛ²¦‹Á…æíúÃðkpƒLxªKÂÍø„›nIÒé¶픸ü‰k_è÷Àôðÿ ¸0>O¾â&æf°%ì ǃ ˶»ù¹ ÍÅež¦uáù4¢OG»€OânT¶µ”2¿nüÚ–áèã&Þ Ø1`›¬÷êp$(êNëšz¦/PÕÒ «Ì¦×c1|?í'ëU¶iøJxÎöÉ£p%èo×G¨— IÛ:µS½óGnj6ó؃ÔÃWÿ»!{Þ^É\R¡?Ä áf‰k¼sÙ‹´—2çæ¡àºTŽ…)|â×”äîkçÀžàÚùxPšÖz­¯/çÂà~\cŠv¥ôUni§?{IôÖ+’|K7~mâ×?iïÚU`³@Ñþ Ø ìˤoç–:ýMP-’^ݨb4I9(Ö»Ó Doû¤ ÇßÉ5_Ÿ$½i¿¾ n¾Rþ¼þN õ‘2?uíÂ¥Þãrd1wZD¥^3Œj¡6íN$þàÂø l Ö]»;àƒ°lŸoÈ⓾›OÊ´¾ñ—.êÞÍã÷ø½Û&Úð$0Ÿô Þ^tqËt–ï"ß|ãÓÑ#àëP7)ëo»”Ôµ'ÔŽ>º„wÆã˜ì ©“qC)æ«ø4ö p^âÍ2câüˆÄßt-÷N¸΂Ìççà_Æ€RÖ/~]¥ ›¿êTП8¼½¢^Öç·kê ðÀt? ®cëâÚÔÍž”´MW;óÑÎ}îVx3lÏ€oë^Ñnwø-œÿ ‡Âƒà%Â:X—f U‹aÅ66ãêˆV\ú t/ÃúÛI©wíû vQËðC¸Â$°ÎÛaû²ševL£oŽoôºJéÆß3‚¦Q#¸ŠuÕÒÙÒ¹qK]9`íâëÌŠ<.]ÓÝ „Š“Üƒíãà$z¸pÓͲ G×ôUÇ™Ö[½ b;°Œ¤Á[K‹úø[QuØÅ¬Äí õ,`7 C7­ïÁa0ÌDz/…§À ø¸@Ô‹m–”©[JŽMâ  ²°^Œß>[Ú‰m“vÒŒkÚ™¯å¹àïƒW€m´ìëàtðI-¢m¤ôGgÛ¯…_Â0ÜÎõIò|3¹l7-QnOì†'£]Òýµ£9_ÚÙ'/Ýøc—pâ<0/皇‹ùOƒ}Z~Ã)3þ¸Î¥øñÖsôrÜ߀צ¤lõÓÁ9½"<é‚ùºþÍûõðLh®ÝÔ¿ÌS¿‡¡éÜ{<Ðÿ¯õà˜жëÂÑp2¸§øpd¼l›G))'®qúE[)ã,¿“$ï¸Ú%¦¿ÌÃ<¯ÇKû¯À¹à^aÙ¿€ç‚}_ŽMòNy c¶ˆº2>i¢×ÑbÃGº´ëÔtzé¦-å`–þÒ¶S›NˆÏÃÛÀâDr²x8 ¼+@òÇ[KY×è◠μW‡ÓÁ· ƹ#ÉK7zmJŒ¢õ^ž^V¼Ù¿¼¨D®Àói˜;Áß yàíÍ[S,W)ÝÒŸ8ËûøÙ¡‡Í¶°9X·HÊÔ路ӧ͸2œ4M×Õ2Íã-à៾Ã[Kêœpéjk½_³Át8Åzúf m[’z8¯•Âé`¸¯M—è¥*iÜN…¯DÄàœêO<,÷…iý¶â Qœ#íæ‰ñ÷€këlˆ¼ë/—^ÛÑW[Œsžý ŽÝ•ý®óøƒðwp¬;«ýv*xP½ú²%ºw˜®œ{柴ö£{° Ü«ƒuß þI›t¨ÚŠkþ­`žå%½4v~l ®î7ÏÅ:~¾öõ´ß~Q¦{‚yv’²~¥MÚh™â¸(g·X÷Éð2x&(î=î•2ÎÏÀEà%a2X7û"ãÔ®|ÛSJÓ¦ §ž¥½þØDŸ°{ÉõàÍ1ú6ÜÖÙv~ì7Ö/?)#.ªZg¿¶Ó¯”q†3Òvu#Jš?R*gGŠÒÎM|×N—ô‰«3låYê Gáà"s ]èÞ0Þ´3˜N{@)óèÑ,Zßè›®ù99]ü™ ºñã­%:]ëå¢sâ*ÇÀ p*ë‚â¢s’¿¬£î`Sgó(ËJ9¨ÛJlK·ô›(y¸yü ß…÷C™güM×'݇à‡à>aÜ+Ôe¶›·¥Î<§@ÿÄ¡ªÅ°ó×uóÌ/ê|“ós°ìˆö®7ëЗXnÚSÚ5õÎqŒÄ2£ç€—Ãm¼¦ã?Ü+gƒ—„䑽FÛÌŤC5hI_•n2‰.a˱þÖõŒ–ÿW¸?ÇÞº½ ¾Ù×ñ¶Gí“¿~¥ÔõhN«.i?bÜ4bÄT¨ÕY©§trËÎO'G§òˆaÅp&¸“âÀ–ÎIú)ø\ÜÒáÔöN¨N“7õÔ6¢.öqWºÍ|“.¢ší9¾Nè/Ãú h|öõ_ŸÆm£›TÞ^i–ÛÑÆ“úÇÕ$þÒµ,7 7HeEpóÚ ŒkŠõZ lßSÁ~ b™öù¿æ‚›¥oqlsê„w!é¤_ȨM œOm¢‡\µ?9?Ŷ›£C\Ä€³sóU…w‚—Àèð¶×AÖD[”Ž¯í¼ ú³M8‡ÖUǹÝÑV½iÛ¿mí7ƒýÀ„¦˜.4ãÚ…-ù¼)XŸ^_ öõ{ 8¿³žSWÃñãíS´kgÛÔÇÆ¼exr¼l³õú ü Ò?î='Âlø(8]÷^ʵ±¿ÚIÊŠÛÎf°:ËS,Ó=ó °ï~ ßjùí×#ÀvØï¶Q1MY_ý¡ŒM¿n¤ôG·LÝN±¬*•JçYøu­o9(}éšiË|â×F9œ¤ƒ“Ï׆?7Ãå„4í`¤™¶ 'ïÒMÞ–ãDõ5£O Ÿ/#Öëe`œéÜΆ½`øO°=ÆÛWnHå¦Z–¥q%ù˜>ù4ÝïçæåÆq(¸ÉNí2ºÖÓCÿùðF°½e|ü¨õnFÿ¯¥µÍ¿ƒY`ž‘ÔËpéOü@\˲ºKK£ wÀ:CT m_Üö¦ Y£Ÿ!‘o¦”¾úízâ}"Sú«Ÿs¹Ý%²N\üH>ýÙfm¼´¯€èÌ.}Vº®¹+ÁƒÂ˦í•Ìi¿mnÒ.³EÄt›õÙ¶ç÷Íð[°©Þ~ûM›v’<âÆ¦ ëWœÿö§LõîGŽñŽðjðR”K—ýó*ð`µ¿|9 †ä‰·î£2¬®Vןô5ÇLkŸyQ9¬ÿYð)0mòíÆiàC„ñJò̸éã‚¶ÑÇV]â“O\ã–¹XÑ‘"í:&gS×tnââ&>a]%ážPÏOuNf_9ž›Bäÿáù\É#qƒq›Øptñ'l¾Í²\<Öïp+ül¾ÀLØ .„{ ¿í+óO™¨—HÊ<ËŒ¢ëb».u¶e{˜Þ ãÔb»ÅñÓÎ ¯c‚½Òì#íÝlܸ½h˜î=`Ùí6rÔ‹%–kY‚ýZ94ëÒR©órs#ò8šÄqpúÆç¥­Š«Éâø6먮¬‰ò0fëN9\£¶Ýùð.$M½á¦¬…bÅ–rw\÷åëð8x` ‡Øæ²Ý G—°û‹ýe݇Á/`ؼù¶FÑnx:ø ãÛƒÕ!2?Üä_¨ÛÛìWóVg½NiùÿŽë% íÛÿ_ û§ö% —~Я´³¾tõ/S±â#AÚu\Ù©©g:ÖpˆN7ilSÂñÇNϺp*l.X'Åqàĸ´Ï¤À; ѾLé6ý–ãå–£‹þã`\^+zØýކà(¸¼UÛ&1mÊÀ»HXÝ’J3ÿvùi³øŠÐþuÓ²Þ+·hŽ•ýÐiÑeì0©ÇEWñIâƒ`Y“á÷ðð¢qlÍÍՠźږá­ÔÖi¸Åvå0¿uR:õAæâ>Øx(j_ŽÁ'™{VÌ~—R Û^Ý#á‡p=ü .Ó{Éõ-–mÍüÎÜmº˜Ôù¥KWÿt˜®í}ÀüݾÖÁðpŠe”X'ÑÄYßPùÀqöÂz.¼v…×ÁÁþP´?¼¦ [ã¿ \Ï–ã^˜üñ.ä7þþ„{¬ûÇÙàÌ6‰iBòh†£Ç´wlÕ…èãª_¦²Ì+@ëë:L˜Zu­°"þ±Õ˜1]UWwwÕµ`^5·Z0?Ìg[™Ï12g»î½š6è–þ²cÕg28éfÀi°*vâ½΂@‰}ÜmÏÏvº2>þf}ʰ~'–›Åƒpx3öЊXŽvNÖÃwA[SÓð.äOXw8Äz¥=æïbPâÚ®}áûà&é+Lå«ðuø”é ö†›ý›pÓust̶h¥} î…ðjø2l ³ å$=ªÅ777Ûöï*ö¥ý¸xù\Þ$s¥tõ—Œ'¼ Ü O†À§Hß:'^t+JòÒ_ÎÁøKW[÷¤éÓ¯m|2µœ_6™‡IjÈÅöm ®ã¬íô……©s/²Í¶ß8uºÖK\7Óá(xLƒRÜÏœK¶í÷àÞp ü샴/.ª^±œR:…£›4¾Y9¬£ovŽkùÇáºoí÷@Ò¥ºMPÕãRÛŒ[-?©êš¸BÕ=nBÕ5v<.tÑ[ê8ß*ηîÕ6¨\ò›Þ6šÇR¹LdúÕ˜‡8ÖîšUwXõøƒU÷ºëU'¬Y­ÜÕ]­¼ ‹‹@7“Ê‹Àî ªùó­¾ïŽêûïì}Rž8f|5ûj¾KÛVcâ–m³­ôO‚ßÂTÐî8|äÄs2»¸ÚI=°ED3l~BD3Þ…aM½i®.¬gÃ[ÁúÑæø,|\`>…Z†¶ÖÑüuSß,Ðf¹˜ ©X±]%þ¸ê\¼³àûð%Pö‚sázH_'Tµ”áä§[Š6»Á–-¥¿ýå‚þ |¼Ý§ ¼K,åø,qf£4Çá«à%k p“üw‘ÌE×Ûà¼Ö‚‹À§`åðQð h—ù›¹¼JW[ç¬{”kÙý*×üßà$pïÊ^‚wXå›­Ü-×õœ=FuÚeʵQ¶U{îÍóáϰ¸çmƹfõg¸¿{£}ꇳû_òÅÛ+êÒ§*›áFW½é¼´üö‡ûáð~pn¯—Àp5X²kᑵZÀáÞÕ…oîcõ0‡ÖjÅÕª)+®U­ÌE` Ó5¦‡MwwW5w²<ú@}žy ªe‡ƒª®Ç¨Õ•§µmŏÄ-;nH2ì/“íªÆ>òP5öºsê§Øj‹½ª¹)íGEöÛÍ«¤®jMôÕɇ`Ïç^Ä9ñæ¾îbõÝ1o~u!‚³þqaý$?aòJÕ*tx÷cÕÔl—a÷àaä«5ÅCè“pÜN2w„âj‰ÛÔ—aËpÁ ·FQo¾¶ÁMcx»‚âà»Jù ÁEp+˜u“ÔGÝš`>—ƒºÄãrqA—}dyÁÂâ×ÎÍLY΂mÁºÿ öÑ8PL×”´£tõ›÷£àÂÝ Ô½¼,™Ÿy¥Žq“QË…ØFÛäañt°íΣvê!•íÈmoøÂæ:r2Ë\,]ýÎ;%þÄÛÿÓàÓàx)Ê[)/ ŽIÒâ­Å±ËœŒ_×ùú|píøôýfPÎ…½acø”i›Ø.åÈZ²ÝƒÁô´ÏÕ›áup(î[ÛÔ¾ž}Ò5lû.ƒ?À×Á6{Oó*ëB°–ŒG3}\ãã×ÇgOX³·Ã P‚çÂÙ0O¥ƒ‰Sª1&Uc¸»~#´`ÓݪÝxÚß{ì˜j72]›Ì×Ï4ži« &BŸ3í.^xnç¡÷|Þœvåéõ›žjÛýªñó¸X\ujoy˜ ­Øð¥.ÛìS­B¾‰ÛÐ)ÜÃa!I¥&a4–a~€ãÓѱãšB‡=ÌÁÿõ»o­¾úÏë믞%yiêdqò}»¥4;'×Çáoàì;¹˜ôÚ4ý†-× €Ó™p0LËR¶†Ïƒ‹Ú2Êzºi8É/j¹.È“Á[ó,H˜68Q}ýxø$ì…ÆÉ™6àñÂa[?.FÛ’ö¤½†ã·OÞ å4p,l£6I‹wI[Òæ„§aéÆaÞn–kÁˆÄû¸‰í®}f›^ ?‚uÁW•öÅÿÉ’÷€ý›yéfÞº‘o;Ák[.NõJø1x`gÂ#PÎËÌI]q-yqðRç8~6ËÚ®mbŸô¨†]Ò”~(ýéÄÅ>aÛãìê÷ÈÀ6{@/jùqj±æq5¼~ß ã,$)Oe;t}¹ŽÍ¾°¸ZÞ¦à»×¾ ~^D”ºÿ9è»WšQ­°ÎÕkÇŒ«^r¥žè…Z°g𷀇Üá;Ë#¼øÜãÝÕ'¯;mxß°¥3:We c¶Þ¯êº¢õ*c«ý«=y¬?†^>úa:`Ü‚jîÌø ’‡«±›ß[™þ ×%–ÈÚléS(וÑ'ªú'Ý~ËiwÈkV®Ü>¥Z0k¥ª{î˜jüd‡h~õ¿>T}{Ö_ëf̸‰ÕÄyÕ¯W^AìÁÅÃu¢:

ž³ãõ“¦Vß~ôÁúãØ‡6Ú¥ÚoÒäêÈ®qÕ‹s¦Íx êZïáj̦÷Uc<ßr¦¹ [@:ÙŠçL›=•ÿ„d•jÁmœi7¬X-xll5a #M¥~Žý—ù8à4Ì«íö®Æ^vf=ß .±¤c–8£vlµo5ž×s·Ü»Z_|øÁ¸îjÏGÆVc9ø»Ÿ=‡Ñ½³êZ‹垆֭r7ãUHO'Y»Voéõæd:Ò†ïe:ü“^½xªûäé\¯ù f ÃDv>tOõ†9—VçóñÂGyCð.Ì3€¿Âÿc8Ü43q,©U¾'üÑÅmçÄ4&åMàvñ{—±YŠÕŸË¿ŒÓÆMÄCú§`^n´/™;e=“/ÑuÝË8uß\‡wFY¸–gù_Áz­°}Ö”z(P2eëË•aE7~ëªÄÖ°óÝòßÊép<\eßš¦ —~ëh|bø0Ôunƹx•ôKÒÆí‰ÚŸŽ½õ¸ Xîõ“ uT7š$ýþ6*m[\#Ѧv U]m»¢s^«Wç|Û ¼|Ö×Ô“á6Xn‡ìxZ¯™ŸÈOçÎѰ/(¾.ÿ2˜§¶±‹jPb½Mû1xŽo[1>{Ãñ·sËøÇxà»oÜ ¯„gƒ²9ØO®aÛ.Aý!ËÞÚÆ~°Ï¯…“Á}å^X¬G¹ö GâO]Õ7ýާõØ 6÷­#áðsû±¼uþèúÛW'®´jõÍñŒñ£ãªqœMÝÏM"Ï4`Wf0c3²¢ƒ9Óîâ:s‘gÚ ^oŽ«º|¸?¦ú3oÌ_yù)Õ[íWãBP¶‘OÒ!‹—z©xðVfÚ'a>7š1ÞRu½pÖÂçýuG «ELœÁ|~RLàŒøÕtþ¼Óôª››Tã﻽ºâ–+ëÛeÒž„ç7àáëí®Î7R†õ+qÛù[ÂIíâõÆþZP{«¥8‰,ó½p!hŸo-æs0ü L7˜ @êXÖŸ,‘ÉhYDÛ3¦Ý6/!†m_;qÞˆñºJ\Û¥Ö¯+LëºOîõ­æÿVp³dêg‚½aíËÒÿ| ^ Êàí`Éom«}Ò«.±Óa;øåp2Ìù²—ÕoP®Ç½žÎCçñ¿£8Ÿ"™Ã¥[Îm/Ê¡`¿nùxÈ­^ ›ó1sš¨zîQËîk¸^hÝ;6l¹–_汤s{7òsÞú aýú’vý¡}Ù'Mâu½Øî}ÿ„WÀs@Ùnׯb»Ržº•a{0Ía0œ›Ù´ý¼œ¿îŠ6ɧ[êâϸÚ;ƒ—Çàyp˜¿À·êúU׺Ôx~w5…¹Õ˜8Ó^üÂäàA?$g=à/ÁýjÊŒªû¡ñÕ<.ãÉþäqWŸV=¾õþ¼]?uÉö·4š¶ p;ÃídÁfûUkŽï®~ǽ¯è;lN5ñù7ðŽ”á¼Ÿ.®{ÙañàL˜ÂGñŸ<“wç3«¹cÇVã»·šãU÷ãUßgð~ÇííLï'‡5H-ÌBI¸ôÇÎj›ÎC”q¨f€‹þHð¶¯8ѽ½*gÃ/à$¸Ô‹“Z¡¶½bùÞþ]Œ–1”ËÉ“jÙ>ÔC"i‡n‰ý¥¤ßŒ;Þ¨ùø,\@ͺio_¿v„÷À$ø3¬nnšW€ã‘qŠ‹j‘<Õ §Xçf;†³¼¡ÎÛqpósÚ§ÿÎâX*ºMê-¬¥×/O‚™ð-p»Ö·×~òÂÛ;?\óÎãìïçƒ&Î÷ƒ@ù ¼\¿±ÇÛ›‡þÅËÌ<-ë¯?áøÛ¹Ö+v^òŸ >¥ß ¶ñ¹ l7CÖ°ºÔÍôú]ëöñtð`_Ùßæg_ç6 ÿIð[8mÌ£ÌS}ê\ú£s<ÜË·áÜØ…sãn~“ÿ9l]=g…5«s¯æ?ýæjüónàu«e¸Î´ñ‡iõÉ3ê‹À\.ãy“~)õy oׯڜدY‚o Ø¡C&þ]þ<õïÈ.r#Ý»ÓÊ ÛgÏ«&u«á}ˆîõfã-i¨Ä¼Ìó1ò¶AÊçΫƯà ·q+W]í\YgÓêF:íL¢]`®5(!Ø;AÔg±é*nŒNÐa78.Ÿ@=üs¨k竵áø,xáp3pr9‘Í_‰êSG¶~˜fIÅ|ÝÔËò–4Ï2}™o;¿:ûQ~ꊇºOx¨kc[›í5͆°U+ÎK’‡¿ú3Á1Я”e÷hþ鸯^Ê|*3<ÔÒ©Ív u¹C•ŸóDq®ÿ»Ks,˰þ2ì¼ t=€\ïú? ÷B9·Ë¹ ßÃÑÃ̽aepŽëWÞYµbˆ~”u¢,”å–í/¥OK×yh?zÀ»½Ï†/‚oZžû]Ñn#xü8uêÏð}#àaîz7´=å ª¥Ô›×x~©ïjÜóVZ«Úd³Ý«ƒWdt¦>XùøÕø—]ËC'»þpži>Ðz¶½`_1ùs5~_—§bÛÓƒpÞî»R§žìiO¿?íŒ!‘-yÁáß½å~Õ^Ôéoó›‘[sÔ~å~«ƒ®÷|zwH ìIFteÊüÄù¼Ó¾ƒ_à ­¾auì¦O©¹c79»-¦í\sׯIàÄñp_|½œ ú«€â¥Àƒè(p’½ n7S/™tægyæ§«4ÝmçE’ø‘î¦]Íz2*õï#¸°§Áº°-äàÁÛ»A˜Çdxì ·Âñ`Zûô[`Ÿ6¥]ÙŽ§é¼”¹q¸©Ùü'¯þ¤]úK3Ôñö,¯2˜¶ ÆÖþ*ǯéwšŸkÞÃüÏྡnØ:‰yM… Àƒê@P&Àçj_Ïo–ÙŠêÓlûÌl#mK¹n³ÓÆv®:ÑVìç àXx*¼<ôïe L‡Ïƒëþ˰)x1³ŸÝoÒ?ÉUï8wóä_ñÇzæOÛ¹Zmƒm«cçò·i6á*÷5δµ&âM8Üb~´à9ú‰¿T];ñ8É[ïɨN{°»çO“ó•A‚ƒ—tüàS)ü,‚ï*vs Øyü‚ê̹«=kN5ùØKè$J@·xµ+ʬ—×$õg2o¿Œßj›Åˆ­º'L©~¶éîÕas­îã—­TÆO7~cìŸR‡WÁÏázø,¬ Šûåà }/ؾN0ãrqHÞÉß…½68`Ñé–a‚£RÒûö“`?33ê_–ô"å&ØIV Â>} x3Ýø1x§ñÖþ,†vúob³¸Y——‚Ã"Yc_&÷ÃÁpê7,vÈÔrí²O:˜ŽHµë*}Ù®‚iŸqéßÁôCi[úS”çœfÇ©÷³[õp¾ú´ªMìðöÎSçòÆ`Ú™àåT;õßvÓ…K‚–vupâ¥hh=S׾ܨÙ7ê†í«;Á½à9°?çƒ}§¸Î¿Â©ðð2ðÌ÷óIÙüEdb5fÆŽÕÎ+M­~ÿ8ßÇ?ø¦jâñqó`¦y¾˜`iŠ¿σuõ.fØófUc©‡Çë)¼ 8ìï‹ù1€‹f‰Å_DØîiÕ¼Š¿ð Õ‚#®ã ð=4”¯ú[QÈW(Ïž]U/»®êâóÿ4ãoòäj¾0·5ܺh.ƒ‡Ñ‰àMñ«ðL°¯´s²xÓô©ugð0óéßxq²99Û‰Šƒá&ð Â@Ä:Yîh‘þêêaþ?p#h»!ìë´Â8‹ˆ þA8ìCÇà=àOyqQõêô—~§1*í‡ÂoÝ=¼^ »ƒáÔïRË•£Z%:¯F“x¡¶þĸ·À½0ÚIÚ¼9‘ÿÑ2pÍ•8]ÛÎ/ ÷å?{œÞÉ uç†OÿÖÃ5àçÕÊ)p%8O;_¬ÇV­tOÂM»ñŽ)ÇÒ¶—mŒ¿é6+oÉÇ9îÞp |ž ‡‚û·û¸b~ÛÁ»A;Žóú‹ãe>©‡‹cV_·Ze…UªSøæZÅï°yÕµ=gš¯ä—•x ðL{Á ¼òøGůÙÕƒû‹­¨vä[wöÁ dH&ÿŒ]ø‹Eóªsæ©Ý÷–jŒŸÁó=Æ1ëœùþvæófóg´n«Æpeš8iJõõ57ªVZqÍú qcñißÅwœ>ºQº²xÔ} VƒàPŒo׎¢¿á[à˜c I¹}˜Œø¨²Ý©¬ Í>t³vºÁ½|+b?¶k·© ÚÒ7(öáoa2´+õˆç˜Ÿ7/£ZÙÇÁ9po«#¥ßÚw«Šµãé˜[ßãkMûõfÔ¥p2tÚSÖ˱ù6(®ñv[Ëh_ig]/ê¬|ÂtÞîBòÅ[ûùT¹þÌ=Èý'ò <ƒ);étMç…ÙKHÖHY.êe*Ö¥ìÓÔ­ÔÅ× ë/Q§D—|ÜGî߃ûŒ[ÿçBæ¿sÄËÀ/á.8¶÷§ûø‹²óV›Yù¿À¾;ÿËÍ:Í*,m¡^Õ7ð¹èmLª±üyáùÕ/_ßž¿0˜ºtšüÊc˪1 ˜fc§TŸá»ýÓ×åÙùuÜYý­|Â#Fœ¾¶y-uã;–Ý—­VÍ\szõ#~‹r> xo^†Éà%@ñÀN,ë_Ál »ëEk¿¹©º¸ÛI&¢éK¿›ÃŸá<Ðß,í—7)ûÀ¶¶ÿ΀›`C˜{ƒ—*oãŠý¦­‹Ù¾zaËÅ©¾ÍÍTýHÇÚyÕIl§mv^ ‡Ì"Ó½ óq8Êlž¶9;…ýÓ®í®1m^>±)΋¦Ø®S[4ãNYAq"˜¦Ý˜¨·ŒÝ`:x©è´Ö‰ê•Ì×;Ðhÿ]8|2ý¬ «CìœÃÛÃc0 6m=~ÎûÔoÛv«oŠýx#Xÿˆe.KY’ò;¥-õ3Û˜>Ëšãø®ÿ–Ê÷q½ ûàL…·Á›áRø5oÏkþu7| ê~Ýl6äîGÙ#E|ð«ç5WáòÖý‚5« &Í«~é™Õ>ÛíSu]vÆÀæŠ p±e>wÖñ+V›Q™×ñ=ÅîcYžöQ9*‹ù'ô£oM¯¿¼êò9Vö[yíêAÿ_zäðwñ8aŽ€Ið>ø+Ü é/mújf_q.ê‰àäìKœÐË‹”ý‘…:ƒÆmÆÙ';‚7r7À²í^V…—€Óë>pÌ”2ßÒß;²~:gúë¿4¶˜‘ÔO¶×'3ë´ ´“¬µï鿬´kC;]õ¢?ïGÅã@O_é^‰ÍçÀ7VYûxûósû°p#Ìç÷Zðb`׬Ŷ{PïÓªû:mß -ÓQ)å\L[íßvcVêâOî!–ñ0œïÇâHø)8&¦Ëgÿ;ð—þŽ2¹z–Ÿ·¿í²ª«þÖ‘#M¬—¿\ÿÒkª1kï‘øËºcý–Bׂê¸-¨&ñm¼ŒÃâ_®:½Z@!ǰ*Ößòžª‹Ï×{þ ÒHí©V½ÌÃnæËàóÿ4O¬&¬9ƒWò]Õ>cÆÔЛ0»Ü´$‡S&Z\¢†EÊü-»¿§Æa©ÄRÈÔvúšôýðKPü¬rWØÒní|"z30Íëùz<®ofJq²»©,©˜óacpc_ZbûW…´}i•;œåxhzÐõÕ×ÿ6¸ ú’rmôe7”qÎQëå˜t*ß=Ø_2ó³å´SÝÝà[ÃYp+¨Û<ôµs¬kæ»¨ÓÆùíÞc¿&±î®Qû¢ÓeɶŠ}ã[’ˆmW¿4ÄrR?ûÙ/¥çOàð?dÝÍ«“x«Ýµ%W¾§qN£Ëõë){Á~·Vc'Ó¥~.1ÄYúòkx´äÈXk¿ Ã_\izòzØ8)lɲlô™°.8ÿFú¤Š I_f {«Á± ß áù°øÊU¹ÞUûzý3ñ_ ŽSÄq2ìº$ýdZë½ <&Âb­Ò F2Ï|ð,pzF7˜|F’mÆá­¶8Ÿ‡â‚¶,Ú˜¶t*Û×Ê¿o«"ÎÉKà0N1¯#ÀËýtð ãʰ38挿¥¿9Ð_½zrYz?mÓ«à,˜ íê?ýŠp&Øæ¾ÄôΛRšmn†KÛøµéd—8¾$ÞÓÿ«­Wº`AõøÓoªÆøÆXFºØàÕ\Uïç£xÂ_º„tTý®¹ÅÚà•®îGª]I¼ýŠü-äƒna÷îÔÅ#°÷üòä“xѶŽüIÇ®jÃM÷¬žrï­ü2%ÿñB«ºý¹e«b«®ô—6õgº™›‰·iýÑãñâÆ7 üÅšÔ»S¿¸!̆_é¼|ùû^?#} ø‘òðÕh6óõIéÓðwð©Ê<GR×3Hl¾WBÊÅ;ìâZ\ܺ{åY@úòÒ} Ö€¥Ù—ƒ¬î›Û¶ëÁy›¶;ž¾ðPÿ#8g«¯ÁMÀtÛ‚^ã϶ñIÊñYžtZËa{³gnŠmô¢ï«wß®DšyZŽzû©g8àí•Ø•qÑõáiê [Þ¼Mv«á!v=þ3Ÿq»Q;ÿøÎh0ƒw»þ“3mLµÂÖûT‡ñ‡ùœo}Š“tPâg üÑŸþ-b¿ék’ôà 2Z†ÆÞê|µó¬ÙU×#ܑƯÿ_ûÇùî§Òiأ׿NÐ 7u‰[×¼þ ‚c;Zĺn —Ã1Ðߦï·‡¶»búÝ`p³8¦£TîïUÙÏÚþ9||ÚMýEu{ÅöÚí$ëôåDÚ'JÙ=š¥ûÓòS¯N%ŸCÄëá®NËÞƒêx%d>Ú,ÇÓ íà[‚3[aœêƒ`ß¹¾½àºQ{ù;LÓIìsít {µü†‡KÌÛº¾^¶«]y^à½È?æt°±¼|n‚HæQòu|.…uZF‰krÚÅE×tM¨.úŠ¿ cŸŽ0¡:tÿ[í“o¯ÆLe»ˆgš¿èÎWõÎëSûĶö)éø>ÊHzÍÎR÷»þϘÕ\z{³'nÄÿt|7âÿmæ«‹áÝ}•«ÉÜß{xØœ4)n³MM½Y†ÄÅMZãû’¤×¦™¶™N[ŠôgÛL»¸aË鯬۰ùœ5€Blƒ—„9p&˜÷úà%`]x)D~„Ç ³yHºaþ>^Ü –7Éš{/ {y«qƒ^»CÜ)ŽÝ$pÌ:‰q}ÅwJ7õ¶Ó>‰ÄïS±{ÌÉ­û#Áyêî›2çðùp1L„¾Ä˰y?6ìËpˆâ2†/!?çŸÒßÜKÛ{¬{~–ýc{û›~ò=ðÂÐNR¯ä“°¶¥®N\ô]üÖÿØUÖ­&íªö«ÿèÏMœi¥•–£A¨ó¼Ù^õ±ª›73·Ú§þ†IŸ5ïo IÜýx5o«ªéüÑŸÕù[ÿó'ŽÒíÖÛ_¨Ö{¨~ºì^{‡úó×r²9ìÝþpƒ^Ž‚æµ™“I»E”„Æùzìào±6ET ‰uXR1Ëí«^ÚÜŸ‚ Á§‚þÊ6??îø d6=ÿ°7¸aúõ§Ði¾º)öõäDô‹cÞ©>C]XÞ¤ìIƯmeÞiÌ­“‡ÈpÕ-ãù"ÊðéÖ××ú¾¿ùBÒåBlgæ® 2¬è²ËÔÿÓî`_ÉÞp0dl߄߾,ó!¸ˆx™Pü¸á$°Œ”‡wÈÅú8×^ ÖYé4÷¬G§¸:aë‡ve;3§tÅ}Ã7Ç‚oœË‰[—äíÓó›ÿ]]ü_1+®Z­>lµÑÎ×™K¼ؽ]©Ô&Áÿò×uÕ9|4² zvý9úÝõŸ×¶¦¬èPûE“è±éôL.zì©­22ÙÊ"ÛM:ã£÷ÚN„m@1®“tŠs #lŒ×ÎErøº|(¤¯òìËô5û/Àמ€íD½Ø'ýIÚ; Cû΋€—°'Á‚âféá#XÅt}Õ·6æntå&6œÅ¥Ÿn¥õSuòîºyÑ»ì‡ÔïÿI«2?]×´t¾¡Š¼{†ñ^–¯ƒvb>É«Œçß¶ešNþvù7m=ˆïØ6ÓöN3tí)÷™Ø5ëpâ-¯Ô¹o4ãêø±ã«1÷ßQ=:yõj_ÿNÌ“ÿYu{SËFcF£IlÔVwWcøûþ£ûhŸ2¨v£³›7¤÷÷ü©ÿeù÷þûl]?‘î\3ù@³1]õ×¾ºøï ·C³ÚVRú½ÅÏ/…Ä%Tµ4ÃÑÇMN‰+]ÇÍ9 ŽJS’~G"m:·ðV°ô%¶Óº¬gÓ¾¯¸ØZ·ÒΰÖGÀ‹C³Äæø}ÃÒÓ¥‰k†£*×ü×€ŸÁëZ™w™­b䆕ýöæYÞ(cr&I÷/myŠ|n£#EÆ7î@j~ÒÖ5sìÎmÅyžü¾ƒßsÇ~Œo¯”yõ*‡ÈãëÑn}•E´«W?Úa^ñ›.ᔡŸ8ÃJâ£ÏÞVêãoÚ'M Vÿã| |×Çé‰Íî­ºü_þª©G‰øºt³û˜HôÍØÄ¯òŸ¥oiELjE,U¤ÛøŒv2ÿ™Tç MÛ#[éJÜÀ_ÏAhÈÚ«O«V7¾n“íjb_©Ó-ýêo÷ç7ó2-ÁÞ°þHi“<Wºâ¬ƒOÇìížö’Î'éïƒOíí. ¨ëïÞ_Žûm$ù¿‡8Ÿ\ܬ²‘•æ)S7þ2>þrñ«3ì+ͯ›`nýæaݽìô•Ñu±iºÆ¥˜¿—¦‘(§R©§ƒsqq%óª¯ôöspy–Ì#Û¨ßyª}ü Õ{t¨Ë»¾>?Å8çú½à[µôeYFòM9˜Õ}‹ëZæªàCËÁ‹›Éb¤kÖ¿l_â¢+]ëšz«a\©?zmÒñ‡àŒ›‡»“#²!ßa˜F£Ñ(N(jë0x0ß„·^4mc[éÑÖÚm¡sÖœÌÿ“ £Z\i«=Ѧ©ã§ð,÷LˆL’Òµ­å¼ˆ?6ö¥§nâðöú£‹½q¥¨·J‘Ø'ÜÎõ`.Ó”69´Ai^^L|i'³P> q9ìNwÄ&ñeÚ¦M™¶×Öw(@IDATôÇnI]Oo}‰“‡Æ¬¾ŒˆsÄ;z»ºk›¾h¯.ù¹ô*MßM%sßþîO2—uKéú‹·n^F|á%d¸$õNŸ¸nâ·Lñ1¬?:Ãú›¨WÊþ(ý͸2¿'Êâ³òV.«Nœ_-¨¿NW+Fç~¿ÂØêÜ©üÉû1|e¿£¸‰-ŽtùWÿFË_þëÔ@û%ßbÀ?®›ÿV÷‰ÉñDBuš÷L–tºJÜžÐ?£OÚ„Ë4ÑY†(ÑÅíÑþçp¿§)ëÓM­ÕÛ¾ÄǯëÓŸ^Õ“§–5ùé“ÓŒ–;7Ò.ãÔ{™ˆ67ÞO‚—ŠwAôxKÚ¯–ãÅf ö‹U™!Lty½n¸ ÀvöL&rëækÝ2šÅÛÞÃO¼y|Þ¼j.¯LÊ cól¥¿)àåÁTN¤¦_ÛôJâ ë×u < ÜUoÙú㔸¥ß¼J½qK[R~\ËïäoW7Û Êƒàë~篛¥Ÿ³+ëî-·|²7n bÌ×_t:2x‡U,wð«{CÚŠw©ÉS)i°sÅz—ã¸Ô*ÛOAÖÉ7EýIyõ×¶ÄÇMyeX.Ƕô;—g´l·Ä] œ7ûŠr øñ€éq7—ಜ²A½ Ö¯kIƒ·WÔEßt{ Ïâ^$Š,ìåGÁ7–¶|ÿÓ!ý>:h;û乤6Î÷µÁ‹\Ƹ™§óc#p>íO\ï bÛ^yò.¢z½eü½Æ}xüèi]°ÌZvοÂe °>^‚ß_?›Ï7mêÐt‰ZD´ñÐÿ¸ܹ“Þ%–²ñ—™ª‹¾¿Ô™ÎºE—z.ý‰oº¦WÔGbc¸Ô'¾WßݳpLV×?È7Àê4cÊ”#Øï«ÿ[8Óü]þÝ ®œö’nÛYû¾39ö_“ȸcÖ”WÈÍ­ Oþwð§€ïŸ?··E™8¶p˜y²HªÞ‰«.¨WŽk‘?†¡ÓÛ¢Ó7%=ߌk†›é†:\–§ßzé–úNez¸»‡G·\œúÿCÿ3®¤Kr5x&x9hæ]–\Ì{ëQêÔ/ ±/®÷À]ƒ(кE}ÿ›|¼¼ÎDÙKÛt íôèï%\™cͺf>¹¾nlF6¯ üé–®¬Céwn†R_fÕÔg]j㟠ÎïÕÁ7nÈÎç÷É`œ‡ý  ^&Bò*Ë(ý˜tœ'î5sàläÕZüŸÖW±ež†ƒñJÖ_Šý‰]l⟸Ø6Ýf|3mì£[ëô<ðMìžW]äá9gÅÖççI5Ê\7ËÙ×ãÔ—€=ðo n íʨ®•åRuµÎ¨™ýy¶¸µ?NŸÛõÍâæ;”鬗íÜ<°ú’û‰ÜŽnuêŸÙÄ¿|‹ä… X®£ÝZ‘íòò€ÚÞ>¡;†éǸ¨ÚJò3‡¾g½ÌÓKì{ÀúÝ W‚>d¼Ô»Ò–•òtÍ;a¼mE/ÖÝ<­Gê„wPÒ®üd¸Ô§ ·ó']ÓMzõéçvéÛéʼÊ|¢¯Z€Is¯.ð+€W­Ru§§rÖ•™Œ¿é f«máŸgZõ÷Sêy×¶úvö€åÊS«î-÷¯Æ¯0·úárÿÛUwëUÀó)†®†ûYb7Oíù¬mþcÕ©¨¼u·Í%°M©+í2)ãºÐ¹gÖÝ4±M¼:%úrŒ¢‹»:AãG3¿FôSFn³0/ª“áïðV8"gâqS´¿=ÀÜ$™mü¡ŽžƒÂtÆ•’òÕÅ·Ô•i–¦ßúDRçý1~¦}µ8RöQé_œ¼†+õòÉþoð~ ñ£¸³àê~ìm¹wÂM ãôÜÈÿ,¬Y‡‰‹ÛÈ¢7ø¾-Áƒx3Ø÷á_à~ýcP|[ðŸàünæm}£‹‹ª­N}S:ÿšéÊpÊÕ Æ—þØ7ueXú2ö†›6eX;Ã}I3¾n›Ö ÀŠkTãï½­:{"×ÅóÖ®ºÊW/m`¥O•<˜Ïçwç³ÿ ú«js ú³¯ÿöï…gUYPýcöŠÕØ{è­õt¿9/]ox÷°üø ^×_ÏŸVÊŶ¤•J×´sÕE_–cùŠqžŸôÉ+®ñJ3Ü£]òŸ)·]N‰Ó-û2í2›äká$øàFiš¯‚OnÚßÿ ÎSûbð©Ì·í¤,»¯øvq¥.ë"n7œþô×­â«nûj´IãP¶Ç±ô³ø–Ê~û§œG̬î+/çÛa#È%4í* P—uª>uôIðâr hç|úø`ž÷‚c}˜Ç6°'¤œ¦KT¯$®W1„óe¶Í2˰mó²Zö…ºØÄ]™oé]te¸ô'¾“ÛרšÆýǺ¬õð½Õv\á?º›ýgÂuŒŽOУMìœ9üÆ m˜Oã;©þ&ûl†0(Y0¶š·ó® ê˜ê«¼ ¨NÚ˜ÇY:«¿ÞT!Ãll]ýmɋ׬ºï›XMxìáê4T{¡Ú××}.Ze0.öI·Î¨ÈK}'b«ë!èkOǨ´'Ø[¯vú¦.öÑJ1_%nO¨çg©+ýjò-ثǴ~:ÿéÀ2¬§ŸfÕßÙÇ©ì³p§Ó¼WÊ|ÝtEQ.]ý„]ÿ×§Âëv2&½Óó*Ø®ãIìsë츤î>ÿ|ë3¤œO^NfC.¤‰‹k}½¸ÚçsÚëü™Îãµ`Püìÿw ëúR0ïó@ùþ(åAšòtCmÔ ëMôƒu“o™O©ˆßýÊoKY÷ä£k[ýö—o?ì¯H¹ÇE—ô ÷ç¦ïÛÙ¥¿ÍÓ1õ¢²3ûΟWÍäw¿î¹ÿÞjÎ ŒÆÏ6깡7jÄÊ%¼•¿_Ðç·ÿOýûo{ϱŽíÈÀt4hF\ý§ªûᩜŸÝÕïùËIýe­ª»þÚAÓp‡ýªÇzë·T]Sé›.­¶â/>6ͤÚNŠCÁ?‹Ð¾UÒÏ=¡ýtÒI¤ô«K|©wÙ¾>%dA5mƤme¼6J;]OÌÀ~š^)ó)u‰³ ÒIŒó€÷3J¥òIpqFÌ÷°&¶”‡ã® ŽEÊ5ʹì³OW}•Kô€ä¬ÜÐÌoYHÙ¶eQþâ–™µr&üª•ÉhhK»:ªs>êJ»9÷4ô®Ñõ[68õ\œ¦Ù2_OÃïG~Î×›À·¶g‚®ƒƒë"{s»r‰^hîk£Äí ì§išéJ]ü±±^¥.z]Û÷1°?J‚½íÙ¿— )*[’}6y©Š5œýÚ¼ô;{Ãó`#°î+Öé[wÝXíÍ·ÀªkèýÛ¹º••!~D‹gšœ³v5Ÿ_fôùöøT8“l ¶½6WžV-€ËùC@çò·œÜ…`pO˜ o…OÃ+áI`FÞ?÷‘ê`3æ—3ªyþµ[ bã¾¶“ƒ–Ó¯ÜöiÕ8.î}Êb7oóý«1WœZA¯Ÿ{Ë Õ¸_M¯ºý»ú#ùà,ñMÅIqÓWÍŸÛ]ýýþ;ëçP'îlpòØ/Þ@<_‚÷ÀàEÀ§Ö]ÀIî‚pŠbNlÃú•¸=¡Ef¡4c2¡uc⬧õuÒ+‰/ýÍ´µaÃ6ºfºä—<:Å—ém¯óñCðAp¦~xk±Þëþ༠~Ú‰yÃæq$7VÇB}ê6¿¯gƒù…ô7víÊH}ÚÅ- îÇaqÚâüV΄ k_Ïœoy—K§9~ ë¶ëCõÁø‰0‚A樗S ì ÚÊ£p#Ø×~dâÁüÞÆ•âüwݸÍ€ˆöJÜžÐõJ¸tµ-íËpü®#ëèA|4xÐ[OõIU¯D—|Œˆ.yô7<ÚÝ ?ƒË!sÐz(é·žPÏOûÔ¾qà¬/POÅöù®p|Ü7Ô+÷ÂEðb¿ÂjÕ§o¹ª:•_pÿݽªq'nÆ!@Î#ý+“é‘olÎFJÏqþž1®«ºxÞÜÞ½’¦u–tng‹1לZ-Øjÿj¼ùÕ¡+έþyòÌjÜlãÛßÉ”¡îvY©ýžoeŠüf:ßb`¡>rwýÝ\'ƒ·uùÀSàéð ðwbm¾Fò¶~üþ‚“hÜ ëÂîp-\ö¯½á¤mç¢îWÒ›qM`~.B/o„áÕ M6ë]¦!X‡ÕD,#Ræc¹¹5{Ð+ÆÓÙî߃2 ÊônöÙOAÑöçp=¸y6ËM~'g?Oƒ-`{¸þ)Û¼m¿R–i8ùª×׸¡”f9C™÷`òrœkûì/­„ƒm³é•!‡‘›íò(å|ÑlkâJ]Ùé'×£>µ>ÖÇáL8V†ôŸye^ƒŸ-¼:Ü{\WσÏÀý`íÕ_ ۵®%ùô„öÓüJ)ë¿í²Ì·ƒkÓuŸtqQõ^¸Ë¹_ÆkÓ”ô™zû(iõ{ð»ÿºwØÆÄÅMÞÎImÜÖ÷_׿EŽƒ¿{÷T(ûýÂ߇ÓÀ=Ä2VyèîjþJkU+ÞwõºÕV«.ûÃÕÔØñw½‹6%b8’ÄÿçZZwêúõY0‘äßpí) ½]鳺vöbKרjîµgT÷Î[½q*Ýþ9ŽÈÛŽ‘ØW¾™ð½§ª›ïHN\0·:î†Kª«ÇO¬'¸}°b«#ÎÃ=f€‹ÑÉh“œ|.è}àmðEx!lûÁ¡àíó'p$dÃ̤ÕM>x™Ô¥N¿¶¢Ä_†;Y¼tg86ñ'­áÔ!6¨zóÖ¯4Ó•¶Æ» üþ.¶f|Â.LIo-.ÎMápÃ4?ûÒ¦)Ö7ýg>ÇÄ–Ñá¸@¹‘¤}-“…œ²¥!£% ¬Fú à z/a–‹<ýâ¼üy+—ÁÖ)y|‹ô?XÌ´¸³¶“V“ø+ãg;´‰Ä>áN®vé mŽ®éZ¿ÇÁþY߸OÙß[Ûáà>ç®ø€q¼“çà ÐË«áG÷¸IÕ¤«Ï¨>ÌŸ‡ÿÃ=“«®/mÍ™Æî¤ÁH;Ì_üûÀN¼†æÿ0àòqÅéÕ»ùÅ¿ ¸®ªù ™l½_5‡wëÓ6¿¯ê>áªë¦Œï²/#+1}>ø$VØêÕãœp÷ß1«Úö®êƒ$“º]­¹ý#N$'à@NõgL¯ÂU<\½*·¯èN‡+Àƒê¸ œÄN`ó+óθYüöÎÌΪjÛ't)Ò{K¤Ko!$„ŽˆQéM)Š( R¤Šˆ¨Ÿ úýVŠ¢(‚ ˆŠH¯¢‚té½÷"ä¿ïwÎs²çÍ9“™d ù̬ëºg¯½v_»ž3CXÎÞœÕ_ÄÿŒÐzn/HÅ|ñªzÊ[¿bZÒzéÞ^ðkÁk4ÔJ÷™ž€ßÃà¸Ê|D«ö—vÓÓ¯ô£’¥%–Y®}i=‚+Ai×VWJ×ä|yþ°™×Cóðh3NPÕ‘z ë´ËcŸSÆôÉ%SJ?&×øßëvëk5çö`ŸbWOõHl`py9}ÜŸ'‚—“ŸP_€Ô•2†ÚÜ –÷Aû]˜Ü_ÂUrYϘZk;zªw÷¬õ¹ï†Áœà™c#eÿ´•qõvñØË´Ô÷&Šcq\ž?>öE_]ö©ìƒ>±Üb0¼ð=ÃüàÃÅú =KÏ@ÇfýßÏ1ÏãWÁ‡B½þìù„dÁ§üßófž½1#ÿ@ÐÈ¥G6~ðö …†Ñâq|·çïÙ383OñÿÂ;3#=~E¾†—;íÆ‹c§k çÆ+wôðÏþ¶ë«éYn½Æ ¯¿¯±Ì o7x'à€5¹1©Ýÿ qr‰å?Y|Ðjü_eæ®þçwñÉ—ŸªþÚÜä õÎt¤—ãÌð:\ ûƒ¯ÌÏfí‚èëñà&Þ–‡íÀo™Ô™¶ë!Y&(é{ÂÔ‘‚ÚKqsû¸ð¥ìó$Ÿ¡cpc­`>e½e~Ó|Ìdý”õ`nÕ«IžM1Ì–½ØVUþ²-L•”6ûçæ¾žë[<4MK_P;¶Ÿ>˜§”²Òþ^ëöãp¯ŸÊÛë´6J{©×Ýõ~ î/-/4Ý®I?}ºWœÓ²|Öœ¡ë×3æV˜ ~î1?\xŽXWÊö²VRÏo{^–;ÂG«ãözòjVWÊ0zWJ÷´Ø:…³é¥ž:ã C öo=X¼ü=G¿_u!—¿còg®>üü ¬ÛË_1’ú»b]?cóp7—ÿèAÓ4æºÿ¯LóV㉇¨áÕ9<¥É(vÒOþGršsù¿Ã¯µÿ=h†Æ’|ÿR_/‡áÚ/òÔƒ·Ÿ¿»ñÖÜê¿æÞ’¿¢œóó5Þ^òÅÆ4 ñ†ó?½+Oè~i´C%þaÄÌxê1®l¾"ûÈl¼CÞl zðæÆÿ~­1Óþ]½x ¸]Ò§JaW¬óOó» Ü WÀ·árp“ξpÝš°1x»PçínÀ—Á>8Ö»ø ׺†ÂSMâ¾ôsK´¥ÿíBÓmÓ ä&HÔÖ´˜Ç4Ûs|ÉgÞv`®ìéW²nóDlÛ×ü¯A?)ÇÂßÁ2íÆ…¹%ú‹e_å÷“TüøjÓö"aú‰ZI§¾Än›‹ÂA`?ìßäÇ¥ï>¹;ò_Þ~Ö@ª'^®çØ–®ÑæÞ_ü–Ðý»¸‡þœ îçr}—õ¨‹é®?×ãs°6x‘Í ×Àýó:åS–¤–¤ßÚ¥—¶çÉc½îM¥]½¥-yJ[©×Ó7ôa48•«³oABÏGÅ3áÐgóÂÒ°ÌúÎø¦°?p Wç©ã´m÷­{åÓàyò(¸‡ô§û¨'1’Ð>Zv}˜ ë´ü5ý½¯<טe–9|qÎÆn˜«1ˆ;mЂÌTõ—Œï…x§ù×þO¾o³WnŒ½wöÆ >Ü>Ë?^·ÒÞl<9ÿAOßß÷ždAõ½d‡~µ>ÏÐÆ)ücë¾8Ccðµóó?\Ô´KÍ?½wƲb:T1ÑfgÑ?ö›ƒi?÷âwø{ÿ_ï¼Þx›ë.ÿÁþ †ds‘¹ï†‰éŽMy!¹8]´Àùðkø3xÙ{(æýøruQ/.4/A7„‡‡QY<@¬÷a¸ ž'Áo]u)û½ £Wî) kOZtCÛ)/ãõôä‹Ý®˜êÁ›3Ús+ùRS%éO&O§ÐºL«×YÏoÚ”valez]7®†€¬þðÌàW螇îÅáÀ5WåÛ–pwØÜçÖ¡?ìóÏáSà7$¿?èûc¾ž¤ÜÿæKÜ×ð‘â<ÜÛñqÏŸh¼3óÌiߘ£1èê˜s~Q°"-¾AoQºteúMì˜ÿõšwÚ¹¾¹Bãíç%ÿ9ûý˜V¸ã²Æãóo âæËúÔ¶Îìw™oHcìí—6~B¸ÿ åJ7ÌÓôwþâežo â?lø¿[ìï/Àÿ¾ßÿ^ói–ÎWù«È¿,ÌF0/üµÿŸî¼šß! âR[½¦×fÀ—€‹Èy?¸`zšÃ2-ºaœn]^P.ìGàTpaj_\ÐúÚð°), .ÚEÁLjuùû¿Cùxšø“`]}‘ôÓ2uÝxlÑOþØëñØË0y •²®Ä_CñsŒŽï[pd\ñ%¦nRÖ¥ÿÀz0¬ËÔ?ÀoÒ/Ô–”u´Œ(Úïƒo Í8Ád—NýØŽõw}Û)©œ>‰_J=ë1¶„úîž÷›)/¶ÕÀui™³átàœn¨•˜®”u{á{é< Ë‚gƒ´yfœ>¬¯,O´[<û(uw S.鯕ÔÝëOZvÒS>uê§¡àåê8<óŸò8üfƒÅàÓ°+äìtNÞó [ÂYðX¯çBy—¥O˜Ç“ø'¡Ô¿ A+°;ü‡Ž {Ï?¬ñ¨ŸºùW÷¦ñN[ú…Æ ÙMõÅí©U*é«X§ò ·Æq+6ÆþyQî´±Õyy.þ:ó k¼Å§þw&æ“WÍÝÛ$‡vjùQüCA—6ΛkHãÆÆ6V}~¦ÆÜ,Öxë)þª’ÿ¼bÐP®gµõ_ 47!ê/úêA8#o”ç¬þ'c´tã?ÏÎØ˜–¯GøÏØÆÞüµÿ¡ÓÍÐ8ÿÿT«‹ÏßÕùÒ¼ œl_¢Ao~ZuñÚ ]sŠZ›¦êvëµ¾?Âÿ€—“q/}˩ϣ`ð _À~’XÌóüžº&í¥L-›z]Jwª—˜7ñR­:]e¾zÜüÚR.y =Ü67¶éOÁà®KÆh¾HêLšþ½>€~*¥¬£“Ý<"†©¿ÌûYw­ÿ\O“ëÛ ×DÖ jâ!î<¬ 6u‚ñÄ<²œ¿…rOí&®ŽÑj­˜`Y%aú—z˰ÌW×{ü||ˆîóûûË ÏÓêxºíºö> ;Ã9`~ë4mqX¬Û‡ÁUà\*õ>Ææ%ª/ìCÆeZ¤^®Œ«—ÒS¼Lë¤[—}0Ý}:›}ó ^”{ÁGÔŽ°x*–{Nƒà pÌþÚBßEÒ‡„±'Ô'‘è†Ö17Œןw„ý±-¿ð¼×ÿÛÀ/Ÿ¾¯qßpßÈïá×àÛ幸ÓþýäÌAïkÞiVÐ×;"Õ}–;m&ze'nšƒÿDjXã/Ýx‹GÀt3¾Óx„oÓ÷àò?r™õÓ–ã·š>K'gõ¹¢vx ºå²ÆØ¥Öàß'žµ±/_•|—¿¢|‡Ëy,ÿ ï4<Ú´éC –7­#ñ«Žòy[¯ÓÙr5ñ/5U3ò:Þ¾˜ëû¢EcŸáßoÆñÓðBâo8‡½ùzãÄ{¯«>ZÄj/b ÂËà¢z œd?úÊÔ'íüâå¿5û‚_;»0ì–’°+Ö½¼×WæðeÚmË>Ú‡WÁCÛøµpxˆÜöYq,Ší¥Í„±FÊ´Øêa§<¥êzâ ëu·^Óõ«/vÇ®øíÈ—ÀqꃴŸÐ2¥N´Û¼èw†Q Xç‘`}ú(å £×}W¦‘­•Oý¿E|d 'Lâ€\¿å\õ¶:çØÃÚ5Г˜Ïùù"|V€ÛÁ6ë’¼û“à#p™f÷P;ù6Æ`X§ë+ë¶Ô£}ˆ­Ìƒ¹mYO†À:àÅûMð¢{ë«KY§÷…cù)¬ ƒc¿’òÓ=¸ÖÕ­Ïó¡ôIúbh¾ƒá°ý§Àú'EÒßÔ‘xOa™f¿ì¯—úð!æ9ç庘®ÏõEéû›ˆ.Ïhó™§”²ØcKÜв‘è õ÷ú Ÿ|XxÞ:Îi.Ø Ð/‡iYž¿)»¥1v½¥Ó<µHãÓÜi'úûyî´·ùgx§ýXcÐ&qÐ3âÞÞiv˜?æ«.“×èÿ¨O㢅cù6ûþâZ¿ ÏaôðÛ·ý¥Û¯L)9ibÛïº,»Acz:þ– ñß)Ïß|ŒÿxcÕfÆhè&ý û#ÇÀÃ`úEàY_h˜ª½6á·àh¸Ü”YH¨Ýô´«]Tsì&õ1á§—ƒ`0ØÅ’¶ïCÿ< w½ð"¸yR_Ú®‡diIÒZ†>(õ1X´n+ãeÕi×ñ, ×ÂÌðl ΃yœåäE­æÄ £9û­öl'mZïh8”;`g¸ ôµuÔ–[ÚL¢ÕêÓæef\×JW¼þ³¬Ã~X·›^\„#aGðb¬‹‹Ò×ð•ÍÐOR£àX¬Ë~”}(u’Æûcž2/S3]{ú_Ë4õR¬ß±Ú·á0Ðvlnº²ßŽÃ â\ð?,o¥l[Ý4Ç,ÙŒûéçû`™€ÚªÃö”¤%,mU†>ü˜—¼À!ຶ.Ê"öÉqfì}í–~÷ÀaoÐÿÖÙñÑìRo2÷1}³/=ù¼ì«zD=ñЇÃðQ¸¦ǘ<¨-=ëf~lÀ,ðð,ñRð"÷|¨.ÂRR_Ú7tŽ ÓžºçÂàj¨¾½±îÇae¨×¹MyÏÛµ\òMh®&”NU-±ˆz‰}T†6™‹PÛœ0 V‡EA±oŽ[¹¾ W·DëÛŽò|ÀÜ’z’PÚµ•ãŠnèÙ³¬öÇ’Ÿü ž%ÊÓ`úÝ`?Í×Mx âŽiðá]f½Ælƒ¦k¬Á}µ5¦¼5¨± Z%w÷•ß~çNóWÓx§ù×»Î4î6? ů¹Ïá"ºúŽK«}åF7¦¹õÒ‰ÞÃÝú]Fê+ÓÞ}¹õƒÞž‘òVc›i5Vfv–£á¥èØÜ踯'íy¸ nÁ97LóvãœÛ.ïw¾Uf~”ãªëÆ]×ê€Û« >ðŸàÆ¿ \iµ­Ø¯H©Ç–0}(Cõ2î³?¤lÔÖÁà!|ܯ‚ƒ‡ÁzzjŸä¶’öËÄÒÖN×{²¼zú¢/_¥·À {[82>ý¯èkóæÝ ,Ÿô´eÝü‡Ã1 \ À¿ sg_ÒŸèõ,ÝòŸØëù x¹„+á¿U>ÆÀ~ ÎÛx‡áÿ‘AgÝ”¡ºkeø&ì®+ψäCí¦;ïæùXö³0Ìÿ9ø>tòSê4lGÖ-ÉÕ {9í+h@v„ßC™/ëÙtu¥–¶*CóGò•¶R7Ý~&LZÆa\ݽ½T“ìí…ˆ»n<ÓëåÓ®e·<º²ß1µÆ ®”mN(žúÍ=á‚ØÖÛòŒñ\õLš\ÛÏÁJðØfÊ¡öN†ŽiÌÅïò·¦ôÊ<V }ãC(¾±íà.j¿…|7N;mãÜ[.ªy˜ß©;ô½iµÙ ÿrà4¼r¦ã«‘±·^Vú½ni~­À#ÀÇÕÛüí¤rlÑúÕ’èoa+p³»~—‚_]] »§P¦•:Å*I›F¢¶Ó]t?=Ü>BƒŸë·œ ôpáú(º žI¯cñ€HÒ¦nÒÎ[»06+Iý¥-•§Ý1 G4|t}|XÎM¼†â\(ŽA)ëWeÚKD\3ÂNp.XwÄzM÷“‘â+éiW{©ï­ #ã½ ORooËšÏ1Ù¶óïÚ»bCì’q¹|œõU¦”±ØŰîØüµsQσ©Z?æqýx¹­ï‡cÀËÃõ¾,¸?]ÇÖSJÚÔ–ú õ¯b»ëu8¬~0Ø”‹a;pÔÛ°½€ZIúPëé‰&oi‹nÿ\ãúavð[×ÿ\`ÿe`xÑZ—e”'áAp oÂQ 8žóÁËÑü=µOr%©3qCmõ²‰ê/}º6ؾâyùpß¹¾oƒàåÜ®>Ì…¯éõÁtüš{ìÍ—öm¯,5Šr|+@G߾㒶wZç†'2¥'²ªI+¶4ÿ¾pöª>ùcZþ÷/³ øýIÙϱþWüþ%ÛSÃe¹ºîbóBø6|®©»±Ï_¬pøiÒÍÖ“ô¦/–·éG=tïÁ,p-øIÂ…èÂõØã9ÜT7‚ý~Ìw3ôB´zÿÒ6I­þ”zÒ £'½´•i¦+i+á=Øô¥¾Ý~–s æ)!Úêoʧ2Œîú;årØ žä³.êïÂ¥ðC°?diǰԉöIl+åûT̹XË'ÀuùE"dRÆ7E €N8†¬‰èeèáíƆÚÊïz]–‡_ƒs´8¬ »òø$8Ÿ‘¬ ëTÊ0픡}HÜ6ýÆÌuúmp ».Vƒ‡ÁtÅ6ÒNÂò!Z¦'ª+)›°Ë:®?žQž™^ú>t û»)Œ/}÷–yÝŸÊáÇ`\ߨ7ýw(Žñ0=m'ÄÔÛé‹”uØŸ¥ÁG¶~³-?8üÞÊÕ°ø(pJí›ð·üçð‡évÄïðëlþiáÆ ¯ðŸ¨;“Šÿö ù&ö¿åïªdâ~¦ WºKñ»ŽLV²öv¶2½“n¹,šR7¿ Â…úyðå~$˜g+Ð/¿‚•AÛ=zPÇ“¤™·“$O§tíeyó{?Ý ·$<\œ_/Uûé'ùÜ gÂáAø'øiÛ±*.jÅvÒŸR7-võRR6éõм©Ëð-ØæóÞ>jüö%¯oí)ƒZ鯕ÔßkÿÓ :| |÷öÓŸÁ:´¾Úº€´›ö0µ$ýjz¡´«§Ū,úJ úGÉáÞ›ü?'e|“¿÷Ýדó+JBõ¬ u%i†^Ÿ€}ÀGìbà¼}²¿~Œn^‰¿R¦nRÚS¦["îï[ÁËÒKtP¾ ›ƒŸºm;u¥ÿeHr7I¿š=a ëwÿ?ÝûiY›ûé°¦NÐÊëÚõœ:ŽÀoá3 Ï|´»/#Öm;±Y·LŠd,©÷Tæ·)>`샾µO9‹¼üG€}±íôu⤸ÐÓ—²¢v¶2ý=Ñ]ÔSƒ”‹©Ô_ÁSà%ê‚p‘{±\CÁó¸hzëî„åÒvÂÒf_æƒ]À‡É£ðx®7Ô…ð+8n³Q^°–„…Á¾»§¶Ûɼ«§/e¿1·ÆQêeÞØ #©ÃúÃW`ñfâi„¾òo,úÜ|öAIÝ]±ñ¦îäM~Ë{Îk@ƪ¯ÊMì¼ê»ÓA¥]Ô–¤Î–á=Vì“•þ–É=®þÏÄÔ×nýÄÿ¸G”Ø»bã~þ õ<ðËÁpÝGöN’vR»°ìƒkÙËrMðqïùäåº$xy=e~¢-I[-C¥ÜÑ-ç~òl1\–†Õ`~kÃÇÁKžÇ+èçÂñ°ü²}üûaÆsÁzç…‘ ø ò|] <çÜjŸ%c± mY¯è;ý¹8|¿·?gÂf¾•å1ÿ÷ŠðÔ"å†(uˆ~ð²w“ío€›l8\^¦nìG¡¯ 3›#m–atª­.$7Ø.àB´-nš'ájx ì‡ýõ“‡Ñ©p ¸I¯—á`uØ7Ö0°ÿÏ€RÎÙõó&®n‰· ­w( ޶w< ^þ—Á}ÐNÚÕ§M)ÃèÚ³‡áÇ`z˜þ@æËÐÃÊtó‹u$Dìb_úC—>8¿Aé/¢S•dì†ÑËu¡3’–ô2Lšëúað²xŽ®©ƒõHÊ”¡i‰«—}ˆ=¡i^Â{Ã×àJðÛ¼Àu2œ ž–)¥OZÖ—a©'Ýr^¾^–î×c›V„ÏÀ–°2ÌîmÓ€}á`8|äë—™@y®‡ÅÀol{n ÊÙàÙç£ýR¸ôu|Ñ)$Ëx’qYƳ} ð,tïë?ÏÄcÀ|öñ‡°+˜ßñ¤<ê¿dþ÷´ûÄ:ÉåD«{éz¡®Y¸~’>ôÓRàÆ¨K§Å™è'ψ¶ÞJò&œ‘‚.X©bÝ$/ÁÀ º$øÉÀMì˜,3ì ¾²?` 0ݱù2N½ñKB’*I?ÆžP»íÚ'/ãÃA±þóá0þt£+e¦…*±öÃ4¥ÌÝ6®„ÛÁî#cdQ+IF¢'ìÊ1ΞøÿÕÐq-×äÿêú£ßõùµÎØÊP½S<ýpm-`ðô¡~3¸³ßS¦Öš­·‘4Ãvâ^ò"þ6ø°÷˜2Üï¶)ÛÔVî¯äÑ{t÷¿my9€­Àvf/üŸÃ¡0üöLy ÔµM•gMmƒÏ‚qÜÑ ]”n’ÂˆÍ r¸8‡ÂHpqJÊ£¶D» Ë Õ×´*ùQ»éÆ•ÔU¯¯§xÒ ÝÐnV^~c`/˜> öEqŒŽg38~ nŒM`4xÈXW)nîrÓ–éöÝô%ÀOßEüîû¦”uXN1 õx'{ò%ôú#ÜÎá°1ä@)ëɺ×µm‘R×f^}=<(ë阦HÉ:™ØÎÕ}4±õ¼×å2? Ó¾ñØ2¶ØbO˜2úÐ eVð²\ ´Ý ׂòy8| »ÞÛÕÉ­>ô4?3oN¸Ô/ƒ·A9üueö–¶ô»¬S]¼èÜîUÏ$?l C`iðRüxn¬ ñ jõ+< [ƒz˜>ºÝkže{D+Ñ–þ4MUP··-}«^B´’z™ØÚ–uؗѰxnyù<ã·gÒöàåoÚ¦Z)'xjsB¹`K]?¸0^„ÅàQPæ…¯‚›q ú¯^S%.ÄÛÀºÜ¼ÆÕE‰ž¸¶²®Ø–ieÞØ ÅMù °o–uüæ‚ àw1¹y‚áGàÆß| 8F/U› áYøx€DÒ¶6‡=aM°œc¶½—¡ÝZ³_F'¹%íl&Æž>x(j³ýÁC+yP[º¶Ø£'n¾His¼ÃáðÓ‘ñvãÂÜk)ëïu¡÷(cú6í¹V&F&Õ?ÓfY&c0¬ëeÜ2õxlŽA×ÕÈfœ úC7÷“kÁ5±¸GêmkO[u=eÊPß?î£Ûá°ÎË–ð*DÊvc{eø¸—7ƒõaIp¯ž_oêÓ*îw×ù'À‡Çqð:X¿¾°ÿe[íôvcŒ/Êü±QeUoYN[]ʲê%×/Úô‹gÚ‡@q[Áq–uažºdº©k¸ãÖÉÏb«/7 hE¸üdë§ã£Á c¹ÑpxÑ)Ú$âÆõb,ë.ÓÕÓ‡zÙÔQæ-õµ =l׋_IÜM{qƒµapÃ(¾Œ}Ý{x=7Á¹MÝWòçáy¨÷'}ÐnŸ/]åJ¸Ülñj%æ/˪×m]9Ço3}0Œn^Û¹KÑ>ü„s*Ø7Ûp|)Ó®}’[’ô,w¬ÿãÖ7)bû½pá¤Tô.”µoŸÁƒów ÔýÒeÿ§>×?ž3õù?w—EŸZÎu;)’9¶Žè S¯ñØlS)m‰›æþ~ŠÞ<f‚£af°œ~¹¼hÔµEJ½'[Ê•¡>´N÷ä(8l×}éÅýKhwÎxXÖG«{{5¸œÑ°Ì¥¯ÿFü‡p9¸ÖÏű+Ž%ý3^êÆëbþºX&Rú^[ê+ÃäM˜ò†)ïZlïI8|à¼ÎÑzp8þœ¨S¯ÄqS¯º[Æ_.*m.|7ú p ¸°Ü0.¬ÅÁ‹n ¸ ê‹·\_Å6ý:ù@8¤Y¸ìGÓ4Ùçô"øi3Ìzêm‡Ü+‚k«·ãÊÚêmíòe¾ Kݼ±Å®-}KZ=ŸëÊo¿üìzR¼üõËóàžP,ïE­¤®„]ÖqöÄ{ ³¾>Cf÷ácpØ–Ÿv·Ï)%kÔ>­¦y ú‰Þuû9ø¬ÎOæô,ô%au8¬?}·ÎÔ‹ÚZ÷é—6óFâÏÄ ËôROZi+õ²õ²Íè†>ôý€ææ·ãÁ±˜æå¿ \ÖmžÁí&jjtL’cžÐWïû`œ ŠÚÑàÃÀ´›¡›$åP+Éb®‡õtãÉSד· Ëv¢F¯ç=y =à_ƒýÁM²œ‘•Q¾Ç‡y=Xæ ?…X—‡¤ÍpÐ?Áàf«·©eSWʱwYºÿ,Ó£×ÔøЇµ} k€ýÈzOˆ©’Ä­¯ÔMl×FÆSž„®?©, [6ë)Ú¦©ÏcÈ8ú\¸(àó1â…¦_Ý—Áö`ݟ˘fø~BÅ4}ö | Îû g@ü—ý@R7©÷¹[b›H™?ºaô²HÝf¼ôAòj‹]¿û-Œ¾<¿´;Æ`Ю%Ï/ýdþ ­)²L]’Å;uºóh³¸Ì=¡6Ú¾àʆÙ}ø²v#úµLY®\àÑ j%=•Kž‰ ËzË~¥®lzÇ'n˜³aiX΃Ìåïø'ø Àìð x¬;ëß6”´ÛëÝOÛ·œa]b+óDOÞÌñäWïÔûê¼l΋ñYÀ‡ þU<Ãæýdê€Ä¥ãc›ÚÃrÑEOèkÙç¥w$è?/¹ÝàÃàa°)ÌÕÔ :.bÓ"Yô^¬¾j•ÌiI×=¡¶ÞJÆ‘Ðr¥žz<\Ý0÷ƒüa¸Ò7óŒà󠨝Sàù¦®Oí¶eC‰”zlBëñ“ü²`?ã‡2´~:ûø`qÎvn†é»aÊD7b³¿>j”¤%ì²NY?í«sµÈÒ­Ìo»£o½0{çÄù9 †Ã -óR]_>|T(IÏ'Ô®®$Ïè^¤óŸ&=î…K åP«ü†Še#ê^ÀÎÉà'ö¤gÝ'$©ÛqL¦™ßv­ÇyÕ6–Å|Àg`6P,s#`5ð/J׈õ¥ÝzHR¯$ã0sÝo¦•6ó$¿aôÒ®®¤?]±®xlŽÕsi3°Çaüpðï™lÓq/Ê¿!e+ÃÀqȳ hz \0Ñz y@¹±¶‡÷‹rØÌ·!¸ÑiÊV†¦M=!›ÁʺË$Ýx'Ý´‰ë¯÷Ëzb3ô‚Ud­L¨¨ùôÿ½ù¬Ï‹q׋kå/à'äÌsBL•”sÝеî#ÿ£U®®Ç¤Ÿ4õs')ׯº®í¯ 7ƒë3y Å}/‰Ú¾y½ä¼ì?ÎíPð«ï¥@ÉøÕ=›N†U`=8l3aýQž1D+±½I‘zyû®­ôeYú -yÔS&õ–º{ѽ¬Oµ†ãaaP®†åÀsÁó+eQ¤îráÔÓ¦öx¹p¢—¡¾û5ìê.è1°'¸ð6_¤nàvâ¢/Å:üzÏ ªÔÓ»¬ã~¦/Ê7®Dï´Ôkîè†öozpœ/Á‰àÃ`{xJBäKp*|ö?y)åØeÿgÚ®§Ì€áp&>ÀÚ‰mÜ m&z@lÜÔ‹~“è&E×îAj[ÿ Ï€õ%µÛü¤.í}˽âú+Øw£ÉUgÝ×ÎI)ñiBçp7øü´—ó=¡éÉãz]æ„Á½©oÿxõF²Ž­Ó²Š¶’Ø 7/|óû¨^¼øGÂR°/|ÖR,ã¥7 ö€û@›{Ö±õ§Ø·ˆºÔýgz=Ÿ6¥´'?Uš?J›¾› 6÷¢c[Ž€™Aù Œÿ¶Q–': uxÈ HgdÕ“qá´ð#x.©Õ¯ú¾ÂU𠸭ϲ¤]š¶`¹äI˜:Ó×NuOª=í8nñ`õàEücˆü ůJÓwÎ{á:8îçàYp êGëÏêc#©å;óèß=ÁÒ²íÄ|/°x@mgÁãÍø;„¶ešºÝòŽós‘ô)a—uÜOó—iÆ{/RößvúSú»¾þìÛÄÔUú5åckÆ–Ké1 õKÛtÅüî]çÌ}jÜOý«ƒkh?p}¸æ/œ¬ÔJ2ß 5FO˜6ºJŒ¿^üVanXì«íøððSÿr°”⾺懕Á¾<“¼ Ó.j¿I|kÝÊm«´EOhžˆ¶²oõ¸iI7M¿- #Àµ­ŸVçÂt÷û|{pN_…é…²z‘uªÎ’ŨÊÅé!àæ&\<4”àëà& ÃÀ¼.V¥¬O[(í™ä[6’ºŒGO˜<ý¦_ ­WÝ üðÅ­\[€›õ»þx0- »Á¹ðKØ6„ cêOHR7W<fƒøH[]lÛÔOko‚‡é(°¼’òæ­ëÚ`g÷³bÙr½–¶2­ÔÍ£X‡ü\~µí'Üáð1p}}I;>¤GÃ:p&\YÓ;£»îW;qìí¤“½]ÞÒf¹Óʺ¢ÚÿÄ3–2iÓnÜ3t¬úJÛºp0¸'åk°=8Gõ:0 H'è°éÊ…¥ž¸¡›ï6X ŋ훠׀¥À|)‡:AÉfIFãA›ºÇÂM ’z¹ØË°¬§n/ãõþ7£cs#z¹*?ƒgà%𓳛s¸ü$¥Ø®¤àÇp,ì CÁ¯ó­×<£a>ÈÁ–v’Tõø’°+ÖõÓzf‚#ÀCÛºv‡9 ùƒbÞèÆÕc‹®]›’°Ôµ•ö¤•6Ç÷œǃiÚ¦$q6„¡“©Sòc™߯V‚¶Ìm©k“7à88\³® ÷«òAð±©xÙ> ^JíÄrYW ÍWêÆ-ï£wNðÃÁ°%¬‡Sa7ðÑ’~ß‚þmXZÖIž3#`ÿfÜú‡CÁým¹º¯1 H'dquJ°w÷@¹¸²!Ì¡>äp'º¾õrö›7²s%p“æSj·[Ö_ß@õÍc{n /Qã3@=¦ JoÊd¼ ýT½7øéßÍ÷¸´Û//í¿8¿N5|ƒZ}u¹áWáÜf¸6¡‡îïá0x Ò¿´¤JŒ+ £·?û¥X÷ºàü¤ÞèÆÛéu[â -P[zê/möÉ ç³p”}&:Qb;®©²½‰ªˆB®#¿Éº¾JÔÛUSûŸÖ_’\u[⦫Çÿ†¥ž´º-ö„Öã¥áþÛëÍ}´(87›4C/בùsA™žù‹ž¤*-é¶é¾°~Âj0ö׺؞g„â\œëÁHp>|€¸¦­×5ý/pîÏk†ÕÇúà֮ضbh¹2%ݰ”ä=ãoŠ„Ø Å¶ì÷—áüfÜþ'jKl³.ö+˜®¿ý °*X¯~s~ö÷s´ ¶¡o¤h7}¬bªËî"dÁ÷PpÁ? ëÀm xþ¼–¿ pq÷VÊ ¤lo&ð±;¸I|1GÊru[™¦î8bK˜2íB×ãñkÊÈŸQn}`}öÇ>NßäA“aiX~ ñ¥>ûÀ_à‹àÁw*ØŽõHò£VºñØ¢'LûûðRßì—ñr¼–5n¥Ôµ/IžzhžHò'n[™¯Lï«ôµ\»üΛ2>]iã|ÜŒökÐÎñ EO>ÃÌE==ó–Ðô2oiOYCýçžô›+óÌ3Ãrà#^ŸÜ WkFÉ:3ì´6Ëuk½+‚{Æ3À±çÂ÷`SX/²§ÁDze¼ôÿ^z>¨•Ôë%x ¸®€—Á3`X,Ó“èKÇV®ŸøÙrñµ{eY æÏ¾I^C±žÁ Ë:‰¶ÒÕëâ8KÑ«€þ·=Ç»-lŠçÞÖp*”óAt@úâëé»\ÜYüY艻`ݾîσ Àýuø¸˜õû_!eP+±®Ô×4UyÌ—¼†Écx¤NãÉWê˜[¢]<<ì«’2©;ñ®ÔñºaýšÑ‡Žu=@ iS•nÜ>f£{À}f‡` ØónUi]ãµèÁà§šW —7j«~ûœ~[‡’ñÿ|fƒàa|¤œ}ó¦ÍFI=¦¥ÿ±YN[;1O»¾$oê0^Ï—<} ‹oƒ—Éù})Ø&oúvi®ÓÌi›¬mʘëÔíe\]Jß&=ë*yJ{©Û^™×¸bë5í-П^¦;‚{×½²ä6oü”е¶´ù p}/sÁbà…æåì¾™Êý÷(qÏ‹Ÿ{ZñbçÀºÓOÔJO›w_,ûi0ÿѰ1äŒOÙ„æÙÖ†Cá9ˆ˜'b²SÓàÞK»æ ÚôÏö`›î3ûžtÔJl×½î7q¥è3óêÿUÁ½¨n»Â† OmcKÐW^þ¶›9@¾x ›¡/eòvy ¾èŒÇæBváºPÏåðÓïÂà!`ž¼`QÛJ6¡’xt7¤õ—ö¤–bÿüäq:x°9ÿ©µ’z=æ)׉ýõ«Ç/V¹»~\Dà§û¢Ä »¬]›u%"·‚å½Ðÿ| …/€6Eß ‡à‡ðG8üdãáéá`ýe= DIÛ†Ê?á*°^§mA‰ÿ?ƒ~%X¿eâ›Ô_“e¼<òÆn%qÃH;[Ò&êwÇ¡OúKô‡uo%cqÍ?ú8¾µŽNã+íÒ­'k1yËxÙN9_Ñëy“ßPqíxYÏ«€ë̽u ì#AŸèóŠºØC/)Ç>ÖëÙþ‚Ãì èŸ?€ù¼÷//P±.׫’6£§]ûð˜ïð"·^/v÷Z)öQÑb?ö„gÁ2IGmIüœ>™[ôVf}çø“§¬SÝvm/lÔVÞ7ÐG€—äó(^þŽÏËUðò·®øu@&ÆNÆ€L¼Ü„JÂèY˜†~åç#@ñ€þ¬»Â. Í|uÉÆIhztCçÎ0 vKO¼,£m7pC- å…‘|˜+1n¿²ñsà¸ç…­!Ô/Ð_7iÆ¿¤¬ámp \JžÇÑ¿ºÁÕÇ€_qz(øIåOp<ì ÃÁ:íSyYµkßOSßÛ3¯ý yýݹ¹³õꃉbãÍ…},ë2¢­.ÉW·wŠßD o&—Ä_÷Ó{a0”c&Ú’Œ/c7´|$鯣—aÖ~=ìeÞèeÞè Ó¶ëyð"rÄ~ºÑ6`}Ócö‰ûµk¾•À ݽñ Xfó? ß÷ËæàZ ì¿X_¹†m'})Ãèîà°.û8+lJÙgu˹Nl†ö+u™^B´[ùÄË<êñ}Ò ëbæûø ŠhwOŽ‚Ešº¶À _ñ|Y|äØž’>wÅ~öÙqdŸ èæøÑ°Ž—‰‹×Ëç‹àBצœ¿ƒ+Àƒa0\ï‡[á$¸®…éÁïÁÐ.ÔP[›C[IJs‚á‹àÆ”ýŽÍêpÃz˜Í¯ÂYàáâfv ‡cº wÂ`ÐîïÁ1)OÃHø¤¾ŒÓ€L¬œÄ™t”‹1›+¡€_w‡‚‡Š‹\YLÛÊ:ˆV’M•E_Ú£—1ùMK™Ø =p^1û‘6“S%Æ­÷ xüúݸý ‚‡Ž›ú"x îƒãà«`ùÔSKÒžiÁDëô2°-ûz?|<”‡¢\þöeØÎj²+¡°ý± ó{îÖoúΰ˜®xù«Û?¥ ÓçÒn=¢$½Œk·Ý¯À•0l7y •Nñ®Ôq?“oœ¥KËø2Žzú{OßÊÐvOʸ¾r¾}Èîç]»ù”žÂ²®è 3íê¨*.~è7×ÞpP_¦ŵõ ¼îÓC×ÞÖàz3ü! ÛÀ`°>å)ø, Ÿ‚\þöÓ<ÖÕnÞ´ÔnybO9/ø;`$ €ã÷›³ÝÁ~Ç/¨•·¼íÇßÚê[Ê'L¾zºöÒ–|¥=ýOÚ²( ƒåÜ»Gs¡< ¦ß åœIõ€›m@úÇå¢WÖ®î&½^†\øƒÑç?½.~‚Ý<=8®m@}®ÒI•¸©´%ÔXÏ›öP·%nh?NƒûÁþ{Xì ›‚›Ññœ ÏóŸÁ<9üìK)‰·ëW™/醶#b~Ò?‚ÁOlöQß,›ƒ}Úýäà×¹ouMOÀFàãi]7‚m(æ+ŸýîdOÞ2=º¡õ^ ^ ?ƒ<`Ê<˜[»õÐÊÐÁV¦¿—z»þÙ~Ý^Æ£ëH†ÎÕU`š’< c3oÝ–¸¡b%ö蕱ö#sëúö2·ÌÀÇ­º—·ß 9o~º£aUÀúð~p})‚ý®à'ÙÛ@ɾ°ÞR²/J[òÔCóh«£}(Ì¿† àM _ûŸ±¦=눎ZI»zK›™Ê9(õz>ãçÙ=ç'ÅþlTi]þ¹ ݾ seo…µá%°|¤ÞçØÂ>z ¶Å²·ñ@6—Iåuƒw{aý<g€Ó: ý›°.x™)ÙLÑ+cÓ®^¶gÜv<`fóÖób¯þ2¿éJúl½öË~îŠä%p/ئ ¨-Øv]ÚÙÒ¾y“ž¾[·býÊ)M–#ôÙVËù)7ø<ÂoáaxF€¾þ8¦málðw¼‘ôÇ:ÓÓêöäO˜¼ mãðöQ¢/SGò$$©’z<}Hºq%õ¨Ç¦ÞW±úÖÇRo¤l·Ì_ÚKÝÄm×G¤bý§Áã°øX\Æ€…á ß²6ÕÏ„_Âuð$(ž±Î¹û%R¶[=4cª‡æ«—·~û«Û¼Üú÷Óð}˜Rj¥;æØêõš§ôk]7ÞÎf¹v’ú Ë96ï¢p$Ìb¹¶ub^Û±\ê@Iõ@}&µ¾òã/P7gÄÃÂMxlnTm˃‹ß¼åœ¸Ø³ÁPÛêíÒ³a,£ÔóÔã]¹ºÿLžôßp5X¼(=ýD®½ÌSnÐR'[bÞŒ‰§Û[¹<ôV5ájЧæ×ÏËÀçá"8~W‚Ÿ4}¿|¬7cFOL+ÓOh¤—¡ºóá寔ùO˜r‰*Êt¥vý¬ç)Ó&¤;öø¸§¼íÚˆÍ0R×/óF·Œ—””ë¶LîƒÙËu?pîbG­¤½ÖÇê'åeAû&`;ŠÇ=Àõu0ü>KëÏüúN»kÊ´ßÃS¾Ä·æ ¨½ó+õ°Ë:î§éîAûä¾ôý(¦íî‡ôÉPiÆVOÝ0ºy”z¼ËÚýgÆ ÕÇÐ’Eò‚èÇ@yùoNÜ©cR,_ÖQ~LšÜpÒÈ-C7G·5?M\#ÁÍ`žaà!“¯­,K´Š{X•ybO~Ãv6í™ïvyS¦]h9?ÅzÙz Ùþiàa£8¾Œ1qÉ•²®ÔQÚ¢ÛÅKÿXüŠñ0ø;Düt´#l ~Ò_nË){CêJÝUBíGéÛZRël—'6˨ý½Öó7¿½—•ß~8–+¥¬§´·ÓSvw=l炬­2ê,mê)ݸ}TÊ2u{WŽqå˼)›Ð´ˆ_Á»þmÃ9MZBLݤ´;¯ÁLÑ}H,^îGj~âß%l‡Âwa dl¯¢{Ñ›îòS`YçFJI;¥mRtëS:…÷“ææAP×öqUHÿµ•mù2z=½´×õÄ K±A»zÄú÷ÝÆuçÃex.:7e¢ÒŸÈ‚èÏ:êêò€I„lC½/©µàYð`›f„H¹øÝ0/ÑËà†)7i©;¯e<:æêH¼Sh¾ˆ}ðr°]?ý .ÿ>¡”²¿¥}btë*±Ž2ž¶ô¯žq/ç¯ý]/ý{¬Ó4xÈl«ƒãŒ_Q«: õ“bÑ—zâ>*²¯:ù7ö”I¼ ÛÍa꽌‚'ƒë+ý-Ëb®¤´EOša|x úGA?º6“7!¦ñlI«‡æ-û½´k‹½^¾Œ[FѦ8›Á‰¾£Ž'©¿]žØÌãXó•AûÚ¶†£ï ‹‚kL¹¶¿1ÚÎà(¸¬S±®Púð#ÒS(ãÃz¬×0½šþ8< yöAw¼®%ã4´þ„êA[ìÑËä*]›ívÓõcúcŠ¡v×òI𱦞|D[eÔ¤=IèÇ*ªjzÀÍ07|²išIUEÿ1/*7l]Ìã†ÍæÙý|Ø Úå/Û‰^)Ú­?¦+ »bÝz1^–P¿À2Ù¨ 1URÇ>±a½>ãu<àÄÃÄÕðMð“ãp/øÉB™µ+hˆ_$>|˜Y¯õ¤Í„˜ÚJé;Û6i³ñÒVÆ£—a½ŒiéÇht?u:×HÊ¡VRÇn˜´2|û¹àšÒ^Jòu²ÕÓS¾´×õÔUÚµÕã±%¿{Á3ˇބD¿(ú¬D{æg(úÇa>ð«ÿ¥ ’6ôñ…à§èeà,xÒ——Ñ}—ºQ'Iæ¥ô®àÕ'™wÔnºñºØï›`6¸ܳŽÁqú»þ³NŰ®'Þ)]{¤Ì[»P¿øíÒ"àXŒ¿ãöéÇpô¶N²Ȥz`ÀÙ“êÁñËëS´È%àåîA“"° =Lü òO°(æÿÜ·À]à¦Ù®€§!â& Ú¢ÛõHæ[›zÒcOX–q³Ú§KÁä)p£þ ì·éæO™zHÒ»"é«•G·/K‚¾Ênå50ÝOúÁòPŠsæáéÜ–óÑc¦+íÆë!»>ü¾gA¨ãIü”„2®î˜ÒN]·ŽÅ~%jKRWÊ%!öÄÆ¦GOZÂØË:]ÃúÅKF½^Þ¼)‡ÚSi‹Ý°.òÅ^í‹r ƒO€ë — {ÌËÞ}ä§x¿wm¯Jüc½—€kàLðSµ¶Áǰù’×б;ï†Ø®m”cn»Šñ Í>n >F·…­Aù5ø˜Õ’qX6z•PüH½1•ñè Í£®$Ô÷sÁHÐÿ>˜ÜÛ€ëØ9ó,q®L7¿}‰?Ë~©H?z “ÔUNõU•>u£yi>ÛôŠ›Ót)uã.ø%àrðâðpw#ûûG¿z”›!e-ŸÍ‘‘8I•Äž¸¡åK©Ç“fYÛ°_GÀA üö' “{£¦ÿö÷ð`w‚>4½Äþš×Cið“‡eêâì*ðpú+<ùtŸ:0µæ@›—‹õí ƒ‡[¤Ý\ô”Væï«n½e™´Ó“Ý´ø²Ì½L‹nØ^Œê±£vÓëñ2Ÿiué”{BËE7Ìz=}ð!¦ÌKƒûqx!úøso–âXœG×Åàee©µ›_;ùØ|ý-å8­Ûx0nCí ÂÆà8~ ž%^¸úá)H~ÔJ&4–ví[0}Hzl†¶g?ÖbúsØ”¬™ýп®#ý¯$4Oú–°Ê0ðcÒ=PP“^Û@ z ÁÐEì'CCãYð¨­|¥îµ;¸YÜ æ^&N^@n^/¶N›ÁvÊ>$nyË)±%_—µûOÓlO9ì‡È·áz°ýN} é=•ŒÃOï3ÃÏ¡ÝÚθõƒs¢/ƒ6ãŽÑOüŽ[†ƒŸVdø­‹¹rN­S?¾?ýd<2!?™7¤LY¾L›žrÉ—°¬7¶zØ›<)cÞÍà;p”ó(É[w¥öü3eê¹bofM^B¡KÁGÞ*0Ö†ÏÃV°0¸Çz®ë3<®Ì™öè 1µlêï•ØQ&¤»½|]ÇŽË‹ßuÿ&ü´§.ÔV}½µYF©÷#6/ÿùa„Äv½è׃úyh®û§K?wËxòîyÀMàFT\ÔåæªŒÍYôæ/¿BÛg`=«P9w©7›± ÉZåw#¾¹˰(XŸ[.ã~½\¶c[}ÇÚ ÇXú"qÃè¶•xl–)mÒËö“×zS¡c\‚Ùšñ²=ó(e]]–ÞýÌœ%L©ø3¡vÛý8§~ò_FÁp8Îß< Ø'×ò™ð=ðá¦øiùgàú±¾´S%eÛ±µ ½dû[Òv§>ÅîCöa0þWpÏ*~û¡£¢ïD™¡+hÙʹ‹n–úã¡ì“ùlk¬ Öé¼ì ®÷™íÞ ZkZ}@ÞCdâßÃ&š*<ͪ©ÔÝœ7ßÀËÅÃvg؃‘à¦ÊÆCo¹_Ëžƒ£›ölè2ÄÜë¶ÀƒÑ2?€çA)û›x•ð.ü°­•@¿äSoÆq*ŽÇCF‰ʾš/y =Ì*Ëm ß‚åaM¸rXÎŒ¾è“¿Á/a3°¬u¥í²=Ì-ú ^×}ÖH}.˶ÉÖQ|eMtÌÔÇ„Nm—öèÎÝ]Íúï&û³Œ%Wý·$˜^Î9Ññd¦¦%{,ÒnêZ…}êãêËà^²Œùއ+!b¾™ p²dòx Œ­gó¤'ƽl΀SÁyrólÛ‚°.˜¯Ü<¥NR+ý'蟃;ÁƒÛ|u0µê2íØ<4Ýô×Áà×àe;õ¾“Ü')ëjWÐö‡Â p$臞ÖmúÓ.,}Ýßÿžû⩟½<47óÃÁOpÊûàÃð[¸‚@ñ[û9¡±™·“ü›„a03ƒu…¦©}B¡c¶Þ åëmºó õü˜Æóô4oãè`p^/¯ÙÀ‹ë“°+| ~ΙëÜöÞ€ÀÀ‹~WØ ¾®­1`æûMSÏÚОu„ÚM7ÞN2Æ‘èZ±¼cïO)ûTÖ[oëY¿Á;ì›có~¦…ÌÝIè>îõm$i Í?žUÀöÒó8Þáà·”Ö3 Αâ™r,ÜÖ±ì€Ld±N†¦§Ú&³iÚ-z7g6”›É‹÷ØÜ0~­·9| ¼Ö‡úEc½%„n¸ïƒz™óxö9±íÒ´›~1ø­„u¤õÐõä¡j½½ëèi:¾û`$xp™W?õF¬;$W÷ òbðòH?vCw¶3xpëÁ&p;hSìßà¸΃½a^xôE»´mØIâ;çÿ/p68ïÚƒý,éd¯çF¹Á Áñ•é½Õm+y‡>Jû õ™—‚e"¥O:éÖ«øx±üòà·YŸ†ÿ‡5u`©ûiôÃa½&ß!tí΋ÀÜà·ó€sx=ü [úÚm½‘ä;’Ì®•wKÒŽ¡þ)ã®7×ßÿ‚~˜n€'@ÿ …ÍÁ‡ªe]þ}Ä /,_Î%ÑJ,k=ÂàŠ87úUp½úÝÁ0,÷ìÕ ëkó€L¸Øä½ó@6iZ4[¤ºqf‚ß‚_5û­€²ì^2nÖv»ÎKN)7v]Oºëb4¸‰Ísx8ú ¡Óš1Ÿi<íÆ‚¹%0æý=\ŽS[;I]W“ø ˜wB’2Ég¼D{W¿ Eÿ®Ú=ý秦=`IÛÀM1~ûh?–Åzʾ§ý*±öC_zho '€ñžÄôNèçø{-ôÓÀº]íʘ?´K×)ÓcëzA8‡ƒz;‰Oê/uC};¶çàl8V/sE{‘o ƒáD¸¿cÎüýë°+øÐò";êbÛJ®XÏ?“׋òÏͬ±õ\²ï©©7¡58wúkìÆ‚9á4p¬>z¿î'Åò÷À5 ŸŒ—óL´%O£é?óè;CýêåïƒÊoR†Áç`A°/OÂj𤖋tj+éá»ä'c@&²\üÑÝ,çf˜®~ln:7œŸ€ÜdóÀzP¾Æ‰¶6p¹¹JÝ<‘Ø =,ýdðñ$þ~Õ´¥^¢=Š}ímÞ+j“X÷Câ -â˜×âp8î@_?õ¾™^>p|(hóSÖ0Ø öEŸ9OÊK O‡sÀ6ߊói;öK©‡]ÖqöÄ˼¥­nöÕþ¤þvùzk«û¥§¸iñ“­í—ùëû¸( n²¡—‹ç©ôçˆÿî…çA_:/e½D+ÑfÙ•a 8¬Zç†p%¼ ¶¯¤Ž„]ÖÉóÓ‡Ê p L é£>«ãx´šO¨{ÙûtîÊ)pP¥u7c-ÃR7kâêúkmX\ÿ®ýCÁõeÛ×Ãf ßÅG—{J±O?®´®_¿y†Í–³´S×Iþò€“0 S†\èn\Åyy¾ ^ꘊëp£½ÊÂp̃›<›µ%©» Õƒm÷98Î2œ _í•}$ÚMLóÑâ'<õv}ÀÜMÒ—ÞäíV°‘²þè†b õíÝp18nåóðr¥û‘¼±×oΑ>ú|¬ÏGÀƒð&(>ì<Àáà\Z‡¤‡_]L‹Ä_e<¶2ToÇ´Ø=dío»ôÞÚ(^I™_Câ]©ã~f ^FyðÄæ%¢Ø§ÀN°|N†QàÚ6Ýo.†=Á¶ö‡»À‡…ëÞñ¥^Ô–ÄfûKÂ"0¬óvðA­˜/P↓K£â:r]® ñjâXô‡âÚzŒ;V¿úwœúÚ5éZU2i7aWê8߯Ÿk’°8¸¾—…#!r%ŠƒäÕ®žxBíÎÀL 8~28½Öd¹)Ê À¹yüÝg%óºÙž‚•À Kñw ÇÁP sBŠljLÕïšÏ2Ö±-ø¢7þGxr˜ v”ô¿ÜÜ3“ÐÛ|=ÕÑ—´´—вñ­¡—“—â¡ù1úÁ2)WIªÒ¬C¿Y‡ª_W/ÀñpdÏ9gGÂßÀov¿Ay ¬G;'íD{Ò¢ZwBõvñØ{–u¥ªmÕ[ÚÔ{’ø/e¼´ãÂà'ü/Ãwà8–Åü®ó¯ÃÆ0 ~ î }l=Î’6J=så|úÀpÜk!å×ð¤Û¶_)‡:Ù$}Ð'îÉ+Á¾õUôÓ /}Ìÿ³©û ú$ø èm½é“>ô1åYýKà<ØÖ°>xé×HÊ&>Nf¸dÊñ€$›$¥—R¦9o~åéWj7‚â¯æ²ívzl†ÑQ+ݵ{_C˺Ê:Ô•².K÷ŸñE´íE<¬›ÃQððâ úÜ<ʰ.¸ÎõßÕàÅmyýëúNý 1µlêË óm¶á76?Çäœm^¶î£wC⻾Öm?Ï}h?û*–ÓW~;¥]cÚÞ„ƒÁ~ém$éæ ƒßR:‡û‚ýr­þvÁÖÉY”x9_ë—Ô5N¤?‘Ž{—Š•›Â&O›q7”ó7.Å x ,Úç‡äCí(n|/¢yÀCPñ1à'ñ²JötX}Š•ô?LÜÌCívø äÀÚÝÇUöJò×C²´$iÔ-ëÜØ†Ÿø7ëü%Dô§_Ç~ þ 7ƒé¢`¹¦ñ{ªwŠ“TIòô%´ßÃ`YøOUKײ--‰Y*U»ã·ÿ®/Ççåûøøé{˜ômܗðü\‹>DD±ÎÞJòÎA^XŠý9žÇiÝÆ×‡£@[IêòRœq,í|]·/±œ }«îãòjÐfÜÇ×KPJY§yÇ1 €Òò°蓾Ž1õS´’·øéaéEä×®¿^vž¯‚b»KÁÀƒÚ‹r{°Ô¶Îu¹3ŸŽC{â†õx™Ö“þe¯…SÁ>%/jËÚJ1nÿ»e憥Á‡ÔÏá;pèSÓË|6€•aC8ô“y¤îKL½ëÖçúSŸ}²œçÁºõ­é—‚ý7ÞbûÖµÜC œ7¢%ÖìtÃvbôã=03ü í‡y )_ú[]?†…àEp=:§o€þú|¼üóXEí(eýi³cæ„wÇý±ßžM=µºø³ËQgS$=iæ-Ñn¿†û.ä«åÝÐ÷¿vÙp©SKÜÀ/À1M‹ë¯eï…´Ÿr ÍšõSÚ´Oé_ØÏè„úÎKÇOH§€‚â'¤ò!¤-cN¨­.íÒlÏ É‹ú"ðÂ;¼ýdµ-Ü)ëê'-ûó(8ÇÖ¹“S5îÖ­˜V¦WÆ>ü˜¼kê`½ÛÊøGß >gÁ÷aXÌ£<^>Ö}\ o~Ç8!éi|¶#~ò^Fƒ™óì£ÊŽ’þ8/ÖgØ_’º½üŸ…Ç!¶þj£“lÇ´¤×ç®ågàVpïƒu¡.)¯¿FÜàÞðáäÜê'÷Ë.p"ègçO±¬ôFÒ¿ÞäÈÓÈÞUTÕGxè 9´S<›Ç‹)[â ݈Υ_­~£iôe>6C ÓjÞQðp{8ü^º”}P/7o©×ËM®¸œâõ~(û¯]‰-á,ØŽ«Rº>áx Ê„>Ùdü ›Ut œ3ûg~u×ÀùàWîËÁÁoóxØîw¹ðX^×É’pì ÿ†NýÐ^Óxbî/Ív’ú]W곂ýöÁä:ü| Cä”Sa-g‚—}êBmIi+uÇæšÕ–9kj*±›ÏõoÞò’ûqÇÕiOÔ¯ò7js ÙŸômb(}aõxl®©ˆyä p½¸–¯†ÈÞ(ú"ûE{.ø ÑÝ;®±½`DS7ïÖp:8÷JOcKšýˆn™vý×> ï²Êò.75P},ý0X¼|#Ù ³a K’?›õ˼$¼,ÜЃíaðÓ¦m¤NÔJ¼è¿¦yèŸ/BYÔJ,W§™4EöU¿xùÿ¼²æãCL-Ѧ$í(týèÁw0Ô}?Æ'dÏ·õ´”1¯õ:G¶§îÅæ¡êänøŒ† à ðÂRÌ7 NKà7àØnóþÌ›ñ v“Œ¯ »e("æ±½Rô¡è[×Ë0ؾ gÁ¡°9ÌŠcúl öswø+ØGË[¿í”¾i§k3ßðKx òx(óc®Ä¼~ƒ²,öS¹þöß ìˇŠL§¢çáU¶eÝí¤ÌÓ.½·¶òrímóM¨ý¾œãúý>Ðož7€6åëð Øž—þ¹ú2úÒ 8÷ËÂõàšìä7’dJö@_Δ<ŽÿË}ópr² 'v,Ù„2Öw2|´Û†‡¥ß ,@7ºy·/å* Ÿ…Ô‰ÚËê‘Ôg}•²žÞ”µ!½ÈÜI^¿éh7¦ØZ­—çKðS°o³ÂGÁvÔJ7´~¶m¥$¯¶2­´'Íþ‹u9G'Âüà…fßrÅË÷¸|¼|< ŸƒøµêŸ}ì„y#öɸëź‚à{`_?‘»ÆÌ÷øéüc°xqh·ïæ©K}ÌI×^®#㊋“ÀGÅßA¿´Ëš¶Ì>Þ¼ô\?ëQÊ9ï²tÿ9Qûv7¿g±ŒÛ£†t¤ŒGwlŽ3~Ì|ë—‚kôÐ/®ÓõÁ‹Ý$ýöÎN²ªÌÛ·{9ƒär%)’AÄ´Ô5‚9¬ú)fE@L˜1¯®ŠbΊº*ŠDÓªˆHrÌ&}Ïs«þ5§ïTu÷„žžÖzóôyÏ{r>÷Vußyÿ&ð"¥ß7;Ã`9ÚF+½âšO_Æ¡21Æ¡è~‘ípQ¸¹êöZ í¨#:I¯ë"> \Ôæï&¼¼¦ÃàæàÆüB0ͽðsØžÙ$Q;Nk7W›å(ê‰S†ù‘xq‡‰Ú ºí§à4RºÔÉĶ3Rê±éjûíëp?(G¯E›ýâ:Jt„×Á?ÀÍÒƒO{@í©'L·óp“övã>^ûÁám.‘ÖÏKÝàp*xi¸”I-§Óδ7nÚ¢ß2׃}À¹ócxx¹°ï£Ü¦3ìYðCð ñIÒ~”ä›~èå–ýYꉟñÌÓgìÑëåáíìÑm«úqm½tÒ†ÒÝvm7İ]Û‰^ö‡a±'^é:¼üÙ÷ÛjÛcÛ£ï@æÎ{Ñ}X°ÛmMó0¾„Ë@q†ë»:ReºáÚÐ#yß¼,zÀAîËŠÙYq‡«e¹˜Œ§ßÅéFÿG8\¼ÊæpÌ€íÁÍÚM@ùx°>Ù8šuˆ¿tKnÈoó-Ãðöãy8=<,’uXy ¡Ÿ‚ë¡Ùþa¶GJc¸éà†ç¡îAø4¸²§¾º±yÐüþû×ð¼µ”é£'l8× [¬‹OÂ3Á Û>¹ ó[ã‹ÁKáàåÐKO~¶³Ä‹F.,[£ þÿ›Bæ‹sêÛ°k?çÿ-¬‰“~NÛJ7:ÑéýtÓ׉ÓÌÃñÚÖ/aö‡íòí—Þ´uDñâü½c-ÛÍv™»uvMê–}w¿¶Ùp=< kÅ´öÅ}p-؆{@Ù¼ØÙoÇ‚oZ”«a8orY@]b±‘²­±õÝåÐ~_Vœ(ÅâÖª¹ˆâ÷àS —€Á1÷ÉÈÅýP8æ€ñOŸ<ÀL§-ù vô¦M¿ñ}:~¸‰(™_eüVHë§vy8œ›ƒùŒF®"ÒG eŒ&M·8öy0\½¬¯›ÝëÀÍCåÍ $Žnê .“áGðhðRcžÆ‘ÄIºø ªE¿·åk¥µ.ͺ:v «úËÁ§´gÀWÁº(Ö}OÐvØwÀíà%ÂÃQCÇïÝðø0†)ŽÏ/à…°x¸\ wÓàw`=#e;¢ëv#}”þLÅ-Ó—ù'/m–íçÅÇClp>›‡o'Œk?VÌË>LÙ£M·4ñʲÒ6û}wøØ®²OÊøÕb½7…sa pÞDÔMã¸Ù7ÀßA»ãïx¿¼(ï"Ø œgΕ¤G],1]7éVÿnñú¶eÜN¢¾¬x=°¸ , (nÙ"7ŸÈ®‚ahóð^ð`sÓü\ 7‚ågÃG")C·ÔoZ7›/€b>‘Ä_×për¸©]½Ê%¨«˜~i¤W½b÷iñç0 ì§ àh˜ åú‰n:1]yøÇ^ºDéHìC¡æœOiéSÝû;°ÿu¯€à¥`ZÅ0Ûàn¿_ ÆÛž_ƒï‚á[Cú×ö}¶„§‚ñõ%Ä>²^¥XÿH©kK»cßòÔKW½éO|‚ê0ûÉö= œÛû‚âx}ÒgÚF+|Í66m·xiG·°ôƒaÑïøîŽ‘m´Ÿ#eD7o‚^Ÿû¢Ùv׫c8œ/¦µœGÃJ üöË4|Øœ) u©Ä|ú2=à$éËŠÝ#-µ‡Í³ad£Š‹©³ðoFß.Öˆ”yÿ Ðëé(qKW½ôg£Â<Ä®_IÜ–oáOëzè–õ^cü47ŸŠÞÖßï(ð‰;íkû£7]‚jiÚãO¸®¶ˆºOן„ aPʾŠ×pë¢ßñ<Ö…Càp D6Eù œ…GBêt+úoàhðÀyÜ †O%eêFJ=6Ó(É»—[Æ)õf|ÃJ±½¾ÖöÐs.¯ [€ Çðàa¶"ÈZTÂ~Nl¯¢ëœ³¿?¶ÓËŒãš8¨µè/û¾œ'#F7ÏëÁÃþ÷àþ`š\ú¼ø:Ü_’÷Ñè¿çSæêbKYÏÅNÜO°lzÀÁîËŠÓY˜‹S£‰üøox<¸ –ÒÌÓÃäPø)xóx m>A*Y q[Ö–½ÜxÔã×uc2MÓŽ©–Ä_×ø¶c<$uMÙeýJý\"\ ®™½a¯¶nœr£ÞÍÍ4ÃApG¬Ÿé~߆¼Ð.nÖ½p,î7j/1æñdx1œÚ’±ÓîAzì §€¾O‚ƳÌÔ¡éÔU|ºôðêÕvÛ™>*õfü„%®áŠî³á°=GƒeÚÆ`¼Åú*—±•Á¾V¬»(¥[Ú}Š÷’[éšNÑæxDlü'ãhßv÷„ÇBÖ¾}ç…üIàÅÏ=%y|Ý9áe!ÔZÌw´’üŒ¿8éF›?Þ(z rQûQ–C,É¢pCø <N_;÷ówÃñP¸·ˆäB~=ûfs#ÁÔÙxF«'^é6uýã!Ùtšn·º¸YÞ>)®›AmæSæ/×4£ _Ë¿<@3OtG±õÀö:؆Ãq¼wëü8 .…g€‡€C·ƒóˆbœ[ßÛSJúH[ô¸å![¯´^P΂ë`Oðm‰émÃ/!åš·åûŸ90÷¡¨·ƒ}ê*ë]·›îxÇŽÚ‘¦-óÂMÝqÜì#Ë?œÏ¾ 0ç‚ûˆ—ÇM›y(·ÀŸÀ¹’ö .¶$?öÏ¡Åî¾e“ ßñ˦Ç2—,¾,ð¸)S¿‹õBð)¾\”YdÙtsoú> Æ®Í'pŸ ƒOŒnJòiù†þL}t££ôÇ7áºË[Ê:ô*»¹.LãáñI°í«§ŒaÆ×-Á;Äo˜RºÑ[!ÝfŒ’V¿uhŽIéwƒ·Ž^÷‚w€ˆ¯Á¡P–{.þ7Â&ðlð-ÃmÙÅt×ÂñàáõÜ Š‡EY'õ&Ƴ>ûƒõ±,Åz¤.MÝðŒCÂâ6ûÛ¸†Y®×Á!‡ëëн”koGL«Ämùý™úøñÂÒÈe$ör•ö$¯”_Ú‡ÓM—4É£é6ÇÂ>ñ©o¸fÂQà˜*—‚y:®Oß:”b˜y–õ2|¤z§óèË8÷@&ô8W£_|»ÊE1Ü‚ÊâKœltqËMžY´ÿ$ð=í¦ÿ!|\ènGÃQàFí§ÏôÉK¿ÒÍÕ6uâ"}ücå–õ±ŒÔÛù?¥ð'^ú*~ÓøÄóø>dS<ýH<]ó ¥½©­í¥¤Ÿã&̱q|3ºÚDÝ|<øõo/‡ÓÁƒæõ°®ÜÚv/ïçÅWáHp콞ö²:˜ægàeóCàãÝ~D2Oô+ÖÏø°äA­%}Pö‘y*ºÚãv‹›t^2ö€é°xˆýÎië8±/óiÚ“ŸöRìÛ£àb8–T2^eú²>ÃéeݯÌǼ#êÁþsü=øwÇí9ðLÐ> > „ÛÁ¶>6†ÌÔE¤¬ÝêÔLdœ²žëf¼¾Œ{ ßñcÜÁË8ûÑ,®nEf±¹Ð}úrÑçÆÿô7À[!›Äè¯79Ÿv!M7ËfYñÇ%ʶ6Ë0¼›XÇŒŸáñëZ®c*°3áð;8v‡5@qÃÿ#›ÂGá*07xûCW®‡³`?ðø<ø´h™^„¶…×À­p.x¹ÐnŸÌ%õlùZ?µYæHú¡t›z7¿¶ÒÙrh}ý>°"e:mM/a‰÷M”Kà÷‰´ Ü”¡;Ý"¯[ñéûÒu\ [‚~ä§8v/„ßÀ]p8”cÀ±7Nd¸²§›k}ú²õ@yªÒ¿uUÊ’E¦ÝÎqóln e¸qº‰yß .huoü>ÍΆÛà,p“p¡ë /…ƒ`:¸Á–åtÓcÓmê½lDíÄU_R–e~ñ§NÎ{ûÀ‹Oð›€íN<ÔŽž4ÚLãö-°ÿäÅð $m\LµMW)óQ·÷´1_¥tÕ»ùM«=_îÛý-ðs8 ž~$¤XÆùà“»‡ôÃá,X ÌÃz{HYqñ0õl>1ž ÆSL¿/œ Â;á@ðÀû4}Z§ÍeÒ¸¶S”nzl¥»q-óQ`ý«¯€õ´>Ö¥ÌïˆþÄ1ív Íl»8K$©ƒ‰£ëŽF/Ó¨G2&qµ«›§îþ0ì——ƒm°_”§ÁçàjX ~¦ñBçüö™¼P;’úÆPúÕKâÄ5¿Èpñ§ïŽA¸ ú²bõ@¹0ÊšeA¹(]¸Ž]sá4ýezãoo§3Ÿ}­íÓÒÅàÀsÛ~˜}àXØ ¶ Å:6뙲s9Ñ[S72,þ2ž¶Å‘6É×´ÍüJ¿ýááø)ð §™S-I£kûí+7ËÈSP6„¬¥2¾q⮫XæÇá“àA›þŒ‹©ÓÏÚ’ã¶6Xî·á8ëèëö-ÁÃPýJ°Žâü™æk›š`ªÃ<4-kcðàð0}>l//ŽŠéµ½ þ./À˜Žõo¶ SGÒ¾¸ Я4í-ëB»õôðš ÎAË< fÁûá³`{’OÓ%hHXéWWÊ4-ËâýLúf^Ýìeœè¥«Þ”ô¯®ØŽÛÁ0œ;o׳áΓ}àÇà!ÿÐæÚw½à*ïÇR±®JéªÇ_†©—’ú•6õ2m3¬ïÃp3èËŠÓåé¶(\Ì'ÃìFµ»Å7¿2χãߺÎMú"¸œ¾"=¾ ûBæÆVè^-Áƒ£›X¾ŒéR¦¶²^ñÇÖtËt$•$ϵˆƒ&¶dPúS¦a×À+ÀCÊö7ã•q ®ÅÃÎÒË“ºý³'7}†:$/ýÝdŒÓ!›kÚ¯¬—¶ùï'À9àáïG5¾úWÌãOðL0îëápÎX¯²nx‡Ëvœ½TܯeRË©ûëóèÁ#á»p ( [ÀûÀ|NûÈ·ÀyoÓFÔzîĦ_=]·æ³98þO†ô×ÛÐo?{I[”2¿øc¿ŒS¦ÓÞMŒSÆ‹ÞÍ-m¥n¾ñw+£´ÙÎMÇðpXì÷a;Pîã;W.‡Õá' ˜î©ð0_e¸z ÖJÝúiy‘ѦI&Ȫ@IDATü¾»Œz`q6…eTd?›az ¬ŒR.78717¸©íH†—qÚæ!Žéîoò‘_¡üÜÔ³øWEw£ö©r/p“pÙÞÁŽ ­œ;–ï…äƒà§ óUR?ã§ž±•á¶ÝÃCWg$×4nX@ïÛbÙÝÒ¥ ³Ÿ¾ã×UâF?®‡í—!}p,º¶”‹:¢ØGÂsÀ|¬KÄ|ìc/TÀËàGpx¨m ‰%úñ`^GÁãÁ'7ÇÑônàÆ]\l«óÃKÆg@1û×|­³åœGÂÃà™ðCpLãiû?8ŸmÀ¹â6}úì•è>u–~EQ2&Ó‰y8×]~¼u587žO‚{ ñu›XîpáÍøD¯¥ioúTÚ†ó—aÑKW½›”ãk;ìûÃýÂ9~"ØGÊM°?\ §-Ž«\^‡³Á|œƒ^,#R¶§Ô¾8®éû2=àDéËŠÛÝÖó¨î[ÀMv´ ÇWzÂvàâþ'¸ÙÞŠ [<,ÄE1ì÷¶MàÝpøy²ù”å›f¸Ù¸y˜_ÂK·©ÇOôú097ˆ¶^b:ñ tc ü7˜ÖLIœèµ±°n=u¥t›ái“‡÷ÇÀÎ¾Ý f¶uœ!’üJ£ùȪ`ú2Žvë¿| þ Ÿ€ƒÁW܆»nσ=À1}'xX{ÿ'§Ìo-ÚGC;z×yr”ù™‡ut^x€Ëð ð X<ì#Šñ­ÛëÀ'NßT<n‡äc›Œ·~Û†SKYnl¥kÛ€c²˜‡ò^ð­âñBd=ͯ ¦Nû ³.Jâµ|½ýÆNÊðnyZ^â”á¥m¸üÓæÄÑÿX\„@¹ œ7ôö‰ýoüäqúàØþºíâÔýx±r$iCêŸø)#þ¸Íx±÷Ý1îLô1.¦Ÿý({ \ ÍEQ.ª^ãV¦)óòù¸Yº!œÒwC¹˜&¸Y¾\–éu,øTøP0­bšiðQx”’:•n©'®Ëið+°žÆ‚k±lëö¸¬SÒ¡vÕ›å÷ò'½®b¼ô‡ñ›ÁMÔÍò­p/DŒ§Äí¦§Ü´Í3àpœÏŸ`#žï‚M`П¹`9_ó4//nJê[Ö£Òûg™ÆüRÏØ“2~Ýij>öÇÀº?~ ·‚b<ß"}¼¸½öçç> -mBÒú›bÙæç<ÝÌßƒë ˜©j§©klñÇ-íM½›¿´%Ò-ã[·RºÅ7\»¢k¿:WœãÛ§èjw>\¿^€\“Ú” ao¸Ò7¨Cæˆofì;÷ /j7‚åî;ƒ’:µ|C6Ãô7m¦(ÛÐ-|h®}ߘô@¹ÐƤ€~¦KÜYqÍHÝ…“MM ÞŽ_]qñ>|sSð†ïFp˜Ö¼ºa9n6j¦ýßMýMðØÌß|Íceð’)ëÖÔ{Út3O@˜_3þp~¢×V3NÊÐ^êÍxñ›z7I–¶~ּ݃÷7W7PÕ^®ñ½ðØ·¶õ‰ð-8>qÅxïGíÇÂõ Ý1²¼l¦æ›±@픯¾¤’vÇ5ŸèqK›õvÞX?ù<GÃßÀÃEѵ=gÀïà} ÍË‚íH<ËQR^\çÞV`»žøø›t¨)Ç7ºn‰ùÅ_ê±ÅÍÁ/·™‡uô0õòh{¥Ó®èÚ–#á\XÌ3íÓ5Î*p˜ïàZò©Ý2ìãÀ¾M~¦ ¨µn܋ۺe8/3×>‹n¿:6æÑ Ì]Ÿ½d¸°^iúöeÐp_Vœ(㲨• ËÍæ(pÑ:Þ?Ÿ¼^–=.Áµè÷°wóQÜ$^ \3!‡U3Aµdëšvóu³Iœ„·”2²0½íú4xîo„»@1lk8~^ž·€}âxn ›Áî`_{|rMâbªçÏݸ[ñ#ÝæFlº™_Æ×oû½Äü®ÇÚ8eÜRo†™þl0ëWÆU—¦X_/8^ ãh[ûõ`x-˜§q?ö­åW{‰õ¶/?‡µÃ’¯ù]¦½ìwÛq ìÖÖÛÓI¸åª—n"jïË8ô€Ú—h."®‡ö{ÁƒÚ±öiàj0®—‹ú\PÊ!z+dèO7 ÿ‚cÁÃ˸O†çÀcÁr´ '©«î² e•yi‹¿â”~7>ŸHÚRöIi[ƒˆÇƒ›©iß>9y=~Ýæ(Ü !bß¿Ö'‚œ‡“ã– »¬æûÕ8ËCìWeøä©dÜÓ7ÚJ]D»’ù¬ úo/–9М—ö§aÆñIÛÍCþ°l ‚eÛgj»y"7­¢kY÷ÀÛáBØ ³RŒ#‘^~çýŰ+x€ZvâŽÆ%z}@[~âk‹hë%i“m´ ΛÇÀËÀµnzûê•qA­%éõ8¿Æ3Å´Æ‘YàÜ¿euø0ܼ»Épõï¿o§h.€qªÆ¿u±ÝKiÂt£7;+q›vŸò_ SÛÅÍMÞ4^ök‡¹%Ÿ¸–½­®ƒ‚ò;Á'ݶݧãzº!žý&$xH{rh·LéV¾á½$u-û)y”¶^éãá%æZ¸ºIY77@ûâ,ø x Øß߬`|ûÂCÃ2N…ŸÀÀ10½¸¡§ nÌ^ ÜhÓ% uÜźÊ!`]íÝHêšzë*ڣ׆¶Íñ7?óYÎ…ƒà}ðzølŠóË ¨8§7e6|ÜÓìoóŒX®¬ Ÿó¸ Ê8x;Ò¬cüiWÜÕH‘zw£nš2mSʰèÚ˸ÝüÚóq n 3ápí Úo‡Á9gýÌ;uŽ®kû½x­“À8Žcêr=º— ÃOß4˜æph·ÌÄGíHlÍr!öøûî8ô@&å8Ý/²è,–ÂT/*íÝšñJt7B_º*<.æ»ô ޽‹^×ÅPkiú5fÑš§›Ë‡áàFàÆðpsÐõd)iK7×:Į܄Å5_õˆz™®W¼Ä/Ý´Óú¯ ?‚÷@ó@K¼¸D©ëà¥a]8KC[ŽÀƵ.·ÂËa#ðRö]0Ì>²7é䛺ïí(PË¥•d`ÏYÚŒŠôé#û®›Xž’¶E¯m{©›s˱\> û°'|2¯Ìsgð îÜv>{ šºbXÄúzQpÞêöª7A]%c“yiúè ÓmÎÅ2NVÚS`ò)ýÑu=p·€™`[_ þ¶ÍÃÿÕp"Ø~ç•RöAô¸ÖÁxæ«Ä®n].‚•àø-˜¯~˵ÏM?œ˜‡ÒtËrFÊ£•Cÿç2ï~Ç/ó.]ª ËEaFú]8M ë%¦qcòIvKð öIé6øsÛß­‚ºŠqƒÊ´n>í¾2—‰îÅãñ°:d“D­¥[[bKº±us/aí¬ë4nd>Y—õLøH®›èŽðnð J»Ë¼Ü,=<|š||nWBDÔúóáoáîü§€i­»›h/±¿l×[Û®q­ËÒÊ­dð}¸ai3ZFéË>5˦ß~¸ .ûõ*x6xIó’éÁ¤˜Îþòã(Ûéá´ø¤ïX•b\/TºÃI3<þÒU #Íç2Ô)åèO?ÛÂ~àá{ ö•óä9ðQ°}ÚzIú:®ñ,/þ¸æs¸ž\ËÎgÅ~=—ÄE]¤/´)eÞ-ËПe[‡†ô}cÚÙHÇ´~æ£êæ"Ðß´%£áÂSîBš ÐÿnÉÓ…[.^¼µô²XÆWw° ? 8«=àMp¬Æëµ jóÉFã{ÓMýc×ÝMï ðeðrb? ×W×mJ›9ü¦B6Qí¶Ëþ›ïÓáðL°¾Jú[Ý>:\ >Íš‡ñ’'jGÊ~ѼLcß- ±îOë<ÞR¶7}Ÿ:•~uû=xyø=|”f®‚í`ÿÿ^^$}ŠÚwõ^’¹£«Äï|‹{æàH®uhÆI^¥k¾Ó8w|ã‘ÃßKÐ>`ߘ_æ}9o1wÚYökì#¹¦ñâàzºþ¾…q,^®ýÔuˆh¨›_lê}çpâôeÅér‘–‹*zOjÜôk÷ÐX<|æÂà–…[–¹^”ºJ¹(›ñZ1Z?ÏÍWýsp8Ì€ûAÙN„Ǧ`]R_Ý硯3¿ ¾¥Ø nXt]ýM¦zs·ì£a{ …¤¾…©®wÓn{ÌÛ>LÛtÅ Ö>¼|ã±#D l ¶Ã·¶{:¸zˆ§”fÙe˜ºñí³Eq›¢­ì“èºÁ>ñàÓþ Àþñp´¯ýÈäGàøß‹À¡ð‰¶ÿ{¸ëcSŽEòÆ<¢4ë­ßòtKJ[©'}â¦À¦_{âª{ø?v/2^p|û”ù±úi`_h³MJÜ–¯õ³›-á ‹û(·ƒ—‹Ljû,Xlc¤Ù–²‰ÓtËôͰ¾ { ßñcعKu·Å²8 Êñt¡¾«]¶Ù¯À›ûóÁ…ÅS-Mìù©¯åªß…Y lï€ÿ€MÀƒÐM*’¶éúDñø˜>aÉ[$aú£›·y<ö7MmM)ûÀ°øu-KÉ%ÆCåDø|vÅ2oƒ‚OÕ6¶óJ° n”ÊÉ`¼²îÚ•n¶VHÿ§c¡dlÔí¯ûÀ1Ø6ç÷åp\Žýfpœô¦ó)Ù9xx~l æ'ÎÉæx¤ÕÒ _·©ÇÖËmgY§Km¥nùÖË9ìáï¥Öö¾6ãÞ ®7߈xÑlŠq†“²¥ž4ÚħükÛúU¸¢Ýu¶Œv¦>e;IÞ—ñêlxãU~¿ÜVdat[„ÆhÚ?ý¿›…OGÞÌ=•¯À£áÃà¦çB¤™oìq“oüM7éÝhÍW÷x\ʺp<ܸ­W¹a¤ŒiØ}º0îJí8™Ÿº%¦)Á[û-ßK@òמ:vÓæÆêfëFgÚ·€Åép ØÅx‚§—ƒ'‚—€ÕÀ:¯ ¯Å'MÆ´µ«$–ïç˜ú'ƒ‡¼vEýup5xi~)¬÷‚cj<ó‰h󣈯Á^`zÛPöaüqM¯î¼Û¶„²’Öx¶ã xl'‚ó΋Ía;pžWIÑKW=RÆ‹m8×z(ºü¿‡i`>_mëöó‡ávH}PkI›š®™/ê WïËrìæ€-Ç¢ûEµ{ œüYp霄éÃb/ãi;¦ƒãêbõðñ•µ‡’E—ršù´b-üixâ4]oˉ½œKÚRÆsÑ? nÊ+à(p3ÎÆ‰ÚiŸi{‘xºJÊnêúS~Âì7î{Àƒþ7p¼¼d36ÝkÁ×CÿÓà¦í!`ÿ¹ñG]7åD·ŒKàg x)Û§Ö†þ0~ê71JtݵàPX;—Òµ¯gCÚ±”YÖÉ­kÚWÖ¿Ô§ór¾OÇ@¿uw\ ÏxÄÕ&އóÌør!ü?ø|œ›^ÚçÀ!àÎx¿„=áN0˲ ¢Í±Ð¦”mKœØf}Ö3áƒàEÆ:%µ.ç0Üuak8Våw°Ø~ëc~eÚ襫?êiÚͯ”¦ÿo/#ç$ûÛ~È¥uHy)C·Y_ã*åÞѲô.—èwüréæN!Y CC)ãÇmDíx ÷pòz]ÇÚzªýkÛß\Èež¥žäÚJ{ôØ]ônJÍÍ/éK×9æFý™¶ÑÍø%ðLpsmÖ SGRn ¥¿Ô7›ä}`Üà-àÆý-ður6VŸ^Î727å“Á×eûÌ3u-ݦݾñ ù DŽGq³,óKXé×&ûѧΟƒ›íÒJÖþÛÈèqí̆+qʳOzå{ÜäÛô—vÃËëÁz{`nÐr<ð+¦w½Ø—χÚðFðâæÛ!EÛp.\/„õÀ±}<O/’Ö§”´%nÂ,û6x¼\³¶+b[Ÿ¶}KðVH¶qo˜æ£=aM N˜ºRƉ¿èñ£Cu±~ö‘b½ÏûÒuôŸ m¸:Ü—©œH}ÿÈâÌB³F±EO-³Àâ×uSÚ °nŒçÀ,pÑšïp’²Fã:g\ð·À‰`šÌ£2=æ:ÌMÁ òUp<¸iºñ=¼°x—yàí*ÆQâ¶| ÚFÃܬÏlðIŧ½ÓàR8<àëá¡ÿ_àÛˆà`]í3ëø{0½‡º’ñéå&Žeœ ö‘ÊVp˜g$íÐ †Å^êÚ¬ƒO¥3áÿ`i%mò yq;3Û5’ö¥m¥½,·Ù¿>5πà æ±èޝs?ñQ;zlÝ\Ûî8Çè}à\´ õ+Áy`úà#à…úð H¾Î#±ÝÚ‚¹#¦ñpÿ#\¦‹8oðûÃkÁ¶iÿ3¼\w®ï”Ó,ÓúÔ!…^iŒW†éXßRô;§oûÐõ“ ÓsÐWë[J·¼µ•yw‹SæÑ×Ǩ²qQöýl—A”‹#z²Õ/wÁ'ÁÍÑMåkpxf£ï•–(I~1”þ¤×õiö¥ðp!KÂQëàÆç&úrp“3Í£áíଠ—G+FïŸÎeÓßÓá‡p=|†ôƒ›¾ý³x˜~.Ó‹¬¸™ù´“&é­·ÒÍ-mnÄo/ægÛ¥NPGJ{tóvœ泬ăÈ|—¥¤Îq˼»ÙÊðR7®m}lÄöë/á p¾—}®>D©Åq•䑃ëרÞ{Ããá,°Lŵõ8pNÝ _‚Màn°®£i›óÈüœkêÖ×|] ÎÍÇÀ ÁzY§Ï€—ûwAÚ†Ú)+嚟úú`¾ú•„—zlq눣üáÚ¿L{9\ –7ëºG)Ýè­ÖÏØl__–cd’,Ç"ÿí‹Êd·#ÔKÙ9½ìeuŸ*÷ƒõ ¾â¾n¦RæUê ÓVÚKô¸æéÆô98F;‡\ÜÆ5ÝË@¿›…u?ŽíÀMY1\"Ñã¦>>xèoÏŸ’}RsóΦný|š7ñþ ¶%›UYžqÜÔŸoÇÁéÔ'uÎõiíK`>nøÛÀa`]z‰åFJ=¶‰æÚ¿éãfÝm_Ú·Çùá¡¿8O"_Eñb`¸Ò‡–µûÏrœ#éõ;Äyõs8v„“àRÈütLG>Ÿ ηMÁ¹hÚ´)åÅ%¨–øWÆw(ÿ0OûÌüß /Û¯?’¼ã·¾Úž×À`|íÛ馛°¦[ÆQŸ—€ñ¼°|,DZ°îhûqjI~zÒÏ¥M{Ó¯­/Ë¡Ê ²ŠëÑî'|9é›þfG5Ãã×uQ½ Ký×AÛ? ã›…‡©–2}S7Ml¥^ÚÌÄÅïf ¦Ämù.ø”ïÆæÁøðp5¾67¬×ƒ›‡¶ñ“¦©ÛÆià“ˆ¯"Ÿ?€sáSà%Â'nÅÍØ|÷…}àd0ëmœlæ) Ó²m¿XÇÄI}†ó½þœØ§ÿ·‚yXîóÀú§/Q;zú®t£'žîD´ï *û~°¯mÚSǦ®¤_Õ=T<|}ã["/O¾‚þ<˜_·©ëïæZÊ0 ezÇÛ:;n«Àåðf8þNA±Ž{‚uú%|<ˆ=gƒëÄ|’Ê5íz(Ïçò}ày 8wÖçzÒ§ïÒÇ¥ÿâ™Ï%±lIüR­ÌÃtM¿¶ˆaÖg8¿/ûGût°í¶!y vÊ.mÚ#±Ç½ïŽq8ú²âö€ "XËR×ïFá¢;\âèb¼ SÊ4¥Þ ]ø³ ë¦7mÝ6¤…¹-Ô/®ŸO=gÁaàf‘°™è7M%vu7e1½r<øtýEx,¬ ŠqÜퟜ|‚ÿ+x`äÐ0NòŽ‹©cSWRfË·èOÓÚ/‘ä¥ý t?ŽÑÿdجG7I^ºI_ÆKxi[ÑôÔݾÛ^CÆ-mˆKPGJ›ºcê!ó´v /~nëéÝnz;Ú"Nâ–¥­©Û×’sÖýlx%lÛv/ÂMç›óîËp'xi0­áæcD÷`w¾»?Ã,ç¹ðið²lš²_ðvüM»óì»àåļÊpõø£7]¢t•¤3Ð|“?ƒu_¾ÝÖ½P¿ ¼<)eº–eáOó‰ /qúîô€Ø—ñé,€Lþ¸©MüqcëØ¹9øÚÐÇüÜŒ®„Ë¡yÈ”ù˜V¿DGíø›ºy%}ÓM\Ý^’¶–®åž3á¦vÂp}Jøäe|7•[Á Sû×áx#ø$¯X'7¤÷ÂN°œ>a™GÊEí)K'éš®isLÎ7N7w_7û½Ù¯¥?:ÉÆT<¬šófI ´¾òEð@»¾íÇY,y±§Ãþ'î÷ ç²—>Ç´Xbº%éSÓ‰ýäÅõ³°/¸? ®»äkëáKðDX<¤Ëƒò8œç¦}*| ì7çÌHbš’Ä/mÑ Ë¼ÓÖMb/Ó/ö¤ñ¿œ/³Ú¨ïŽWÆ&éâ´H^ÖIi¦iYû?ǬÒñcV@?ãž=…h„rq” ´—$®67ŸHN‚l?A¿ ÌÛÅT¦-u‚:aêÆO}Œݰ”c~±'Rê±Å5M75móÀ'º‹á ¸ ó2ó!ð°×¿øTók8|RÊa`ú/Á^ðhx ÌŸJÜŒò$„ºÌĺ+¥½²ð§v/)Ç‚ýèÁÿvðU¶}©”ý½é–ñ¦Ía;§GʺE7ßæ{©k+í†9Æ›Á Øëwø6Èù’|˾Æ<*1­}gÚ²üf=Ì,qÔ×0q˜§O÷þÏöÁõàœV|p Ÿ‡m!òP”?ƒãZÎ_Ã-£—¤þ©SâÅÞÍŸ°Ò.ža™ê¦Ó#ÌnëÎoߊØÖ‚óÇz{ÙõèúTwlj-æW tú·eíÿóXÜE>æú. žä´/nš:08¹œ<µœ4¹¸ÿž˜ë˜ƒ,ŠI+¯QM7§š¼`~5þ¼jþ¼¹¸së|Þ@,—‡Ì©mý\7£ˆ Õ ¥\À Óu‘Z§¸¥îbÞ6†ðw0Ÿá6$‚»†›o¹±é·žæç“¢â[áºìŠî†¬q¾§ÃÁ¼|•j˜q”²îú“O©7m†5Ó%¾®b=³‰×†Åøa=×€—ÃïÁñz1|ÊrÓ§Ú”Ô³´G×Ý ~߇'ÁÒHòý&™x‰t£.Çj¤¼Sç¸ÆW?îpvÃãz@ì[ÀF 8æwÃ^zÇÄz?õ/uÌÑ^Êax|Ò>ÎìƒÆ+ã–þØ-ËUºJ‘.ågŽ8§½ô½N‚ýáé°léÛÔÿ7ض„KÁƒÒù¢˜_YnmæGâ%u*׫6ýýe<íúËvkK¾eZms««Ù£vc_ú'þ3ృŒÌ*kW'l²}u{ÖôÁjâM›û`µÎ÷VëÌ›WÍY° úÛm×°¸2Ö6Wج»y5å¾»«9÷ºÚû2æ=ó‚þÍ (Pt]PígA N]©šþ©­›GÚSkLUÓ(qç%Å´¿„½Ay6|lK³N˜†e;×|½yX—×F¼c.eÛSXlºM=¶¦=iK×~òÂùx'ÌmO€Cƾ[Ÿ<¢Xûm¸ì¿Ôµ£—¶r,}ëôY8·„¡."ÖÑu³-øÆam8ÌÃ0Ó:×] Æ»¾¶[ݲŒ—õ€:D¬£áºMÑÞK† +Óôêƒö¯©+WS˜]¯á'­ºvµÉzÓ«™<È2m¥jOóƒƒÕ¼ù<À˜a] ¹ÕÅ3¸€‡˜ùÕà|}.wOšV}м>ÅÕßÙ§¬½I5pÛuÕ¼9÷×IS߸eûúRô@·‰³Ùõ“¶{ \8Ñ|ÊgÒðô^o:m[m²ÒªÕSV©6yrµß‚jWføn,†zñ¤_ ·èj`AõàÀüêô¿±`λö¯ÕܦçÍ€ò18Ÿü¿ÏEÂÃÚ 3‹¥é´ˆôš±»ˆË<Ë ’i‹^†¹ÙyÓ·U›Âtx)<[âA©\¾^ôÈùt7FŸ4š’r¬¯‡¡—%ËÓP;zÓ–0]ÅpûÑ|î„/ÂQ~@ílºé#Ý`x†Øì¿gÁÿˆüž ƳO,S)ëf%¶¸ÚÌ×þ²L¿,ÄöAâñš³º†=òäùsªï^z^ý0T­7£üçUqM½ûqFÙàQFïGedÁ}€E3°ÎfÕ´[®¨?/«¶Ý¿zúäIÕ™û{qèo}?‹Bæ²Ä×d»[‹cr²‹Í gÁìwM«&ÝÆq¤}¬Ä‚š{_5ÿÞÙÕœûgW?¼ùŠúÜܶںÕj÷ÞQÍᵚë-‹=n¯ê^ÎõØF“¶™oÒ¸i¹ÑÎ7òƒàép(l ÛEçàÿt_Íž·ÂGàÕñèo„ëÁ¼Sjí÷©éð]¸ ”Ä¡×jÑ[©{‘–q¸Qw»x¤ÏJ7ºiý±y€]nøö‹ol‹}²ã¦¾q˺–:IÇ]Ò¾²­êö—>ŶÛf%ñ£§ÍÆ7Þþp2l¦9΂HÙþ¤MX7·,Ïpý±•nÓÞ-Ìy¼Ü9Q;ã§n:ã=¶ƒÀyì…Ò0çÓ£án°ÂSàXóÌçÇ9ð5pþ+kƒ‡pæ†6%õmù–ÍOó¬ó]eÍjê½wV·m²cµóTÇ20‡qp¯~7Ê·Wó÷¿¡Ø|v5ðûø&/£¾&µ3a9X̰ßI o¥oZ™o®VÍ?wÃjþEkW“×d5L™WÝ1w úß9V¯¿ì¼êº­÷©¦ýãüúÁiÙ´ªŸKÝõÀöûb™÷@gžóùþÀjkW“š¿ÃÕ³&ÕÛª”8É…ádøM|8Èñ2ƒí`eº¬šT ¿q½ÌâåáÙ".àyâ¯Ò7‚çΩŽÿûyÕgxõÆ{µj¾Ÿ©Õ¡C7§¶i‰ä™ š~íÚ2·<Üžï‡Í {â\…íÃðypSS’ö^ô÷À›À0®IÕ™ðJ¸J±_¯Ÿ ï‚ww¨!ûe¦¾ÑK?ÁµÄ¦§Ô[¡­Ÿ©£>õ¿.#QK¼}^Û2W?Å} ¬ i{êeÇU‰[†¥^q[1—ÿÏôCÚh ÒnÁÃáõð:ðiÖ1T’®åký´ÛÂÓÀ4ƽv€©`~Ýúó"ãÔ-ÿغ¹±¥îúc‹k9¥®?RŽƒíØv‚Íá­vÿý±À±ÙsÔ:|kÜ#ÀËÀîPŠyº¼œ/Ö¥,·WÝ/.ÉzJ3ýSW©?ªœ»ÝÕëøÒö Ì#dÏ[x­õ^opè+£) sáϤáa¨º•+Ò¶ã {œdݸê-ÿ²zÏûWƒWžÓY 3èkKÜö}_–} ¬µq5xÇõ­EºÃÁÕ‘ì*ž3©Ú˜[íƒÛÜUMÚíŸÕ¤o¨ªíYÒ®_ŸyÀ׬/¦½ê ߬„³¿º‚Ûó™ñ?ìW-øûš|!‡MrÚ¼ê.nÍo¼úâê;÷Ý^ÝËÛ‡)¼²sóè&uÖ”Å%^Ââ/Ý2,ºµwSr“Zö†ÇÃÿówóvW®…sà³ð+0íêàõ'u5›äàéÁiYÚ ¯¶žŽhç¹£þöõ,Ü›:! 7IÓ§¾Ãéæ•zÙtU«è6õØJ»QX¶Ãî0n l£uÚnuJ}SŸøË¸$ïHÂ;†¥TR÷^Ù$¼tÕźœ^âþ ~„‰‹Ú瀗8¿Â ¼ ¾¶_Ò¾¦KPW)ËŠ^ºM½ô›¡õ²¬Øµ5%uÑnwƒmÀ7<^|2÷¿ƒþ p}8W“§®å¸~ÌËur÷‘°(ökBù>|.€k€£³ó·RŸäOÐbI'ÿd¾£4{³Ýª]×X»úüÜÉÕNìAî}s5õ™¬¾ÍhÅ]ÔÜ}é$\¬âZéù¾@~-zðJ¾bùõ-˜0VàÆÇ¥sžvɯªKvšYM¾èŒeö±×bÖô_+ú’Ž×¿V/,ãÖÌØ£š|Õª¹[ï_m4uJõCÌž|ÎUír[5ÿÈË«Á-ïæ"G»‚¯ý]=‹3õê&Á$WÉïfK½j5v„-ªù¿Ù \ƒm‚ì/¹ëÖê™×þ¥ºÜß"¸ï®Îæ2Rkë쑚¶ø­¶º~jŸd‡G›ŸRnZgã÷iÿà¦eë¹ÆÔé“'ÞNw˜ÿdðuþ‘à·Ôsp^„þ"Ð57^ãÛ­ÖI›’|Kw4z+uëgâ—¶è–©”®zÓoœÍáŰ)<¬ó·À>ßü¶¬€ÚÑcK]š®q—…Øwö¹‡÷h¤W[c×õ@ób¨›~A­õ´ƒ)[ÿªé¸ï§¶—8ÀÁxÖMqŒ#IÜf9M{ê§½›Þ+}òÑMÙºÆ÷ß<°޵í²??Ç@YÞ!✵Ÿ^ Ÿ/ ΙÃà±°xi´œôÅ,ôßÂÉp°ÔkÊr_JÙ––eáÏ^a«®[M™}kuÛ6ûU/]iJuò|N¿ñìjþ+.ª·cEZ€oze°°ˆÅÓ|àÞf§ýUñ‰ª׬Z ¬Ìo Ì¬Ž¹è—Õ'·?°šô·³ê~X¼Ìû±‡ôÀ²»!™ÿ;{¸¥>]áëð _†™ú¢¿±“ÝÌéÅç }ËtѸû¸hVbÑü…OY0ÕS[o^zÙ¹õ¯ºù6Ť½¤VúÕÝ„îêõSì{qgB673uï¯Á‰@tüÄôˆd^–®‰àt°|¹ ^¿ƒ^Ò¬wüÉÃtÑ[éª÷’ÔÓpÛ­h‹]›ýåFî“›¯·ß ŽÉ-p\)¿t˺E'j'®û°’r[¾%ÿ9…¤ëƒõé%iW\ã5Û«-uŠ«M)Óµ,­þñ{<lëWà9à¡§ßþ²~^6ÍSÛp’rš®iRß2¬Ô'ùÖ­ê²ÖÖþè1ñÆÌõ‚±2­ÿú–üÊÌ–Õ\¦øPµú ³Ë¡ÕÀ…§w“1«Ï¿ZÆÍÿWkßrmÏŽ‡TS™˜î0³:iÊ‚ê |9æÁîiþ~žõ K´þàŒjåbñ#oÎ_Ø–oÓM¯àÛ·ÓæÎ¯>ÉYÕ+WY«Z/úô”M@·)¥­Ô+n2èfûÁL8 ¶Å ÇâgÃÏáðUððvs#3m™/ÞE$ó²tÝȬûîà%ÀÑðkàUpX¿RÊrÔoý oê 7?ë[JÒh3?Ѧ«ÄÖÍ寮*Ø–ÿß(¯†S!õH¥?ºñÕÍßü|ªü;¸é/­Ø¿ï‚·€Œ—“´µÓ_±usµÅžñ(m 3?%íÚ ýaðù¶ÍÃ~+¸çÐ9°+؇†›—é{‰å§¯Œ“²GªW3]·üSnò÷ÐߎÇ5oªŽCXçæ\´ˆØNß|¼\+©wÖ×úØ/»€}§˜¿s˲¿?çŰæüOÝ“/¦…}ÉïðOã÷ðïäðÿø”Áêù|l9çeWS¹‘ÅO~[y åW«Sæ™áï-ïDÔíøèE¿ª^Í^;å’3ê¹±<«ô/SV9þe5 Ùájð’_VówœYÆíøq÷p×?öìhÿdµÑËYqË«nü:Mu1Kþí{qR²mðï÷|~öÈi«Uk=pO½Q”U*u«Xú#n>Ì7'Üé *Ï5ØÞ§ÁM`ši ¨{h—yk/¥9õ—67f7È-àOàV´¸Á½~ Æé¶E¥\ÝRÇ[û›¶ØÍ/wâVJYÇè¦Sô—˜‡mðI×Ë‘¦ë`'°¯<„•”¥›ö¨Çî˜ðaRíÿ9î ¬+ÞÅó^Ö+Áz–’¶i‹^ºÑ›mOü„7ó´Ü‡Â àYíÀÿÅõ0]ì_~\敾@­¥™üqíkuó‹M·ÔñvüêݤYîL"­O‡ÃÁñ²ކ¯åf Q‡ˆy¥ü¸Vœ×J3L¿yé:_v€ƒÀ¾Ú””oÞ7ÀépÜö§iíƒfÙÓø²¿^|ûT'ó›J/ãWöª÷ý†W|Ì4âñß:\Iíߺwµ€)_¤~òâ3ªWìtP5pÑ™‹Ì…ñ¬ê„){œ‡tÂôÓ°åÉ'ÿoñÉÞÿC5° Ç¥ìL]Þb‘~©Æ_Ñyóë¸óçUºøÌêþHÇd~C@éV3mVÛ§Àmpóý/0ls-·óÓ ù$8¼fúäu(ºiŸ nNÙ|Pk1ϦÄÖÍ5À¿À†`žo7\Ó$]³ñ—n©“´ó{>ünƒ^ùTKÂK·©ëöÛå°)ø4ëS£µ‡…õ ¨uûtK›ºyy)Sr`h_ñಽˆtË«l“å”þèæ¡è­éÖÚá¶ÙÌËãš ¸œk©‡óƺé:æMIÚ£—®yë·ï?ê Gí©I],ߺì ëÁ³á ÐnžÎo€}‘4q1(e½ŒÜô›¯¶”ç:Ø\cÛš¾²­Ê¹ðøÜ¦Ï›ÔZîÝvßê•ü-’“œ{ÒÕd¿á¿4_ðKÆËÂõaêr.ïÜßr¢Z|'àå9ŸÃÓ.ÔE6ÂùG7®u^fÁ§ÁË‹e§—f>e]‡Óí7j/,^´n…MÀÃÄM=e©GÊ:&Žc¦=}šz$ÍXºioÚéD¢'Žvõfý<à}ú&¼Œãœz (Ív•}ЊÑú™|S—¸†ZÃïÁnàá¸6Dʸѓ_é?å;ßgªð ðã Ûb9Öý»°_‰Ûòþ§õI’*uÔ¯.α>^ ‡Á®ÀËó:,cr=þŸÃÀ>™kð‡ÄªMvªvXc­ê ¾ð7ï5©&?âf€Üˉ;®2Þ8—½‘\ýÁj„h»¹T³þvNçíÙ¸Öo"¾"ëDê·!uåõÿxÊÿŸñÏÿÀoªÁMóL6$ÖøzøR`õ²ý«Sù«\>P½Š¿p _î™ÚþƒAn n>õº‘½ö7Å0ãxPý7|Øê×9dX· .6]*ÖÑJ97£—®ºõ}.üxPxè[Æûàd°\ëÞ”ÔK{tÝR·M«¹,/áÚ"ÚR¯ØôÇÖKgDê¿Zø\7hãyžö{ÊÒmê½üD­%áñ/ 7í1¯¦Þ­ÚìCE=u*uÃŒs$<öåyðÈ%.iã§›˜w$zÜ p®ü.ËMê])ÆuY¾ºÎé·ÂÖ`˜€CàO`Þ͹7RýI2DÊú% i+ýê)Ã:zÑq=?ž;áIãšñâùñ•V¯>ÿÝÕìª.˜ÇúØY¼ò»Œ/$&‘V±|Ǫúîü~è6,Ò¹Õu|°iÿ7„\`}YŠØîàê!tâoøþ9üzÌ”ooe¶¢­?–ØñöjàŒ«¦Lª¿ÒšÕwAºŠÚºž¶‚§Â·áU° 87lÆÝð8 þ|%î!k:e¸¦²‰Ö‰FøQæÙÔã×5Oë²\ Û‚›îà“Ø¯Ápã%j§NÝôØtÛjúäSÇ=a¥«.YoêæéAïEêq øÄèx°(e©C+daX3^ü)sY¹ÍrÍ7ýÚ­ 㯠Ï/9ŽÆS_›OÿÓáy ÿxØÞH3]ìq“ßp®®?ïæ:ãiú¤)õØtÿuÁ•íøù–k&8玃 øÖìp­Ø/ŠéKiúË°èÆ ±•n3ÒŸtºÖÁº_¿ƒÓშ¾ÀÓÿÎ{ÝRM~.Km,~/vÉj84•‹fÓûxäœ\Mæ‹4 ¦L«öºíÚju¾ à&övpóò5w6«Ñ=ô‡ÀoÁp7 ÕrƒÆ»Èf§­ã›÷h(Ó?=y¸ÉÝ_ƒõ^à›ŠýÀ'ë3!ObI— !õ‰?uµ±™F»e*Ýòèf¯#·ã·¯‡õå$q½øìá¿%ü¼t¥¨µX^SJ[·úÄ6V®õIÞ¥®Íú Ÿ K ÔZœO^ÚŽ‚ÍÁýèãàX¦˜—Ò«Ç#aqµ•öè–ï\ˆ?nÒ4Dlþð3ð ½×ÌÆ Ü‡Oþ®ÓEÌ»””ÕË-ã6õf^ oÚõ[±¶Ù'}/b?…sàj0|: òÃ*þÄïC¦ð?¾þÂjp*ï&º‚Šòr²1ÿ×À¯7¬ßþí²ÁŒê«7_U¿­[Ak½âU«X‚1Ùvf50ÿåê~^¯1 ?ì¡[œ/Í•¸ÙIëe=wgøÉæügCÓªfßQÊÇ9ƒ ŸŽ\îr¼Üàܸoç‰OÔJ¶…¸-ëØý,»4zéZ7 7÷ƒm9”Ý`38ž¦˜Oò2,zéFï›q‚6¥LgýV;á÷àæk¸}èfxhcZÕc`{ÒÇÉ+.Aµ4ý»ÙZ±G÷Ó2»å¡-öÒîžœ8ºú=4½Dêfþ$>¦ú?ÈqŒ^>­:–¤°i;ê)ËH^M[7»™$^ô!÷ðpÖŸ£{1ÛVÛ´6XGÇò@ð`›ÇRÒ®ne4ÃJ¿zêf{®‡óá×p\³éŽÕöü†ÐJ{ßR Á5§Wçw…¿8ƒÕrÑšÕàM«pñ¨î»åÊê—|/kÿwe"4aÜû2“bÜ+2‘*À¡?øþ%»ÕWîâ¸yõ_Y0dºù›Ç\ÈûÊÁjÁÆÛódàÿ28Xý‘þì ‡‚‡”¶‡-k¶®é'Êr•”_Öm5psöâr\»6¾ x|Ö‚ùÐMl§(Ñ㶬­ 4¶¦›8Ý\ãæ5²ý|%xÐE|kýëûÊZ[¸a›^‰ÛòµüÆ7ï²>†7ãjIì›à› Ÿb˽¡Ì/z\¢v-/Ṝ/bÞŽOÐOËf%Uïn»†EŒ[^†b›rJW½ô·ôG/íꥤ×`|l ÁÁµ¡°òëÿ¸È·8ο䛴˜Y;Ú–TÊ|›y4ÃôÇÖÔMk¿^=mÕê'ën^}u­u«ù> üǬVCŒ0ä.êü‚Kù2+Š7±¯ÝáIÌ•yñ™M×:–‹|\+2‘ ç÷éçí<³z f[þ¬¹þ®¿+ÿVxñ½Ô¹ÚÃçÎUøÂâ.Õó_ È+Àw`q³[œ,nÌÍM$~‚:Rn0ã)™³e=¢[g¯À‹Úº‡äcàTð#¶ŠžbÞ¦7ŽÃR&¦Ž-a¥M]1,’¾ñõë#aø6"uöpóbpX¶òv`”†ÂÚS¦®uü.|ü\»)ešaM¿qkûé[°>X¯^’¼u»‘t¶1ó(6]í«ÁCa0ûĶØÖ%â=í‹Ô§[y±9NêJlM·ºðgÆ!ý¥mcü^R¦Ã;À‹Š€_ÂaPŽ“ooßö4óÁ´L$ù–uLÆeX›6ýöï<þSŸþŸÁõ6¯v7©Zg>SßážÖ·þ“áŠîºïnH¯óWVð?N¼£zÆÅ¿ªØéàÎVR–½tm‹D «mÞüï¹­º‡ÿ±ô1þ¤»ó1¦oýÃaMøì¿Z“}xÓ{Z/bñ?q¢µa¼êë&Ò—QöŸûOz¿À:9˜×MÕþ7¶™F™|…ŠÆz©|{áw&T»¯¶Nµ¼Ü@ˤÔÇ««Rð à+z>©ìÔ5[VêHPgC×f›®/·€â+÷Ã#À§ì¤õp:nÓ%oÔ%–2Ÿä§›96 ðàøx¸ì 3À5š8q1 +‰wØÈÃöJßËž¬Ê6Ç–4’Žå3Ávz°¾´wëÓyàšçhŸ^,|Kbún`^ÄîøOÿGÃóÁ|ÌïãðrðÉßzšg¤Ìßö'R†-=å–n³\71mò7Aöàß1Á„¼PooŽ[ÜÝZ+4dÛÒ—‘{ÀÍ¥/£ìv«Av?vÚßÿÙÏ?“ùÀíAÿLñí[?ê.ko\­>µu¨ŸÚ]ÒÜ8ÆÓo½|¼z£nz‡«—ánâ¬ûÕà¨yà|Þ‡òTg^Šbhúcoºeí¥{i³ßÇAƶ¯™iv¿”èG+Ïçœú›Á7ë‚’²RGǧi+à _^”í즷êÊÿbC¸¶Åõ¡”i[–ÖOí½ÂÊxËCo֣쫅“i Úbµ9ÕÜ5ætúgyÔm™—áÂà;Mþ*ଳíÕÔI“ª9üÆV³–yÙ9Ãþ`1F4lþªjË»*¾8_ fU-F6+DTëícñ6l•çoÄW–諭û–ú)ÙMz$1yëIbá&ÏÏB3|Cl;Ô1ZSð ô=Áöu«§íLY¨‹Hòohÿ¼’wòjÆÕï%d}ð @ÿ›Ðw‡Ÿ€ãâÜñ"±/–V¬SÙŸK›Ÿéïe=ÉsîÜV_ó¬6i^58-WÆEbN ƒgÕ…;×ÊüÇF+ÑB·¸¾ ÓN辌¢øÓ¿åÿànåïž®?¿F7Št+lVŒëÅ7ó§T ø@>ííæßHÜ3<$ÜúÔßc!nBo[‚ŒsíAZ™HâSÿ“F¨°}®ügËYa~:^ÖÍþ_žc-Ëz+±þéßžu÷ËLÄš4™˜þ_!Yl,¿••Ûæ4þ¯à©6¯/Ã÷@ÿ0|ÿtB󨻶keU¶‡WX'õŠ©XÿÕX4´g€×ÿ+ŠŒU·&ßÖ¶·ðI¼ÛSa¶Ã^{HòMŸù”˜üFŠ_æ[–­=y˜_é_œ<›qÍ'#ï7Z)ëÙ-uïöôe_ç©9ãx#åÙ­œ‘l#åi}Ê:™ŸõJ_Û'e¿Œ”Ÿé—DÆ"ß‘ó´¥íï,I¥W´46§-ñËŒ©üòt³ø—g™²¬¬&ÜÕüü|§¶bÒM¸vYÿ•ØÞøƒ@Õ*kU?»íºêlž ù›iîHmz€å9RüáÂÓþº½»Ô’¡qƒ÷³Û´I»e<€`x)بKÀÏíÕ»µÏ¼xwÒ9(P‡èúSžúp’z‡ßf®ÅÈÏþ?®Qûù)àë|_Ÿ_ ×iSNéF7Ÿ»ñðvÄÃwW°Œ‹ÁøI‹Zç_ÖOÝþ°ÿ"Úšq×|·€£!oÎB÷éÚßê0OÇÈzX'Ǭ))#añ'^Óo™éOã¤]‰çÇ[ƒ_îÛŒoÞÃ_Ú~œN:uë–ï4èŸÐÂ_õKMw°þWâ÷ÿ‡ýÈ`Eo¨ƒ:›Ùã £;?ÿeÆi,ûÞ×—Qô_,©÷õûè9']{Â"‡/J½hÚGÜ:Uß¾ö¢ê&Êÿi° zó4òðï ðUø;ø½‚à p$øÙ°¿ÝUJ†_[{vŒè6ÓÇo^%ÚO‡çÂn`Öo¿/Ÿ¸¨‹‡u¥½¬§ö¦xØ 'eYÑ=(£›6‡²ºöH–‡ûŒ‡ìÁ`_úõ à6¸Ò†m×C¹7%Ï²ÌØˆ:¤LýÉÏ8Ñuí ‡Âæp8R§¡ŸåÅ/é1×b˜¶²üVÈŠósĺÕxnk^søûgt'´Øà{ΘûçWø Ö—á{`a— ïß>´þ¼Œ^À½ÝÝÎÛæDŸ_u;ØŠuçÌ©îY}½j'‚;oêlšaÜG3 ‰7íò›ùSAûGÀ=—ŸTσ#Úöá¶ÈòP¿é4DRŸÒnÚ÷·Á7‡/ƒ¿)à%¥YNüq‰²HœØt»IÊ6,ºnIº٠ë&`´×§Û/àFÈajÇã:ðÆÌR¼Cü©Ÿö¦$,ù®n¿[Âñà—ë²5x9`ew.U¨2“WÓ5Ί&©cÏzñÆoàn¾¢IÄÌžRív—½=ÅIu£É¡ï3Ì­åÿ˜ÀÍYnU·ßú2Šà € ¶ŸYoJ—ûùÒM+óû¦£H·ÂFa‹t÷½žEã6àbsÅZUSø2àDjß9蚺miÚrxúÚÙ ׿aà%à¹àÐjó›ø>‰{påÀkæ§ß0_aËâˆi•nyZŸïÀ,ð’âÁXFÒ¡vôÒ{lM· OXlºM1N(ÃJ[ôÒµ_|ºÞö„,™O£Û¿ù¨ÇxnؾŽ#Øö‘$å/º®¢kþñk³†ápL=ìÿÛƒu3N¯¼VÆ›pz>æãÀ¼€¿i2éV¿3?ŽìºUêÃß/g_9›²\«ž¹\ ¨…ÑYƒ|nv8£ºbjÐÝc¢®ëí¥ÿlƒío_?mUvá{;§Q&:JÚQúµëO˜ÕJð5˜ ^´ùk`€FÒ£vÄ5äg͆·{ÑHëÊ2#)_t]ÇÕàMàiÙ/§ÝpRæ]Æ+í¥ný±õr˼¢ÛÎáÚjÿm›ÂCÁ>½N‡²u?ƒþ2üºõ3æ®’|XŽ©abÿ €ã€™^Û‹»ÜÖÕ>OÔÎüP×þ/# À‚êîg­ÝjŸ¿á4ÑÄñÕÍU«W|ùŸ?0P¿¹›hÍ—ú·xÇ¥B+r¡¬ùü©©üÚÌ¥·±]ÝÉV<×KÝžŸ íŸü2ÿ±Ñuì÷aZœwEª²nîA{ôÒÝö{€Ÿ û½ l¿‡-À½¦vuÓÍ„-Á|G##Å3ÜÃþ˜®UŸ¢=Ä,s¤ôD©¥¯éO<Ý„Å-úékýÿöÎήªJ÷ëÞ[C’„$„ 2fQ„„8á*íl‹Ószm«­?ì§¶ØÒòZÛÖר€Ú Ý`~(BgŠ 2e$Ì„Ì!©TÝ{ßÿ;U«¬ù»l™|²ùSµÝFYÊ«ŠÒIóÏ3ÇJ^¢Õó+ÆÐºtÔôu2Ír]VG¥¿‹ò«÷Oº÷°I¶yइæ__rf¿'»_®žbÚUoônð*ðb‚­Ä08W=FÕÅñžºz’ŸJË®œÜåTQ|÷ËÕõUМø‹à QžïºÆóÇÛiI—Ci8:@Ë…RäRÔ?7J^OSˆ3õþ矓ý:þÎ ñ§ËJ”}DùHÁïž§\ç^~ œÎ_zV*ûUà\€Úh}›#ÁmÒLŸë~6*Ö´ û³`î¾YŽQMuªNô@˜’µ§'ÏöÙU·Ú¦ ’ßcÕÝKoX•6¤“ ¬^Òܸ0|¶B;gý~!¿»ªi´»Ž°¢æÿù×=WYñ¡›ZϪ¼§ý:Ýhë2?v©+Å"åð(P¯û å¡yø[ÁB ¥§t$rE©+9…•JZ¡ÉŸ>ök•ŽÂ5ýðP“|PÚ}øP^Rd/ß4"¼Ü?}œöûùRWeW­›1V~Zaÿ. ‘QõO@ñÚ“4ù=žŸ×´ÂGZÝÑ¡žŸ†ù_Þ> œ•÷B þJË¢kxû¶äsÖ4ç ¦5³ö!ü²®9†y.î^T‹hÊ¢ž_Þ’¶LÍÙÅ-e¯¦Û(ÝatƒzÌý‰ßÝæ§[þ1ú2Õ¸éÄó¨·Ç†ñ1@¶Ë¤ÝÐ ª5ŠoÜŸvu_~,E³Ö) ûx+àaxÛ¥á’ö+¬ôدSšêý¯R¬Êû·@F‰äR óíÅo/LeVwƒÎŠâµ'þ N^ ¤Ü=L׫ ºO1qã@ë$Òk)tï>Rz/ž‡Âe ýoð  ¼¼-“Rõü/2¨tþ+às@v»Ê¤ôrŽ“ö+VGÇ¥áižÏE×´`Q"®d0© I:í´¿£ë®ûßTå!å¿|¼HÔÛ×hÍå@¼é9ºø3öãþëf“W#³w¶FæÒ«b1 F^Wb^®nyü;x°?Ó\Ó2UÛfçï\ KH˜³ÐjV-µ% •Ý»nˆÕÜF³£Å€ZZÉ¢ÖNå¼stRi²¼ÍðCe×ÎZØ:´\ÉÅ?”esEà®ò’ßåJq¨'~¸H™¨§ñm ž¥¦ÔÓÜŸ”þBtìHÇó딯üRnÊO‡ËÛð0þ”ôh=Ìãµçz:~OŠ£0¿Ö庤ϥÃ<Ü]?'We&‚)@CõkÁM@ü¸ ¸ip˜H{iú¹´«ûpè9Lg‚ ÁëAœ ®é¼žæAÇýVæ°`ŽõMO@À÷vÖXíL³Â`~íŒ T¬4ñ+Ñåêc­°;ÇÚÅŒý'#«+¶ÀZ0qÒ ¦Ÿeµ|rò8š»•l¤a?ZÁX§š¡ =l*‹}r^³…O%zZeÉC¼ÙPÁÅîÍ¢¥ëƒûå–ú¥Ô~>ôÔ¥\~Þè?uJ¹t¤€UŒn0P“eÝ&[åùc->¹Œ&Hãé•**šÊ÷ï3°ì©@LܼýE»¹±¦ƒgæOSn)4¬üIð€”¿Î¿\ :+jWÛC:¾Î»(@yx¸ò—"Õè€DáºÎÏ»«s?v·9´í_óóîúéc÷Ëu¿Úe 3ÀI†è¯ÁÕtÆyà> ÅìñÜ%è€Rú4Ú0 œ>¤ü%2ЦƒU@F“⹤ýÖïÝUË­1¿×x;Ð>¢öì¦ÿ4’‡VliÕ?IùÛ&Ëò…Ö×Î<ÓêBùwýg¬ÊÒ 6¬7Ûð/:c· L?½a`2U;s Ê•fÐ[Än$ÝãQ0R’•½ËDZ·ìŠVËÞÿóÖßc;7q!û0~|îwW«g-#àfðx3ÐÂ2õÌ5÷}-2L‹Çw7}®Ô¯kJ¯Ó±Œ õh?$À€_b»×«ùn/‚“pÏǯI»ò˰âÖýJJ¯÷0w•Ír²¯¦'ÄÁÇÀ: ¸ƒ5à ½ÃVñ2´´x\ÉUºÞó?ÿ—ÀT€ê²m@ÆZ ñ²ËïiÈRÂÀÆ'Øl½=9rŠm¨7Ü3ÚöÎ}ÉrÃ0§ÔëîèÁ”$sÈýái¿‚oždMuËÒ™ù ½ÿÅ#§Zaåé¥T×bÇÕ¶r‰í`Ê<†¤þüXkbAJ¢lýÇZ is§‡0f=Ë X÷ÙÎøœµ+ì…™g—½NW=í•!ýøÜ_ꪗ)…|9Ð"³@CÏow´—¶™~ì.—µQª OŸÓy‰‡ÉÕZƒË€òSø‡@aÕg!}½Â]<ÜÓ®Ÿ“ë~)Y-´ó4=Üã¥åç€)@qž·¶øq~4€™Üš‡Â%ŠŸN/ lùSʽŒ‡WƒW‹ !É0¬Ê?”?$tEX”e]Ð÷èU_ÎWë¾rª7bêVÂA2B¶°ök'[Å_SÈÚ5*«îïáþ»ˆ¹+wŸkUIB‚Y, áµÀûi¡ÞÀU˜:HÿË*ª0Â{|KÕO³Âa–e4àïÖÜbK§ŸÍŸåe/bYù9@æéÇçþ´ë~)â߀ÀëÓ‰ø—~ ‰’–bóëñ&¢0aÒÞ5<Íd¾]¸Á›¤eLc€$v©?n©¿9v³²þ2jÆõ®%ž–»¦ü5? V "^4B"CE¢8Upž<ïtZº.-Λ\]§r¨‡ÿ p $+Át £EŠßãámã×qH °¸¹0sÕá~”¶â¼)”ùì+-ÿ¿ä0ªÑÄÞ=Håýðá }ÍK¶,Æ>±k)ã;)+&AHwˆ)€î2×oã:+ò#”Õüàˆ) KåmáGZÃð«9nãœ0\Ž5´‚wÍà Ä^z<Ê‹™©‰ï­Yj_1ß2ßb'}ºÉs¿»JBÊFJí1°|¨×¬^éùàwà%Vzv(JÛ‘¾(§zÀ4‡Ékvêu« R¶ëÁpPÝN+A“t庤Ót:ï¸ð&ð8!Pzékq^ŠYsñ.Ƴ t¥I—Ùýr•—Œ õü_¾è›&eºw>Ø ÜPÁ›ˆ§áÇá€Ú´„ÚSìºú=6âϸsŒí¡-«9u3m¿‚ôƒ?@rÝ>­§vLïúÿì¬Þ鶇uLµL¹þˆöö³Î²šÕ·$¿ËnçÑß#ê9†ô³Yfåÿ$ÆÀ¥ì®wÑ6 zïc–9?â2°¬7§ÒÐ?Ä(Q¾jÍr»pÖ9–[µdŸF²èÓI¤Ÿ ûÓ®û¥è¤˜×õLÔ†Ñd&Êé \‰_Û|´ïßöΗ†)ÝÁï¯ßT0<´N@"Ã@â®âIÜm>úË_ÏG®C÷¤ø4Å­âç<@ Cù|°%pîBÀäS›ü:Ê[ÑÒçÜï®ÊpPÏÿëÀåF…%§û?:Ætô#8”$+}6[³Kæ2§3´µâ|iÕ2»ÄÛ[•)¤û ˆãb`æB«gg­† íBÖ\Á;µ6g³?¶Æ2“˜¥ÝAŠqp@ ÐÝâhxî0šÉçQ=WL³âŽ´â†ý3vów™ß«gØLC¤!]g ]WÜŸvåÔ#—R–> H¤˜Û€>PSÚnz:œj#í…{˜ž£zçó€”ôgÁ¿åï¢|ÒP¸û5îzºrÝïJ?–ö+®ŽuÝéàZ çžRÜ¥÷KÐ~ÃÒ×»ÿ4âœ>4¢©—kÀ;î•Ô*§5 <Ýc`Ö|Œ€[0Ø+yb¿dq"½ò¦·=a5¯ÁÔǯ¦­yá3¬û§«¹éiÊRŸ&VåyЧ»t¼=Åò,ö«aÕ³œ+íë]Ú·„v,mìu5»¸¾…î>¯ °˜Ȱ*µ8}‘ÍÎåm iÆ°Š¶éÌç­æýkG…qíÆ×“ÓJO-ð@*ϯ&±4ý(Vİ,=ÿ]TšE”ç7‚»Æ@º¾¸?íº_=bµcš—ž®!ôw€ß),¿o{Çí…©\”žŒõ€­pe˜´«»‹·_Ç.éòèç$QXVꪗ1ø°N · Ók-€Ê¢üÛ“t¸ûK]ÅÓHÂÁÇ€ •ï»@Ó *ŸÇÁÛÆ¯ãƒd€‚2ìRœ>ßFæ²öU’û {4Øc™xK@{LfÒk'O‚ö®µ“³¿ÎŽ˜¾»RüC8XϤεS¨8#,ÿ¦$¯ü©©¼¬˜·Xs«mÃÉ0 h!=À€*OÈ!``ö|«_y‹5ðƒ]LEx“沆Óo¹ðQ3½*èChêµwç׬§­0%ÚÜg-ÍþOŽcµ?ýËz>õIº+‡ÛkêÈ×ZãÃ|ø£ùêø{ ¤ëŒûÓ®ûiÊ’wôïÆ=¸âz þ›zí.§³ÇºNJP=á;Á\ ùpðQ=sîÊßÅÃÜMç/ŠÓÞ±‡ËØ9H!OW òfpHç©pϯ#¿ŸwW÷8(½ÿ”—x»| DÏzKøÈNŽiÄ<=ðcù"ê/iæ ÄëÕþhjà-O6·m´?F¯=éÍëaùÃôrêǤ©6Q›¨aLØêÉöäÆÎª6“‘kvÓ†­¢í¼€iˆõL]Ö0u醭'îA2 grˆÀÈaäXHeù2•eÁ6š¬I;¬ñÔMV3ç%ËÌÜÊRqjƒºsê"&#jöJD¯á°uoÒâiŒuOnõp³‡FXñÏ£¬é‘ív(‰ÏÝ\ú¬ð¿þ„“,sÿŸ÷©%)Ça7H×÷§]ùÕîéQÑ7JåI‰É/Ãà“@Ãõ®¨ñ&J6íÊ/ñtÛó+sÁÕ@ã: µÇ°®“Èuø5®ckÏŸ>§8~¬^¾zæ_Jûa0hTÂ󛈎]Ü_êê¼Ê=œ¤øÏ2vöEðm ÑéOÇÃ= ÌÆX‰ ¤i×NåGp!mÎXç4qm~;f»í=òeËŽh°Œ€A $?)ûݬÚRgÅÍõV|n°Öa™ ¦¤{ÅC¡h?ÅØ¸KùÌX€á»•ŠŠ¯Ä=žp$Ø–Ygð*mÍ¡‚|Ÿû<~äE†èó¼—Ÿ³›¡´–™ÊR.YÒ£QäÞr+Ù›¨\²ŽÊVj¼’ó,‡Ê$äH3ƒ¥ü(úÓ|££mîqtH×÷§]÷+kÙvW‚=Z=Rõ`µ°Š'Ûú¸'/íçT›s:–”‘q ˜ $€ßÅ•RLƒÃä8íÊ/ñ¼ävƯ8²[ÏRÊóòú:ø ãF¢0÷»«pùýØý2*”îGÁë[ΫLï?¨‘VÎð¶Æ—?¤—`ÍÓàLÖ¦fòö>÷ÛøuOÔ\CüÚ¦"mTòŒ ËÐö%gO*îz*ÄuM»’tÖ£ìy*¤7hyD½‘Uÿ΃MwrÕ–›ÓæÛ8æÒ¾H¥xM…¶—&M½ ‹i'AYÌšüâ\òÚüÉ5K«ú‰·ƒsÁr*Õ%¼Þ÷yÔ¨j1çCe½ósƒí6¢csW¨çŠygÿ> ô|¤¼¿¾ ¤Ð$§#7}û•Ö߀¯)¹HQ*MKÞ7í'hQ¾é¼;ò+¢ÒÒÜ¿æú¯êñ×ÌÓD9·—WiXé± W­˜.ó€ÂTŽ‚Ÿ€PþP)2›ÏˆX¤—i´âªÍíδó­¦v«LÛ4—_ÉT:&ŒS&kS2øµËàŽ¥#´òá`þµÆ÷tÜåTë9ù%ú-þ”F-˜^ê¡Kô›ðßE©›\ÀÏCnÚ¯óéc÷+ à_ÁY@ò=ð9ÀŠ”Ößxi~ëÞÇ‚SÁWÁ À뉦9n2š< oë=ÉRf´ O$›Ýeù•wuîÙÌ[h™ÍEF11ÙPM!½È€Wæ^Ì2²êˆóÊWž93]£í®ZkD5æÎXã hGÉDxyH×%ùÛ;ö0½ ðÓT1ÕkÐp¾+¸tOQÚ󢸨ç/ù/ðvp8ðôüw#×ýx[¥4Ýöò÷käÊ ‘bþ!PÏ_Çã€Fdxxí¹é0ù…àÕà[àX Ÿ¾ ¦³ÁÝ@âñJýÉÉø ]c@½ ``Ž”&± s4ñY Q5ªBó1Ö5×Å3«€çÕNJ•SúX—ëXÐóûx/HYžn:'*)¯0WÀ¥~õüµú_¢Åro”¿â8ð¶ú=Ì]?§2(ÌÅýiW~ ÿ¿ hQ£~£W)l/?Þ}$}Oî×õRþ¯ßRþ’Á«ÀŸt€øõ¥þädü ‚®3à•ºë1#F0 ´Ç@ºN¹?íÊ/Hñ/K[üêE¯êK¡J™z<)e‰»ëa:¯ô~ ´PÊòJðÀ²Ñ䨍»·+ž¶\÷ëB÷ËÕp½Œ–¯ƒ‰@y¿Ü t^yx>îoïXÊ ¢×´Èh yœ ž1ì !ÁÀ¡`À–C‘v¤ ôG\ÑéÞÝŸvå¤ð¥05çíçgâ$uŒ7?ﮇ»Röô®â„†Î~r•¿» ê>q•þX ²NRâ*ór |TA’ö—ëÜ8p6Ð+‘£€ÂÖSÀ3@í“OaàmMWþ` 8HÂ8H#z0ÐRd.îO»ò Rž÷€ÓíI4þÔÓ–R=xÚ÷q¡Œ ÉÑà, ÑÕó®~½·¥e˜@zd ¨‡þà×âÝGü~ý„îKi¼ hêB ÿ.p•¶K:  7z€0z€ÄH"Øiæþ´ë~)ì`¸ è•@)ÄÛØI‰«§ìÊÑ㔈+éý¨×¿|hd ­ØåïJ§ôœ‡q*ÙøgrË5p ”Wi¹¼|r?|| húCË•àœ¿_Ëa": ‚CÄ€*zH0 ZÒŠÌýi×ýî—×ëu¿2$×õš5<®küzw j•Aø4  ×褘¥¬•ž WìRèû§÷å5œ ´œý9îv ´•ž—G®oâ×°ÿß‚O2f´‡ÀÊ%ƒÀããmã×qH0 ô0aô0¡‘\0Ðí)·Ò0kX\ õ-@Š\"Å%øÂ=P½ÕtÂ)e]û×@†Ãþ”þþÎ) •Kë^Ýâ×èÄ/Ω܂¤=¿”ÿ߃÷)zŹÈÐ(€Äã—ú““ñ'zž5 !Á@0Ð{ HѺ¤ýé0õU7´èî Åù: 9øÿ‡…¹¤ÓR\M'hC )ØãÀ5@J[жYÙóQ–\ekêØ»àæjë-—í©µ¬Î±½t†O¿6k,ñÞôÆ‚ÒX.qp¥Ÿv NýL\Í÷kCS•ø øÐ‡ŒÅ ‚^b €^":² Jp…]êê2Sý¼ h¥ü< Ãà`¸ ºV=j‰üW£')ßnz—$×I±˜ç?ýû±¢x?*“î'$ÊÄ€WÔ2eÙýš¯i·Ô/#@¯ò} \ ¤H¥Äµøî0hx^ Uqºn>ø/Bö¢¶50 dFN°‰CFÙäºz›œÍ%8–m¨Çq׿Z¾ân;1¶ðy×'0Öo|ÊNÞ¹ÙÎoÔRD³ÿ¯¯h£¬ë”¿DF€”ºÊ¢Í|¾ N W¹4­±xÏ_á.i¿‡… ‡UÊ` (^Ûs&Èêý¸¸p7~ÍÉkZ@C麶ÕÈ1ŒO/~1 ú”,&ÑSmÙð#m0Ÿ’žÈ…G4æ,§ÏOës“úuº;ž$„J®%°V.9bØ^ñùüô öOOÝg+!ïŒvoo“¿F)¾¦/Á8¬2^Ü`À›rC‚` —P= ‚ò2®‡òû±û¥Ô%šCø)P˜ §€®”ª …ä£RÌÛjZ–¹ýw>Î.~3˜Ÿ+XaWÖç-;c«eŽÙÊ»y¼$85]Oj5(|Fljzc³aÝafŽäõð÷+†CsF[¹{§}|ë³¶fËsÉt€”ûBp˜TF•k.XÕrŒÓFéGÏ_Œ„e`Àš2dYÁ@Н‹í¹ “2•‚Õ¼»^ë»h €dX€Ù°cÐP«y›mjÙ=9-ŠvåôE6~öüÖ¯ÿUB1£ Á@¿f €~ýøãæ«„éS;Í2ëﱆ™gÛE ­¿bü®dµ¿½L-®$åïœî¢\ Ùà¼õf;k­.˶Ä+o±½³$/%øeáÁ@™ˆ)€2Ù]e`çKVœ:߯Ödí&æÚ÷~å>«Èê{½ß_‰"£d/FÀ´mf÷Œ²üÎ:?z’m\µÜîªÄòF™‚þÆ@Œô·'÷[Õ °Ãß¿ ïó‹žµºI¬¸×Þý•,2Nñ¦)røõí¡/Ì<“¯†Á@Ù ì  ˜†ý³3çÛ4þ¹ô¬³V¯ÐŽÿ>7£ï ,xÁlÔ«Á;1Sko×E'¼®"g.ö)}•0úê“ûê3 Ì\`ÙÁ{“ÝôÞÔ³A§¿`™‰»›?àS-7ɾDZ˜|7€}RåæÓÂú¸QH0 ”‰0ÊD|d t–lÁjî¾#ÙH÷ïYLgï~œOõRs5Ç^-¢oœÂN|v¸ÀˆÀi³Î±ÉÅ|ìX-Ï/ÊÙ7 o>׸«>Ä€VÎϘoÇ7åìð7Za8Ýéjþ÷Ç µ{°XÎ}Ê2{è÷³…ñ'ÖÜjyF7ªÉŽñÛ 7è „Ð'cÜD_e@»çéÞøèÏûÕ‹>}“e4§^®ÝþºË³nBåžû’eXÇЄ=ð¥UdWÃî¦ñ‚`ààààø‹ØÁÀ!e@šRÂÿÞÉwö»­YaV£ÖTc3ŒÙ¿@û~¶^³$¦’‚20@H,ƒÎ2ðÈ2+^Êt? sÄá VÕ+3CÍFîiþš07uBõÞL”<¨~¨þgwÐÇøÉ›Î¤ÍaM–•­ÆÞ¿‘Ê=#€O'íæôñG· T4aTôã‰ÂÌm<Ê3ˆ:™ÁlªSÍ¢ öHl˜LÑŽ®æ{‰²ÕÎ@Õþ£ü}žbÆã&µxŽ=tª[d Äi¹‹aÕ}7Qú` º ºŸ_”¾0À+tÍÚ³/|¼Cß/`è¾B‚` L „P&â#Û` ³ ðªÜ®-ê5º ßú¿S·¤µˆî+$ÊÄÀ_ªb™ ÙÁÀþàÀ\Ql¨ò%ÉØÿž\ó>F hlßÿÇÙ` 8” „p(Ù´ƒ`½¿eY”âÜ]å5VÅß< Ù`3€ç{€žH"ºÉ@•7'ݼëˆ TÙz{”®sžï¶×a TQÙK‹ªm ^j10lV•žã` è=Âè=®#§` Ë Ì]d™‡nL¦ö ü‹;ªø5.»øÀ–ºæ¥ ¼Ý°²Ë„D„` è1Âè1*#¡` çhlY1²¼å_¿~HëüyÏgvˆSälK½Ù3ƒ“{ؽz©=zÂüÖWqî‘|0 ”2@)#q T«Ù ¸¥8¿¨å€FZQ_Ö“2­&ÑM¨ÈOÆ(F1‘a‹U~}à($ÊÃ@T¿òð¹f`æ9VKoùwuŒÜ>¦åsºŽ]êë?0`n˜hE6’ó}•lÍ_ œÊ(h”"èG „ÐvÜju2P,´¼þŸ±§ùýQfƒªlC}¾ø¹f+‡[–‘ŒçÖ,µ;f.HFªó¡D©ƒ>À@}à!Æ-ômè%çgmÒ¡Wæc@¿™dÆWu«jò|…ÿifC)?Þé‰e ÆQH0 ”‹¾°³h¹¸‹|ƒ^c`ãz+n\gÏŒšb‹Øp<Ö@vÎV>Tk´fá‘¡f×cʽ…2|Ó:ÛÆ=…Á@ˆ€2’Y]e€¹ó$Nîæ Ö´õš[¯dÑbEmýûëÉÍ[7eízF4žªä2GÙ‚þÂ@ýåIÇ}V=³XV‹¹‘ë7 °š«¦YQk*u@ƉÞ\¸sŒÙŽLÞ^ØÍÜÿ‡f,´hwªþ×7ÐˆŠØžbÜC¿``Õ2+Ì8Ûê2Y{ï€&Û~çhËüz¢Ù”,£ì'|¾ØÖó½¿Ͱâн–-íu*$sÿ•XÜŠã/  jb À¡f8ÒzMë-¿ñ k}Œ=Hïú=k†[qÒvˌݼZ׃9\Rš™ØÍ®…_;™åÐùûç5ËíÇŒbdV/ààØØÁ@Ï0@Ïð©½Ê ׎dQìm{ßl¹#ØhŸ9ö²¾ ®½æý5üñ)VxaP2ÜÿG64z‡¢Ü!Á@0P! „P!"Š t…™gZvÍ-vë˜É6 e;ïvæØÝf™ñŒè³Áê—C”¯Œ¿?ÍìéÃ,SS´Ç¶m§î¸¿e/ƒr*ò ‚v ]Z"0¨l6>iEÓÕг¾‘W' xOZ>Ί£öXfòÊ^ e¯­~íï^‘l÷«<Ú8Äæ}Á2ž4öÿ ‚Jb €JzQ–`  ð.}a:sê/³Å#§XŽÊ|+îùàNnö–æ­wõ”€†û¥é¢ümöƒYÖØPczõI1k¯y„/†òïÂCKƒ^d  ý„^¼»È*èG ÌZho.í7L ä‡íµÜÇW›Í}©ù=|m#ØÓ•]óüJw;‹ý.k¶n¨åYÞ—ËíŸk²öÅ—F¯¿ýüâV«x ° Z9(e`ê"ˬZj‹ùnÀ”ò}»k̾zŠÙwŽ·¢†å%ê¥Kaó¿ËËð=ŽŒˆzê{د§Xñƒó›‡þIûyΟÇ*ÿÏç!$*šžîTôÍFႾÌÀl¾°ryób;^·»€žø¿6äl4÷¼wæËMÛj¹ùÏ›M}Ùl ÔþF½5€²î¨!P/_ãùõ¼n€¾·M·Œ5[5 «†[½ÿºÁÍ_÷û2—^¾r™mb¯‚¯üñíÂ` ¨d:ª÷•\æ([0 t‚׌³ÌÓ3ì (ø‹¹œoñ5¿žw$À©ÌNÛd6‘ƒêÄ«POÿÏ£ÌxÝÐÖS*‘LÆ~ÁÆ>ŸÆð`²!$ª‰0ªéiEYƒ.00s¡Õ±u°>hø?„¢>ÞüIôêÇíaÅ  ?~—åG4X±.o™,=}5 lâSÜÅ>Ïf¹=ôúÙyÐêéÓ×m—<@KšŠöƒG—Û&"ÖÖ°î`åÒxÕO\‡ÕÂ@Õò¤¢œÁ@7àUÁ ûï«3ŸÈÌ6ŽƒqŒüŸIå?‡ÀÙì0A$µ´>*À¼þ6°«àþž•ýOÚV[·úÞæËYx¨µ­é7çƒ`  žR”1覣¬QìYF2 Ù·y/ÿ”7ZmÃËvxCÁ†×ÕÚËvÛ–{n·]élE`,e_°Blç›f&üÁ@u2@u>·(u0pP Ì<‡-z –¥`f€e(õaË pHYs  šœ5IDATxí˜U噀ÿ)4i ¢Ò)¢  (TDQPƒ-¶èÆžhL6ö$5jb‹Ë&›Õh4Fc4&–]»bר5+v fî¾ï¹÷ãÌ0Øg(ßó¼sþóŸ¿|íÿϹwîÜIiµ¬öÀj¬öÀj¬öÀªå^cR³Úo°Sj^»ne?/_Ù ¬m_ŸR™uï´MGí¼Eºë¨iÎÑRáß6NϼÑ>ýÄkýwXõA»W ¹¿Yj±ßTõDEšÿÏ”æ?UžªŸ,O…gSªš’ÒÜSû¥B§]ÓO7ß&U® ©XŒÌÛØ¶_ZpÜË©Œm |^Y*çXæ6X(Ke_”¥Š>Ÿ§4aRÚòÖ^©åÔwÒ}ù¾+c9ÛWFÃ겉í¿píý)5¯®ëêÂ:’!½²všuè”ÔzaíÊYZežÊ&¤Ö÷Þ“ª|Ã\VH©ßgiM·K¬œa_hÕ*“¦§ý¦²Í/4½áR3’`nyZ¯áV+þÕU&* éŒmt$`ا+~€gÁªYÐç§teÏz‹sHÍuz=Ý©æl¥-¬ì `ðËxäoöê‚tkÇBZÌãßÂ8ó !5¯J¯-¬Y9K+sd+Ÿ°• ©<=’æï8&UÌÚâYE›º¤yÏ?”T4O¾TnD¯l—VæÐ6ßÌÉpx÷îTvå ”xÀ#'ê–yôz”~³a:•-«æe àX+e¬Œo(&Ú”•U¦²ç'§v O âe^¯iÁLøº¿·‡·A*?{­ô·^IçTÍO<:¬Ü²2fu>ð±r=΂{áv¸°¬{úhHÏôÄðÏÒòBj3µEz÷æµÒ;éþ´/o þ¹P½ x#mçÏÙóƒG7F?KÐvµü?yÀdvµû[¾ÀFž½“׆£õ¯ÂAÐZsK(°âóï÷WxƒÛÖ‚Óá,X«%øÛBÇZf­¸’¾2`«ÿcØ :–êÏå¸vyE2èö­$!ì÷3ðܤ±íwá—cµ¢l˜8¶“Z–»#·Neì›e¼½ZxüÑ%xÞx·Æ6ïÊ´,!nß `8¼ qOŸAÙ@zMѱ²íc Çãn°'ì î,q;ð6`Y©ï™²xõ_ø¹Éö©òãV©¬3tÉo/Y>·¼³þ5ëïÒelñs*S—÷Ú¤xØ­4õ^ø»vþ¯“F_mÈßs¯\é ¾ž`oAù[÷ñ|ð\ÙÏÀp Ÿþ Ãc`½sÙ.ˆù¨Zzi½sÑoo¶MÛMé˜}·CÚc~eêæÈ}Ggs.ý$Ës„®c‹¦h?>]r ´¸´k*\Ó!.^/¾ÛŸ•2!ïü}Ú³ýòëXŠKŠˆ ¸ŠÝº[ƒÛ¶÷ïv¥rÖUì5W¯×¬wKOÅxžÛV™Ž)Žo¿MáPÚƒs9ŽãÚׄˆ±–äXÎm¨¢o¯¢ßø0ÊŸNë“ k“ o°Ã|¦,UßÈBâWÚ¿füe&nsËD¶Ø&•½µVZkß×Óg“ºÍbƒ­5º¶(¤ªŸm’*n|!Ûf †ÕáØ8Öꙵ±]à*³l° „eƒÿ|é\ Ü]¢çSÁÝÀÕêýÛDš^‹q}>ðúõp4¨Ï¹ð}p¥ûŒ0lïo Çþb[J}v¯ûGy&ŸR*üøùTí‡Têë8Kxp3r\»öìôè§wÖ<ÓÄ8K|ÔAËDž|8ú~–8áµT]_ðHãæðÉ›½´ŸžnÛ5[AIs;08íw-•Gp| ŒAquE›÷“R½jX7öû(M&ûO“ź#Áà\Ö9†Ï¡‹õa‡×ë#tñX±F§Ô¶Í¸t×ÉnÁ§mòpnuj¹ÿ[ée|ÇT‰¥–-yÒ:*µÝî“´ñ”òÆI¦Wþü™4¬ãÇÙý6¿eêÈrþh›¯J×l힤lÏ„xp;þ"(¦‡À"‚¸†:ƒ¾°|"øÊa,xî-à °ç&‰IánáóAŒúƹÇÛr$‹Çf\i^55Í>â4’‹¶[¬´ãQö„—Xù¤í»íœÍ»Ø> 5P‘¥–ImRùÇ뤡×?˜ªgì;f5æv`3ūҸ{©ƒ+L\]b«òd¸b›õöá½ÿp»Þl«³Í­Þ9“Ä`æÁ§:ko¢„KJçŽs\ öçoÀÁ„ô•†c…þq4yóóYnn+ ™µñôÔÚß>æp½^éV•ª[ÎLÛxóZJY& `0±¤íÚÜOg·ÞF©Õ°Lw§±Î ÑiŠAÖ©5Õú‰à×z ¸ o†ÿ„.Ð ÜÖCÜ Ü¦ƒä–n|(Œ1)."ÖûrÏ„ûè#_\Ö‡L¡Ðö‡ƒ`Ü>¨kçÔKyÔ³ §s\"q~céø—J–Itž› Sg¦ç¯ïšZlù K@Õ!< hÅ94Õ‘:äÅzÛ;ŸýÕÉ2qËs1)ÛÛîÏ`ö…Ë Æ1}hŒök¤àËàY…ôùSÒÌM§¤5øUµs/VÞ¨ä×Úk¤É3ggó,¶}C 4j©å¥ûSuõÍéÃy‘4K×5BÚU§Â¿&‘˲^nEW—Oô® ËŸ+Uq‰Ž=®„¿ÂuÀ¦“å¿9š&ŽmÇópX©l0¬7Þ.-dÛÙ×ùœÇ9b~bŸ(¸ï韽Œ´Ÿž8b׈Ì>ÖUó;銲ŠôÛßµJ­«Ê3¨nX¾"Ý.LÒ]Ÿ&¾wWãú44â2I'è±sªüKï4b^‹T=g1Ià-cbçTvÝ›©3NXŸî›ƒq•ºë\ï›î &…«ÌQuܽ𸠒ÎÀyìS:·ÞÕ:·®UHõ"brØ.V°ý÷,«“×7æT¯Jõ^{ž‚+À$:b÷P7õmãàix¾P•˜ó\*;sHªäWÒ~¥^ñeà<žþÔ;mÉnî.MOúî”~õ×¶©ðh%of”ÞȈ74|Cèeœx1ïò¶ÌÞë Oí+ù=VøæsÀàvª3Oå}\Ñàw`˜Öwƒà5±½Ï²èü_ƒmƒ µe÷Úœ÷û9ÖÛ¥£:ªëÚsª¿ºxÍÝg=˜&žu¶íàà±'{˜äI ¶¹Ê +Ò:¾AöIþˆo•|çñ1üö<>¹ºS*ôSüóµèÜ$­÷H»9ˆLm— aÐû¬(×bÀ>ÃÒôÁ[§mÚN+šÕÜÛµ#cДÃ@g=Wƒ·„AàaPt®AÐùÅà[×¶„;ˆ+Y‰±‹g_ÿÉÚªÇTLë[y,«ß®Ééœz­ | xNépÜŠ:›8¢nÍ×¨ÈÆHÝwJן>8nÂw/ô‡š§êóz¤Â„¡é½vãÓPÚ6}YwDÑimvM;²AžÖl +ú€ôãvÓh´£K—ÔŠ÷¿u ÎÖ¢³uº¢C;BÎ@΄KKõ²@ØN§³ˆ²_ú¸ê €GqÇÐÙ®dÑáy—?)•O.ÕÇ>ݱ¾€Á¥²s˜h‘ˆ&…ö;P~^‡HÂHìÐ'ìvûevà—Ã)Om6>HÛñ¤»eIzm›é§Mú¨£+Z¯™Öè¶mjQÕ]·ÎŒTiWÅXt‚I ò«™ÓÌÁu@¬ÐÑ”ï÷aP\éD:À:¥òK‰¦^ù$ˆqm§Ä5õR'Ç6É”þp1Dr¹ÝT§ô„óÁ‡<Ÿ¢D“Æ0àa³eëbœ])ÿ¼yËÔ®¬’[Iñå^b·T§Ft¤ê|Å{º¢Ã6„ŒveÅŠ¢X#^w ¥|\®TW“âJŠsŽH ˳Mœ{TO‰±ÕýHP·`Pœ?$KÙ *9ª»(ö êebyÛ’MÁà÷ƒ˜wwʵ}AUÓ“¿„nÿá$ë€ÞtˆÿêWLmqW­Nó9@GŽƒs`&\&™¢£Ãñ¶5±º=Z§>bR:¶ã= ‚·—Á¹L'G·u%’·x¶ð–ãùGp(¨‹6Éöð(έ+Õ C4Ìcg :´ ¸úއ) „3‹g‹þŒk®<ËÁq  bbyÍ)àf°þh&‚óºˆï'ËŠÛóOÁ>—€rŠs˜Ö;·‰ íÊUjÞºÐÙ¾'XL„ËÁ1ÿa¨·ö˜+Uè(JsБ®q:„˜:­.gF¯9¦ã0“ÈUs½èÌH†£(¿¾&Še‡‚mö†wÀ]h+P¬w9°tþ`éèØ&`Czr9.Ú™ êþ[¸v}¡hKìfùúè›5jê?TÖh¨Ž›_:êL3[”“ápeºu¦¢#¶Së”pBcÇ5èáDË¢¤pªs(®º `6¸ ]_Âyà˜¶7‘Ô?ˆñ¼¦þS!ô5`¡ÅL<÷ºíC¬3±3n#?¢ü˜ôêe‹»Ô6ÕŸKK/*³×(f}çqÔÑÓa¼}À±Ÿ…`ØÏÀ¨Kàx&AŒ«Ž@û·¶/MJä%Ú©³ ¢½àX0 ç·m–«„2Ës’Ï\Ç´0Èò>p|ýÀUáõhC1“Gù¢xÈ®ë _€mu¢r0ô†jˆ16¥¬4áËΣá#08ÁápŒwÛ] Ö[6Éuv…:¶sŒ_‚¶EÀôg´„¦ªFìëõ9 Nã`.è‡Ûà50 ÔË1Ê_K0뚬èÓ,ë¼þ •WÁàèp·ù7`p5)öÉK8Âzc²(íÁ1ºÁÛ¥²Ïn»n£!nóêàV¯s•HËÑ9<*Á¶ö9 ¯9¦ã»ch‹+]°Þ[zuI~¾ ià8›ÁG¥²:oîŽÊz°¨ƒ¢Ý+„D–«¬Fª¸F(Å¡'˜a”NÐy:q DÀ£Ž3a\=¶ÓY?e .„á` "ð;Rž &—Û¾âX®¼º$æôš‰Ö£Ôh Ç·à)pLÅy׆CáXP‡HŠ™žÎã˜sö¡¬þ&º6x ¼çÔþ! ¿ôÍ7Ke™îµ†õMJ46”Ty¤Ó#‹=Zgð£­+«/<á×ùÀÛוïÊs,ïÃnç:Ï \n¿g@H$Zœ' ¹c¿\Ùb$lèaÝÏ`\@´ÉP—Ã@{ÔÅcô5ðê`½×†Þð<ô€ð…m«'8î¸Ò‘C–8^·m“”0B£ÍòpŠŽ0@äŽ`‚¸Ý{¬€Ž×u¢mL„pbì :-‚ï< þ(¸¾‚»`0(ΫœqÚ ¬õÂö Ý¢ÖDT6€›ÀÝ쯰 ¸+¨I‰vPUxíÓ7®þ§ du>œ×—§úDÇòº×ô]“’¾Ê©¤Ê¤ hD$FÞ©C²«Å¾åKÁ~:Ñ£âŠq¼^`t´2þ ŽýSP"©ŠgÅŸ± óu–M¬¼Ø7Ÿ(– F^ÔE1qCŽ¥ WÁÐR¥»‡Éº+¨»Aw×Ò&ƒ¯Üÿ™•Šv•ŠÙÁ>ޱ#8öF`_çorIÁk¬Æk€NÖ_ZY¶Î‹>3ƒzZ@l»¸hìI`28fÈilw1 „ó ®;o]2¸®ÊR]>èΛ?o [–Œ&³}n„#àgàSübÀ ¾·Œ.àʾ´A‰mJøÑ~î”óàø¸+êÇ&“ÈPÚ mµ!„‘U:÷º}<×(¨“&À÷ÀW:`=P\ OÃë0çŠñóAË—³†üбõ‰º4VÔ9Ä~&³6¸+©‹:)›‚ç8ëÖ‡}Á~¿w<“Û€º³Y/–õ‰+~<„)ÿ´¥vÔe3Í–¯8©È?°õSsÓkD¬RÓiaÙíΕkˆmu„ÎñÞîkâª71l¯Óÿ öÏK82_eS[trCÉãñû¥†Ú~@©ì|êý ˜°ú@ûB~@ás¸ÔÛÕÝöö·¾4ô•õ»ƒmí¾n%’À>êöÿšõß„ÐàgAQI%` Ê]ÑH๎q‹TJÇaV y§Ú^§¿ìäÛAÊ*øQŸ3j·³½:׃¢nyÉ~ðúPxÏ`ª£ÁÑNE[®_Rþ¯iw$¸‘Þ"¬ßÆÃ\p}`ü ô‘¢/"q´-¯›×—‹„Ñ:É«˜(¿…k³Rñ#Ñ>(&€eueoJ¿â!{ø'Ê‚«ì`rh\8Ócì8®*Ï•º [ª×yy1@¡o¾>ÆÊ×5¦ìÜúÂq§ƒ;S/p^ÑGNý}òÿÜ=^¹(>KxwÛ{œ±„íúïˆEõ&ÜrO‚|ð8¶0'¾/)Fk”¢–5NbUEÙkA@§= >09¾c‰×uôqð+PœWñþZ[ ˆòPW‚d—ðGÌg·ß:¯Ž£á°Îsƒ®Xm7× æ ø ØGûµ½vè } Žu ¼ÚæyøÇ¹—•½ µPÂP%œØúgà›°?˜¡Ö{´íwÁ we,< OÃn Ø|ry/up%høš¥£ee6XCãA±Mc$æ_\ÛÐïÈRCç‘ë³Êg–¸É~:˜ õ è ‡â8úRé7Ãû×÷¡;€>ÕæØqíw(LE_Y§®>¡¸Òg‡Ô¬ÓðԪàԺíºü!GE¦D¬d {ܪ ¸RA•ñÜíPш‹ÁÀŠ™nÿ¼¨´Ê+ƒe9 "±Ü2G‰W[ö ¢>ÃÏ/]Ë÷«¯­õÚ¤ÔצxµøÓÀ„-Ú·3¨¯xëóhÂ< J´-ž-üé‚Q.€ÏàiˆЯÎ#&ƒ˜xûAè+wÙÖÍ+Sˤ5:óñüõwNÍùn!}½„2¡þÑÂá¬íŸò»»3ù¸Äñ‡âÊ™öŒ† bqo=޲r0¼>È EçDj;"Vƒíþw€s9¾É¥‘:DC’º‚¦¾ÊÅC½?#ëmPÇçÓ&}à<êè8ù…bð/ƒºÄ¶#A8†çm˜ïÃb½ãš.ç3ñ“ïK]iØŠ_#—ÅÍØíUzCë›Å˜ÒnñÒy×tí)}S¨´à+~}9“ ¾È›göL…A»¤ëIÕO**²¬Žà ¥'h¨JŠÊ©td¸u}ýQ’=9êÀxhÔ:Õ~hý:°œ ŽSW©ÎV…ÇÆHŒqDcçÚ¨kmq¬Î…”7oeê>´%/¶×ÎcK•=ê´Oí®ne[x^…  8¶íšóÏF1y¡ÿøôßgôÌþ”lž1›Aìø“¼§öN¾¹ízÚ¦}‡Öë;/§Ä7~L¿¬(þ®/)ÊÊ~ÑÓ»$Â>[§/Ngub™¨|\é—‚[“²eñï6h05TÑØphÔÙÆÝ`8œ ]@CÉÑ~uчâ"âJZœt]\®Ç\ÑÔùvŽ“ÜQ ¨z÷uÕr9XçõZÉïØ¶±þ2æƒbRDÛRž WCölu'þÙcË´`10FÆ-bfÙX¾OLGŽÌ~gB·zdÐöéQÎ|"øµü:I°ýÖÙŸv?ÌP/A8Å «¨h„¢3j'@v¡ž:AƒÝ⢯|L¸p„NoHBú’$v­úưŸ·®˜/’=Ÿ8Ö©Ç÷@1\™¾Äc 7ôðØ j‹õÚîx&ü8pQ)Öy]âV©¿ƒù#·M“ŒICqóÚ{|Èe“QÙo!é–[=üƒ¤òÏ›§A'¿˜^2•ÙBå¬Ù×ø¨ûyºÏiBåS馼”=ýF¶ÚA§¹%ÎGÓQ^_v¯K4ÞëÂ; Ñß‚)p>˜ý1äeSN°¯NQÚóéDuŠcíŽÖ»Ê.¨}¡t^_¿Ð¿;íL‚;àø níw̃ºÆPW¯ï7ÁH¸l»¼Q*{.•mÚ¥ÖöHCÿã/éÞufQ¹˜¸ù÷çoöß™¶å›ÆøÃã’|Ö2•On•Üï“4wqÁ·‹{ÏNUßIß)û0û®]lµ[›jÔŒMYÑ9®j Qù¼hå@°ì+ ûÛçÐŽ«S'C´qž§N±}mÉëÒÅ¥c¸+Æñ»N}Á·kô+ “b Wçðø|t ÄÖ¥BŒ‘÷Óú\3IþŽãCuìRvÛKgÙ7¿ýYúÞÐiÁâ‚OŸla–æUV¥ñm¤²šÉß½+-59 g–f6lŒ`eÙ®¤ÁŸLËÞÈ0¸ÆÕi«¬u!žkÌ?AãwÛ÷„xй&W¦˜ÉL~¾®$wÇY»Tˆ ;GCòXéâ$Žy;Ýì«^®Öú¤Æ_¹ù9-ǹ¶uþ²ToÿÏA ;ÔÝÄV>ûÇ5ëBô‘:ë3ÝoZ­l7%ÍÙwRöÿ­bQp©aa’Šˆõó¦‹Ä¿I›Ç(‘™ ÂUµø²yšGš·à›>†rêÖfæªŒåƒ  ÆñA®iÔ»u’èg›¾ðpJë㚇Q¦˜­Ê€âa‘Ÿ‘(Vþ{îŠA¯Ïîßã7J}\™ê¢DÚ_½=ʸl7ò6Yw6„L+ò:Dû¨Óo‹Â¿Âaׯ$n|Å`½úÇ5G²«0«²øie'ɤç˜Ôâ¾^é.RqAX×ê;¶ãÞzÝ~Ò?ËÌGh烋cª¨ÛŸO¿a€«Ìz‡—F\§*¯…^–O‚à“ñgà5W•×^ûçW3§™ør)¤®un\¬ãèØ!ÎgÀœÇ€*ø½F‡©”½~&¸}»ËÙv ˜c¡7h¯ãŠúŸŽ­„âÜäQgû¸môº8F‹nÁœ©óUÓƒk-ƒK KgÞÝÒ ½Æðò1šÊDL_Ð'µ˜¡9‹ÿ·Î#kó½6·¦u§½–}žo;ºð²3SУF¢W‘ïSÖ(ë7?K¾l’¸ª|™ãÖy&¼ÛÃßÁ¶®Ä|Ǭ+ÈTgNŒs#ó'¥²s+$n_î@Ž_—Dà Ôy`Ÿ î®ðÑàÃé¯Á6Ž£mP±¿ó~”](?w%ë,“þüT?}g‚¸Óž8gZz¦ýÍiú}RÅÜО õ‰ß3tIOvì›Ó†~–æ;X&y.ø ’–¿Nh_æ6ôEOöZ«*U6,ÝÝùä´=÷¢mäP±Á± ±NêJÙ•ãVîÚBŽ¡`{ƒÁúÜA†ÀÏ nž›÷€ŽsŽ|ã¼Æ6Ö!ŸÖQç¶UìgpMW½¾\Ô[?:¶v»+ü¼¦t}Úvì>4îj• dFµ±Š¸ÃW‰å kñm㣳ÏQÒ|1B†Œ»yzæG¼ü»Ž©ð{âŒ^©À·ZOâUê¥înk{‚ÎÒ(·f ˆ zŸ›*>4Ø-°-htƇ4j&÷pˆ`d¥7\ Úa›þ ê>0éÅz“½ ÞNŸ…}Añº~²­¾îÁ»àŸ·î³ÏÐôÖé¼õǩ݉ánÃÓ Ät›¬Mc~ð€ ™ðtC:í––óMV~ѱ«U1À*๎֨ŽÆ*:Ämû8tšÕâÑ•bæ›ÕŽñ6(:}8¶sŒ‚ÃÀ~ FOÊÊâa‘Ÿ{ræ|y©Ý/­v9ß7ŸLÖdÇRÿ³àð–h½¨³AÛôƒý=·½4<ÚN»GÂmð.<óA1Ø^·}CÊú‚ͺ(ÝÇdsf'ü>gØ:ck>©œzïô5_E·†Ã¶Må¾SXG«pâ›\S‘ÈL•\ ¬¢áëg¥âÇœæP6À»•ê ¼Nqw0ó‡»Vö?ágb‰ãÅØêPm=ó×hZ#»Ô”Š…ÚíjŸ×jžšÈêe` ŠRUê¡­;ÃEððš6xMŸ˜Ø[Ãó`àpx>‚£AÑGÚlŸq0œÓq‚)Ÿ÷B“’ÇÐæ4Ðéfy>| qKƒ$’Ál¾L”ïÁd°o{Бù&Vgp|£S]Ž«46Û @C¢n:_±¼UV*Žr©ìdƒåù—ÐÔ]Ýέ7ô‡í'Á`â_ á#ms.oúÅöãA;ÇÐÖ!àáUÐ$å´º\ñH1°Þ4 /¬St’ÁVìû˜Ý#@ÑxÕWÂÃY©ØO‡íŽ•Oú¯Jì*öwLu qŤV¯Ž0žuQ¿°Õ£uûÀdxBwÛÅX3‰pÌÝaZ±:ÛU7£üœTªk²‡ ÐìN0t¤ àÊБ{C>PÖ¹‚c%ØV1xç€ç&EHì,½¨øÜt° ä8ŽíœµƒFU&ù@æël_ŸØÇëŽk€LXƒ§ÎÒ^¢] ûô„«Á þF58†z{ë @A»‡ƒ·³]`6(Ö½;y²"È¡(9µ¤¨Aì ®d·F©| b¥œKy;8 ú€Œ­w{Êÿ ŸÂ 8Öœ¬Ttª àØ:ÕD°¬ÔNÇ]œô§ýDq<op7v$oE&àhÐFW­r<ø0øØîöÿ9ìW*å¨Ä|úÃñM”Ýà+p Çïá;ŠMW4F§mÓAå7EÍrWjl¬Ø./ä<né­ÂÝÅÁ$ˆ•éü&ƒN]â"[¹çJÌcÙ‰öÅq Üy ÎÎcðÝá Ôïa4<ÌÄVìëØ1¾68–mÃÖH õôZ$—;Ãx˜ ¶ÕgÚ´Bˆo >­ Ã¾n™:Q‡j¼G6Ëo+×µJ£NG˜X:ßpìÓAq>ǺLçqk?Ë‘Lêè˜ç‘d#È>Ž#Š»€×ÂŽs)«Ãù¶èK1“Þütü¸f{}»Šó¨³É¥Î1ÜIL$¯¯0Æk„¢ƒ¤/è8ÞlgÀ”IÙÏ¢5¸¶Äöj½eê8½àxÜ Fƒâ<&W>x&‚:Ù×@(Å:¯_Z:FÀ£¿z:Ÿ²/L‚GáØlvS\äÞî¹×$‚9Œ²>Qƒãk—;ŠÁ>¼>”H®âYÿ©¡f{>\±4Ìpn[·‰ñOp(GC¬Æ)‡c£ÎsbЬs\®S_‚+ÌyGNv19ÎÊG½úDùÛ”í£tƒ«`&œ Š:8·A ).Rö\ ÝCoƒþ?°%¨‹:†^GPvÜíàSPì·ÂH$€R4F*Þ¿·mÆkôß@D@).Vb/ƒ;JlŸvÞ®÷àXPLçõþê|­ź#¶ÜïP§þ¿ƒ! „Þã(Ÿ¾÷Ññvh"¸0Ã[ÀÇ ÝÎ-ß…@»ßëÊ ™*>t`wˆÕôå à`¦[¨ŽhÈPA³â+è6nN6ˆýa/xn‡ÍÁW&äÚ O†® Œ„»A=÷ű» ¸b•๒׭X³ð@œk›»…c™húãmØÔçxm &ƒÒ_Š-šÐOa¦k¨ ày8ïï”ãáßÀk:Ø~TŠ‹ˆ«7/¶êö¬˜H:VÇ1½w{Ë1).„÷àPL$ÛÛï\P×ÁsE}£¯A00ê±øÌ‘—èuê yQÇÜL¼Qð0œ‚¢/Ôy,|Ê ™«èÔ ƒ‚bpn†/Á  ×a®:ƒªÔzÔ¯ãXn©îTë@ÇsÎ8w,®Œ«á0ׂWtv¬lûêh ÿ€N î ‰sj“¾¸¨ÔÐñúºlÏÁ-ànèøê ðee¹$Àr´¨ï×~šOÇéóx.ƒŽ2(¶÷|ä¥*w¢ó«Áv3àM˜®Xms>ÇQæƒ[ö§`àö†}`"üö„ý 8¦Iu1ØßynÙ:×¾бB,Û_‰ä½€rpåøõ2¸’Ü ¬?´täýöÍD»Ö·1 r,¨×Eð%œŠ«Þ± ®‰ã[´7€úYoR¨wèC1um°v©£‰¯ =à/p> }a[¯G›±”ߥöøÅÚ&úSÃuJ>ú•tÕ833ø=ètÛƒ§ ÛEìOYçèÛš4N5Ð:Õ`éHË&ƒ2\á:ò`P¾u94¯³Î €q tƒžàÔy=ª§AVGÅqb|ûØF›/Ûù°ù$ÑÇ„Ûl§O´AÞ%Æ+ž5ñŸu%ÀèARýOAC]Qá°Q”]¡Š ­@§èdƒîÑ@ëT¯;†b0ÖûK8æÂ9ÎÓùJœÏý¹GéÔ¹"8gQ6èð îÚ¤.ê(ê6œÇ²:ZVGuUì7v‚УåñàŠcº»Œ…wA‰¶Å³&þ3@'*ÖL±üè4 ³½Gåµp„«[çí :Å Ûæ¸Ò¹NÕÑ®ÅvûÅ~ö©K|¹WŸ„ Žïö¯ôÇþ%ôRu5YÕÓzËÚëQÇþ½!j’Eù÷”ÕÛv²L%ÚÏ–ÑÏå2hN7â\2~ ¾yâÉó.çö³Î'úDåù¥àX³Àº¾ðlÃ`D©l[wÇ©K¯£Ò¹½E8ˆcTÇ?ܵæÁgp ôu1¡MZuRßæà8ž[o{Çÿb|uÓu½î€i`»VTÞm](;:‚[àP4ܶuRoP\-Šýºƒ÷Q·wq åHÐQ×@OØ \…:Þ@4î Vådp¾¼œž?É•m§NnãDzúXV_ÇU¼~8ß™ îÒÔ¥x[ˆ€Û_ñšãˆc;žú?›ƒ²ãáPl·ÂˆÎÓé:GÑøA`ð~»€AÒpØ/¶}ïŠ}f¥b]!“aÏRIÎ gZwjéº:8ל'’b&‘¤qîQ=NÛ«§Q‡s@ñº(ŽAÝ–ò“ð¸‹(;@ßÄp'ù(Ž>PO¯»{½Ý®Kj[Îån&qžÊÊklô´i _SÞ’¿\]cìþ§§ J»ð¤جcÍz ÷8tdà½Sg):ðM¸ ˜}l§óòÁà4s”mt¦ýÃñ ”m·ƒÚrd®Â6‘P|ïç“áºR;Û„8Oèd9×r~±¿â}]½Mr“J´¥%^£Uë¬ïºÍ*Ò‹•Û³Ãm•~YÑ©ø¡š²¾5»)Í›¸¬=:sx*Û+]tÔÀôÅÙCÓ‚7L…#ú§êÖ#Òk,ÝõÛtOë`´Á¤#<ê$e< OƒAseí Þ~¶5°®"å0ÈùeEGÛÖ„Q pûäÏÓ„ ݼy7x¶‹$±}MÛjƒ»œsoCø-Ì„“@QwÅc[EÛ–ÝÓzý·L›Ý,½uI÷T¸Ÿ¿¾â/{ªþÖ.UŸÔ/øŒÿU´Mëï²$ÂZãÒž'ñ×(Ÿr_|²¬øgJþyÒüéOx7î'üÕ IxXFc¼«¡èXƒû˜ÿŠN×¹ïø6W›ŒUd Hß ü|:Û1 Ì`Ûn»Q¶}w!òöRÙU æŠãª‡mž'ÀS ¨¬T¼m™Á.Y]YZ—à÷â×S‡_ çÞQõ¾zšÒ¯æyš?ÉÓwSðåaƒðÛ©e7¾ú­4^Ó:¬9'î†ÿ™¿A›Ø¬îïòû…4HøÌ««D9>…«¡(™CuªŽôø’ Ž d2øm<WãW`pcÅQ,n»¹£AuõÆ<ŽçØöw·qCqlƒn°hõgSw+˜°&§xMÔÕy¼f»y,€ËN»ß¹Fño/kSœë7ÿÖoÏM3]Ò–#³EÀK'®ºe&Ÿß–æôzzjíX½ îaõdóê"—=“½|£ÔÒÕÿCø tÖ;¥£:Š«Þ@ ƒéËHët¦Ûó¤RÙóèczµ`½l^::Và-År´1aöÇù ŸòMªèãñ01l§N·‹=Á@«Ÿ„̧à&šm›Ϋú”¥[»aA3üRŸè7¿ºçÊgÓ¼î;§“´˜õµol½Š/é3ÇLH‡üúÅ4»M=Á¯=ÑÈSa½ñéíÖkfMl°ìAU·ˆG}ð-pUê¼ápçø·Æ8:Ž«_ÙL*¯ÍÛå9=w+ô¨sΓa*ö÷º =M–*¸ ¡Iì½^ý½VÚSÍwûu¸’_í÷^*›ÝˆH¨Ì?+Ró­?É>õÄÙÒK>;—z´1[¤+þdúVW¬m!Fï˜Þúxb:qÁü,øvseÕ–oÂù¶ ›ÁeàêÒGÚd@v…»ÁºÎ`r}™ó9Æ<¶ñ ƒ‰±tWõ(¸ l ê˜ëm úΰ>\Šc¨ºØ6ƧX#ÍÊyi×yhÚö–ÒðæjÑñK}nëÁ‡6HmØ5f=õp6~#zÖݤ.ÅênÙˆZ¶°Á¿¹\Ì·ÒFLMín™Ÿ­ê˜!ç#¸:6/pr'Ü [A7Еåàê3Óáop| ^wK78Þ—?‡ÐÛã?À~nñ`ø3„¿ló$8Æíp½œÍ·çfË~îåfŒÞž//äÁë÷·Ñ©Òï|óžl»^.N, ª]‘R~nŸ 6¦ì-^ ÛÚG|²÷õ¿Û»·”-À¤è ^]€âò~!Tö¾„“Wü¾,U½}÷Òßj–¦MTïß®ðsà4è +Xב7cÖnV™:U”gׯSw(¸êm·?ÜŠÁ_-+ L·øãà·``;±´²•ܵYêÞe“´íºRoê -ÖÌ>îeÛ_ÊJü•Ö°bÜj~ºÅû ·'£Ó¨t×QSÒ˜~¼­³&7‚¯¸úÿy}Ó…å·¦Õz¦}ííR?é3Òji¨(ã_ÞçÍ¿Å×Ür£_À?Ä*<þÎÏ*d_…K>Ì=uXš¿u?ÞÛo¿ò®üˆÇª²¤æ­SÿF«pðàT8–×W ýW¿Tù¦>üÀë+¸5®bð‡ŽJ·˜à댼'¸ï[|–k‡ì-ê•Ú?«LðA²òŸ¤­ó #î‡V~ð/(¾±µ²fÁª“_¥æG¿šø FãeÇ©©¼|öêh¼ÇšxKßÓ]!Y ÜVêE²R·H°+Rõ3¾Ñ»2“¶³[Õ¼¼=Wœ¦«NÜæºy*oè_ªäÃæG¯öÛ‘ßÞPï§|òÍWØòª“„èí{RÙ«Òüü¯[늜oMo‘fMi•zÖu}eªómÒUJ^‘¾ì=#íÒuV*h>–¿P ¼ŸW˜Ô6U·uºdòüÿ: ‡XáJ«\t푞yt4ñýÖé€ÖU©bëYi.‡Õü¾¸ê‰5SÅüÎï²é€NsÓå“'eíZႺ$ çÀ’ô[¡Û•*_z0-è;: {³k×â‹T˜Û&•u›–î{ÿÓÃwHÍþQúö mh#”_% ü²Å6©‚cU¬;#•}Â'•ø€jÕÓWÿî=ü³ú¸Ú«=°Ú«=°Ú«=°òzàÿ#¬§Ó®lPIEND®B`‚app-2.8/src/images/socnetv.ico000066400000000000000000000400561377436340000163700ustar00rootroot00000000000000@> @(@| ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿB*%s…—ËœÝ޵,`?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ~–ÊÿÌÿÌÿÌÿ­é-_4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?2¬ðÌÿÌÿÌÿÌÿÌÿ‡žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4L>ÃÿÌÿÌÿÌÿÌÿÌÿ‘³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ9$ŸÞÌÿÌÿÌÿÌÿÌÿ!{Œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^x ®øÌÿÌÿËÿ„Ò5<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-&’o§~±N²y9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ9v†zlDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ %q|Ž¥ˆ˜+bNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDmWC 8XcNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/]G"z€$tw4Q,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿM3 ‰ÂËÿÌÿÌÿ¼÷$twÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿOd ƒ  } ZXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"y˜¼ùÌÿÌÿ ®ê&qjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#t}ËÿÌÿÌÿÌÿÌÿªê@3\[ 8] P9 Qc'k[ÃüÌÿÌÿÌÿÌÿ­ê5FÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽ¥ÌÿÌÿÌÿÌÿÌÿÊÿ/¢utttuv}–²€zzº{zrrut±qwt¥™lkkjhhgf¾ÌÿÌÿÌÿÌÿÌÿÌÿ*daÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÌÿÌÿÌÿÌÿÌÿ µú+00 wS&ut3Pˆs“ÌÿÌÿÌÿÌÿÌÿÈÿ.VMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5D"¢ÜÌÿÌÿÌÿÊÿn³zw- ƒ@ { { <„ $t’¦íÌÿÌÿÌÿÌÿ’Å33ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.P-ƒ«Žè…á h‘ _ya‰9 |  u5ˆ\yg$SVŠÌ¦÷œó„°/Q&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ w&p  –yy7o&   /a9}› bE|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿu~ -Mwh„ ÿÿÿ ÿÿÿ xhvZ(Š{wÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt=YŽ  ˜”Oÿÿÿÿÿÿ Q”¢  ŠKGvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs ‚&‡  ƒ?xv n}G‰  ƒ%‚ uÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs i™ ÿÿÿ;Yh§¡i_4 “l uÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿr@Œ  {  6|„9:~y. |ÿÿÿ„}9uÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsKwp&ÿÿÿÿÿÿÿÿÿÿÿÿ rh€\Xz_s ÿÿÿ)vtDvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ iz ’†z*    &w‚‰ ÿÿÿ rq›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]¥  zr„—    •}f| !¥Nÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿf]z )s\yoi, ÿÿÿ ÿÿÿÿÿÿ  =bmsNd* z_TÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqJ}/€»8 € ÿÿÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿ{ E¶u&|U]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxD{l„b K] O<ÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ   JN ZE`rYsKeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6G%r|‡–$u~'73 F¶~+  }  ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿw|  2tª1 El2G$u}‡–%r|6Gÿÿÿ/Y(š×ÌÿÌÿÌÿ“í3T#w{R€  Z35Tÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ   Z59\ |]wa,a”êÌÿÌÿÌÿš×/Y(‰¬ÌÿÌÿÌÿÌÿÌÿsØk| ‚z  ÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ v€   u )puÔÌÿÌÿÌÿÌÿÌÿ‰¬ÚÌÿÌÿÌÿÌÿÌÿâ } ÿÿÿÿÿÿ srÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ  |kÿÿÿ s ãÌÿÌÿÌÿÌÿÌÿÚ–ÆÌÿÌÿÌÿÌÿÌÿ”Ð  u yyÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ  ÿÿÿÿÿÿ€o ÿÿÿÿÿÿ v ”ÐÌÿÌÿÌÿÌÿÌÿ–Æ(knÂýÌÿÌÿÌÿÄþG³s= y ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ z€  r4yE¸ÄþÌÿÌÿÌÿÂý(knÿÿÿ&oo—Σê•ÔK¯Q Ivtÿÿÿÿÿÿf*.eÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿ   e37_ÿÿÿ €pzTIH¶•Ô£ê—Î&pnÿÿÿÿÿÿÿÿÿ! cW¥sR y ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{y N‡­!Pp *ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Y_ z3qn"R9 JF ÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿ P@ HB)xE| Xd ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Uf d¹` w ÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|  hºj"s `Y ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Fwz >Z,sv7e& ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ 8V5~y9m7 qlP ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿB° |VŸÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿ ÿÿÿ ¨†Y$x¬G ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  'i  }s@ ÿÿÿÿÿÿÿÿÿDz‰v  p ‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/Š q   #}Lti %q~B€ ,m„Ap ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ }$Œv ÿÿÿƒiuO\yj }  ÿÿÿ~‰*mÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿys–ÿÿÿ ÿÿÿ 4b  8zƒŠx4 i ÿÿÿ ›llÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~ €Œ ÿÿÿÿÿÿÿÿÿ …cy`aZ~  ÿÿÿÿÿÿ Ž* lÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿy T=‰ H ”) /¡8ÿÿÿÿÿÿ %ŒNGlÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿy } …,mxO€ „S{g.ƒ ykÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.LMB²3tQ  «‰g! n'   ?` !sŒ¥ j3n*—4"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ==‰¹ÄÿÌÿÁþ‚º++-yw:‚A  |ÿÿÿ ÿÿÿ ÿÿÿ  MuHwi$_V”ÛÄÿÆÿžß(h`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚ŸÌÿÌÿÌÿÌÿÌÿeÀvTqG sÿÿÿ ÿÿÿÿÿÿ $nSoa ‚žéÌÿÌÿÌÿÌÿ ¯ê1X4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸÚÌÿÌÿÌÿÌÿÌÿäKJIGLFG™d;B<Pˆ421184—>3-4l‚52///0V…ÌÿÌÿÌÿÌÿÌÿÌÿ"z€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÕÌÿÌÿÌÿÌÿÌÿ‘ÜFEDBDFLOyGJP›JOHIM[ SWP¨rVTQQSUXI ÌÿÌÿÌÿÌÿÌÿÌÿ!|‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#y’ËÿÌÿÌÿÌÿÊÿ!n• af?b v*|M "3-¦äÌÿÌÿÌÿÌÿ ²í-Y9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ9 }­òÉÿ­ñ!xœ# Op ‡ƒ? %dY›ÚÇÿÊÿ¢â(meÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=30SP>3 Bt Q7Y: ˆ0 7%/XW.\_<<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 8~w€& ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 2…7˜{£vµF¡ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhŸ»üÌÿÌÿÆÿx¶ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,G: ·öÌÿÌÿÌÿÌÿÆÿ(fgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(hwÌÿÌÿÌÿÌÿÌÿÌÿ‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/WTÆÿÌÿÌÿÌÿÌÿÌÿ$t{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿB*‡¼ÌÿÌÿÌÿÌÿ›×5Kÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7E ˜™Ó›Øˆ¨0W'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü?ÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿüÿÿÿÿÿÿü?ÿÿÿÿÿÿýÿÿÿÿÿçÿÿÿÿïÿÿƒÿûÿÿƒÿÿÿÿÿÿÿÿþ7îÿÿÿÿÿ¿ÿÿûÿÿßÿÿÃ÷ÿÿïÃÿÿÿïÿÿ÷ÿÿÿÿßßÿûÿÿÿÿ¿Ÿùýÿÿÿý¿ýþ¿ÿÿþÿþÿÿÿüÿýÿÿÿÿÿÿïÿÿÿÿÿïþþ÷ÿÿïüÿÿ÷ÿÿÿÿÿÿÿÿÿÿÿ=ÿÿýÿÿÿýÿÿÿÿÿÿïïÿÿÿÿ÷÷ƒïÿÿÿÿÿÁÿïÿÿ÷ÿ€ÿÿÿÿÿÿ€ÿÿÿÿïÿ€ÿ÷ÿÿ÷ÿÃïÿÿÿÿ÷Ãÿïÿÿÿÿçÿÿÿÿÿÿÿ¿ÿÿÿ¿ÿÿýÿÿÿÿÿÿÿÿÿÿÿïþÿÿ?÷ÿÿçþÿþÿçÿÿûÿÿþÿßÿÿýÿÿÿ¿ÿÿþÿþÿÿÿý¿ÿþÿÿÿÿ¿Ÿùýÿÿÿÿßßûûÿÿÿïçÿÿç÷ÿÿƒ÷ÿ÷ÿÃÿÿÿÿÿÿÿÿý÷ï¿ÿÿÿwîÿÿÿÿÿÿÿÿÿƒÿûÛÿÃÿÿÿÿÿ÷ÿÿÿÿÿÿÿ¯ÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿü?ÿÿÿapp-2.8/src/images/socnetv.png000066400000000000000000000200411377436340000163720ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞbKGDÿÿÿ ½§“ pHYsõÈS`tIMEá ‚ß0®IDATxÚÝ{ytTU¶þ9ç5¥ªR•JR™!„„ D&EÄ =<šJkC# ØA B ež‘Qž 4*ƒ€È„0„„¤R©¹êÎ÷žßÖå¥iØÖo­wÖªUY÷ÞÊ={Ÿ½¿ýí}öà7 Œq$ HXàÿïÜ€Z@%à"„Ðû¨?†(x6ày@ûÇ1ãFg­æÀÄ¿> J/ÄBYF¸Uº«SáÌs­Ûw÷=&¥œ–ý¯€1޼þ¸EQÀÚ—Ú¿Üþû‹ñM¯—gÇyû|wrOŒ=™ŒÖqð„°®¹Ð„Pô8…€o6Ío™uè_…€Öeu‘?,*nó˜Ý£= (,Ë£+c<ÿ ý§«¸^i¢•f”îlÔ>Œ@€ü°LW@øÁþ{Š¢QQR·ê#(éÞû€ÊÌôJ’Á“ýï§t³b ‚3Ú=ë¾úJÏë ÂM…?Û¯ÃížoL¸&Š"”$ *Šò¤”ð/î@ÞxÞ”ðÇ!Qaii©ù Œen½þRy¶È’!¯OSÉUˆ&þÙêjmrr2‡1VhšŒzÜS†1¾¦cÓÿþú“ðù¦Â;v̲}ûö¤Ù³g_`MÉ¡ë÷ñÕß;ôIyóæŸ>}zvyy¹ã8$TåIX Ëú?.ŽóDû[Õåúo6/L¹U]®WM´¹$IP’$Èó}èС„þýûW‹¢µZ­¼cÇŽ´öíÛ;“’’BA`‚ p[ ,Ë@–eä+ý.qðöƒ÷›ûÍñù?þlý¹XÁ2œØ4 b°™¸, !‹"‚ÞU€ ˆ¦i™¦ieÏž=-rss}úô©ç8Ž0J}}½&22’KHH` ‚B ,Ë)))̉'bív;k2™$­V«¤¦¦^8räHÌ¥K—,C† ©c†ÒÄ`EÝEK³l–€ø!XA†³ºûÃ%B@êãWæ{ïÕ%Åû.XÒÓÓ}ûöm°Z­’ ã8´|ùòÔÁƒß|ýõ×ë#""dQaCCýÙgŸeÌœ9óbBBOQöù|¤Ùl– ±±‘ŠŽŽ¾¸uëÖ¤©S§^eY­VéùçŸ÷¬Zµ*ùСC±cÆŒ©bYY­VÑívSÛ·oO¨ØodÛÜ èþe~v#—:`HõC@†SÚfÇ Ÿ®<}Àõº!ãàù­ @ˆ„àj¯œÚ7Wl?—Â?~wòòò†!Â+Œ(ŠÂÇ‘ªð‡´X,’ª«Õ*J’Ýn·–çy„1†@€ðù|äСCkvìØ7wîÜŒöíÛ»KKK£L&“0|øðê´´4öÐm=¿Ìû¬Së ‡ an¤Ú¦±c~Î}n@ãC`…ãU œ¢(Âo¿Z™´wÝúÌ”Žë´Ñ7Þx£†ã8h0A Ãá ßzë­®III–-[ú‡ R›œœÌ‰¢ƒÁ ¶ŠšÜÜÜÇqÈl6KM-ÀívSZ­VÙ½{wLee¥ñ­·ÞºE’¤BQ¾qã†nݺu-‡¾¡¡A¿uëÖã,˱±±‚,ËeYôéÜ9±z VVTDN›ÿù™(KŒð($Š|¤¢„ gÿáµ»]°üe\ÑÕ‰'¶å8®–a‚  Ë2())É*...}î¹ç¼úÕ«W·…Bdvv¶7//¯ÁívkÒÓÓ‡ÃAÛl6ÑétRV«UE¹\.Òh4Ê‚ À:øöíÛ—À²,Ú³gOüåË—Í6›ûè£Ê£££Å~øÁRPPÐnÖ¬YC¡¡º×í£þúÖË—/oiаHÊŠÈßJ£‚ÀÙÙÙÞ#GŽDuëÖÍSWWG—””dOž<¹,333È„„~Μ9e<Ï£7Æ5ª³^¯ËËË ­[·…B!H„`Yi4…aIJ,qûömí;w &Lx*??ÿú˜1cnR…ÀãñíÛ·DDD\7n\‡©S§^$:t(ºsçή¦Üà·ÐÂG¢("Y–áÿøÇÚ}ûöÅ{½^²¸¸¸í'Ÿ|Rf·Ûy„0›Í’Ùl–E>Ÿ|íµ×v»=´dÉ’sß}÷]ìäÉ“³–/_Þü~? I·ÛM-]º´å´iÓ²Î;gÙ°aÃI‹ÅÂ÷îÝÛ­ ®b†Íf;uêäÿè£.¶«««Óœ;w.jàÀŽÿ„?²`ŒEQŠ,ËŒF£8iÒ¤ö‹-:w/ª{<c hšVNœ8™ššêˆˆÿò—¿Ü2 òñãÇ-³gÏnͲ,!a±XøÔŽ=úMÓØçó‘ÑÑÑÜõë×uqqq|L̯þ,ITó„øøx®¨¨èâ„ r³³³]‚ @unÝ0Æ€çy$Ir»ÝÔíÛ·µ‡CŸ””€@ @(ŠÂ+ŒF£F}´uëÖ”·ß~ûV0$hšV|>yòäÉ(Š¢”Î;7¶k×Î1GaY–P™Þðáë/^œN’$æy)Š!›L&Ù`0(ÑÑÑB\\\°¦¦&¢²²R/Âÿ&E<ÔÔdƒeY„1†·oßÖ®^½:}éÒ¥¥ÅÅÅ­%I‚a „^¯—³8ìp8(ŸÏGÑ4­P…wîÜi/++3 iذaÕÙÙÙ¡¦QàôéÓæyóæ¥ªW¯^ŽÞ½{»dY†.—‹R£„Íf%I‚.—‹äy]»vMo±X„ÂÂÂ+“'Onûâ‹/Öʲ EQDEa„ÐCáaPQ òõ`0HŒ1♸¸¸Ð²eË~æ8]¾|Ù°{÷îø &\S_(Š"ä8ƒAbÆŒYA(z½^êÒ¥Kãïÿûz‚ °Ïç#-‹Ä²,"I« §( à8mÛ¶-îĉÑ~¿Ÿ¶Z­ÜèÑ£«¢££š¦q0$ ƒ,Š"œ9sfë1cÆT¥¤¤p@€?~üSÁ`Zºté9ƒÁ hµZ!ôÀšù(UœÆÆFêĉ¯×«Y»víI„ÐëõJÛ¶mCëÖ­ÓÞ¹sGc³Ù–e Œ1عsgÜ•+W"kkk kÖ¬9•À#„€(Š!t©u:¢‚›ú ùùùµtx½^røðáÝfΜ©ÉÉÉq¿þúëwL&“ ‰ºº: ÇqDDD„ìv»II’ФI“®¼ùæ›]ÿùÏÆ8ÐÂ+¬EEE¯>HxžçÑl§NŠŽ‰‰azôèѨ( ɲ 1Æò™3g,Z­V^·n]‹'NDuîÜÙí÷ûÉ‘#G^‹‹‹ãõz½‚ Š¢°N§S|>©Õj•pRt÷; z½^aYEEEI6›-HÓ´œ••øüóÏS=jÓétâþýûã^xáGjj*CÓ4P±¢¦¦†v»Ýtuuµ6+++!ªð÷KÝÑÄ_·n]bUU•±¨¨è²$Iˆ$I¬£Çã!ËÊÊÌÛ¶mkuìØ±èqãÆ]›:uê•.]ºxÝn·&777)y<RU€ú5Z¨ ~«V`µZ%‚ pçÎ}æ.]ºx.\xaúôé—Ïœ9õý÷ß'Ÿ8qÂæñx¨P(„hÇC*Š?üðÃJÇ£Y¹re Çqˆã8$Ëò}«Kds%,AàêÕ«“yžGï¿ÿ~Ïóˆ$IÅëõR7nÜÐ}ûí·v‚ ðСC«m6Ÿ””ÄX,QQøõ×_ÇvêÔ©1 !«Õª¨+ÐTh„Ð]aU0ôù|¤¢(@u ³Ù,‘$©ää丿ýö[[^^žBÒÒÒ¿¤¤¤0sæÌiÅqÙ³gÏú§Ÿ~ÚËq)=zô5kÖ$Ï;7mìØ±×1Æ@«Õ*÷ºú—š}ÐG†B! …ÐÂ… SBxÔ¨Q7Ýn7éóùÈŠŠ Ë²eËR+++# Ë'MšT‘‘‘Á>¼fïÞ½‰” °´´Ô:tèÐ:ÕäTÁÕV)¬(ŠeYÔÔ T¥X, !¢¢¢¤W^yÅqäÈ‘XAÛí&Ïž=õÒK/9{ôèá]ºté/«V­*e†œ1cFVYYYT]]F–eøç?ÿùvLL WRRÒ: à "šZ ;gŒoË|µ+ƒhpx«‰½èO~ñ¿*‡Z»sçNûùóç-!Ü¢E ÿŒ3.ët:EuÕjìv;SWW§EÙl6^B·¦FY]uŠ¢p8Æ“!¬( 4R("$I‚f³Y²ÛíBLL wîÜ9“(Š(>>žQ¤&[ƒ®ëÛ·oÃŒ3Ú|ñÅ) Ù™™¾?üáwöîÝ«Ì> ×ÓΆÛ¯“c¢B†Á¯U úhnÜ1sü‰˜Éó»è%|!8‚/ûv¬i]êzôèáìÝ»·K«Õ*mKJJ.FY£ÑÜU£$I°®®Žž2eJ¶¢(pΜ9´Z­b4å¦&®ÒZÕ<©Òçæ¢‘ –UUUºùóçg‚€æÎ{!22R $MÓŠF£QxžGn·›š5kV«üãeA€ï¾ûκgÏžtãtܰÿ$jåÿ¡ qôq'I~÷Þ´¦Â€VÆài‡3âÅ5—ët:…¦i% $IbF£¨±\5%„Ðjµ EQ²j4M㦀§*A­4<`éõzÅãñ$IbEQ šWð<ŒF£$²X,‚V«UT.¡ºÇqˆeYµ‚ !„øùçŸ÷tëÖÍûϾ¹iM…½„!¿{o ¼¾û– @#Ë2 sl$â8ŽPbŒï®šê¢(4M+ªß«,LÜl6K*—oŠ n·›$ ù^¥ø|>’¢(…çyV0&IR‘eªÖ¥Z‘F£QÔ2»Óé¤"##¥pñ!Ÿÿþer¯OC*­RÝ÷+yUé4ÒùùóÓòóóo©V€¡Pq@¯×+¢(Bƒ„ßï§5¬úf  ¬VëÝÕ—eFDDȪըá®9óW•Æó©wïÞŽV­Z>lkÑ¢c³Ù$ŽãMÓ˜$I,¤( K’UräñxH•±,‹B@È0 Á0 R³ÀM›6%vïÞÝ•žž8pà@lÇŽ}4M+‚ —ËEùý~’aâäÉ“¶ëׯëvïÞÐÐРùë_ÿz³†$8Gyil¼_¸ö¯fÇyŸ^°òG²u‡^Ûá³»Λ’ ïÔëql4“7rbùÔâY™–„ÖiÓ¦]…BÄÁƒ£.\¸]TT”•ŸŸ3;;;¨( Pý®²²ÒlY°`AFiiiìÔ©SÏ©[b}úôqΚ5+³gÏž.žç„PiJ>ÔdHUÃ0Èb±HjhUèõz¥®®Ž¦( Ÿ={6jêÔ©—yžGA`»ÝÎ-Z´¨íÙ³gë† v«S§N~Qa8A"%IB[¶l‰gY–xðÍ&Cjâ!}ûöuîÚµ+®¬¬Ìè¾}û6P…/$&&†B¡dY–عsg©S§¬¹¹¹Þk×®L&“!ûý~Âl6«¨MY, BÔH§Ó)Š¢ÇC†B!Bx×µ~üñG ˲(555´dÉ’´C‡E›Íf1...øê«¯ÞÉÍÍ @¡ ( 'Nœˆºvíš^«Õ*|ðÁõ‡U†X ÿ;¶jåÊ•)—ËESÅkµZEE¸qãÆÓ¦M»l³ÙD¸~ýºvñâÅéG3wîܬٳgŸŒŒC¡Á²,²X,’(Š(!D·ÛMšL&YTY–A]]†¦i%\TM‰‰a7oÞœôî»ïVÅÆÆ $Iâòòrý‚ Zåää\ ƒ„ H¥ëO=õ”gäȑե,®òñ÷Þ{ïæñãÇãׯ_Ÿø<Åó<¢( oݺ5aÇŽ‰N§“=zôõÒÒÒÈ‚‚‚Ф¤$Îçó‘:N¡( CAcc#e·ÛÙuëÖµhݺµñâÅi;vô0 ƒúõë×@’$¾_þÿW…M&“ !ýû÷o0RqqqÃ0dzz:ãr¹¨˜˜!Ìܰšèt:–eY"22R=zôu³Ù,9rÄR\\œÅq1hРjŒ1üæ›oâ)ŠR~÷»ßÝ5jÔ­0»ÃÇ‘III\”±Ê$5b4åôôt&Ћ-Êxùå—k»víê=sæLÔÛÀY–a¿~ýccc™`0H©ây™Íf)""BV›ü~?ñâ‹/Ö}þùçÉ$IâÆÆFªS§Nþ?þøJBBBhÁ‚ÙË–-ËŒŽŽæÞy窜œœË²ˆeY´víÚäÞV»Côz½Ì²,"»Ýn²±±‘ ƒD  - ß«W/÷ÒOô›w† ƒ,Ë2¨¯¯×^œ8qb»ÂÂÂ2»Ý.ø|>2**Jt»Ý¤Ñh”µZ-~î¹çÜ»wïN”$©æÔ©S‘GAá1cÆTMŸ>ýªßï'œN'½bÅŠ–>ŸîÖ­›óÙgŸu]¹rÅ2zôè›jEØh4ÊjB8…†ÅÅÅY~øaÙ²eËÒ›«ø|hŸœŠÓ]k¸¾+{GM±"¡CAxÈ!·;tèpCõÓÉ“'gsGtêÔɮ쪻?ØétÒÑÑÑÃ0ˆ$I¬î"aŒA¸¨ŠTü$I!?~ܺgÏžÄâââKV«UTVUUé.\˜Îºk"úþ>½£#¤=°çtÊi×ÚÏïÕhtBF7ÄtmÿLËåÛsî½î0Jäþ½_·jÝÑß”uù|>R¯×ËEEEY­Zµò4¨.ÌÄ[·né6nܘ2qâÄr’$q0$"""ä0¸Á¥K—¦æååÕ¥¦¦2jûÝÑ£G£~ú駘âââKjŽîƒcxê³IOµ[ûuÖ½ó“ÞM‹¿í÷‡wÔ'ô ~=fÒ¬pCƒþ~×£­š?;‡ŠÉt«Ý^jçÆ‚€:¿eË–Œ¬¬¬FšþµIþÔ©Sq‚ !Ðjµ2Ȳ !„øôéÓöp·A„réÒ%›^¯³²²Ü+V¬h©*@U8„k¯UYÚÝÜxgÃÃÚï+IÀÅ¢FšƒþíhIMœ‘+Z´ú¤=&™k4eY†Ÿ~úi*„¼ýöÛ·B¡ñóÏ?ß¹té’iĈ·cbb¯×K’$‰¿øâ‹„^½z9rssýòæÍ›,‹0eÊ”ŠñúïV%öd¡]'*ÿ2ÅÚ8#—óÒÚ‡(à" 0:ßÜyzñüó™õMgàÓS2ñ§‹ñöBÜïö{åƒ>¨2â‚ R ƒÜ­[7OEE…Y§ÓÉ.—‹ÒétŠ,Ëð—_~±öìÙÓ!þùçÉÇS§N-'I7÷„è7rÒµªa/•ûtä]_¿c7rpܨҖé9¡‡´Êza“féqÍB% ö–¼ßUTFƒAŒê÷òíÞÿõέGí—enÚ´)þâÅ‹‘ÿûß+7nܘ˽òÊ+ Š¢ÀcÇŽY***Œo¼ñFõâÅ‹Sív;®.=r8ûaçêDçOÇbV'g¼ú»êœN½=ùÉ|alâ»ï€Ç|>è^%ìÞ½;úàÁƒö>ø âã?Ι9sæ˜>}z›)S¦\™7o^ZÛ¶m}ùùùµOàœÀ¿5Jßíor`¢èqŸ¸W GµlÚ´©…Vpk2·VËqd90”ÒÁÑ·Ï‹u¯½öZÃ^©&à}ŽÌä?±7«î0åÝv‘‹Wç&%0à§ŽIî¡ûOÿ3*Ê.€';6@Ý7ߨû¤ÞŒ¢Ä#Í•© ¯®B·s·­ßMy¯Ã~oSáï› Aw=I%œ=º':é†Óxß›U7ÌOXø]” †Üö—Ç:2Úw÷4Zt÷7s‹…{B>¿á~Â?0›Jу8Â2âb“ùЀª”{˜Umœ‘ÍùÞåÇöaÀ;Ö¬œX x¬‡§‘G;>üK.8öS"5 %Ñ›öÞß/tyyhÝcüxú>Šø?w|þÿ!fê¥6š;àIEND®B`‚app-2.8/src/images/socnetv.xpm000066400000000000000000000036251377436340000164230ustar00rootroot00000000000000/* 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+ "}; app-2.8/src/images/spider.png000066400000000000000000000037541377436340000162130ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞbKGDÿÿÿ ½§“ pHYs  šœtIMEà.-–©I tEXtCommentCreated with GIMPWTIDATxÚíš{lSeÆíÚ®e`'c &Ó·à„)4ŠAEÅ»‚4Æ?¼ÆKŒŠñâ=D‰ÁAD4!1Š‘(A@ÍçÆ¬c”±…­Ç?xÆéÖvm£oÒtíÎ÷ï}¾÷}Þç{O!miK[ÚÒ–¶´¥íÌ[à8W?¨òS}cg7pÞ „©¾yFç:ÈÄ8®?ð"°è Tœ­Ð$®‰#ü~ŠÏöhnž‹òú^ÀEÀaè;ۉР”9Q\_ìîÊ·zB%Ȧ0¼ò+ºn40½›s—£`$ð=°FŸ#‘fp‡ˆpˆÆu€ŒîBž3€ë"\s¡þ_x€\/—e/;€ g—Âz9°E¶ÿKÀHKÞÇšÿ`ðP ¼Ln~?“B¨UaP Ì´ KÑ;–÷ÎÖìQµù¨ª€Û€%úÜ ¼ |oå9À4`à3 F¯jŽv;wÐ ŽA1Ž>ö¬ùÀEQ_ã†S ïw6©åJžæ«N*¯½ªÕ(¼?Š‚ ŠÕ@?`Ÿ¾÷k?Š€]lÆÝÀµÀd­g/°X ì׎7ªšXÇ ¸òóH»dgIÚ: ZáêÑĆBüz`3pò5¨×jààe}ï“^¨BÆæh‡ï¶¿)ä7-Ž·ÙŒmÓ˜V°)¥­·5Hw¹vfð™$­KëW+‚PD­VEçîØ+]Æ©JdÇH–Ò --¤¸X¨ÅΖsí;fI¤ïŠ”‹€>6$W*y<_yŸÛA´F{àÊIVÉL—“†þ6Í \¦ï?x%ú<ÇÆ±À¿Àví\v‚ÖÙ7™u¿Ÿrúi‘‘!6wt„Ta½ðR]óˆ ù.³ŒOꢥƈ4—©þÎŽ)ǧ ˆ0WLž¥±;,óôV¾O±nÉukÍ¿H»õƒÊ¢¡}¯r|ŸÃáØïñx ŸÏgx½^Ãív–T™j©F7é»Dv)9Æ{“!’¸&KTr ”÷•~¿Lyy9.— —Ë…Ûí&33Ã0X¾|9Á`à)`­êôFíþfàhwÀ|ܨ~Gu¾V’vHkžßïZXXˆÛíŽøª¬¬¤¦¦Æœ;¬nÒFE)yZgK²-ÑrÅÛÀóúü„ämðŸt?ÚµYÀP¯×K0<Åa—Ë…Çã9ñ¹¤¤Ä ÀtÍe¦Ö ›nÓÕÀ_Qž™À·ÒÕ F!Û¤¿w*ô­¼pÀëõžæ°ó Tï'©þ›kZ¬—!¢ŒÕÿ*Ôv[™,œ6óI9½W5ºÁfÜ\•“9'nF  ¥å”ÈÞ <(TQꯘé`HŠÏTš4 ¬G@?›éÔ¯ºI­EcÛÙN5:pènFFFn·›P(D[[À!à •DÓñFå|HÎfªLöV#Õ«w“'ògumQ" ¬“P IÀTi×òCƒó•Aöw¼ŽƒÒkÅ­îãÔ´9Ò~ÑG‘R&ßUìe9 îÖqµ­“¹²Æ ñíÐ$›õÃÆÖ?èˆìÇØYX²ÛröÏ–bìTjž¨pv°›{´3ÕQ8é…#cÄdÅŠÅ ìÇ̳k€Çø­Q®=,ލþÑ&5+egw¯PÅPr2ú@}¾œ-Q*äj‹ü§çÛ7*{Ãå@£QŸZ¤r,-¸j­Ù¯~ £+xDx±¥p¶rx˜bÒc$ÿVk+ ü"ŠØ¥ˆ«^ˆ£¢…Bƒ"Éè*v$Þ™57Á’õZÅ6åP£Þ«N^·TBªxO‡¦f°xxŬ&1ZHóFý[W¦(sß´& b%¬5`üXµ`ªTCëW•OTîÌ&éé‹cÚ±õ*$r ã¡Î’qq+Æi:©åÅÑ™ ,ðŸçÁ:|©r:¸BN9$bÂʇ%"Ë€[´ø‡â\¿O•,n0Bά’P‰¥¥nòŸ£](<j[´Û%"CkGh´š¨ýH¼ÇõcñFÀ0‹îŽç¹Bp±œ»@ Îjæc±…º>£1[:h¶&ÕJµðdÁËÀøDœ-~¸™ãÎSÞ;ªCO²ŽàÀׇv•&¨û7ý“£„àPKº5I÷ô«ÔÍíä5Yœg"ê’þt˜9ÜIÓÓ|ªTEŸùÛ`$ðÉjª¢¸þ€È¤‡X_ÉÞ±œþ›Hé21ÚšžÈH–•ï*¿£éú6sêïÎjsê8.ÆÓ^.'É'¥$¥ÊŠ”Óûˆ­Åè)ùÎþî–©° ß>bÿ-qÒ– Ë·Ôô–s-2$|·º—¥‚½:R×ô”r–¶´¥-m=ÆþÓpиßRƒIEND®B`‚app-2.8/src/images/star.png000066400000000000000000000012761377436340000156730ustar00rootroot00000000000000‰PNG  IHDR5âÞîbKGDÿÿÿ ½§“ pHYs  šœtIMEà #²sÊtEXtCommentCreated with GIMPW&IDATHÇÅ•½NA…¿cY¶°EáE¢+ÇHQ¥Â¢ÁMSa r›ÊJ“2ïà¥ÈP"EÇøÛë½if­e=‹W)’•ŽöG3»ßνçŒòŸ»]øp|¬òŒWy_Üh4àöö6׋užA—ççr2™ÐšNé´ÛùHDd£Œ1" bŒ‘'GG/‹gŒ‘äI)yTJ&–<´”1Q”ø³Qí‹âµšM®}Ÿ…R ´F%H¬èT⬳hÓíSß‹òKk™¥hÓzTJn g¾h·V³É§z«j•í(ÚèªïÖi]1ÆÈE­&²°Ý!¯¨S«9Mã½Fä[üÝ¡]™ð½P ²‹‹•l­äõç¹3Ä• ÁÎŽ>–ƒƒ9<<ÄØÖ"ðr¹4¥*üƬÆ:/ÅëÒ(îÇ=}<èÃÀdЗïG?erŸ“Ì6å‹´}7ëJ¥"'''Ò¾¹1pü•á=Àÿ(0´Ô> °&îlGkÁ^^ ¼ƒÒ ŽyMGª’¿÷R-—åÛ‚|>*Ë׺È\r6`Jp*áÁkzäOOO P„†–GÆp·# “Ì`?_eðlU¶§¶x§ÔRû×¾•W]]a‚:Õ*­KÕ™ŽÙm;¨´U@A@”EEA­JÛÚªE±J‹,EÙDˆ¢ =d!$yYòòbHP 1Ë[^ÞËN–·=çÎ;Ìí3Xf|„þÑ;óÍyIÞï~ß=÷,ç^¾>ƒÄÜ"¬J.ÆêÊNl­ `ó9ÖŸõ„Q€Ðx®™¼àPVM+À‰ ü½¼„ƒ&KÄ}>¸Zi›ÍÆËߥ>—-!b)²ò@ðy`2™°?§+’Kð“þØrÞ+ÀÆš–-„?Ftâ qR\¬0Ìf³"¸1`^>¡@À"èÄY$Þü}LP‰# lËð À±œ«2ÞÛ¼Úlº‚±™½5¯¬úŽ/åÂÓéD“á~Oˆû‰´@„àai¨Çg©xó°ËN°ªÊ‹N{°¶Ú‹ughoãÅaK-°´ÀŠÈ4L•beb©áÀb±@êrE>X«³ãêѼµ¬ @8…,Å€çR¯²ŽK’çQ{þÖ&žÄ|XA{¸Üå&7V~åÆªJVWy°†E8 ,N2!ö„v‹€_Bä•€ÓU£ÑˆââbÞüdå%¦‹A+`!€ê_ý<Ü.õ3&«CM¾£­ëã³°0Í‚÷KÜø Ø…%¥.ü³Ì¥å.,#¬¬ôb©©6 å$97 ¾à»4ò–E€ðû~òê3yÙóÍSË„øwìÓàÁð§;;ÇNœÂËqXTèÁ¢ü^,.ìÅ{E½$‘-!Œn¼“߆e‡N¡¨¬ä}@peÐó|öÞzR£C<=o€Ê{îP„Eµr=]X²; ¯¸€¿ätá­“Ýx;¯‹ zðNa“oŸº€œäºA'~õ`ǧ;=!.ä9bëð²wßÍhI›s£r°0˅׳ºðç,!· oæE0x±0Ö€¼¢R@û[yÏU€WžÍ[©>u1øßLü«»îRO!ÀNkÎN¤uãÕÔN¼–q¯g*ðYÂké­øxüî^É}Z› _ćŸººé;^}þL7ÝqÄz:Û0{k6^JêÄüä¼’Ò?¤uba:‰@[anB ROê«.Ä/×ü% \Y$meè`Ëà°(Ÿ”ß|³zòøÓ–/ñ±.¼øe^:ÖŽùI$B²ÏÇCÅr¿_9H"ðå®þ•€ã>G‚‹¡†_¨½X/u~ÆoC©9˜y s[ðüáVüžD˜{´óŽ·cÎFÚïeÿI^ß÷Œ¾V_Ÿƒ.#ܸxÿóꇚÒùÑó‚€²ë®'Ödc ðl<‰Ð %ÂÂñ.DƧèf/U ?Õ#0+Ê€'÷80=Ö™ûœxj_fì®Å”]_#6ñœ·Âùü‘¿³¯U—ïâôtÁGÂÉJ3Bè·º²ÒZ‡†-„!ÿ[¨0ï²`òî&LýÂi{ìxrw=¦FŸÃèe)èéh…Þ½¸ºZ#D7qiÂT›;pÚÜŽÓír¾ ¼Hæ^'¨à±†tpÙ D$ ‚¹Ï'>³cÒn&G7`Jt=¦ïµc~d&È–éÓû4â;Å$²±Ú øÜ(¯kÕÏ ôsHvÚáõü…¬¬LF Ç_¶ñ 0<Æ…‰;mxœ,aâŽZ²+¦Å4ã­èl@OqµlRš¡ò./™»ó «=”iÚÛ»ÉÚ`iîS"…Î`•*'a@Ÿ˜ÈJqÏ_Ìô‰|aèç=»£ã¶ÕcBT&}fÔ/œxuÛ  Ô™ª³A^Uø=È8U[R±dû!¼»' »RÐÛÕÉÄõ“b94Õ/K„W9Ñ‘Láh&Ê?÷x…È¿CÕŠG©W5šwã£,x<ÚN"Ø1fy&ðÁsÑ™ºTw×I+̽w[+ö%`Ì*¦ÇQÒ”z R:± £ 6ÄãB{›"­Zƒ¤Ù¡ò Ÿ6…Õ°rD¦›g($áQM ÙÙ‰G77bÌ63‹²bü'6L$Æî´"#'_™7 ÐÐÜ9µâªF´Pãåè±TLßg¥|2Ç£­˜K˜w¼/^IïÆ†½GÔvS„ ”†3i>mbp;^ôtXk’^2-õ8M ŽlÁˆÍfŒÚbÁèmV<¶Cá_9pÚ-pÓêWZ:Ÿ ¥5eúO­KƳ -x.±ÏjÆ GZðâÑÌ=Ö¢„˜µ1­-ܬQ°ÚlÜlåR\õ*ëëëE€ ×ÿ`ôƒ¶]A¨l,/Âð xd“n%"-J„q,§ŒY‘…œ¼¸<^œ±w£ÖÚŒ¨˜Ã˜ºÇŒg8ñÌA'eNÂIB8I'^<ÒŒ©ŸqöL¥æ#7&ÍÛ‡[ô| ùu‡_®ûÁy€^fÊVÐÏ ô£+ œøÛžSºÞªDÁ"%Œ"ÆE˜@bôöFŒÿ0³×ǸåɘL‰ÓŒØ&Ê›ðô~fío"!(sdÄ7‘LÚ^†*S™æ“f>‡ä ·îÔ–HHHˆ&w® ë¹€4>ø%¡?¡|ŠIs[3îy×€¡ë0|½²ŒÜ´„íVŒ%Ç8.ÊŒ ;(Tš)Lr”°SÒDØÛDBØ13ÆŠ1fz6੘z<×€i+r#• óœêY ¤§§ó [€‰æáÇL&,È“!Í}y‚ðX•ˆ!k¬º¶ÃX„¹ÙL‘ÁŒ1‘ŒzŒ£P9q—•¢„•' AÙ#ás ¦ì®#˩Ĉõ¥ÚRkó±lõdfd ƒÍ‡°L\…À‚‚jšûPÂO Ã&@¨â¤T–Ÿ‰¥$€€ßëÆô÷öâ·k­x((ÂÃÍ´%0rã·µ•¢Äv3Æq” è „øÔŠIŸZ ´}*ñßG%›•"Kjj-Í{á1ý°  CEœ£8H7 0p Ür¶ØÕŽ'ïÅÕ(>&6(k Õmä-A¡Ò‚‹[b§vÑ3ê[ û¨ÐZkâk¤FáÜ¡ž|A^n®•æ<–p›¿¢èŸëŸ±ÝJ®¿]Úîç¿·)÷þÝ„×ZH^]‚·Ä3Y‚™B¥™­²G¶Ê ·×`Èrèä¥Båj’ÉŠ 'Í÷qÂí:ù~±9ÖÖ/B¹”¤žzÉðûQn,ÇÌ%qïûxh½¢„™,œã&¢‘¢„‚Â¥œæyÜ÷ò&¸y”;h®“#""î¤çµB |pÅ%—#„L_ЮËx˜n½U=åúŒ~¡ ª²+vÅŒ¥‡ñÀ›_bð¢¸q6~³( ->ˆ×WÇ⋘8äåd@Ÿä¥¥všç$|¸èûúÛ÷ˆ¸Á£þ¶A¥C¬DZl~¯WZ›,°5Ô¢ÙnA{k³º;x¦ºJy{¡äMF£æ8‘Èßnò—¾"#Öp K`òêpt>á¯Uê |·eÅ:^*:a\Èpjk!ò|•Ž/IgPlçÑ¡‘//+k`‡Gäoòý#@_·@é© $ígô)Cr©‘ ®æ˜ Ÿ@sIÌWê*éHþDf&xp±ÓPW‡²’’s4·Q„Ÿ‰Ãë_¡[ã2¿—.ŽÜ g8»ä¢†/IŸ§ô¶¢¼yyy*0yCaáiš×#„[…üÕ O«Ð·€Â¼¾Ð-@:8\Ör)ËN=‘åܾ¤´TÝ j!ar³³KƒÞ-’áõ‹Ý’å]¦€Kƒ›>ëùÎ}>ýJ«º ®í³¶ÿs²³UÝ‘tüxÍçw„›…| Àà»L!¾»èÿ7@7}ÿ³ùËÊÔ…Œ´´´tšË=·ï/†Aò@BÇeŠ äå{äÿ‰÷ó¯ã—êyþÞû÷ÇÑ<î'ü¤?Éóøá—¸:ƒ-¡nÁ‚‹x„›„~×Üpà wÕK˜ÐO q÷SŒÿ9·³®y× ƒØû71q sÿ+#¢_пãÿãßèι ìî¥IEND®B`‚app-2.8/src/images/sw.png000066400000000000000000000102531377436340000153460ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÙMM ‰…é pHYs  šœtIMEÙ Žu+IDATX  ßïÿÿÿÿÿÿÿÿÿýüûÿÿÿþþþÿNbNFiOüûüœðìðmÁ±ÁûýýAQA §Küüüµöòö»ª»ºúüiOX8ý®«s· ùý…òûOÖýKK ÈóüÿOjyjjyj Ü”óùóúøüùýüþ ÖýƒjyjjyjF f2í"†Ã;Öíÿ·ÎèþþA:6) %Ûîÿøúýüÿÿÿÿ}<Q'z°2mÕóïóκ©º~ÝéJ]J ? úè*é˪‘Ëü•ÒêþßïùýþN%r m-üåùøîãñãêÕ(Þìõ òøïîêî£øöøÀ õíæ%÷ãú'Vx9 a ÿÿÿ²TPšÓéü¢^µñ©û/'E†óûû¢™ãã~ùý  D ^3YôÁãû|w¿ó²ñõûJõûîøþÇ…Èò¬ôøõÑððØ2~qDöþþA™ççåùøöð Òùœ ! 8@ó¼øùüûü8óøÕêÿÆÙíûïòëèë´í툹 ÿ ¢t?þú÷ïëèÓêúßâð½õù/õû"òùÅäúÓùõóô÷ûoÀÀK© –t§tüû÷ôóÔìýÆóùúýðøðø¿ãóæþýýÿoÊÊ})&&£ÿð/œÿÿüú³ÝñÚòùòùAQA?Ì ï H"*º÷õ÷Áïêï¢Ç¹Ç³ý[³³ˆÉ^^ ÆÙÙ<ðññpêÜøø”´ííœé?#ï÷ÿ_ï ?þ¼õþü³PP$4**߆†ý7¢¢|š@@dêêý®ã Õ)ÿ%9É ðöú0<9ÐÄйÿÿÿ ShSõjyj–‡–5ü8äðé ð  öüüó ²òñååµ ÆÜÜã%"¹ééýññ²ñööÿî  ÞöóöÆ·¥·oæïþõöÿ ûýýòùùðñöññYùõþþ€0--û]ãýóôññu¬ÿó¦÷ jyg–‡– :þüûü¶J–‡–Äúúæ~º»dl&%ýýýÝ&ùÐççîååêl@@NX$ö÷ýý÷ûûeþàîîqý÷þÿÿÿÿDUDL hEøæøùßøöéö¾òûò° 6 ôUIüûü¶òïò€tÿïé øÿàæíûÁúÚîõòõȸ¦¸kæïÿÿÿ  " *^Rò;øøõùÂܱä\ å þˆP#ÇÝK$þm /ôñôÉêäêªÏÃÏ´ìóÿÿÿ  & 8Q I1Vjyj–‡–ߨ÷.Óûÿøóüì ÚóÉ1 ùõðïôùüÿýùõÚõÈ¡º#?÷ûþ üÿùÿùþôëúÚôýë÷ã÷÷÷öûþÿÿÿ J]J• ?óïóκ©ºfÕýüÿ üøúüþýÿÿÿÿýùþûüðìð ÿ @ôñôÉçáçµ ÷ùóúûþÿÿÿÿÿ úö÷ùúüüýþþÿÿÿÿþúûüþþÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿµJô‚ÒÉœPIEND®B`‚app-2.8/src/images/symmetrize.png000066400000000000000000000015041377436340000171240ustar00rootroot00000000000000‰PNG  IHDR @ÈsBIT|dˆ pHYsþþO¢þtEXtSoftwarewww.inkscape.org›î<ÁIDATH‰µ—ßKSaÇ?è¢(6¡åÏˆÊØÛnR$ˆ _×A%TWþB#F»Pú'$ZXD¥ b1P#Fê&è4›è‰ŧ 7йәgÇÎá}¾çýœ÷<Ïûã(á$¬µU=v:é±Û©ØÝegi‰_±ÅE™@DLu Äçc4fS„?=$évÓ/"æƒ;:x¾°ÀV.4냃$ñ™=ZKo/ß´ YïîfÂbrj›:;)×UWc7¬ š þ19IJO´²Âº©`Ù‹DxŸH°§¥!=?Ï‹“˜NV¯—±h”tnQ…B$].ü"‚2cQJùAY̶µµ©¾úz®——ãØÞfgu•ÔÜDBÞÅ•R-ÀP"" ’<kÐjà)p ¸¯¥Ë- l¯”ˆ|ÎóbgD䝿Ó ÈÜÊܟΉµ â¿}Ù€»dêã@û) ü‚ºýßJó´_naÀn °e®7³š«@3p­> šNJ©ðø*"yâÍ™—›Öí,cºU­”r÷€2`ØÈ‰_΋ȗB¡€þ§>Ö<±2Àghfè@+gyÚ›s@·¨&˜œ©’³ãÀ £ÐCÅUW§ºššè«¬Äa³Qº¶F*g|fF99}¼Ì¤à‰ˆ,+·sìrá…HJÎ6’öz¬Ù/LŠ­ˆ@C]ÃÃG¡YÇÙjoçU|p èéaB šõGøXÌfÝRU…]/àŠ¡\jØqÎ\¦þëX–—Y×MM±Æþªe8‘À?4DZK³‰ðFD4OކLDp»éVv8̦ÏÇ(ûg*Ó ëÐR[«app-2.8/src/images/symmetry-edge.png000066400000000000000000000002051377436340000175040ustar00rootroot00000000000000‰PNG  IHDR szzôLIDATX…íÓA DQïéiŸ¨›ÔÿA›Ìž6{k @Ø_UÁÃn†²Ç¦Š.§âÝ4Hç¬þ‚» ;@YÔð—à‡yN¼“UIEND®B`‚app-2.8/src/images/symmetry.png000066400000000000000000000103201377436340000166010ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEØ +]F† tEXtCommentCreated with GIMPW+IDATX  ßïÿÿÿÿ\ÙùÙZÿÿÿÿÿÿÿIÿ’ ’þFÿÿÿÿÿÿÿÿºÿ&'ÿ¸ÿÿÿÿÿÿÿÿîÿ ÿìÿÿÿÿÿÿÿÿýÿÿüÿÿÿÿÿññÌ ËÿÿÿJÿ“ “þGÿÿÿÿÿÿÿÿ]ÚúÚ[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\ÙùÙZÿÿÿÿÿIÿ’ ’þFÿÿÿÿÿÿÿÿºÿ&'ÿ¸ÿÿÿÿÿÿÿÿîÿ ÿìÿÿÿÿÿÿÿÿýÿÿüÿÿÿññÌ ËÿÿÿÿÿJÿ“ “þGÿÿÿÿÿÿÿÿÿÿÿÿ]ÚúÚ[ÿÿÿÿÿÿÓÓÿ @¶µÕeëÜIEND®B`‚app-2.8/src/images/system_update_alt_48px.svg000066400000000000000000000004021377436340000213340ustar00rootroot00000000000000app-2.8/src/images/text_edit_48px.svg000066400000000000000000000002711377436340000176030ustar00rootroot00000000000000app-2.8/src/images/texteditor.png000066400000000000000000000036771377436340000171240ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEß".ÓÀt`&iTXtCommentCreated with GIMP on a Mac•ä_[IDATXÃÕ—kl׆Ÿ3;³;;»Þµ½¾`c›««R¡AQTZ¥”´EpQK¡´¡ Q HXŠŠ¢ª€š% ª!¤D•¢4TéEJ $)L(JHÓ’–IÁ±½c{í5f½×™ùú#^²667åOöhΞÕÎûÌûï›sW7Åçßäøw¯Îô'·•MŸÞôZGÇ÷×͛җ-Ë »÷Þ…ÖÞ½{œÏB{zwßÕ±xW¼Ç&uðPÖ¬­õÆgÍš>5™<9\¨FD¢9Û$PƒŸüP  ”PjDëÄÍ‘Mv’~{*á2‹7ïKE÷EZÊË1NŸæm(þŸZÇqÚººc(¥ :ŸóyÑBñ‚±ˆcÛäþQCEåXÖnuØó/[úâLŠÅ8 æB™Ìß´«â?D”!âù‰!â¿)@Sƒþx‚¤ŽN¦¤¬–/ûØ÷ŽÏqXë!>Ó# 9§ë%úU+P¦}zC­àióO¯†CÎ †Æc„èiOyYGN†xü1,Sçì¹66ß/î¸9nÚ³Gwæ]ÌÑGJ‚Ñ‡Ì V€( Ãë§óðT”—Ð ³ò—Q,K'ÖÝÍææX|‡rŠü>kÞ®tÝø²k8€ä ņCP8_¤ë>bÇV3¦4K2WżuÍ,™ôõcúxô/º®Œ%¿ÈÝ>e¼&µºY}¤¼¹†€hŠKÿ}†2ïûh¾*f¯Šâ3\;uÝÛ ,¿FÃvY×—ÞÚÜ„öè… Ã]ؤcï¼¼_¸Ž».‘Í$PÑö(ÿ~ÎÀð¸\ó:¸úC`ÈÎM ‰yþ»ä°3½xZ×cE&²öI“––¢íÝüs‡`x ú‹–âø¿ ìY@´k†`44mPÜAPä>\†¿¤žûêÙàŽ+\ìàµÇ„B&n`&çõŸbš¾<@p® À0m°«|ºÉ÷b•Nåõ33Ù²m'JóП°Ù±¶‹ 5V¨Š“îf‚~•HBÈèƒù¯â„RšáãÒ±EÊnãlßm<Ððk¼^ÇÕXsw³¿â'RlòfâI‚¦‹iš„B¡<€äµF] ¯|…Nè}ïý„på4zS¥Ì_шÏçCÓ_ÿb'ky( ÀÞÎí—€é÷‡ ä­¿&À…6Ü ÍàÒ©ÇGêÈ:>æ,}C×ðhÂØâKl[›%è‡ÝÑ_ax½~B¡‘HdÄ×ýðHay€K2ú"E暦sçŠ]8vM¹˜F–6^"à^m{e”`ø„Ãaªªª°,käwõ gØäúþƒw  Ã_ÌüU{é‹÷!â⸰os'~ÓÑóߣϞˆnXƒAª««)**BÓ´¸ÿüXlœ\?nóC˜eÓøÑƒ/ÓÜÃq\RÅ_7·ã÷›œêý*gâ·ãó™©­­%‰àñxFß­Œ–ŸÙ/8®‡Ì»³Ö.`ÓÃÏpôx7Ž# d4vmè 4ä'nײÿì=,/–e1fÌÊËËQJá8ŽãܼùØÇ;Ï‘)ZÀoŸz‚ßïK#©¬ÆoÖt1¹ÆÀcyö„ ŸÏGEEÕÕÕˆ©TŠd2I*•b$„‘ÓPäJê^/Éø'ˆ6‹ó¹n¼ú»¸ ã|k†èüü•û((¼^/‘H„ššD„ÞÞ8ÙlA¡Ä†Õ`º~æ×@&9@²¿7ÛËÂoøqA.÷uѰ8×ÒXÿÂrŠ‹u|>“ÒÒRêêêp]¡ý‰©T;—£nÂ5oÖŒo?ÿÇ?÷üpÅr®_ˆD¡»í¸ý ô·ìoaö “HÉ—ð†ÞàñWV1®n•UµTVVR^^Ž®ëD£QZZ[)))fÒĉ„‹C444,ußþ76n|ؾ¾ƒ^¯ÃoâËãºÈ¦ºñ^³‚öäD>>³†ù‹&PV^y%ljíí477³rå}´¶µ°ukãú—ví~Q)å\o_#"’N§%“ɈˆH}ýäÇ˾&‡ŸŸ/Çn—3§OËÀ@R²ÙŒ¸®+®ëJ6›•x<.§N–šä¥Ý‘h´M6mÚðĵGuÀ¶m^?ÔDoo•ã1mþFÂa? A)íŠK"‚ëºØ¶M2•$1 ~ò$Z[[xnçï¶<òȣ݀Ï41 X,F6—ût»]pR¿Ep‡\Î&“NSVaëÖÆŸ56nk¼ÙcÔ•d³YI§S’ËåĶm±m[ÇÇq®X/"⺮¤R)¹x±SÞ:ú–{üIJä;K–ŠˆçVÎqcåÛåTFN|ü‰ÔOwgwO—q£‚CB0eÊ”ŒRª¶ ßlKïo:Ô{Ï‚…6ÿ/íM{AÅdÚìIEND®B`‚app-2.8/src/images/tip_24px.svg000066400000000000000000000025061377436340000164030ustar00rootroot00000000000000app-2.8/src/images/transposematrix.png000066400000000000000000000011611377436340000201560ustar00rootroot00000000000000‰PNG  IHDR22?ˆ±bKGDÿÿÿ ½§“ pHYs  šœtIMEà &}ï¯IþIDAThÞí˜Ï+QÇ?ÏÃ’H6¤$½²U~l‰„¢”•z%!l”¥=±C)aã …• ¤(²ßc3ê%ïÜyóîÜu¿ukjÎ9s>sÎ{•••UŠE GG¾9¶"f*ä9?]9rH7’ÿ¡ªu 8U¼Ýø¯Š8Î-Zòâ]BŒüÐP@Š€g ;Ìã £!~FcAbÀY† QiÎÂ>ê,@b)CÛç·_¸w(|\c´V«P˜ä@xIsÿ (ˆJk%…öÙrmV›‘¨,‚»B’C®M»`s…íQ•àPž²Zß¶-:“ò3Ùû„{À•{ý , ¶aWäBxËã¿l‚í;PDƒb¨üÃg_°Ÿ dFHê8Ï àsÆá.Ü IM¦ñ+Þ¿6Ó ­Š¶ª|7¿uÓ ‹Šƒ“¤^Ť/5‘<øØ$zc¦@:„p€S ˃8@­‰¶z424H‡¸ò‚ÜkõjßwLjr{á-Îûˆ¹'Ä[ ë\Þä#æ€ïÅýŤ]sŠžŽûˆY ¼šü½®….d{Cˆ»­¤^ÑVÙLÌnÅ)³B'È´ð°Gw}ñ«<ÅGdB'È©ð  ñg£°e±²²²²²ø÷÷à‰Œ [»IEND®B`‚app-2.8/src/images/triad.png000066400000000000000000000031531377436340000160210ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYsøøÏÁæetEXtSoftwarewww.inkscape.org›î<èIDATX…µ–mL”WÇ÷aW·k^TÊÎ……¥Š®ˆdm(.u»ÝÅnkmiÒ¤»IûA6nÒnöƒÙ—¬i±±±k+M· *”°ÅÖ(Ôb™Zi2t ˆe^`æáìŸvÀÄ“œäyÎ=÷žÿýŸsï¹JDŠRÊ´våÊWóss×΋ÿñèõë}çÛÛÿý¹Ý~”{$¦ÐŸ ëÖÕV¿ôÒãI‰‰A“õ«V­úy^Þ¢ —.½}/hÁ´””õÏ=ýtyHp–Ùl ¹¹/Ü‹àÂÀ’ÔÔµK—ÆGrʶZÓ”R©"2`SJ-°ÙxrÉ |>&zzøtp£"¢ÏÀ4£cc^Ÿ/¢ÓË5¸ŒöœµyëV>mldwSÏ|öUGŽðnY§’“ÕCsp¹«ëСº:»Ñ!ðyGG‹ˆL†ÚÓÓU~U;pkz: iPTDL}=¥¥Ô*¥¢"‘i]±lÙšª-[Îÿà÷Ù“'ݵ{öŒl*+ûH õ{ŒÿNMቬß}‡§¨ˆ×óŒn€øÔääGórrþ8ßbÉ‹8 b^~™+3je%ј"024ÜÒ™dAr2ÓôŠ LºNLl,þ˜t‹…¤hТ9D¾z—®39IœßO¬¦1e2õÂm­i”R ™™l]¼˜´þ~.~õµ"2:A)u°5+‹ûÑRSñ+u³ÛëÕ×3~íg³l6Ù¼™ ÝÝx‚ùëíųe ³³©¸•÷µÀ»ÀûÀ#@|E§œÎÛ9÷û™ð?ÏÈúõ¼-ÿ"‚2›I­ªâ“šÒÂÁ¡¶ogü­·°ë:MÀ;"2M«R*¾¨ˆPœŸÏ¢¡!âìvF;;y½½]jBü³iӮ˗ÄÅÆ& |ûm÷é3gþÑñå—ç()á#çÎêÕu¼ø}>ü¾ÒRŽF=N°øð¸Ánú}eå'=î¾®®iÝûÆ_/ËÊZg²ZI3›oîV׉Ñub4©ØØÛ¹µÙH™E*û”Rn£ýáÂÂí®®^kºóÀU”—ÿäüÅ‹;´yón• ¥“‰@08€ÙL¢RJEI¬¶ÍŸqlqZZšær1 iLÅÇ3a2-€Ë…SBs‘‘‘±™Æ<ϘæpÐxö,Acˆ¦¡ON×Ò‚öÅœ¾›àW{z­« ë¢#££ÒváB3"BI GìvÆŒW©ÃÁÕ«iNÛ¸(…¸ CŠ%ÅÅ•¯îÜy©ýÜ9O·Ãá~oÿ~gÅÆoš”Rª°yyü63“ûuå ®Ž޵µñ×[ TïEê÷J©mÀu {Â)¥,Ù™™e‹åÇåË ãããÝ@Äf´X8SºµËz P³aàÿ^DwS[J©8àyàWÀ›ÀégŸåŸK—²ÁlF¡£¹™]­­Òu±¹ ÀˆØñÔS|ïóá ^Å"¸¢wõjÖÌù=0W-/ç•7ðLN2éõ¢ˆà®®¦nÎïšS òó7úý~_‡ÝÞ "ßGò³ZɶXÀolÇ)),‰–ˆ6–•½¶ïÞm%ÅÅ‹õýJKkšš›÷}‡‡¾ š; Êçc4€0JÖ=êã‡CG_W—ûðÁƒý?ËÍý¥Ñ¿°¬cÇè6Þ!N'ãO<ÁŸ¢¥ ìE´<'§â§óŒö•…… sW¬¨4ÚÛÚäʾ}쬩¡ÛéDy½pâ?¼ø"ï>,»¢–‚Ä„„39Ï·XF²?.ÿQJÜ·G“’x ½^¯ôD @ÿÀ@ð°Ñ®ë:×z{¯Í´ˆÜ>šMÐP KAKkëkïÔÖ~mXœ¿ïÞí8ÑЕҹJ}ýýݦ§ÿº÷›oþò`zzæÔÔTÀÑÙy¹©¹y‡ˆÌØZïVþ×ñ‚Ã4ÂäIEND®B`‚app-2.8/src/images/triadcensus.png000066400000000000000000000022041377436340000172360ustar00rootroot00000000000000‰PNG  IHDR22?ˆ±bKGDÿÿÿ ½§“ pHYs  šœtIMEà --2§ÂIDAThÞíØIheà'&mLRm›Fmm:8´ÖtPµuV\¨ˆ ‹‚(.¡èFPQtçÊ…¸qU¡*ºpVpÀ¡]hJÕªÕN¨Õ´Vkí”Nir]ô~.Li{ÿkóÂå‡?ßO¾ó½ïyÏù^†c8N¨hê%q9nÆzl«×L\O±Ï£³^Ëé!ìFe¨`ÊTZýØ€6ÌÁɘV|w¸2+G¶fÓCSk gà´`dÞm/€™ý_Á4Õ¸;Ý› ïŸ؈Mø„7èýùnA™JhvºS_@ìN&ÖãלüçXœ÷i¼P¦Œ „ÜMùUR2ß £1 ùUò„ð+KVÆbuá¤+¸'@¦ãv<ŠW“©õYóÎ,›v\† @Vãîd©9DoÂxÌÃ#i¥Œj0«¦.£ ?U•Ù­õ OàYÜV•™ î(Ã&GaÊa@<]Øô¬¼+6€áIC­@LÀ·øóqêÖ<ñ«à®ªn¶[BòšÅ¼ˆ];° ñ`Új+æâ{¬‰ºW[¥Ñè¨åŪ#›Ÿ¶¹7'<ç9;ò >ÄkÇb쉯‘˜K1+Ðqƒ‹ò÷‰X†/Ð{4þñÑ2è™Îφ{ñJ6 §á,œ‡ Óv›ðsÙ€H9µ¢+'¿ïdÃsÓÍöb®Æõ)·/Ëd7öafŒ_k¸sÎÅG)·ædhk®¸½J·„Ä•Âóý‚Æ\ƒÓªK=Hè¢èAKã«£å úrDqÒ¶ØæC|ÛŽ;19$ßNô¥·dÝ®£9·*G®ÂÃi¥›Â‡þ* ðdÊçͬ½$×ÖuQûZ–Ë!j?àñ´Ð–ˆAÛ±¦p¿6v¥‚žÂúšDCȹ¼p^‹çp]Ný¾*‹þF²q̼Sã€4DÈΉ‚·§\Æ¥Þwd´³/J®/wñ—cKj¤hŸûpzLá6GðÆç½6*þÙ8ðw²¥@fF} #™ÞXöt¤Íi³±ÛSZ»Žwí È•x&¢6%ëv¤\:Ò…z"p ™S ÈF¬ª{=@&çævS2Ò >)·µ¿b ÛÒJGØæä9+#›dzÍȈº+>ip,35™šROɆßK70Ýx=Úf¤?Ä“º›ÝõSó~T4áëü–Û?³]„%eàHê»7|è̦—â]üXÀ5Éž”\wº—²}Oapp-2.8/src/images/walk.png000066400000000000000000000016371377436340000156610ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿ.l— pHYs  šœtIMEÞl„É&iTXtCommentCreated with GIMP on a Mac•ä_[úIDATXÃí—ÏK”AÇ?ó„9J!¢K º®‹Hh«ÿA—ˆm‚ Ñ-ºt­@J¢‹t­?AªÕ$6q·È @èX”ô›uóéÐÌ:½­ûú®b—æ×÷™™ï|Ÿgà©¿dP2uv§Á¥¿G¿œ8ôs •x ÈN:QH§s_å¸X;ÒÝòÚœD¤¢êAW°údf0Þ9ÔZi[œyÇ@æÑ*Ðe-/*¸ ïvâÅ'.gÝöɉ"]x¾£¶õ¼Ý(hOÐ÷-œ$•Näæge¼C S4‰ ­Å·8fê+ÎÂ1÷ø ]xdí™ñ"hÙ*¸]l:7ýíÎßuBÐþ¦I(h¥XA9ÓS¬¬mà€l_£b¥dV×VÔfw®œ]û›œSóÈÃúÿP´«ìZŒz‚Ž…Íqœ*(rrtÿ÷™ó¹S4`YÅ{ç‡,ª•)šZfó™}éÄ,cÙ5»3ºLÕ£h›|~…T:‘sTÍF¤³«€ryíC‹ÿàªhÖ‰dó€¹|ækw2¡+“×ßTF¼Wºœ–êW!/­ðÏŽåËÍ=9ºÿ»‰fSõF5KDA'«v¯¯+ãZfŸ H¥9÷ØA§· ^–éñðn—ˆF9KÕ¸¶ùTÖ ¸1Øg¬òRâ—”#†tq6 Üh+h€d5E 'æ‡I§g$x7ðÅα‚dÌn•]7L’ÐéœB«Ý]7¸\ˆ"ÅÔ`íF gµãz‚nôÙཇ•3€j]ž[þœi+ºOÈXs­„µ OÔïHX0ò-áÌŒ´]¹ìØûÉï1c|Cà ±ZïfJQu¥4·0üÙuàq>óÍÙ}MëNêQ²åj÷²téBqÙm¸|áù[àp0ÄÊ7nõ´»soÞîÝŽüéÈŒ´«p ôúzûþŒvÉ”ž¯'ºÖûíÊÚlÙÑúý1ýó?cäò Ù­{Hg•IEND®B`‚app-2.8/src/images/wallpaper_48px.svg000066400000000000000000000005411377436340000176010ustar00rootroot00000000000000app-2.8/src/images/webcrawler.png000066400000000000000000000100521377436340000170470ustar00rootroot00000000000000‰PNG  IHDR †VÏŒsRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÙ * Ÿ&] ÛÛéìíöûýþö÷ûóôùùøûÿ ÿÛÖäßÖÏÞ‡ô€eòé›3:ˆ5ÆIH+ ÿ ÙÛëÛÚéôöúëîößãñ(%ÿüûûÿ ÿãßìÎÇ×¾èäèQúõšòTëë¬fòé› oaEC,ÊFF)ñôùÿ þ"üîøÿ'$áéôüÿ ÷öúÿDO7B Ügëë¬2ehðç™tJgdAª ÿñóúùüý,.øùý òõúÑÛìæåï+'#" ïìóòøüîìô ÿ ñîüVûý–ùÜŸýùòÌ#w&76'Ç??%ÿåéóöùü// ùûÿÿ +,´½Ú¬±ÐãëöÖÜëÕÛëûùû&%îðøùúüñíþNþ%œüøÏþ%IîÜ+.SÁDE)úûþìñøøúý:9#üÿöúýyˆºÆÐä µ½Ú("üüþßâî ùÿèïøþÿ ìéóéøôÇËqî£ wbDB,dÿûþþñõûIF* ÿäìøóøý<9#(%áæð êðù êðøúùüôó÷ ÝÜêÞÛèûöùEçá ’.;ùñéÏÈi12!¨(*óöûûÿ,)30ïôúôøýÿßçóÍØèþ"Ùáïýÿþýÿ  ìéñÿÿþÿ ÝÒ´ ü %öøüîóù 10óøûþ ÿñõû  þ³ÀÚ   Õâñ  ,7&!+ ÿE_ vØÜìóõúÒÙëÿñôûõùü Üäî ÓÞìþÚÚèÿ" ùù\çëôìð÷ÓÜëúþÿÛáîýþþÿÿ  ÖÞë ñøüÿÿÿÿ  ýþ e',cûý ('ÿÑ×éÔÛìèíõ   éîõùýþ?1:- ÿ ?3þþüý=ûüüüþòôùûþÿÿüµÄß?7!±ÀÝÿÿ Õáí ñûþÿýÿÿýÿâèóÿ% ëîö ÿØáð+$éð÷õûýæðö ôýþýÿÿïõûÓßñþþþ  ý øèôþÿõùýó÷û§¼Ù>1ÍÝìô÷ýÁÙì  þþÿÿýþÿýÿÿÿÿÿýþüþÿþýöÌÊÚòþþÿÿûüýúýþ Üåò°ÄàºÏå ÞìöÈáð&ôþåôüãóüûöõõúÿöõøðåìýúûÞüÇìóó.ö ýüÿüýÿçï÷H;!»Öéå¬Ï<-ËãóÙíöýÿÿÿÿýþýýÿýüþÿýÿ ØÞî÷òçüô÷ñþýûÆÚüü«þÿ` óþÿáëõ ìøÿ×éö) )éôúëôûÞîõþÿýýÿþÿþþþÿþöúýÿþþþûãçö)ýþö›õôñ‘ññ÷'áëõ$ þÞî÷ïú;#.>%N/0 þ äëõþùþþ ÿäâêàöèíßÜÖñÕþüûáÿîî÷üûýßæò ßëõ9%ýR1ýÿÿ9 V1 ý,×áìüÿþþþþÿþÿ ååð¿û¨þröö’þ 5ý Jýÿü÷ÿú õÿC( þÿþÿÿÿ ' " 6 ¼ÏãûøþýøýÿøùþùüÿþóìóüñèâKûùÐõòòŽ_èàŠ“öñòÚÿüþÿüýþûýÎàðG(»Ûíÿÿ  .ÃÚîüüþÿýþÿûüþÿÿíêï{öòŸéðð¡øZ ÿüýûþüýýý­Îè ¯Ùî   6 ÈßðßæïûþûûÿÿÿïåëÙýõò” håÜ‹Íúó^þüýþÿþüÿýýÄÝîëöû ôûÿîïôúüþý ÿýúìïèúõæ<ðì–öôç˜m‰ˆÏÉ~£ ýòz(üüÿÿýÿªÑëüýý ôüÿí÷þíóùïòõÿ÷øúåéðõéåFïåœìrʼ!ÎâÞ¼Äþý(ÆZ$2(ÿ(" . ! æôúãï÷ðõùÿãí÷çïöýôùÿìñößæÏôíæ<ðä”Ýmðé‘ÎnåÛŒÞëé ðìûÕØì'Ûéö×êö) ÿÍäñüýÿþúü÷õúÿÎÖêýéðêíéüççˆÚȧ^÷ð²ò#]íå›U«n%„ L+% ¸Ñèòòø÷ùÿùìóÿùêìèõéìœûþŒð؈ó"rñÞŽ_ , & 3 þÿùâßßðäé½þôõÕðëÝÜûó«ûo6Ø-ðlùIEND®B`‚app-2.8/src/images/webcrawler2.png000066400000000000000000000020431377436340000171320ustar00rootroot00000000000000‰PNG  IHDR †VÏŒbKGDÿÿÿ ½§“ pHYs  šœtIMEá &¾i–DiTXtCommentCreated with GIMPd.e‡IDATHǽ—YˆNaÇߌ=¦™Ì0¶±•0(KQ¶¢¬¹‘5qg¸QˆÜ¸à‚ %.’} MÑX†™±ï†ùÆ>˜™ï¸ù¿z¼ù¾3¶§ÞÎyŸó>Ëûì'Fjh tÆc~@Ä»ÀEà(PTña PÔA„õØ´üSÁòˆBÃÖG`èï Ÿ TfUÀy³?¬çw`·Á¿Qz½xÆ¢ Ÿæ18 äšý6s ´Æ5@ ¬òx¬‹*¼ðND `¹ðîö»ÌY§@í+ €ýb”XE'†`¥pƒu»W@^N¿Aû|]$Ks“ _a„3ø-Âíô·)0@ÙR4ó,ûžÜÖ¡8ÐÜ|{#|NÚ·€¯ÀHƒ¯0Jôö…§É÷}´/2¦îd÷ä‚T𸡠ô3ü7™3+Ã×zQŠ3Úï¡qHóð³õ­Ø ”šÀ€²°"uNkäÿJ Îdƒ­7b eÈUࡱë+ðR äÒn¾¯õ±Â˜~„)*÷O+àKcÅB`©÷@©9ÐWÀÝò¬‡¿&|O¯1e_ ô²½˜ €Ç¯)Â×I©ŸÐD ¤ëÝW “RÈ6îŸ ®•ùf!fðŸ.»zDq=Û%‰ü41w‚k• _¼s.¿+.~aP¢÷<à 0Aû›zöH‘~°8  TŠ]™Ì1©Zí3XæR-ðxªýþ÷¸4tîÉ6´w¤„ƒ™&8ï„ð¢·8^(­>4åÕÀC“†#âsÙ;{\ÒÁt`i˜ [×ET¯¦‘ 1 ê9ð­¥j€ö&Ò}k–“R•ÑBCTbðk„Ûâ‚Z/@óLï[ÅÀ¨dãÚ sØu¿¾ œ@÷Í(MÃiªbU ÌS • ¼Ø.¼ÁŽ$QÀEù¢FÌåÀ _‰Ï×ÅÀD¥d T³ ¬Ö¾­"Rêt&kHÍUðÅTó+5ŠVÆ$¤t/àŠX\(®ê~÷Ç¥©Æ®*Åa?u~˜bâÿ>i°ña®ï†‹£úo˜©žäpIEND®B`‚app-2.8/src/images/zoom_in_24px.svg000066400000000000000000000005601377436340000172570ustar00rootroot00000000000000app-2.8/src/images/zoom_out_24px.svg000066400000000000000000000005321377436340000174570ustar00rootroot00000000000000app-2.8/src/main.cpp000077500000000000000000000075601377436340000144040ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt main.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 #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); // Todo update/remove translations QTranslator tor( 0 ); QLocale locale; // set the location where .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" || option=="-help" ) { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION)<< "\n" <<"\nUsage: socnetv [flags] [file]\n" <<"-h, --help Displays 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"; } } // Create our MainWindow MainWindow *socnetv=new MainWindow(option); // Load our default stylesheet QString sheetName = "default.qss"; QFile file(":/qss/" + sheetName ); file.open(QFile::ReadOnly); QString styleSheet = QString::fromLatin1(file.readAll()); file.close(); // Apply our default stylesheet qApp->setStyleSheet(styleSheet); // Show the application socnetv->show(); return app.exec(); } app-2.8/src/mainwindow.cpp000077500000000000000000021526231377436340000156370ustar00rootroot00000000000000/*************************************************************************** SocNetV: Social Network Visualizer version: 2.8 Written in Qt - mainwindow.cpp - description ------------------- copyright : (C) 2005-2021 by Dimitris B. Kalamaras blog : http://dimitris.apeiro.gr project site : https://socnetv.org ***************************************************************************/ /******************************************************************************* * 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 #include #include #include // for SVG icons #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "texteditor.h" #include "graphicswidget.h" #include "graphicsnode.h" #include "graphicsedge.h" #include "graphicsnodenumber.h" #include "chart.h" #include "forms/dialogsettings.h" #include "forms/dialogwebcrawler.h" #include "forms/dialogpreviewfile.h" #include "forms/dialogexportpdf.h" #include "forms/dialogexportimage.h" #include "forms/dialogranderdosrenyi.h" #include "forms/dialograndsmallworld.h" #include "forms/dialograndscalefree.h" #include "forms/dialograndregular.h" #include "forms/dialograndlattice.h" #include "forms/dialognodefind.h" #include "forms/dialognodeedit.h" #include "forms/dialogfilteredgesbyweight.h" #include "forms/dialogedgedichotomization.h" #include "forms/dialogsimilaritypearson.h" #include "forms/dialogsimilaritymatches.h" #include "forms/dialogclusteringhierarchical.h" #include "forms/dialogdissimilarities.h" #include "forms/dialogsysteminfo.h" //Assume no debugging messages 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 QtInfoMsg: fprintf( stderr, "Info: %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 } } /** * @brief MainWindow::MainWindow * @param m_fileName * MainWindow contruction method */ MainWindow::MainWindow(const QString & m_fileName) { qInstallMessageHandler( myMessageOutput); qDebug() << "MW::MainWindow() - Constructor running on thread:"<< thread(); setWindowIcon (QIcon(":/images/socnetv.png")); appSettings = initSettings(); int primaryScreenWidth = QApplication::primaryScreen()->availableSize().width(); int primaryScreenHeight = QApplication::primaryScreen()->availableSize().height(); windowMinWidth = 1024; windowMinHeight = 750; if (primaryScreenWidth > 2439) { windowMinWidth = 1440; } if (primaryScreenHeight> 1559) { windowMinHeight = 1024; } qDebug () << "MW::initWindowLayout - primaryScreen size"<setFocus(); // Check if user-provided network file on startup qDebug() << "MW::MainWindow() Checking if user provided file on startup..."; if (!m_fileName.isEmpty()) { slotNetworkFileChoose( m_fileName ); } statusMessage( tr("Welcome to Social Network Visualizer, Version ")+VERSION); } /** * @brief Deletes variables on MW closing */ MainWindow::~MainWindow() { qDebug() << "MW::~MainWindow() Destruct function running..."; initApp(); terminateThreads("~MainWindow()"); delete printer; delete printerPDF; delete scene; delete graphicsWidget; foreach ( TextEditor *ed, m_textEditors) { ed->close(); delete ed; } m_textEditors.clear(); codecs.clear(); qDebug() << "MW::~MainWindow() Destruct function finished - bye!"; } /** * @brief Called when the application closes. Asks to write any unsaved network data. * @param ce */ void MainWindow::closeEvent( QCloseEvent* ce ) { qDebug() << "MW::closeEvent() - Start closing app. Status message to user..."; statusMessage( tr("Closing SocNetV. Bye!") ); bool userCancelled=false; qDebug() << "MW::closeEvent() - Checking if Graph is saved..."; if ( activeGraph->graphSaved() ) { ce->accept(); qDebug() << "MW::closeEvent() - Graph is already saved. "; } else { qDebug() << "MW::closeEvent() - Graph NOT saved. Asking the user."; switch( slotHelpMessageToUser( USER_MSG_QUESTION, tr("Save changes"), tr("Modified network has not been saved!"), tr("Do you want to save the changes to the network file?"), QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Cancel ) ) { case QMessageBox::Yes: slotNetworkSave(); ce->accept(); break; case QMessageBox::No: ce->accept(); break; case QMessageBox::Cancel: ce->ignore(); userCancelled = true; break; case QMessageBox::NoButton: default: // just for sanity ce->ignore(); break; } } if (userCancelled) { return; } qDebug() << "MW::closeEvent() - Calling terminateThreads()..."; terminateThreads("closeEvent()"); qDebug() << "MW::closeEvent() - Deleting other objects/pointers..."; qDebug() << "MW::closeEvent() - Deleting printer"; delete printer; qDebug() << "MW::closeEvent() - Deleting printerPDF"; delete printerPDF; qDebug() << "MW::closeEvent() - Deleting graphicsWidget"; delete graphicsWidget; qDebug() << "MW::closeEvent() - Deleting activeGraph"; delete activeGraph; qDebug() << "MW::closeEvent() - Deleting Scene"; delete scene; // delete miniChart; qDebug() << "MW::closeEvent() - Clearing and deleting text editors..."; foreach ( TextEditor *ed, m_textEditors) { ed->close(); delete ed; } m_textEditors.clear(); delete editNodePropertiesAct; delete editNodeRemoveAct; qDebug() << "MW::closeEvent() - Clearing codecs..."; codecs.clear(); qDebug() << "MW::closeEvent() - Finished. Bye!"; } /** * @brief Resizes the scene when the window is resized. */ void MainWindow::resizeEvent( QResizeEvent * ) { qDebug() << "MW::resizeEvent(): Window resized to" << width() << "," << height(); statusMessage( tr("Window resized to (%1, %2)px.") .arg(width()).arg(height()) ); } /** * @brief Initializes default (or user-defined) app settings * */ QMap MainWindow::initSettings() { qDebug()<< "MW::initSettings"; //printDebug = false; // comment it to stop debug override // Create fortune cookies and tips createFortuneCookies(); slotHelpCreateTips(); // Populate icons and shapes lists // Note: When you add a new shape and icon, you must also: // 1. Add a new enum in NodeShape (global.h) // 2. Add a new branch in GraphicsNode::setShape() and paint() // 3. Add a new branch in DialogNodeEdit: getNodeShape() and getUserChoices() nodeShapeList << "box" << "circle" << "diamond" << "ellipse" << "triangle" << "star" << "person" << "person-b" << "bugs" << "heart" << "dice" << "custom"; iconPathList << ":/images/box.png" << ":/images/circle.png" << ":/images/diamond.png" << ":/images/ellipse.png" << ":/images/triangle.png" << ":/images/star.png" << ":/images/person.svg" << ":/images/person-bw.svg" << ":/images/bugs.png" << ":/images/heart.svg" << ":/images/random.png" << ":/images/export_photo_48px.svg"; // Call slotNetworkAvailableTextCodecs to setup a list of all supported codecs qDebug() << "MW::initSettings - calling slotNetworkAvailableTextCodecs" ; slotNetworkAvailableTextCodecs(); qDebug() << "MW::initSettings - creating DialogPreviewFile object and setting codecs list" ; m_dialogPreviewFile = new DialogPreviewFile(this); m_dialogPreviewFile->setCodecList(codecs); connect (m_dialogPreviewFile, &DialogPreviewFile::loadNetworkFileWithCodec, this, &MainWindow::slotNetworkFileLoad ); qDebug() << "MW::initSettings - creating default settings" ; // Our settings are always saved to this folder. settingsDir = QDir::homePath() +QDir::separator() + "socnetv-data" + QDir::separator() ; settingsFilePath = settingsDir + "settings.conf"; // dataDir is where our built-in datasets and reports are saved by default // initially dataDir and settingsDir are the same, but dataDir may be // changed by the user through Settings... QString dataDir= settingsDir ; maxNodes=5000; //Max nodes used by createRandomNetwork dialogues // hard-coded initial settings to use only on first app load // when there are no user defined values appSettings["initNodeSize"]= "10"; appSettings["initNodeColor"]="red"; appSettings["initNodeShape"]="circle"; appSettings["initNodeIconPath"]=""; appSettings["initNodeNumbersVisibility"] = "true"; appSettings["initNodeNumberSize"]="0"; appSettings["initNodeNumberColor"]="#333"; appSettings["initNodeNumbersInside"] = "true"; appSettings["initNodeNumberDistance"] = "2"; appSettings["initNodeLabelsVisibility"] = "false"; appSettings["initNodeLabelSize"]="8"; appSettings["initNodeLabelColor"]="#8d8d8d"; appSettings["initNodeLabelDistance"] = "6"; appSettings["initEdgesVisibility"]="true"; appSettings["initEdgeShape"]="line"; //bezier appSettings["initEdgeColor"]="#666666"; appSettings["initEdgeColorNegative"]="red"; appSettings["initEdgeColorZero"]="blue"; appSettings["initEdgeArrows"]="true"; appSettings["initEdgeOffsetFromNode"] = "7"; appSettings["initEdgeThicknessPerWeight"]="true"; appSettings["initEdgeWeightNumbersVisibility"]="false"; appSettings["initEdgeWeightNumberSize"] = "7"; appSettings["initEdgeWeightNumberColor"] = "#00aa00"; appSettings["initEdgeLabelsVisibility"] = "false"; appSettings["initBackgroundColor"]="white"; //"gainsboro"; appSettings["initBackgroundImage"]=""; appSettings["printDebug"] = "false"; appSettings["viewReportsInSystemBrowser"] = "true"; appSettings["showProgressBar"] = "false"; appSettings["showToolBar"] = "true"; appSettings["showStatusBar"] = "true"; appSettings["antialiasing"] = "true"; appSettings["canvasAntialiasingAutoAdjustment"] = "true"; appSettings["canvasSmoothPixmapTransform"] = "true"; appSettings["canvasPainterStateSave"] = "false"; appSettings["canvasCacheBackground"] = "false"; appSettings["canvasUpdateMode"] = "Full"; appSettings["canvasIndexMethod"] = "BspTreeIndex"; appSettings["canvasEdgeHighlighting"] = "true"; appSettings["canvasNodeHighlighting"] = "true"; appSettings["dataDir"]= dataDir ; appSettings["lastUsedDirPath"]= dataDir ; appSettings["showRightPanel"] = "true"; appSettings["showLeftPanel"] = "true"; appSettings["printLogo"] = "true"; appSettings["initStatusBarDuration"] = "5000"; appSettings["randomErdosEdgeProbability"] = "0.04"; appSettings["initReportsRealNumberPrecision"] = "6"; appSettings["initReportsLabelsLength"] = "16"; appSettings["initReportsChartType"] = "0"; // Try to load settings configuration file // First check if our settings folder exist QDir socnetvDir(settingsDir); if ( !socnetvDir.exists() ) { qDebug() << "MW::initSettings - dir does not exist - create it"; socnetvDir.mkdir(settingsDir); } // Then check if the conf file exists inside the folder qDebug () << "MW::initSettings - checking for settings file: " << settingsFilePath; if (!socnetvDir.exists(settingsFilePath)) { saveSettings(); } else { qDebug()<< "MW::initSettings - settings file exist - Reading it"; QFile file(settingsFilePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::critical(this, "File Read Error", tr("Error! \n" "I cannot read the settings file " "in \n" + settingsFilePath.toLocal8Bit() + "\n" "You can continue using SocNetV with default " "settings but any changes to them will not " " be saved for future sessions \n" "Please, check permissions in your home folder " " and contact the developer team." ), QMessageBox::Ok, 0); return appSettings; } QTextStream in(&file); QStringList setting; while (!in.atEnd()) { QString line = in.readLine(); if (!line.isEmpty()) { setting = line.simplified().split('='); qDebug() << " read setting: " << setting[0].simplified() << " = " << setting[1].simplified(); if (setting[0].simplified().startsWith("recentFile_")) recentFiles += setting[1].simplified(); else appSettings.insert (setting[0].simplified() , setting[1].simplified() ); } } file.close(); } qDebug () << "MW::initSettings() - Recent files count " << recentFiles.count() ; // restore user setting for debug messages printDebug = (appSettings["printDebug"] == "true") ? true:false; return appSettings; } /** * @brief Saves default (or user-defined) app settings */ void MainWindow::saveSettings() { qDebug () << "MW::saveSettings to "<< settingsFilePath; QFile file(settingsFilePath); if (!file.open(QIODevice::WriteOnly ) ) { QMessageBox::critical(this, "File Write Error", tr("Error! \n" "I cannot write the new settings file " "in \n" + settingsFilePath.toLocal8Bit() + "\n" "You can continue using SocNetV with default " "settings but any changes to them will not " " be saved for future sessions \n" "Please, check permissions in your home folder " " and contact the developer team." ), QMessageBox::Ok, 0); return; } QTextStream out(&file); qDebug()<< "MW::saveSettings - writing settings to settings file first "; QMap::const_iterator it = appSettings.constBegin(); while (it != appSettings.constEnd()) { qDebug() << " setting: " << it.key() << " = " << it.value(); out << it.key() << " = " << it.value() << endl; ++it; } // save recent files for (int i = 0 ; i < recentFiles.size() ; ++i) { out << "recentFile_"+ QString::number(i+1) << " = " << recentFiles.at(i) << endl; } file.close(); } /** * @brief Opens the Settings & Preferences dialog */ void MainWindow::slotOpenSettingsDialog() { qDebug() << "MW::slotOpenSettingsDialog()"; // build dialog m_settingsDialog = new DialogSettings( appSettings, nodeShapeList, iconPathList, this); connect( m_settingsDialog, &DialogSettings::saveSettings, this, &MainWindow::saveSettings); connect (m_settingsDialog, &DialogSettings::setReportsDataDir, activeGraph, &Graph::setReportsDataDir); connect (m_settingsDialog,&DialogSettings::setReportsRealNumberPrecision, activeGraph, &Graph::setReportsRealNumberPrecision); connect (m_settingsDialog,&DialogSettings::setReportsLabelLength, activeGraph, &Graph::setReportsLabelLength); connect (m_settingsDialog, &DialogSettings::setReportsChartType, activeGraph, &Graph::setReportsChartType); connect( m_settingsDialog, &DialogSettings::setDebugMsgs, this, &MainWindow::slotOptionsDebugMessages); connect( m_settingsDialog, &DialogSettings::setProgressDialog, this, &MainWindow::slotOptionsProgressDialogVisibility); connect( m_settingsDialog, &DialogSettings::setPrintLogo, this, &MainWindow::slotOptionsEmbedLogoExporting); connect (m_settingsDialog, &DialogSettings::setStyleSheetDefault, this, &MainWindow::slotStyleSheetDefault); connect( m_settingsDialog, &DialogSettings::setToolBar, this, &MainWindow::slotOptionsWindowToolbarVisibility); connect( m_settingsDialog, &DialogSettings::setStatusBar, this, &MainWindow::slotOptionsWindowStatusbarVisibility); connect( m_settingsDialog, &DialogSettings::setLeftPanel, this, &MainWindow::slotOptionsWindowLeftPanelVisibility); connect( m_settingsDialog, &DialogSettings::setRightPanel, this, &MainWindow::slotOptionsWindowRightPanelVisibility); connect( m_settingsDialog, &DialogSettings::setCanvasBgColor, this, &MainWindow::slotOptionsBackgroundColor); connect( m_settingsDialog, &DialogSettings::setCanvasBgImage, this, &MainWindow::slotOptionsBackgroundImage); connect( m_settingsDialog, &DialogSettings::setCanvasAntialiasing, this, &MainWindow::slotOptionsCanvasAntialiasing); connect( m_settingsDialog, &DialogSettings::setCanvasAntialiasingAutoAdjust, this, &MainWindow::slotOptionsCanvasAntialiasingAutoAdjust); connect( m_settingsDialog, &DialogSettings::setCanvasSmoothPixmapTransform, this, &MainWindow::slotOptionsCanvasSmoothPixmapTransform); connect( m_settingsDialog, &DialogSettings::setCanvasSavePainterState, this, &MainWindow::slotOptionsCanvasSavePainterState); connect( m_settingsDialog, &DialogSettings::setCanvasCacheBackground, this, &MainWindow::slotOptionsCanvasCacheBackground); connect( m_settingsDialog, &DialogSettings::setCanvasEdgeHighlighting, this, &MainWindow::slotOptionsCanvasEdgeHighlighting); connect( m_settingsDialog, &DialogSettings::setCanvasUpdateMode, this, &MainWindow::slotOptionsCanvasUpdateMode); connect( m_settingsDialog, &DialogSettings::setCanvasIndexMethod, this, &MainWindow::slotOptionsCanvasIndexMethod); connect(m_settingsDialog, SIGNAL(setNodeColor(QColor)), this, SLOT(slotEditNodeColorAll(QColor)) ); connect( m_settingsDialog, &DialogSettings::setNodeShape, this, &MainWindow::slotEditNodeShape); connect( m_settingsDialog, &DialogSettings::setNodeSize, this, &MainWindow::slotEditNodeSizeAll); connect( m_settingsDialog, &DialogSettings::setNodeNumbersVisibility, this, &MainWindow::slotOptionsNodeNumbersVisibility); connect( m_settingsDialog, &DialogSettings::setNodeNumbersInside, this, &MainWindow::slotOptionsNodeNumbersInside); connect( m_settingsDialog, &DialogSettings::setNodeNumberColor, this, &MainWindow::slotEditNodeNumbersColor); connect( m_settingsDialog, &DialogSettings::setNodeNumberSize, this, &MainWindow::slotEditNodeNumberSize); connect( m_settingsDialog, &DialogSettings::setNodeNumberDistance, this, &MainWindow::slotEditNodeNumberDistance); connect( m_settingsDialog, &DialogSettings::setNodeLabelsVisibility, this, &MainWindow::slotOptionsNodeLabelsVisibility); connect( m_settingsDialog, &DialogSettings::setNodeLabelSize, this, &MainWindow::slotEditNodeLabelSize); connect( m_settingsDialog, &DialogSettings::setNodeLabelColor, this, &MainWindow::slotEditNodeLabelsColor); connect( m_settingsDialog, &DialogSettings::setNodeLabelDistance, this, &MainWindow::slotEditNodeLabelDistance); connect( m_settingsDialog, &DialogSettings::setEdgesVisibility, this, &MainWindow::slotOptionsEdgesVisibility); connect( m_settingsDialog, &DialogSettings::setEdgeArrowsVisibility, this, &MainWindow::slotOptionsEdgeArrowsVisibility); connect( m_settingsDialog, &DialogSettings::setEdgeOffsetFromNode, this, &MainWindow::slotOptionsEdgeOffsetFromNode); connect( m_settingsDialog, &DialogSettings::setEdgeColor, this, &MainWindow::slotEditEdgeColorAll); connect( m_settingsDialog, &DialogSettings::setEdgeWeightNumbersVisibility, this, &MainWindow::slotOptionsEdgeWeightNumbersVisibility); connect( m_settingsDialog, &DialogSettings::setEdgeLabelsVisibility, this, &MainWindow::slotOptionsEdgeLabelsVisibility); // show settings dialog m_settingsDialog->exec(); qDebug ()<< appSettings["initBackgroundImage"] ; } /** * @brief Toggles the use of SocNetV default Qt StyleSheet * (.qss file defined in project resources) * @param sheetName */ void MainWindow::slotStyleSheetDefault(const bool checked = true ){ if ( checked ) { slotStyleSheetByName(":/qss/default.qss"); } else { slotStyleSheetByName(""); } } /** * @brief Loads a custom Qt StyleSheet (.qss file) * If sheetFileName is empty, the app uses platform-specific Qt style * @param sheetName */ void MainWindow::slotStyleSheetByName(const QString &sheetFileName) { QString styleSheet = ""; if ( !sheetFileName.isEmpty() ) { QFile file(sheetFileName); file.open(QFile::ReadOnly); styleSheet = QString::fromLatin1(file.readAll()); } qApp->setStyleSheet(styleSheet); } /** * @brief Fixes known bugs in QProgressDialog class. i.e. Workaround for macOS-only Qt bug: QTBUG-65750, QTBUG-70357. QProgressDialog too small and too narrow to fit the text of its label * @param dialog */ void MainWindow::polishProgressDialog(QProgressDialog* dialog) { #ifdef Q_OS_MAC // Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357. const int margin = dialog->fontMetrics().width("X"); dialog->resize(dialog->width() + 2 * margin, dialog->height()); dialog->show(); #else Q_UNUSED(dialog); #endif } /** * @brief MainWindow::terminateThreads * @param reason */ void MainWindow::terminateThreads(const QString &reason) { qDebug() << "MW::terminateThreads() - reason " << reason <<" Checking if graphThread is running..."; if (graphThread.isRunning() ) { qDebug() << "MW::terminateThreads() - graphThread running." << "Calling graphThread.quit();"; graphThread.quit(); qDebug() << "MW::terminateThreads() - deleting activeGraph and pointer"; delete activeGraph; activeGraph = 0; // see why here: https://goo.gl/tQxpGA } } /** * @brief Initializes the scene and the corresponding graphicsWidget, * The latter is a QGraphicsView canvas which is the main widget of SocNetV. */ void MainWindow::initView() { qDebug ()<< "MW::initView()"; //Create our scene scene=new QGraphicsScene(); //create a view widget for this scene graphicsWidget=new GraphicsWidget(scene,this); graphicsWidget->setObjectName("graphicsWidget"); bool toggle = false; toggle = (appSettings["antialiasing"] == "true" ) ? true:false; graphicsWidget->setRenderHint(QPainter::Antialiasing, toggle ); graphicsWidget->setRenderHint(QPainter::TextAntialiasing, toggle ); //Disables QGraphicsView's antialiasing auto-adjustment of exposed areas. toggle = (appSettings["canvasAntialiasingAutoAdjustment"] == "true" ) ? false:true; graphicsWidget->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, toggle); toggle = (appSettings["canvasSmoothPixmapTransform"] == "true" ) ? true:false; graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, toggle ); //if items do restore their state, it's not needed for graphicsWidget to do the same... toggle = (appSettings["canvasPainterStateSave"] == "true" ) ? false:true; graphicsWidget->setOptimizationFlag(QGraphicsView::DontSavePainterState, toggle); if ( appSettings["canvasUpdateMode"] == "Full" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::FullViewportUpdate ); } else if (appSettings["canvasUpdateMode"] == "Minimal" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::MinimalViewportUpdate ); } else if (appSettings["canvasUpdateMode"] == "Smart" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::SmartViewportUpdate ); } else if (appSettings["canvasUpdateMode"] == "Bounding Rectangle" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::BoundingRectViewportUpdate ); } else if (appSettings["canvasUpdateMode"] == "None" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::NoViewportUpdate ); } else { // graphicsWidget->setViewportUpdateMode( QGraphicsView::MinimalViewportUpdate ); } //QGraphicsView can cache pre-rendered content in a QPixmap, which is then drawn onto the viewport. if ( appSettings["canvasCacheBackground"] == "true" ) { graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); } else { graphicsWidget->setCacheMode(QGraphicsView::CacheNone); } graphicsWidget->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); //graphicsWidget->setTransformationAnchor(QGraphicsView::AnchorViewCenter); //graphicsWidget->setTransformationAnchor(QGraphicsView::NoAnchor); 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(); graphicsWidget->setWhatsThis(tr("The canvas of SocNetV. \n\n" "Inside this area you create and edit networks, " "load networks from files and visualize them \n" "according to selected metrics. \n\n" " - To create a new node, double-click anywhere (Ctrl+.)\n" " - To add an arc between two nodes, double-click" " on the first node then double-click on the second (Ctrl+/)\n" " - To change network appearance, right click on empty space\n" " - To change/edit the properties of a node, right-click on it\n" " - To change/edit the properties of an edge, right-click on it." "")); qDebug() << "MW::initView() - Finished initializing view:" << graphicsWidget->width() << graphicsWidget->height(); } /** * @brief Initializes the Graph */ void MainWindow::initGraph() { qDebug() << "MW::initGraph()"; activeGraph = new Graph(graphicsWidget); qDebug() << "MW::initGraph() - activeGraph created on thread:" << activeGraph->thread() << "moving it to new thread "; // activeGraph->moveToThread(&graphThread); // graphThread.start(); // Used in toolBoxAnalysisProminenceSelect and DialogNodeFind prominenceIndexList << "Degree Centrality" << "Closeness Centrality" << "IR Closeness Centrality" << "Betweenness Centrality" << "Stress Centrality" << "Eccentricity Centrality" << "Power Centrality" << "Information Centrality" << "Eigenvector Centrality" << "Degree Prestige" << "PageRank Prestige" << "Proximity Prestige"; qDebug() << "MW::MainWindow() - activeGraph thread now:" << activeGraph->thread(); } /** * @brief Initializes all QActions of the application * Take a breath, the listing below is HUGE. */ void MainWindow::initActions(){ qDebug()<< "MW::initActions()"; printer = new QPrinter; printerPDF = new QPrinter; /** Network menu actions */ networkNewAct = new QAction(QIcon(":/images/new_folder_48px.svg"), tr("&New"), this); networkNewAct->setShortcut(Qt::CTRL+Qt::Key_N); networkNewAct->setStatusTip(tr("Create a new network")); networkNewAct->setToolTip(tr("New network")); networkNewAct->setWhatsThis(tr("New\n\n" "Creates a new social network. " "First, checks if current network needs to be saved.")); connect(networkNewAct, SIGNAL(triggered()), this, SLOT(slotNetworkNew())); networkOpenAct = new QAction(QIcon(":/images/open_48px.svg"), tr("&Open"), this); networkOpenAct->setShortcut(Qt::CTRL+Qt::Key_O); networkOpenAct->setToolTip(tr("Open network")); networkOpenAct->setStatusTip(tr("Open a GraphML formatted file of social network data.")); networkOpenAct->setWhatsThis(tr("Open\n\n" "Opens a file of a social network in GraphML format")); connect(networkOpenAct, SIGNAL(triggered()), this, SLOT(slotNetworkFileChoose())); for (int i = 0; i < MaxRecentFiles; ++i) { recentFileActs[i] = new QAction(this); recentFileActs[i]->setVisible(false); connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(slotNetworkFileLoadRecent())); } networkImportGMLAct = new QAction( QIcon(":/images/open_48px.svg"), tr("&GML"), this); networkImportGMLAct->setStatusTip(tr("Import GML-formatted file")); networkImportGMLAct->setWhatsThis(tr("Import GML\n\n" "Imports a social network from a GML-formatted file")); connect(networkImportGMLAct, SIGNAL(triggered()), this, SLOT(slotNetworkImportGML())); networkImportPajekAct = new QAction( QIcon(":/images/open_48px.svg"), tr("&Pajek"), this); networkImportPajekAct->setStatusTip(tr("Import Pajek-formatted file")); networkImportPajekAct->setWhatsThis(tr("Import Pajek \n\n" "Imports a social network from a Pajek-formatted file")); connect(networkImportPajekAct, SIGNAL(triggered()), this, SLOT(slotNetworkImportPajek())); networkImportAdjAct = new QAction( QIcon(":/images/open_48px.svg"), tr("&Adjacency Matrix"), this); networkImportAdjAct->setStatusTip(tr("Import Adjacency matrix")); networkImportAdjAct->setWhatsThis(tr("Import Sociomatrix \n\n" "Imports a social network from an Adjacency matrix-formatted file")); connect(networkImportAdjAct, SIGNAL(triggered()), this, SLOT(slotNetworkImportAdjacency())); networkImportGraphvizAct = new QAction( QIcon(":/images/open_48px.svg"), tr("Graph&Viz (.dot)"), this); networkImportGraphvizAct->setStatusTip(tr("Import dot file")); networkImportGraphvizAct->setWhatsThis(tr("Import GraphViz \n\n" "Imports a social network from a GraphViz formatted file")); connect(networkImportGraphvizAct, SIGNAL(triggered()), this, SLOT(slotNetworkImportGraphviz())); networkImportUcinetAct = new QAction( QIcon(":/images/open_48px.svg"), tr("&UCINET (.dl)..."), this); networkImportUcinetAct->setStatusTip(tr("ImportDL-formatted file (UCINET)")); networkImportUcinetAct->setWhatsThis(tr("Import UCINET\n\n" "Imports social network data from a DL-formatted file")); connect(networkImportUcinetAct, SIGNAL(triggered()), this, SLOT(slotNetworkImportUcinet())); networkImportListAct = new QAction( QIcon(":/images/open_48px.svg"), tr("&Edge list"), this); networkImportListAct->setStatusTip(tr("Import an edge list file. ")); networkImportListAct->setWhatsThis( tr("Import edge list\n\n" "Import a network from an edgelist file. " "SocNetV supports EdgeList files with edge weights " "as well as simple EdgeList files where the edges are non-value (see manual)" )); connect(networkImportListAct, SIGNAL(triggered()), this, SLOT(slotNetworkImportEdgeList())); networkImportTwoModeSM = new QAction( QIcon(":/images/open_48px.svg"), tr("&Two Mode Sociomatrix"), this); networkImportTwoModeSM->setStatusTip(tr("Import two-mode sociomatrix (affiliation network) file")); networkImportTwoModeSM->setWhatsThis(tr("Import Two-Mode 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(networkImportTwoModeSM, SIGNAL(triggered()), this, SLOT(slotNetworkImportTwoModeSM())); networkSaveAct = new QAction(QIcon(":/images/file_download_48px.svg"), tr("&Save"), this); networkSaveAct->setShortcut(Qt::CTRL+Qt::Key_S); networkSaveAct->setStatusTip(tr("Save social network to a file")); networkSaveAct->setWhatsThis(tr("Save.\n\n" "Saves the social network to file")); connect(networkSaveAct, SIGNAL(triggered()), this, SLOT(slotNetworkSave())); networkSaveAsAct = new QAction(QIcon(":/images/file_download_48px.svg"), tr("Save As..."), this); networkSaveAsAct->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_S); networkSaveAsAct->setStatusTip(tr("Save network under a new filename")); networkSaveAsAct->setWhatsThis(tr("Save As\n\n" "Saves the social network under a new filename")); connect(networkSaveAsAct, SIGNAL(triggered()), this, SLOT(slotNetworkSaveAs())); networkExportImageAct = new QAction(QIcon(":/images/export_photo_48px.svg"), tr("Export to I&mage..."), this); networkExportImageAct->setStatusTip(tr("Export the visible part of the network to image")); networkExportImageAct->setWhatsThis(tr("Export to Image\n\n" "Exports the visible part of the current social network to an image")); connect(networkExportImageAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportImageDialog())); networkExportPNGAct = new QAction( QIcon(":/images/export_photo_48px.svg"), tr("Export to &PNG..."), this); networkExportPNGAct->setStatusTip(tr("Export visible network to PNG image")); networkExportPNGAct->setWhatsThis(tr("Export to PNG \n\n" "Exports the social network to a PNG image")); connect(networkExportPNGAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportPNG())); networkExportPDFAct = new QAction( QIcon(":/images/export_pdf_48px.svg"), tr("E&xport to PDF..."), this); networkExportPDFAct->setStatusTip(tr("Export the visible part of the network to a PDF file")); networkExportPDFAct->setWhatsThis(tr("Export to PDF\n\n" "Exports the visible part of the current social network to a PDF document.")); connect(networkExportPDFAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportPDFDialog())); networkExportSMAct = new QAction( QIcon(":/images/file_download_48px.svg"), tr("&Adjacency Matrix"), this); networkExportSMAct->setStatusTip(tr("Export social network to an adjacency/sociomatrix file")); networkExportSMAct->setWhatsThis(tr("Export network to Adjacency format\n\n" "Exports the social network to an " "adjacency matrix-formatted file")); connect(networkExportSMAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportSM())); networkExportPajek = new QAction( QIcon(":/images/file_download_48px.svg"), tr("&Pajek"), this); networkExportPajek->setStatusTip(tr("Export social network to a Pajek-formatted file")); networkExportPajek->setWhatsThis(tr("Export Pajek \n\n" "Exports the social network to a Pajek-formatted file")); connect(networkExportPajek, SIGNAL(triggered()), this, SLOT(slotNetworkExportPajek())); networkExportListAct = new QAction( QIcon(":/images/file_download_48px.svg"), tr("&List"), this); networkExportListAct->setStatusTip(tr("Export to List-formatted file. ")); networkExportListAct->setWhatsThis(tr("Export List\n\n" "Exports the network to a List-formatted file")); connect(networkExportListAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportList())); networkExportDLAct = new QAction( QIcon(":/images/file_download_48px.svg"), tr("&DL..."), this); networkExportDLAct->setStatusTip(tr("Export network to UCINET-formatted file")); networkExportDLAct->setWhatsThis(tr("Export UCINET\n\n" "Exports the active network to a DL-formatted")); connect(networkExportDLAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportDL())); networkExportGWAct = new QAction( QIcon(":/images/file_download_48px.svg"), tr("&GW..."), this); networkExportGWAct->setStatusTip(tr("Export to GW-formatted file")); networkExportGWAct->setWhatsThis(tr("Export\n\n" "Exports the active network to a GW formatted file")); connect(networkExportGWAct, SIGNAL(triggered()), this, SLOT(slotNetworkExportGW())); networkCloseAct = new QAction(QIcon(":/images/close_24px.svg"), tr("&Close"), this); networkCloseAct->setShortcut(Qt::CTRL+Qt::Key_W); networkCloseAct->setStatusTip(tr("Close the actual network")); networkCloseAct->setWhatsThis(tr("Close \n\nCloses the actual network")); connect(networkCloseAct, SIGNAL(triggered()), this, SLOT(slotNetworkClose())); networkPrintAct = new QAction(QIcon(":/images/print_48px.svg"), tr("&Print"), this); networkPrintAct->setShortcut(Qt::CTRL+Qt::Key_P); networkPrintAct->setStatusTip(tr("Send the currrent social network to the printer")); networkPrintAct->setWhatsThis(tr("Print \n\n" "Sends whatever is viewable on " "the canvas to your printer. \n" "To print the whole social network, " "you might want to zoom-out.")); connect(networkPrintAct, SIGNAL(triggered()), this, SLOT(slotNetworkPrint())); networkQuitAct = new QAction(QIcon(":/images/exit_24px.svg"), tr("E&xit"), this); networkQuitAct->setShortcut(Qt::CTRL+Qt::Key_Q); networkQuitAct->setStatusTip(tr("Quit SocNetV. Are you sure?")); networkQuitAct->setWhatsThis(tr("Exit\n\n" "Quits the application")); connect(networkQuitAct, SIGNAL(triggered()), this, SLOT(close())); openTextEditorAct = new QAction(QIcon(":/images/text_edit_48px.svg"), tr("Open &Text Editor"),this); openTextEditorAct ->setShortcut(Qt::SHIFT+Qt::Key_F5); openTextEditorAct->setStatusTip(tr("Open a text editor " "to take notes, copy/paste network data, etc")); openTextEditorAct->setWhatsThis( tr("

Text Editor

" "

Opens a simple 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(slotNetworkTextEditor())); networkViewFileAct = new QAction(QIcon(":/images/code_48px.svg"), tr("&View Loaded File"),this); networkViewFileAct ->setShortcut(Qt::Key_F5); networkViewFileAct->setStatusTip(tr("Display the loaded social network file.")); networkViewFileAct->setWhatsThis(tr("View Loaded File\n\n" "Displays the loaded social network file ")); connect(networkViewFileAct, SIGNAL(triggered()), this, SLOT(slotNetworkFileView())); networkViewSociomatrixAct = new QAction(QIcon(":/images/sm.png"), tr("View &Adjacency Matrix"), this); networkViewSociomatrixAct ->setShortcut(Qt::Key_F6); networkViewSociomatrixAct->setStatusTip(tr("Display the adjacency matrix of the network.")); networkViewSociomatrixAct->setWhatsThis( tr("

View Adjacency Matrix

" "

Displays the adjacency matrix of the active network.

" "

The adjacency matrix of a social network is a matrix " "where each element a(i,j) is equal to the weight " "of the arc from actor (node) i to actor j. " "

If the actors are not connected, then a(i,j)=0.

")); connect(networkViewSociomatrixAct, SIGNAL(triggered()), this, SLOT(slotNetworkViewSociomatrix())); networkViewSociomatrixPlotAct = new QAction(QIcon(":/images/adjacencyplot.png"), tr("P&lot Adjacency Matrix (text)"), this); networkViewSociomatrixPlotAct ->setShortcut(Qt::SHIFT + Qt::Key_F6); networkViewSociomatrixPlotAct->setStatusTip( tr("Plots the adjacency matrix in a text file using unicode characters.")); networkViewSociomatrixPlotAct->setWhatsThis( tr("

Plot Adjacency Matrix (text)

" "

Plots the adjacency matrix in a text file using " "unicode characters.

" "

In every element (i,j) of the \"image\", " "a black square means actors i and j are connected" "whereas a white square means they are disconnected.

" )); connect(networkViewSociomatrixPlotAct, SIGNAL(triggered()), this, SLOT(slotNetworkViewSociomatrixPlotText())); networkDataSetSelectAct = new QAction(QIcon(":/images/science_48px.svg"), tr("Create From &Known Data Sets"), this); networkDataSetSelectAct ->setShortcut(Qt::Key_F7); networkDataSetSelectAct->setStatusTip( tr("Create a social network using one of the \'famous\' " "social network data sets included in SocNetV.")); networkDataSetSelectAct->setWhatsThis( tr("

Famous Data Sets

" "

SocNetV includes a number of known " "(also called famous) data sets in Social Network Analysis, " "such as Krackhardt's high-tech managers, etc. " "Click this menu item or press F7 to select a data set.

" )); connect(networkDataSetSelectAct, SIGNAL(triggered()), this, SLOT(slotNetworkDataSetSelect())); networkRandomScaleFreeAct = new QAction( QIcon(":/images/scalefree.png"), tr("Scale-free"), this); networkRandomScaleFreeAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_S) ); networkRandomScaleFreeAct->setStatusTip( tr("Create a random network with power-law degree distribution.")); networkRandomScaleFreeAct->setWhatsThis( tr("

Scale-free (power-law)

" "

A scale-free network is a network whose degree distribution " "follows a power law." " SocNetV generates random scale-free networks according to the " " Barabási–Albert (BA) model using a preferential attachment mechanism.

")); connect(networkRandomScaleFreeAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomScaleFreeDialog())); networkRandomSmallWorldAct = new QAction(QIcon(":/images/sw.png"), tr("Small World"), this); networkRandomSmallWorldAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_W) ); networkRandomSmallWorldAct->setStatusTip(tr("Create a small-world random network.")); networkRandomSmallWorldAct ->setWhatsThis( tr("

Small World

" "

Creates a random small-world network, according to the " "Watts & Strogatz model.

" "

A small-world network has short average path lengths and " "high clustering coefficient.

")); connect(networkRandomSmallWorldAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomSmallWorldDialog())); networkRandomErdosRenyiAct = new QAction(QIcon(":/images/erdos.png"), tr("Erdős–Rényi"), this); networkRandomErdosRenyiAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_E) ); networkRandomErdosRenyiAct->setStatusTip( tr("Create a random network according to the Erdős–Rényi model")); networkRandomErdosRenyiAct->setWhatsThis( tr("

Erdős–Rényi

" "

Creates a random network either of G(n, p) model or G(n,M) model.

" "

The former model creates edges with Bernoulli trials (probability p).

" "

The latter creates a graph of exactly M edges.

")); connect(networkRandomErdosRenyiAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomErdosRenyiDialog())); networkRandomLatticeAct = new QAction(QIcon(":/images/lattice.png"), tr("Lattice"), this); networkRandomLatticeAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_T) ); networkRandomLatticeAct->setStatusTip(tr("Create a lattice network.")); networkRandomLatticeAct ->setWhatsThis( tr("

Lattice

" "

Creates a random lattice network

")); connect(networkRandomLatticeAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomLatticeDialog())); networkRandomRegularSameDegreeAct = new QAction(QIcon(":/images/net.png"), tr("d-Regular"), this); networkRandomRegularSameDegreeAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_R) ); networkRandomRegularSameDegreeAct->setStatusTip( tr("Create a d-regular random network, " "where every actor has the same degree d.")); networkRandomRegularSameDegreeAct->setWhatsThis( tr("

d-Regular

" "

Creates a random network where each actor has the same " "number d of neighbours, aka the same degree d.

")); connect(networkRandomRegularSameDegreeAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomRegularDialog())); networkRandomLatticeRingAct = new QAction( QIcon(":/images/net1.png"), tr("Ring Lattice"), this); networkRandomLatticeRingAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_L) ); networkRandomLatticeRingAct->setStatusTip(tr("Create a ring lattice random network.")); networkRandomLatticeRingAct->setWhatsThis( tr("

Ring Lattice

" "

Creates a ring lattice random network.

" "

A ring lattice is a graph with N vertices each connected to d neighbors, d / 2 on each side.

")); connect(networkRandomLatticeRingAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomRingLattice())); networkRandomGaussianAct = new QAction(tr("Gaussian"), this); networkRandomGaussianAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_G) ); networkRandomGaussianAct->setStatusTip(tr("Create a Gaussian distributed random network.")); networkRandomGaussianAct->setWhatsThis(tr("Gaussian \n\nCreates a random network of Gaussian distribution")); connect(networkRandomGaussianAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomGaussian())); networkWebCrawlerAct = new QAction(QIcon(":/images/webcrawler2.png"), tr("&Web Crawler"), this); networkWebCrawlerAct->setShortcut(Qt::SHIFT+Qt::Key_C); networkWebCrawlerAct->setEnabled(true); networkWebCrawlerAct->setStatusTip(tr("Create a network from all links found in a given website" "Shift+C")); networkWebCrawlerAct->setWhatsThis( tr("

Web Crawler

" "

Creates a network of linked webpages, starting " "from an initial webpage using the built-in Web Crawler.

" "

The web crawler visits the given URL (website or webpage) " "and parses its contents to find links to other pages (internal or external). " "If there are such links, it adds them to a list of URLs (called frontier). " "Then, all the URLs in the frontier list are visited in a FIFO order " "and parsed to find more links which are also added to frontier. " "The process repeats until it reaches user-defined " "limits:

" "

Maximum urls to visit (max nodes in the resulting network)

" "

Maximum links per page

" "

Except the initial url and the limits, you can also " "specify patterns of urls to include or exclude, " "types of links to follow (internal, external or both) as well as " "if you want delay between requests (strongly advised)

.")); connect(networkWebCrawlerAct, SIGNAL(triggered()), this, SLOT(slotNetworkWebCrawlerDialog())); /** Edit menu actions */ editDragModeSelectAct = new QAction(QIcon(":/images/cursor-pointer.svg"), tr("Select/Move"), this); editDragModeSelectAct->setCheckable(true); editDragModeSelectAct->setChecked(true); // editDragModeSelectAct->setShortcut(Qt::ALT + Qt::Key_Right); editDragModeSelectAct->setToolTip(tr("Toggle the Select/Move drag mode.")); editDragModeSelectAct->setStatusTip(tr("Enable this to be able to click and move items and also select them with a rubber band")); editDragModeSelectAct->setWhatsThis(tr("Drag Mode: Select/Move\n\n " "In this mode, you can interact with the canvas: a) left-click or right-click on items (i.e. nodes, edges), b) move them by dragging them with your mouse and c) select multiple items with a rubber band.")); editDragModeScrollAct = new QAction(QIcon(":/images/cursor-hand-drag.svg"), tr("Scroll/Pan"), this); editDragModeScrollAct->setCheckable(true); editDragModeScrollAct->setChecked(false); // editDragModeScrollAct->setShortcut(Qt::ALT + Qt::Key_Right); editDragModeScrollAct->setToolTip(tr("Toggle the Scrolling drag mode.")); editDragModeScrollAct->setStatusTip(tr("Enable this to easily scroll the canvas by dragging the mouse around.")); editDragModeScrollAct->setWhatsThis(tr("Drag Mode: Scrolling\n\n " "The cursor changes into a pointing hand, and dragging the mouse around will scroll the scrolbars. You will not be able to select any items or move them around.")); editRelationNextAct = new QAction(QIcon(":/images/chevron_right_48px.svg"), tr("Next Relation"), this); editRelationNextAct->setShortcut(Qt::ALT + Qt::Key_Right); editRelationNextAct->setToolTip(tr("Goto next graph relation (ALT+Right)")); editRelationNextAct->setStatusTip(tr("Load the next relation of the network (if any).")); editRelationNextAct->setWhatsThis(tr("Next Relation\n\nLoads the next relation of the network (if any)")); editRelationPreviousAct = new QAction(QIcon(":/images/chevron_left_48px.svg"), tr("Previous Relation"), this); editRelationPreviousAct->setShortcut(Qt::ALT + Qt::Key_Left); editRelationPreviousAct->setToolTip( tr("Goto previous graph relation (ALT+Left)")); editRelationPreviousAct->setStatusTip( tr("Load the previous relation of the network (if any).")); editRelationPreviousAct->setWhatsThis( tr("Previous Relation\n\n" "Loads the previous relation of the network (if any)")); editRelationAddAct = new QAction(QIcon(":/images/add_48px.svg"), tr("Add New Relation"), this); editRelationAddAct->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_N); editRelationAddAct->setToolTip( tr("Add a new relation to the active graph (Ctrl+Shift+N)")); editRelationAddAct->setStatusTip( tr("Add a new relation to the network. " "Nodes will be preserved, edges will be removed. ")); editRelationAddAct->setWhatsThis( tr("Add New Relation\n\n" "Adds a new relation to the active network. " "Nodes will be preserved, edges will be removed. ")); editRelationRenameAct = new QAction(QIcon(":/images/relation_edit_48px.svg"), tr("Rename Relation"), this); editRelationRenameAct->setToolTip(tr("Rename current relation")); editRelationRenameAct->setStatusTip(tr("Rename the current relation of the network (if any).")); editRelationRenameAct->setWhatsThis(tr("Rename Relation\n\n" "Renames the current relation of the network (if any).")); zoomInAct = new QAction(QIcon(":/images/zoom_in_24px.svg"), tr("Zoom In"), this); zoomInAct->setStatusTip(tr("Zoom in. Better, use the canvas button or press Ctrl++ or press Cltr and use mouse wheel.")); zoomInAct->setToolTip(tr("Zoom in. Better, use the canvas button or (Ctrl++)")); zoomInAct->setWhatsThis(tr("Zoom In.\n\nZooms in the actual network")); zoomOutAct = new QAction(QIcon(":/images/zoom_in_24px.svg"), tr("Zoom Out"), this); zoomOutAct->setStatusTip(tr("Zoom out. Better, use the canvas button or press Ctrl+- or press Cltr and use mouse wheel.")); zoomOutAct->setToolTip(tr("Zoom in. Better, use the canvas button or (Ctrl+-)")); zoomOutAct->setWhatsThis(tr("Zoom Out.\n\nZooms out of the actual network")); editRotateLeftAct = new QAction(QIcon(":/images/rotate_left_48px.svg"), tr("Rotate counterclockwise"), this); editRotateLeftAct->setToolTip(tr("Rotate counterclockwise. Better, use the canvas button or (Ctrl+Left Arrow)")); editRotateLeftAct->setStatusTip(tr("Rotate counterclockwise. Better, use the canvas button or Ctrl+Left Arrow")); editRotateLeftAct ->setWhatsThis(tr("Rotates the network counterclockwise (Ctrl+Left Arrow)")); editRotateRightAct = new QAction(QIcon(":/images/rotate_right_48px.svg"), tr("Rotate clockwise"), this); editRotateRightAct->setStatusTip(tr("Rotate clockwise. Better, use the canvas button or (Ctrl+Right Arrow)")); editRotateRightAct->setToolTip(tr("Rotate clockwise. Better, use the canvas button or (Ctrl+Right Arrow)")); editRotateRightAct ->setWhatsThis(tr("Rotates the network clockwise (Ctrl+Right Arrow)")); editResetSlidersAct = new QAction(QIcon(":/images/refresh_48px.svg"), tr("Reset Zoom and Rotation"), this); editResetSlidersAct->setStatusTip(tr("Reset zoom and rotation to zero (Ctrl+0)")); editResetSlidersAct->setToolTip(tr("Reset zoom and rotation to zero (Ctrl+0)")); editResetSlidersAct->setWhatsThis(tr("Reset zoom and rotation to zero (Ctrl+0)")); editNodeSelectAllAct = new QAction(QIcon(":/images/select_all_48px.svg"), tr("Select All"), this); editNodeSelectAllAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A)); editNodeSelectAllAct->setStatusTip(tr("Select all nodes")); editNodeSelectAllAct->setWhatsThis(tr("Select All\n\nSelects all nodes in the network")); connect(editNodeSelectAllAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectAll())); editNodeSelectNoneAct = new QAction(QIcon(":/images/selectnone.png"), tr("Deselect All"), this); editNodeSelectNoneAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A)); editNodeSelectNoneAct->setStatusTip(tr("Deselect all nodes")); editNodeSelectNoneAct->setWhatsThis(tr("Deselect all\n\n Clears the node selection")); connect(editNodeSelectNoneAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectNone())); editNodeFindAct = new QAction(QIcon(":/images/search_48px.svg"), tr("Find Nodes "), this); editNodeFindAct->setShortcut(Qt::CTRL + Qt::Key_F); editNodeFindAct->setToolTip(tr("Find and select one or more actors by their number or label.")); editNodeFindAct->setStatusTip(tr("Find and select one or more actors by number or label. ")); editNodeFindAct->setWhatsThis(tr("Find Node\n\n" "Finds one or more nodes by their number or label and " "highlights them by doubling its size. ")); connect(editNodeFindAct, SIGNAL(triggered()), this, SLOT(slotEditNodeFindDialog()) ); editNodeAddAct = new QAction(QIcon(":/images/node_add_48px.svg"), tr("Add Node"), this); editNodeAddAct->setShortcut(Qt::CTRL + Qt::Key_Period); editNodeAddAct->setStatusTip(tr("Add a new node to the network. " "Alternately, press Ctrl+. or double-click on the canvas. ")); editNodeAddAct->setToolTip( tr("Add a new node to the network (Ctrl+.). \n\n" "You can also create a new node \n" "in a specific position by double-clicking.") ); editNodeAddAct->setWhatsThis( tr("Add new node\n\n" "Adds a new node to the network (Ctrl+.). \n\n" "Alternately, you can create a new node " "in a specific position by double-clicking " "on that spot of the canvas.") ); connect(editNodeAddAct, SIGNAL(triggered()), this, SLOT(slotEditNodeAdd())); editNodeRemoveAct = new QAction(QIcon(":/images/node_remove_48px.svg"),tr("Remove Node"), this); editNodeRemoveAct ->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Period); //Single key shortcuts with backspace or del do no work in Mac http://goo.gl/7hz7Dx editNodeRemoveAct->setToolTip(tr("Remove selected node(s). \n\n" "If no nodes are selected, you will be prompted " "for a node number. ")); editNodeRemoveAct->setStatusTip(tr("Remove selected node(s). If no nodes are selected, " "you will be prompted for a node number. ")); editNodeRemoveAct->setWhatsThis( tr("Remove node\n\n" "Removes selected node(s) from the network (Ctrl+Alt+.). \n" "Alternately, you can remove a node by right-clicking on it. \n" "If no nodes are selected, you will be prompted for a node number. ") ); connect(editNodeRemoveAct, SIGNAL(triggered()), this, SLOT(slotEditNodeRemove())); editNodePropertiesAct = new QAction(QIcon(":/images/node_properties_24px.svg"),tr("Selected Node Properties"), this); editNodePropertiesAct ->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Period ); editNodePropertiesAct->setToolTip(tr("Change the basic properties of the selected node(s) \n\n" "There must be some nodes on the canvas!")); editNodePropertiesAct->setStatusTip(tr("Change the basic properties of the selected node(s) -- " "There must be some nodes on the canvas!")); editNodePropertiesAct->setWhatsThis(tr("Selected Node Properties\n\n" "If there are some nodes on the canvas, " " opens a properties dialog to edit " "their label, size, color, shape etc. \n" "You must have some node selected.")); connect(editNodePropertiesAct, SIGNAL(triggered()), this, SLOT(slotEditNodePropertiesDialog())); editNodeSelectedToCliqueAct = new QAction(QIcon(":/images/cliquenew.png"), tr("Create a clique from selected nodes "), this); editNodeSelectedToCliqueAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_C)); editNodeSelectedToCliqueAct->setStatusTip(tr("Connect all selected nodes with edges to create a clique -- " "There must be some nodes selected!")); editNodeSelectedToCliqueAct->setWhatsThis(tr("Clique from Selected Nodes\n\n" "Adds all possible edges between selected nodes, " "so that they become a complete subgraph (clique)\n" "You must have some nodes selected.")); connect(editNodeSelectedToCliqueAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToClique())); editNodeSelectedToStarAct = new QAction(QIcon(":/images/subgraphstar.png"), tr("Create a star from selected nodes "), this); editNodeSelectedToStarAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_S)); editNodeSelectedToStarAct->setStatusTip(tr("Connect selected nodes with edges/arcs to create a star -- " "There must be some nodes selected!")); editNodeSelectedToStarAct->setWhatsThis(tr("Star from Selected Nodes\n\n" "Adds edges between selected nodes, " "so that they become a star subgraph.\n" "You must have some nodes selected.")); connect(editNodeSelectedToStarAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToStar())); editNodeSelectedToCycleAct = new QAction(QIcon(":/images/subgraphcycle.png"), tr("Create a cycle from selected nodes "), this); editNodeSelectedToCycleAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_Y)); editNodeSelectedToCycleAct->setStatusTip(tr("Connect selected nodes with edges/arcs to create a star -- " "There must be some nodes selected!")); editNodeSelectedToCycleAct->setWhatsThis(tr("Cycle from Selected Nodes\n\n" "Adds edges between selected nodes, " "so that they become a cycle subgraph.\n" "You must have some nodes selected.")); connect(editNodeSelectedToCycleAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToCycle())); editNodeSelectedToLineAct = new QAction(QIcon(":/images/subgraphline.png"), tr("Create a line from selected nodes "), this); editNodeSelectedToLineAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_Y)); editNodeSelectedToLineAct->setStatusTip(tr("Connect selected nodes with edges/arcs to create a line-- " "There must be some nodes selected!")); editNodeSelectedToLineAct->setWhatsThis(tr("Line from Selected Nodes\n\n" "Adds edges between selected nodes, " "so that they become a line subgraph.\n" "You must have some nodes selected.")); connect(editNodeSelectedToLineAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToLine())); editNodeColorAll = new QAction(QIcon(":/images/colorize_48px.svg"), tr("Change All Nodes Color (this session)"), this); editNodeColorAll->setStatusTip(tr("Choose a new color for all nodes (in this session only).")); editNodeColorAll->setWhatsThis(tr("Nodes Color\n\n" "Changes all nodes color at once. \n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeColorAll, SIGNAL(triggered()), this, SLOT(slotEditNodeColorAll()) ); editNodeSizeAllAct = new QAction(QIcon(":/images/size_select_24px.svg"), tr("Change All Nodes Size (this session)"), this); editNodeSizeAllAct->setStatusTip(tr("Change the size of all nodes (in this session only)")); editNodeSizeAllAct->setWhatsThis(tr("Change All Nodes Size\n\n" "Click to select and apply a new size for all nodes at once. \n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeSizeAllAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSizeAll()) ); editNodeShapeAll = new QAction(QIcon(":/images/format_shapes_48px.svg"), tr("Change All Nodes Shape (this session)"), this); editNodeShapeAll->setStatusTip(tr("Change the shape of all nodes (this session only)")); editNodeShapeAll->setWhatsThis(tr("Change All Nodes Shape\n\n" "Click to select and apply a new shape for all nodes at once." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeShapeAll, SIGNAL(triggered()), this, SLOT(slotEditNodeShape()) ); editNodeNumbersSizeAct = new QAction(QIcon(":/images/nodenumbersize.png"), tr("Change All Node Numbers Size (this session)"), this); editNodeNumbersSizeAct->setStatusTip(tr("Change the font size of the numbers of all nodes" "(in this session only)")); editNodeNumbersSizeAct->setWhatsThis(tr("Change Node Numbers Size\n\n" "Click to select and apply a new font size for all node numbers" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeNumbersSizeAct, SIGNAL(triggered()), this, SLOT( slotEditNodeNumberSize( )) ); editNodeNumbersColorAct = new QAction(QIcon(":/images/format_color_text_48px.svg"), tr("Change All Node Numbers Color (this session)"), this); editNodeNumbersColorAct->setStatusTip(tr("Change the color of the numbers of all nodes." "(in this session only)")); editNodeNumbersColorAct->setWhatsThis(tr("Node Numbers Color\n\n" "Click to select and apply a new color " "to all node numbers." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeNumbersColorAct, SIGNAL(triggered()), this, SLOT(slotEditNodeNumbersColor())); editNodeLabelsSizeAct = new QAction(QIcon(":/images/format_textsize_48px.svg"), tr("Change All Node Labels Size (this session)"), this); editNodeLabelsSizeAct->setStatusTip(tr("Change the font size of the labels of all nodes" "(this session only)")); editNodeLabelsSizeAct->setWhatsThis(tr("Node Labels Size\n\n" "Click to select and apply a new font-size to all node labels" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeLabelsSizeAct, SIGNAL(triggered()), this, SLOT(slotEditNodeLabelSize()) ); editNodeLabelsColorAct = new QAction(QIcon(":/images/format_color_text_48px.svg"), tr("Change All Node Labels Color (this session)"), this); editNodeLabelsColorAct->setStatusTip(tr("Change the color of the labels of all nodes " "(for this session only)")); editNodeLabelsColorAct->setWhatsThis(tr("Labels Color\n\n" "Click to select and apply a new color to all node labels." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeLabelsColorAct, SIGNAL(triggered()), this, SLOT(slotEditNodeLabelsColor())); editEdgeAddAct = new QAction(QIcon(":/images/edge_add_48px.svg"), tr("Add Edge (arc)"),this); editEdgeAddAct->setShortcut(Qt::CTRL + Qt::Key_Slash); editEdgeAddAct->setStatusTip(tr("Add a directed edge (arc) from a node to another")); editEdgeAddAct->setToolTip( tr("Add a new edge from a node to another (Ctrl+/).\n\n" "You can also create an edge between two nodes \n" "by double-clicking or middle-clicking on them consecutively.")); editEdgeAddAct->setWhatsThis( tr("Add edge\n\n" "Adds a new edge from a node to another (Ctrl+/).\n\n" "Alternately, you can create a new edge between two nodes " "by double-clicking or middle-clicking on them consecutively.") ); connect(editEdgeAddAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeAdd())); editEdgeRemoveAct = new QAction(QIcon(":/images/edge_remove_48px.svg"), tr("Remove Edge"), this); editEdgeRemoveAct ->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Slash); editEdgeRemoveAct ->setToolTip(tr("Remove selected edges from the network (Ctrl+Alt+/). \n\n" "If no edge has been clicked or selected, you will be prompted \n" "to enter edge source and target nodes for the edge to remove.")); editEdgeRemoveAct->setStatusTip(tr("Remove selected Edge(s) (Ctrl+Alt+/)")); editEdgeRemoveAct->setWhatsThis(tr("Remove Edge\n\n" "Removes edges from the network (Ctrl+Alt+/). \n" "If one or more edges has been clicked or selected, they are removed. " "Otherwise, you will be prompted to enter edge source and target " "nodes for the edge to remove.")); connect(editEdgeRemoveAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeRemove())); editEdgeLabelAct = new QAction(QIcon(":/images/format_textsize_48px.svg"), tr("Change Edge Label"), this); editEdgeLabelAct->setStatusTip(tr("Change the Label of an Edge")); editEdgeLabelAct->setWhatsThis(tr("Change Edge Label\n\n" "Changes the label of an Edge")); connect(editEdgeLabelAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeLabel())); editEdgeColorAct = new QAction(QIcon(":/images/colorize_48px.svg"),tr("Change Edge Color"), this); editEdgeColorAct->setStatusTip(tr("Change the Color of an Edge")); editEdgeColorAct->setWhatsThis(tr("Change Edge Color\n\n" "Changes the Color of an Edge")); connect(editEdgeColorAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeColor())); editEdgeWeightAct = new QAction(QIcon(":/images/line_weight_48px.svg") ,tr("Change Edge Weight"), this); editEdgeWeightAct->setStatusTip(tr("Change the weight of an Edge")); editEdgeWeightAct->setWhatsThis(tr("Edge Weight\n\n" "Changes the Weight of an Edge")); connect(editEdgeWeightAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeWeight())); editEdgeColorAllAct = new QAction(QIcon(":/images/colorize_48px.svg"), tr("Change All Edges Color"), this); editEdgeColorAllAct->setStatusTip(tr("Change the color of all Edges.")); editEdgeColorAllAct->setWhatsThis(tr("All Edges Color\n\n" "Changes the color of all Edges")); connect(editEdgeColorAllAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeColorAll())); editEdgeSymmetrizeAllAct= new QAction(QIcon(":/images/symmetrize.png"), tr("Symmetrize All Directed Edges"), this); editEdgeSymmetrizeAllAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_S)); editEdgeSymmetrizeAllAct->setStatusTip(tr("Make all directed ties to be reciprocated (thus, a symmetric graph).")); editEdgeSymmetrizeAllAct->setWhatsThis( tr("

Symmetrize Directed Edges

" "

Makes all directed arcs in this relation to be reciprocated: " "

If there is an arc from node A to node B \n" "then a new arc from node B to node A is created \n" "with the same weight.

" "

The result is a symmetric network.

")); connect(editEdgeSymmetrizeAllAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeSymmetrizeAll())); editEdgeSymmetrizeStrongTiesAct= new QAction(QIcon(":/images/symmetrize_48px.svg"), tr("Symmetrize by Strong Ties"), this); editEdgeSymmetrizeStrongTiesAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_T)); editEdgeSymmetrizeStrongTiesAct->setStatusTip(tr("Create a new symmetric relation by counting reciprocated ties only (strong ties).")); editEdgeSymmetrizeStrongTiesAct->setWhatsThis( tr("Symmetrize Edges by examing Strong Ties\n\n" "Creates a new symmetric relation by keeping strong ties only. \n" "That is, a strong tie exists between actor A and actor B \n" "only when both arcs A->B and B->A are present. \n" "If the network is multi-relational, it asks you whether \n" "ties in the current relation or all relations are to be considered. \n" "The resulting relation is symmetric.")); connect(editEdgeSymmetrizeStrongTiesAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeSymmetrizeStrongTies())); //TODO Separate action for Directed/Undirected graph drawing (without changing all existing edges). editEdgeUndirectedAllAct= new QAction( tr("Undirected Edges"), this); editEdgeUndirectedAllAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_U)); editEdgeUndirectedAllAct->setStatusTip(tr("Enable to transform all arcs to undirected edges and hereafter work with undirected edges .")); editEdgeUndirectedAllAct->setWhatsThis( tr("Undirected Edges\n\n" "Transforms all directed arcs to undirected edges. \n" "The result is a undirected and symmetric network." "After that, every new edge you add, will be undirected too." "If you disable this, then all edges become directed again.")); editEdgeUndirectedAllAct->setCheckable(true); editEdgeUndirectedAllAct->setChecked(false); connect(editEdgeUndirectedAllAct, SIGNAL(triggered(bool)), this, SLOT(slotEditEdgeUndirectedAll(bool))); editEdgesCocitationAct= new QAction(QIcon(":/images/cocitation_48px.svg"), tr("Cocitation Network"), this); editEdgesCocitationAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_C)); editEdgesCocitationAct->setStatusTip(tr("Create a new symmetric relation by " "connecting actors that are cocitated by others.")); editEdgesCocitationAct->setWhatsThis( tr("Symmetrize Edges by examing Strong Ties\n\n" "Creates a new symmetric relation by connecting actors " "that are cocitated by others. \n" "In the new relation, an edge will exist between actor i and " "actor j only if C(i,j) > 0, where C the Cocitation Matrix. " "Thus the actor pairs cited by more common neighbors will appear " "with a stronger tie between them than pairs those cited by fewer " "common neighbors. " "The resulting relation is symmetric.")); connect(editEdgesCocitationAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeSymmetrizeCocitation())); editEdgeDichotomizeAct= new QAction(QIcon(":/images/filter_list_48px.svg"), tr("Dichotomize Valued Edges"), this); editEdgeDichotomizeAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_D)); editEdgeDichotomizeAct->setStatusTip(tr("Create a new binary relation/graph in a valued network " "using edge dichotomization.")); editEdgeDichotomizeAct->setWhatsThis( tr("Dichotomize Edges\n\n" "Creates a new binary relation in a valued network using " "edge dichotomization according to a given threshold value. \n" "In the new dichotomized relation, an edge will exist between actor i and " "actor j only if e(i,j) > threshold, where threshold is a user-defined value." "Thus the dichotomization procedure is as follows: " "Choose a threshold value, set all ties with equal or higher values " "to equal one, and all lower to equal zero." "The result is a binary (dichotomized) graph. " "The process is also known as compression and slicing")); connect(editEdgeDichotomizeAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeDichotomizationDialog())); 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\n" "Transforms network so that nodes become Edges and vice versa")); connect(transformNodes2EdgesAct, SIGNAL(triggered()), this, SLOT(slotEditTransformNodes2Edges())); 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\n" "Filters Nodes of some value out of the network.")); connect(filterNodesAct, SIGNAL(triggered()), this, SLOT(slotFilterNodes())); editFilterNodesIsolatesAct = new QAction(tr("Disable Isolate Nodes"), this); editFilterNodesIsolatesAct->setEnabled(true); editFilterNodesIsolatesAct->setCheckable(true); editFilterNodesIsolatesAct->setChecked(false); editFilterNodesIsolatesAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_F)); editFilterNodesIsolatesAct->setStatusTip(tr("Temporarily filter out nodes with no edges")); editFilterNodesIsolatesAct->setWhatsThis(tr("Filter Isolate Nodes\n\n" "Enables or disables displaying of isolate nodes. " "Isolate nodes are those with no edges...")); connect(editFilterNodesIsolatesAct, SIGNAL(toggled(bool)), this, SLOT(slotEditFilterNodesIsolates(bool))); editFilterEdgesByWeightAct = new QAction(QIcon(":/images/filter_list_48px.svg"), tr("Filter Edges by Weight"), this); editFilterEdgesByWeightAct->setEnabled(true); editFilterEdgesByWeightAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_F)); editFilterEdgesByWeightAct->setStatusTip(tr("Temporarily filter edges of some weight out of the network")); editFilterEdgesByWeightAct->setWhatsThis(tr("Filter Edges\n\n" "Filters Edge of some specific weight out of the network.")); connect(editFilterEdgesByWeightAct , SIGNAL(triggered()), this, SLOT(slotEditFilterEdgesByWeightDialog())); editFilterEdgesUnilateralAct = new QAction(tr("Disable unilateral edges"), this); editFilterEdgesUnilateralAct->setEnabled(true); editFilterEdgesUnilateralAct->setCheckable(true); editFilterEdgesUnilateralAct->setChecked(false); editFilterEdgesUnilateralAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_R)); editFilterEdgesUnilateralAct->setStatusTip(tr("Temporarily disable all unilateral (non-reciprocal) edges in this relation. Keeps only \"strong\" ties.")); editFilterEdgesUnilateralAct->setWhatsThis(tr("Unilateral edges\n\n" "In directed networks, a tie between two actors " "is unilateral when only one actor identifies the other " "as connected (i.e. friend, vote, etc). " "A unilateral tie is depicted as a single arc. " "These ties are considered weak, as opposed to " "reciprocal ties where both actors identify each other as connected. " "Strong ties are depicted as either a single undirected edge " "or as two reciprocated arcs between two nodes. " "By selecting this option, all unilateral edges in this relation will be disabled.")); connect(editFilterEdgesUnilateralAct , SIGNAL(triggered(bool)), this, SLOT(slotEditFilterEdgesUnilateral(bool))); /** 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(slotLayoutColorationStrongStructural()) ); 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(slotLayoutColorationRegular()) );//TODO layoutRandomAct = new QAction( tr("Random"),this); layoutRandomAct->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_0); layoutRandomAct->setStatusTip(tr("Layout the network actors in random positions.")); layoutRandomAct->setWhatsThis(tr("Random Layout\n\n " "This layout algorithm repositions all " "network actors in random positions.")); connect(layoutRandomAct, SIGNAL(triggered()), this, SLOT(slotLayoutRandom())); layoutRandomRadialAct = new QAction(tr("Random Circles"), this); layoutRandomRadialAct->setShortcut(Qt::CTRL+Qt::ALT+Qt::Key_0); layoutRandomRadialAct ->setStatusTip(tr("Layout the network in random concentric circles")); layoutRandomRadialAct-> setWhatsThis( tr("Random Circles Layout\n\n Repositions the nodes randomly on circles")); connect(layoutRandomRadialAct, SIGNAL(triggered()), this, SLOT(slotLayoutRadialRandom())); layoutRadialProminence_DC_Act = new QAction( tr("Degree Centrality"), this); layoutRadialProminence_DC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_1); layoutRadialProminence_DC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Degree Centrality.")); layoutRadialProminence_DC_Act-> setWhatsThis( tr( "Degree Centrality (DC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Degree Centrality score. " "Nodes with higher DC are closer to the centre." )); connect(layoutRadialProminence_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex()) ); layoutRadialProminence_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutRadialProminence_CC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_2); layoutRadialProminence_CC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Closeness Centrality.")); layoutRadialProminence_CC_Act-> setWhatsThis( tr( "Closeness Centrality (CC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Closeness Centrality. " "Nodes having higher CC are closer to the centre." )); connect(layoutRadialProminence_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutRadialProminence_IRCC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_3); layoutRadialProminence_IRCC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Influence Range Closeness Centrality.")); layoutRadialProminence_IRCC_Act-> setWhatsThis( tr("Influence Range Closeness Centrality (IRCC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their IRCC score. " "Nodes having higher IRCC are closer to the centre." )); connect(layoutRadialProminence_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutRadialProminence_BC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_4); layoutRadialProminence_BC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Betweenness Centrality.")); layoutRadialProminence_BC_Act-> setWhatsThis( tr("Betweenness Centrality (BC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Betweenness Centrality. " "Nodes having higher BC are closer to the centre." )); connect(layoutRadialProminence_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_SC_Act = new QAction( tr("Stress Centrality"), this); layoutRadialProminence_SC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_5); layoutRadialProminence_SC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Stress Centrality.")); layoutRadialProminence_SC_Act-> setWhatsThis( tr("Stress Centrality (SC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Stress Centrality score. " "Nodes having higher SC are closer to the centre." )); connect(layoutRadialProminence_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutRadialProminence_EC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_6); layoutRadialProminence_EC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Eccentricity Centrality (aka Harary Graph Centrality).")); layoutRadialProminence_EC_Act-> setWhatsThis( tr("Eccentricity Centrality (EC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Eccentricity Centrality " "(aka Harary Graph Centrality) score. " "Nodes having higher EC are closer to the centre." )); connect(layoutRadialProminence_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_PC_Act = new QAction( tr("Power Centrality"), this); layoutRadialProminence_PC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_7); layoutRadialProminence_PC_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Power Centrality.")); layoutRadialProminence_PC_Act-> setWhatsThis( tr("Power Centrality (PC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Power Centrality score. " "Nodes having higher PC are closer to the centre." )); connect(layoutRadialProminence_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_IC_Act = new QAction( tr("Information Centrality"), this); layoutRadialProminence_IC_Act->setEnabled(true); layoutRadialProminence_IC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_8); layoutRadialProminence_IC_Act->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Information Centrality.")); layoutRadialProminence_IC_Act-> setWhatsThis( tr("Information Centrality (IC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Information Centrality score. " "Nodes of higher IC are closer to the centre." )); connect(layoutRadialProminence_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_EVC_Act = new QAction( tr("Eigenvector Centrality"), this); layoutRadialProminence_EVC_Act->setEnabled(true); layoutRadialProminence_EVC_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_9); layoutRadialProminence_EVC_Act->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Eigenvector Centrality.")); layoutRadialProminence_EVC_Act-> setWhatsThis( tr("Eigenvector Centrality (EVC) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Eigenvector Centrality score. " "Nodes of higher EVC are closer to the centre." )); connect(layoutRadialProminence_EVC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_DP_Act = new QAction( tr("Degree Prestige"), this); layoutRadialProminence_DP_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_I); layoutRadialProminence_DP_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Degree Prestige (inDegree).")); layoutRadialProminence_DP_Act-> setWhatsThis( tr("Degree Prestige (DP) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their inDegree score. " "Nodes having higher DP are closer to the centre." )); connect(layoutRadialProminence_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutRadialProminence_PRP_Act ->setEnabled(true); layoutRadialProminence_PRP_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_K); layoutRadialProminence_PRP_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their PRP index.")); layoutRadialProminence_PRP_Act-> setWhatsThis( tr("PageRank Prestige (PRP) Radial Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their PageRank score. " "Nodes having higher PRP are closer to the centre." )); connect(layoutRadialProminence_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutRadialProminence_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutRadialProminence_PP_Act ->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_Y); layoutRadialProminence_PP_Act ->setStatusTip( tr("Place all nodes on concentric circles of radius inversely " "proportional to their Proximity Prestige.")); layoutRadialProminence_PP_Act-> setWhatsThis( tr("Proximity Prestige (PP) Radial 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(layoutRadialProminence_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutRadialByProminenceIndex())); layoutLevelProminence_DC_Act = new QAction( tr("Degree Centrality"), this); layoutLevelProminence_DC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_1); layoutLevelProminence_DC_Act ->setStatusTip( tr("Place all nodes on horizontal levels of height " "proportional to their Degree Centrality.")); layoutLevelProminence_DC_Act-> setWhatsThis( tr("Degree Centrality (DC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their DC score. " "Nodes having higher DC are closer to the top.\n\n" ) ); connect(layoutLevelProminence_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex()) ); layoutLevelProminence_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutLevelProminence_CC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_2); layoutLevelProminence_CC_Act ->setStatusTip( tr("Place all nodes on horizontal levels of height " "proportional to their Closeness Centrality.")); layoutLevelProminence_CC_Act-> setWhatsThis( tr("Closeness Centrality (CC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Closeness Centrality score. " "Nodes of higher CC are closer to the top.\n\n" "This layout can be computed only for connected graphs. " )); connect(layoutLevelProminence_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutLevelProminence_IRCC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_3); layoutLevelProminence_IRCC_Act ->setStatusTip( tr("Place all nodes on horizontal levels of height " "proportional to their Influence Range Closeness Centrality.")); layoutLevelProminence_IRCC_Act-> setWhatsThis( tr("Influence Range Closeness Centrality (IRCC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their IRCC score. " "Nodes having higher IRCC are closer to the top.\n\n" "This layout can be computed for not connected graphs. " )); connect(layoutLevelProminence_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutLevelProminence_BC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_4); layoutLevelProminence_BC_Act ->setStatusTip( tr("Place all nodes on horizontal levels of height " "proportional to their Betweenness Centrality.")); layoutLevelProminence_BC_Act-> setWhatsThis( tr("Betweenness Centrality (BC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Betweenness Centrality score. " "Nodes having higher BC are closer to the top." )); connect(layoutLevelProminence_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_SC_Act = new QAction( tr("Stress Centrality"), this); layoutLevelProminence_SC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_5); layoutLevelProminence_SC_Act ->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their Stress Centrality.")); layoutLevelProminence_SC_Act-> setWhatsThis( tr("Stress Centrality (SC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Stress Centrality score. " "Nodes having higher SC are closer to the top." )); connect(layoutLevelProminence_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutLevelProminence_EC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_6); layoutLevelProminence_EC_Act ->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their Eccentricity Centrality (aka Harary Graph Centrality).")); layoutLevelProminence_EC_Act-> setWhatsThis( tr("Eccentricity Centrality (EC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Eccentricity Centrality " "(aka Harary Graph Centrality) score. " "Nodes having higher EC are closer to the top." )); connect(layoutLevelProminence_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_PC_Act = new QAction( tr("Power Centrality"), this); layoutLevelProminence_PC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_7); layoutLevelProminence_PC_Act ->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their Power Centrality.")); layoutLevelProminence_PC_Act-> setWhatsThis( tr("Power Centrality (PC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Power Centrality score. " "Nodes having higher PC are closer to the top." )); connect(layoutLevelProminence_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_IC_Act = new QAction( tr("Information Centrality"), this); layoutLevelProminence_IC_Act ->setEnabled(true); layoutLevelProminence_IC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_8); layoutLevelProminence_IC_Act ->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their Information Centrality.")); layoutLevelProminence_IC_Act-> setWhatsThis( tr("Information Centrality (IC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Information Centrality score. " "Nodes having higher IC are closer to the top." )); connect(layoutLevelProminence_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_EVC_Act = new QAction( tr("Eigenvector Centrality"), this); layoutLevelProminence_EVC_Act ->setEnabled(true); layoutLevelProminence_EVC_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_9); layoutLevelProminence_EVC_Act ->setStatusTip( tr( "Place nodes on horizontal levels of height " "proportional to their Eigenvector Centrality.")); layoutLevelProminence_EVC_Act-> setWhatsThis( tr("Eigenvector Centrality (EVC) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Eigenvector Centrality score. " "Nodes having higher EVC are closer to the top." )); connect(layoutLevelProminence_EVC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_DP_Act = new QAction( tr("Degree Prestige"), this); layoutLevelProminence_DP_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_I); layoutLevelProminence_DP_Act ->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their Degree Prestige.")); layoutLevelProminence_DP_Act-> setWhatsThis( tr("Degree Prestige (DP) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Degree Prestige score. " "Nodes having higher DP are closer to the top." )); connect(layoutLevelProminence_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutLevelProminence_PRP_Act->setEnabled(true); layoutLevelProminence_PRP_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_K); layoutLevelProminence_PRP_Act->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their PageRank Prestige.")); layoutLevelProminence_PRP_Act-> setWhatsThis( tr("PageRank Prestige (PRP) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their PageRank Prestige score. " "Nodes having higher PRP are closer to the top." )); connect(layoutLevelProminence_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevelProminence_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutLevelProminence_PP_Act->setEnabled(true); layoutLevelProminence_PP_Act->setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_Y); layoutLevelProminence_PP_Act->setStatusTip( tr("Place nodes on horizontal levels of height " "proportional to their Proximity Prestige.")); layoutLevelProminence_PP_Act-> setWhatsThis( tr("Proximity Prestige (PP) Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their Proximity Prestige score. " "Nodes having higher PP are closer to the top." )); connect(layoutLevelProminence_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutNodeSizeProminence_DC_Act = new QAction( tr("Degree Centrality"), this); layoutNodeSizeProminence_DC_Act->setShortcut(Qt::ALT+ Qt::Key_1); layoutNodeSizeProminence_DC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Degree Centrality.")); layoutNodeSizeProminence_DC_Act-> setWhatsThis( tr( "Degree Centrality (DC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their DC (inDegree) score. \n\n" "Nodes having higher DC will appear bigger." ) ); connect(layoutNodeSizeProminence_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex()) ); layoutNodeSizeProminence_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutNodeSizeProminence_CC_Act->setShortcut(Qt::ALT+ Qt::Key_2); layoutNodeSizeProminence_CC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Closeness Centrality.")); layoutNodeSizeProminence_CC_Act-> setWhatsThis( tr("Closeness Centrality (CC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their CC score. " "Nodes of higher CC will appear bigger.\n\n" "This layout can be computed only for connected graphs. " )); connect(layoutNodeSizeProminence_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutNodeSizeProminence_IRCC_Act->setShortcut(Qt::ALT+ Qt::Key_3); layoutNodeSizeProminence_IRCC_Act ->setStatusTip( tr("Resize all nodes to be proportional " "to their Influence Range Closeness Centrality.")); layoutNodeSizeProminence_IRCC_Act-> setWhatsThis( tr("Influence Range Closeness Centrality (IRCC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their IRCC score. " "Nodes having higher IRCC will appear bigger.\n\n" "This layout can be computed for not connected graphs. " )); connect(layoutNodeSizeProminence_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutNodeSizeProminence_BC_Act->setShortcut(Qt::ALT+ Qt::Key_4); layoutNodeSizeProminence_BC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Betweenness Centrality.")); layoutNodeSizeProminence_BC_Act-> setWhatsThis( tr("Betweenness Centrality (BC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Betweenness Centrality score. " "Nodes having higher BC will appear bigger." )); connect(layoutNodeSizeProminence_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_SC_Act = new QAction( tr("Stress Centrality"), this); layoutNodeSizeProminence_SC_Act->setShortcut(Qt::ALT+ Qt::Key_5); layoutNodeSizeProminence_SC_Act ->setStatusTip( tr( "Resize all nodes to be " "proportional to their Stress Centrality.")); layoutNodeSizeProminence_SC_Act-> setWhatsThis( tr("Stress Centrality (SC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Stress Centrality score. " "Nodes having higher SC will appear bigger." )); connect(layoutNodeSizeProminence_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutNodeSizeProminence_EC_Act->setShortcut(Qt::ALT+ Qt::Key_6); layoutNodeSizeProminence_EC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Eccentricity Centrality (aka Harary Graph Centrality).")); layoutNodeSizeProminence_EC_Act-> setWhatsThis( tr("Eccentricity Centrality (EC) NodeSizes Layout\n\n" "Changes the size of all nodes to be " "proportional to their Eccentricity Centrality (aka Harary Graph Centrality) score. " "Nodes having higher EC will appear bigger." )); connect(layoutNodeSizeProminence_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_PC_Act = new QAction( tr("Power Centrality"), this); layoutNodeSizeProminence_PC_Act->setShortcut(Qt::ALT+ Qt::Key_7); layoutNodeSizeProminence_PC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Power Centrality.")); layoutNodeSizeProminence_PC_Act-> setWhatsThis( tr("Power Centrality (PC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Power Centrality score. " "Nodes having higher PC will appear bigger." )); connect(layoutNodeSizeProminence_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_IC_Act = new QAction( tr("Information Centrality"), this); layoutNodeSizeProminence_IC_Act ->setEnabled(true); layoutNodeSizeProminence_IC_Act->setShortcut(Qt::ALT+ Qt::Key_8); layoutNodeSizeProminence_IC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Information Centrality.")); layoutNodeSizeProminence_IC_Act-> setWhatsThis( tr("Information Centrality (IC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Information Centrality score. " "Nodes having higher IC will appear bigger." )); connect(layoutNodeSizeProminence_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_EVC_Act = new QAction( tr("Eigenvector Centrality"), this); layoutNodeSizeProminence_EVC_Act ->setEnabled(true); layoutNodeSizeProminence_EVC_Act->setShortcut(Qt::ALT+ Qt::Key_9); layoutNodeSizeProminence_EVC_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Eigenvector Centrality.")); layoutNodeSizeProminence_EVC_Act-> setWhatsThis( tr("Eigenvector Centrality (EVC) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Eigenvector Centrality score. " "Nodes having higher EVC will appear bigger." )); connect(layoutNodeSizeProminence_EVC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_DP_Act = new QAction( tr("Degree Prestige"), this); layoutNodeSizeProminence_DP_Act->setShortcut(Qt::ALT + Qt::Key_I); layoutNodeSizeProminence_DP_Act ->setStatusTip( tr("Resize all nodes to be " "proportional to their Degree Prestige.")); layoutNodeSizeProminence_DP_Act-> setWhatsThis( tr("Degree Prestige (DP) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Degree Prestige score. " "Nodes having higher DP will appear bigger." )); connect(layoutNodeSizeProminence_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutNodeSizeProminence_PRP_Act->setEnabled(true); layoutNodeSizeProminence_PRP_Act->setShortcut(Qt::ALT+ Qt::Key_K); layoutNodeSizeProminence_PRP_Act->setStatusTip( tr("Resize all nodes to be " "proportional to their PageRank Prestige.")); layoutNodeSizeProminence_PRP_Act-> setWhatsThis( tr("PageRank Prestige (PRP) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their PageRank Prestige score. " "Nodes having higher PRP will appear bigger." )); connect(layoutNodeSizeProminence_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeSizeProminence_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutNodeSizeProminence_PP_Act->setEnabled(true); layoutNodeSizeProminence_PP_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_S, Qt::CTRL + Qt::Key_R) //Qt::ALT + Qt::Key_Y ); layoutNodeSizeProminence_PP_Act->setStatusTip( tr("Resize all nodes to be " "proportional to their Proximity Prestige.")); layoutNodeSizeProminence_PP_Act-> setWhatsThis( tr("Proximity Prestige (PP) Node Size Layout\n\n" "Changes the size of all nodes to be " "proportional to their Proximity Prestige score. " "Nodes having higher PP will appear bigger." )); connect(layoutNodeSizeProminence_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeSizeByProminenceIndex())); layoutNodeColorProminence_DC_Act = new QAction( tr("Degree Centrality"), this); layoutNodeColorProminence_DC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_1) ); layoutNodeColorProminence_DC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Degree Centrality.")); layoutNodeColorProminence_DC_Act-> setWhatsThis( tr("Degree Centrality (DC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their DC (inDegree) score. \n\n" "Nodes having higher DC will have warmer color (i.e. red)." ) ); connect(layoutNodeColorProminence_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex()) ); layoutNodeColorProminence_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutNodeColorProminence_CC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_2) ); layoutNodeColorProminence_CC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Closeness Centrality.")); layoutNodeColorProminence_CC_Act-> setWhatsThis( tr("Closeness Centrality (CC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their CC score. " "Nodes of higher CC will have warmer color (i.e. red).\n\n" "This layout can be computed only for connected graphs. " )); connect(layoutNodeColorProminence_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutNodeColorProminence_IRCC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_3) ); layoutNodeColorProminence_IRCC_Act ->setStatusTip( tr("Change the color of all nodes to proportional " "to their Influence Range Closeness Centrality.")); layoutNodeColorProminence_IRCC_Act-> setWhatsThis( tr("Influence Range Closeness Centrality (IRCC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their IRCC score. " "Nodes having higher IRCC will have warmer color (i.e. red).\n\n" "This layout can be computed for not connected graphs. " )); connect(layoutNodeColorProminence_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutNodeColorProminence_BC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_4) ); layoutNodeColorProminence_BC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Betweenness Centrality.")); layoutNodeColorProminence_BC_Act-> setWhatsThis( tr("Betweenness Centrality (BC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their Betweenness Centrality score. " "Nodes having higher BC will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_SC_Act = new QAction( tr("Stress Centrality"), this); layoutNodeColorProminence_SC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_5) ); layoutNodeColorProminence_SC_Act ->setStatusTip( tr( "Change the color of all nodes to " "reflect their Stress Centrality.")); layoutNodeColorProminence_SC_Act-> setWhatsThis( tr("Stress Centrality (SC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their Stress Centrality score. " "Nodes having higher SC will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutNodeColorProminence_EC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_6) ); layoutNodeColorProminence_EC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Eccentricity Centrality (aka Harary Graph Centrality).")); layoutNodeColorProminence_EC_Act-> setWhatsThis( tr("Eccentricity Centrality (EC) NodeColors Layout\n\n" "Changes the color of all nodes to " "reflect their Eccentricity Centrality (aka Harary Graph Centrality) score. " "Nodes having higher EC will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_PC_Act = new QAction( tr("Power Centrality"), this); layoutNodeColorProminence_PC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_7) ); layoutNodeColorProminence_PC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Power Centrality.")); layoutNodeColorProminence_PC_Act-> setWhatsThis( tr("Power Centrality (PC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their Power Centrality score. " "Nodes having higher PC will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_IC_Act = new QAction( tr("Information Centrality"), this); layoutNodeColorProminence_IC_Act ->setEnabled(true); layoutNodeColorProminence_IC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_8) ); layoutNodeColorProminence_IC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Information Centrality.")); layoutNodeColorProminence_IC_Act-> setWhatsThis( tr("Information Centrality (IC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their Information Centrality score. " "Nodes having higher IC will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_EVC_Act = new QAction( tr("Eigenvector Centrality"), this); layoutNodeColorProminence_EVC_Act ->setEnabled(true); layoutNodeColorProminence_EVC_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_9) ); layoutNodeColorProminence_EVC_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Eigenvector Centrality.")); layoutNodeColorProminence_EVC_Act-> setWhatsThis( tr("Eigenvector Centrality (EVC) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their Eigenvector Centrality score. " "Nodes having higher EVC will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_EVC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_DP_Act = new QAction( tr("Degree Prestige"), this); layoutNodeColorProminence_DP_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_D) ); layoutNodeColorProminence_DP_Act ->setStatusTip( tr("Change the color of all nodes to " "reflect their Degree Prestige.")); layoutNodeColorProminence_DP_Act-> setWhatsThis( tr("Degree Prestige (DP) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their Degree Prestige score. " "Nodes having higher DP will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutNodeColorProminence_PRP_Act->setEnabled(true); layoutNodeColorProminence_PRP_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_R) ); layoutNodeColorProminence_PRP_Act->setStatusTip( tr("Change the color of all nodes to " "reflect their PageRank Prestige.")); layoutNodeColorProminence_PRP_Act-> setWhatsThis( tr("PageRank Prestige (PRP) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their PageRank Prestige score. " "Nodes having higher PRP will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutNodeColorProminence_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutNodeColorProminence_PP_Act->setEnabled(true); layoutNodeColorProminence_PP_Act->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_C, Qt::CTRL + Qt::Key_P) ); layoutNodeColorProminence_PP_Act->setStatusTip( tr("Change the color of all nodes to " "reflect their Proximity Prestige.")); layoutNodeColorProminence_PP_Act-> setWhatsThis( tr("Proximity Prestige (PP) Node Color Layout\n\n" "Changes the color of all nodes to " "reflect their PageRank Prestige score. " "Nodes of higher PP will have warmer color (i.e. red)." )); connect(layoutNodeColorProminence_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutNodeColorByProminenceIndex())); layoutFDP_Eades_Act= new QAction(tr("Spring Embedder (Eades)"), this); layoutFDP_Eades_Act-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_E)); layoutFDP_Eades_Act->setStatusTip( tr("Layout Eades Spring-Gravitational model.")); layoutFDP_Eades_Act->setWhatsThis( tr("Spring Embedder Layout\n\n " "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. \n" "In our implementation, nodes are replaced by physical bodies " "(i.e. electrons) which exert repelling forces to each other, " "while edges are replaced by springs which exert attractive " "forces to the adjacent nodes. " "The nodes are placed in some initial layout and let go " "so that the spring forces move the system to a minimal energy state. " "The algorithm continues until the system retains an equilibrium state " "in which all forces cancel each other. ")); connect(layoutFDP_Eades_Act, SIGNAL(triggered(bool)), this, SLOT(slotLayoutSpringEmbedder())); layoutFDP_FR_Act= new QAction( tr("Fruchterman-Reingold"), this); layoutFDP_FR_Act-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_F)); layoutFDP_FR_Act->setStatusTip( tr("Repelling forces between all nodes, and attracting forces between adjacent nodes.")); layoutFDP_FR_Act->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(layoutFDP_FR_Act, SIGNAL(triggered()), this, SLOT(slotLayoutFruchterman())); layoutFDP_KamadaKawai_Act= new QAction( tr("Kamada-Kawai"), this); layoutFDP_KamadaKawai_Act-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_K)); layoutFDP_KamadaKawai_Act->setStatusTip( tr("Embeds the Kamada-Kawai FDP layout model, the best variant of the Spring Embedder family of models.")); layoutFDP_KamadaKawai_Act->setWhatsThis( tr( "

Kamada-Kawai

" "

The best variant of the Spring Embedder family of models. " "

In this the graph is considered to be a dynamic system where " "every edge is between two actors is a 'spring' of a desirable " "length, which corresponds to their graph theoretic distance.

" "

In this way, the optimal layout of the graph \n" "is the state with the minimum imbalance. The degree of " "imbalance is formulated as the total spring energy: " "the square summation of the differences between desirable " "distances and real ones for all pairs of vertices.

" )); connect(layoutFDP_KamadaKawai_Act, SIGNAL(triggered()), this, SLOT(slotLayoutKamadaKawai())); layoutGuidesAct = new QAction(QIcon(":/images/gridlines.png"), tr("Layout GuideLines"), this); layoutGuidesAct ->setStatusTip(tr("Toggles layout guidelines on or off.")); layoutGuidesAct->setWhatsThis(tr("Layout Guidelines\n\n" "Layout Guidelines are circular or horizontal lines \n" "usually created when embedding prominence-based \n" "visualization models on the network.\n" "Disable this checkbox to hide guidelines")); layoutGuidesAct->setCheckable(true); layoutGuidesAct->setChecked(true); /** Analysis menu actions */ analyzeMatrixAdjInvertAct = new QAction( QIcon(":/images/invertmatrix.png"), tr("Invert Adjacency Matrix"), this); analyzeMatrixAdjInvertAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_I) ); analyzeMatrixAdjInvertAct->setStatusTip(tr("Invert the adjacency matrix, if possible")); analyzeMatrixAdjInvertAct->setWhatsThis(tr("Invert Adjacency Matrix \n\n" "Inverts the adjacency matrix using linear algebra methods.")); connect(analyzeMatrixAdjInvertAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixAdjacencyInverse())); analyzeMatrixAdjTransposeAct = new QAction( QIcon(":/images/transposematrix.png"), tr("Transpose Adjacency Matrix"), this); analyzeMatrixAdjTransposeAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_T) ); analyzeMatrixAdjTransposeAct->setStatusTip(tr("View the transpose of adjacency matrix")); analyzeMatrixAdjTransposeAct->setWhatsThis(tr("Transpose Adjacency Matrix \n\n" "Computes and displays the adjacency matrix tranpose.")); connect(analyzeMatrixAdjTransposeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixAdjacencyTranspose())); analyzeMatrixAdjCocitationAct = new QAction( QIcon(":/images/cocitation.png"), tr("Cocitation Matrix"), this); analyzeMatrixAdjCocitationAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_C) ); analyzeMatrixAdjCocitationAct->setStatusTip(tr("Compute the Cocitation matrix of this network.")); analyzeMatrixAdjCocitationAct->setWhatsThis(tr("Cocitation Matrix \n\n " "Computes and displays the cocitation matrix of the network. " "The Cocitation matrix, C=A*A^T, is a NxN matrix where " "each element (i,j) is the number of actors that have " "outbound ties/links to both actors i and j. ")); connect(analyzeMatrixAdjCocitationAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixAdjacencyCocitation())); analyzeMatrixDegreeAct = new QAction( QIcon(":/images/degreematrix.png"), tr("Degree Matrix"), this); analyzeMatrixDegreeAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_D) ); analyzeMatrixDegreeAct->setStatusTip(tr("Compute the Degree matrix of the network")); analyzeMatrixDegreeAct->setWhatsThis(tr("Degree Matrix " "\n\n Compute the Degree matrix of the network.")); connect(analyzeMatrixDegreeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixDegree())); analyzeMatrixLaplacianAct = new QAction( QIcon(":/images/laplacian.png"), tr("Laplacian Matrix"), this); analyzeMatrixLaplacianAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_L) ); analyzeMatrixLaplacianAct->setStatusTip(tr("Compute the Laplacian matrix of the network")); analyzeMatrixLaplacianAct->setWhatsThis(tr("Laplacian Matrix \n\n" "Compute the Laplacian matrix of the network.")); connect(analyzeMatrixLaplacianAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixLaplacian())); analyzeGraphReciprocityAct = new QAction( QIcon(":/images/symmetry-edge.png"), tr("Reciprocity"), this); analyzeGraphReciprocityAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_R) ); analyzeGraphReciprocityAct->setStatusTip(tr("Compute the arc and dyad reciprocity of the network.")); analyzeGraphReciprocityAct->setWhatsThis( tr("Arc and Dyad Reciprocity\n\n" "The arc reciprocity of a network/graph is the fraction of " "reciprocated ties over all present ties of the graph. \n" "The dyad reciprocity of a network/graph is the fraction of " "actor pairs that have reciprocated ties over all connected " "pairs of actors. \n" "In a directed network, the arc reciprocity measures the proportion " "of directed edges that are bidirectional. If the reciprocity is 1, \n" "then the adjacency matrix is structurally symmetric. \n" "Likewise, in a directed network, the dyad reciprocity measures " "the proportion of connected actor dyads that have bidirectional ties " "between them. \n" "In an undirected graph, all edges are reciprocal. Thus the " "reciprocity of the graph is always 1. \n" "Reciprocity can be computed on undirected, directed, and weighted graphs." ) ); connect(analyzeGraphReciprocityAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeReciprocity())); analyzeGraphSymmetryAct = new QAction( QIcon(":/images/symmetry-edge.png"), tr("Symmetry Test"), this); analyzeGraphSymmetryAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_S) ); analyzeGraphSymmetryAct->setStatusTip(tr("Check whether the network is symmetric or not")); analyzeGraphSymmetryAct->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(analyzeGraphSymmetryAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeSymmetryCheck())); analyzeGraphDistanceAct = new QAction( QIcon(":/images/distance.png"), tr("Geodesic Distance between 2 nodes"), this ); analyzeGraphDistanceAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_G) ); analyzeGraphDistanceAct->setStatusTip( tr("Compute the length of the shortest path (geodesic distance) between 2 nodes.")); analyzeGraphDistanceAct->setWhatsThis( tr("Distance\n\n" "Computes the geodesic distance between two nodes." "In graph theory, the geodesic distance of two " "nodes is the length (number of edges) of the shortest path " "between them.")); connect(analyzeGraphDistanceAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeDistance())); analyzeMatrixDistancesGeodesicAct = new QAction(QIcon(":/images/dm.png"), tr("Geodesic Distances Matrix"),this); analyzeMatrixDistancesGeodesicAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_M) ); analyzeMatrixDistancesGeodesicAct-> setStatusTip( tr("Compute the matrix of geodesic distances between all pair of nodes.") ); analyzeMatrixDistancesGeodesicAct-> setWhatsThis( tr("Distances Matrix\n\n" "Computes the matrix of distances between all " "pairs of actors/nodes in the social network." "A distances matrix is a n x n 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(analyzeMatrixDistancesGeodesicAct, SIGNAL(triggered()), this, SLOT( slotAnalyzeMatrixDistances() ) ); analyzeMatrixGeodesicsAct = new QAction(QIcon(":/images/dm.png"), tr("Geodesics Matrix"),this); analyzeMatrixGeodesicsAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_P)); analyzeMatrixGeodesicsAct->setStatusTip(tr("Compute the number of shortest paths (geodesics) between each pair of nodes ")); analyzeMatrixGeodesicsAct->setWhatsThis( tr( "Geodesics Matrix\n\n" "Displays a n x n matrix, where the (i,j) element " "is the number of shortest paths (geodesics) between " "node i and node j. ") ); connect(analyzeMatrixGeodesicsAct, SIGNAL(triggered()), this, SLOT( slotAnalyzeMatrixGeodesics()) ); analyzeGraphDiameterAct = new QAction(QIcon(":/images/diameter.png"), tr("Graph Diameter"),this); analyzeGraphDiameterAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_D)); analyzeGraphDiameterAct->setStatusTip(tr("Compute the diameter of the network, " "the maximum geodesic distance between any actors.")); analyzeGraphDiameterAct->setWhatsThis(tr("Diameter\n\n " "The Diameter of a social network is the maximum geodesic distance " "(maximum shortest path length) between any two nodes of the network.")); connect(analyzeGraphDiameterAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeDiameter())); averGraphDistanceAct = new QAction(QIcon(":/images/avdistance.png"), tr("Average Distance"),this); averGraphDistanceAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_A)); averGraphDistanceAct->setStatusTip(tr("Compute the average length of shortest paths for all possible pairs of nodes.")); averGraphDistanceAct->setWhatsThis( tr("Average Distance\n\n " "Computes the average length of shortest paths (geodesics) " "between all pairs of network actors (vertices in the graph). " "It is a measure of the efficiency or compactness of the network.")); connect(averGraphDistanceAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeDistanceAverage())); analyzeGraphEccentricityAct = new QAction(QIcon(":/images/eccentricity.png"), tr("Eccentricity"),this); analyzeGraphEccentricityAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_E ) ); analyzeGraphEccentricityAct->setStatusTip(tr("Compute the Eccentricity of each actor and group Eccentricity")); analyzeGraphEccentricityAct->setWhatsThis(tr("Eccentricity\n\n" "The eccentricity of each node i in a network " "or graph is the largest geodesic distance " "between node i and any other node j. " "Therefore, it reflects how far, at most, " "is each node from every other node. \n" "The maximum eccentricity is the graph diameter " "while the minimum is the graph radius.\n" "This index can be calculated in both graphs " "and digraphs but is usually best suited " "for undirected graphs. \n" "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(analyzeGraphEccentricityAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeEccentricity())); analyzeGraphConnectednessAct = new QAction(QIcon(":/images/distance.png"), tr("Connectedness"), this); analyzeGraphConnectednessAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_C) ); analyzeGraphConnectednessAct->setStatusTip(tr("Check whether the network is a connected " "graph, a connected digraph or " "a disconnected graph/digraph...")); analyzeGraphConnectednessAct->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(analyzeGraphConnectednessAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeConnectedness())); analyzeGraphWalksAct = new QAction(QIcon(":/images/walk.png"), tr("Walks of a given length"),this); analyzeGraphWalksAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_W) ); analyzeGraphWalksAct->setStatusTip(tr("Compute the number of walks of a given length between any nodes.")); analyzeGraphWalksAct->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(analyzeGraphWalksAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeWalksLength() ) ); analyzeGraphWalksTotalAct = new QAction(QIcon(":/images/walk.png"), tr("Total Walks"),this); analyzeGraphWalksTotalAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_T) ); analyzeGraphWalksTotalAct->setStatusTip(tr("Calculate the total number of walks of every possible length between all nodes")); analyzeGraphWalksTotalAct->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(analyzeGraphWalksTotalAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeWalksTotal() ) ); analyzeMatrixReachabilityAct = new QAction(QIcon(":/images/walk.png"), tr("Reachability Matrix"),this); analyzeMatrixReachabilityAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_R)); analyzeMatrixReachabilityAct->setStatusTip(tr("Compute the Reachability Matrix of the network.")); analyzeMatrixReachabilityAct->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(analyzeMatrixReachabilityAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeReachabilityMatrix() ) ); clusteringCoefAct = new QAction(QIcon(":/images/clucof.png"), tr("Local and Network Clustering Coefficient"),this); clusteringCoefAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_L) ); clusteringCoefAct->setStatusTip(tr("Compute the Watts & Strogatz Clustering Coefficient for every actor and the network average.")); clusteringCoefAct->setWhatsThis(tr("Local and Network Clustering Coefficient\n\n" "The local Clustering Coefficient (Watts & Strogatz, 1998) " "of an actor quantifies how close " "the actor and her neighbors are to being a clique and " "can be used as an indication of network transitivity. \n")); connect(clusteringCoefAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeClusteringCoefficient() ) ); analyzeCommunitiesCliquesAct = new QAction(QIcon(":/images/clique.png"), tr("Clique Census"),this); analyzeCommunitiesCliquesAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_U, Qt::CTRL + Qt::Key_C)); analyzeCommunitiesCliquesAct->setStatusTip(tr("Compute the clique census: find all maximal connected subgraphs.")); analyzeCommunitiesCliquesAct->setWhatsThis(tr("Clique Census\n\n" "Produces the census of network cliques (maximal connected subgraphs), " "along with disaggregation by actor and co-membership information. ")); connect(analyzeCommunitiesCliquesAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCommunitiesCliqueCensus() ) ); analyzeCommunitiesTriadCensusAct = new QAction(QIcon(":/images/triad.png"), tr("Triad Census (M-A-N labeling)"),this); analyzeCommunitiesTriadCensusAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_U, Qt::CTRL + Qt::Key_T) ); analyzeCommunitiesTriadCensusAct->setStatusTip(tr("Calculate the triad census for all actors.")); analyzeCommunitiesTriadCensusAct->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 using the M-A-N labeling scheme. \n")); connect(analyzeCommunitiesTriadCensusAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCommunitiesTriadCensus() ) ); analyzeStrEquivalencePearsonAct = new QAction(QIcon(":/images/similarity.png"), tr("Pearson correlation coefficients"),this); analyzeStrEquivalencePearsonAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_P) ); analyzeStrEquivalencePearsonAct->setStatusTip( tr("Compute Pearson Correlation Coefficients between pairs of actors. " "Most useful with valued/weighted ties (non-binary). ")); analyzeStrEquivalencePearsonAct->setWhatsThis( tr("Pearson correlation coefficients\n\n" "Computes a correlation matrix, where the elements are the " "Pearson correlation coefficients between pairs of actors " "in terms of their tie profiles or distances (in, out or both). \n\n" "The Pearson product-moment correlation coefficient (PPMCC or PCC or Pearson's r)" "is a measure of the linear dependence/association between two variables X and Y. \n\n" "This correlation measure of similarity is particularly useful " "when ties are valued/weighted denoting strength, cost or probability.\n\n" "Note that in very sparse networks (very low density), measures such as" "\"exact matches\", \"correlation\" and \"distance\" " "will show little variation among the actors, causing " "difficulty in classifying the actors in structural equivalence classes.")); connect(analyzeStrEquivalencePearsonAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeStrEquivalencePearsonDialog() ) ); analyzeStrEquivalenceMatchesAct = new QAction(QIcon(":/images/similarity.png"), tr("Similarity by measure (Exact, Jaccard, Hamming, Cosine, Euclidean)"),this); analyzeStrEquivalenceMatchesAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_E) ); analyzeStrEquivalenceMatchesAct->setStatusTip(tr("Compute a pair-wise actor similarity " "matrix based on a measure of their ties (or distances) \"matches\" .")); analyzeStrEquivalenceMatchesAct->setWhatsThis( tr("Actor Similarity by measure\n\n" "Computes a pair-wise actor similarity matrix, where each element (i,j) is " "the ratio of tie (or distance) matches of actors i and j to all other actors. \n\n" "SocNetV supports the following matching measures: " "Simple Matching (Exact Matches)" "Jaccard Index (Positive Matches or Co-citation)" "Hamming distance" "Cosine similarity" "Euclidean distance" "For instance, if you select Exact Matches, a matrix element (i,j) = 0.5, " "means that actors i and j have the same ties present or absent " "to other actors 50% of the time. \n\n" "These measures of similarity are particularly useful " "when ties are binary (not valued).\n\n" "Note that in very sparse networks (very low density), measures such as" "\"exact matches\", \"correlation\" and \"distance\" " "will show little variation among the actors, causing " "difficulty in classifying the actors in structural equivalence classes.")); connect(analyzeStrEquivalenceMatchesAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeStrEquivalenceSimilarityMeasureDialog() ) ); analyzeStrEquivalenceTieProfileDissimilaritiesAct = new QAction(QIcon(":/images/dm.png"), tr("Tie Profile Dissimilarities/Distances"),this); analyzeStrEquivalenceTieProfileDissimilaritiesAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_T) ); analyzeStrEquivalenceTieProfileDissimilaritiesAct-> setStatusTip( tr("Compute tie profile dissimilarities/distances " "(Euclidean, Manhattan, Jaccard, Hamming) between all pair of nodes.") ); analyzeStrEquivalenceTieProfileDissimilaritiesAct-> setWhatsThis( tr("Tie Profile Dissimilarities/Distances\n\n" "Computes a matrix of tie profile distances/dissimilarities " "between all pairs of actors/nodes in the social network " "using an ordinary metric such as Euclidean distance, " "Manhattan distance, Jaccard distance or Hamming distance)." "The resulted distance matrix is a n x n matrix, in which the " "(i,j) element is the distance or dissimilarity between " "the tie profiles of node i and node j." ) ); connect(analyzeStrEquivalenceTieProfileDissimilaritiesAct, SIGNAL(triggered()), this, SLOT( slotAnalyzeStrEquivalenceDissimilaritiesDialog() ) ); analyzeStrEquivalenceClusteringHierarchicalAct = new QAction(QIcon(":/images/hierarchical.png"), tr("Hierarchical clustering"),this); analyzeStrEquivalenceClusteringHierarchicalAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_H)); analyzeStrEquivalenceClusteringHierarchicalAct->setStatusTip( tr("Perform agglomerative cluster analysis of the actors in the social network")); analyzeStrEquivalenceClusteringHierarchicalAct->setWhatsThis( tr("Hierarchical clustering\n\n" "Hierarchical clustering (or hierarchical cluster analysis, HCA) " "is a method of cluster analysis which builds a hierarchy " "of clusters, based on their elements dissimilarity. " "In SNA context these clusters usually consist of " "network actors. \n" "This method takes the social network distance matrix as input and uses " "the Agglomerative \"bottom up\" approach where each " "actor starts in its own cluster (Level 0). In each subsequent Level, " "as we move up the clustering hierarchy, a pair of clusters " "are merged into a larger cluster, until " "all actors end up in the same cluster. " "To decide which clusters should be combined at each level, a measure of " "dissimilarity between sets of observations is required. " "This measure consists of a metric for the distance between actors " "(i.e. manhattan distance) and a linkage criterion (i.e. single-linkage clustering). " "This linkage criterion (essentially a definition of distance between clusters), " "differentiates between the different HCA methods." "Note that the complexity of agglomerative clustering is O( n^2 log(n) ), " "therefore is too slow for large data sets." )); connect(analyzeStrEquivalenceClusteringHierarchicalAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeStrEquivalenceClusteringHierarchicalDialog() ) ); cDegreeAct = new QAction(tr("Degree Centrality (DC)"),this); cDegreeAct-> setShortcut(Qt::CTRL + Qt::Key_1); cDegreeAct ->setStatusTip(tr("Compute Degree Centrality indices for every actor 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(slotAnalyzeCentralityDegree())); cClosenessAct = new QAction(tr("Closeness Centrality (CC)"), this); cClosenessAct-> setShortcut(Qt::CTRL + Qt::Key_2); cClosenessAct ->setStatusTip( tr( "Compute Closeness Centrality indices for every actor 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(slotAnalyzeCentralityCloseness())); cInfluenceRangeClosenessAct = new QAction(tr("Influence Range Closeness Centrality (IRCC)"), this); cInfluenceRangeClosenessAct-> setShortcut(Qt::CTRL + Qt::Key_3); cInfluenceRangeClosenessAct ->setStatusTip( tr("Compute Influence Range Closeness Centrality indices for every actor " "focusing on how proximate each one is" "to others 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(slotAnalyzeCentralityClosenessIR())); cBetweennessAct = new QAction(tr("Betweenness Centrality (BC)"), this); cBetweennessAct-> setShortcut(Qt::CTRL + Qt::Key_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("Compute Betweenness Centrality indices and group Betweenness Centralization.")); connect(cBetweennessAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityBetweenness())); cStressAct = new QAction(tr("Stress Centrality (SC)"), this); cStressAct-> setShortcut(Qt::CTRL + Qt::Key_5); cStressAct->setStatusTip(tr("Compute Stress Centrality indices for every actor 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\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 although the weight of each edge (v,u) in E is always considered to be 1.")); connect(cStressAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityStress())); cEccentAct = new QAction(tr("Eccentricity Centrality (EC)"), this); cEccentAct-> setShortcut(Qt::CTRL + Qt::Key_6); cEccentAct->setStatusTip(tr("Compute Eccentricity Centrality (aka Harary Graph Centrality) scores for each node.")); cEccentAct->setWhatsThis( tr("Eccentricity Centrality (EC)\n\n " "This index is also known as Harary Graph Centrality. " "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(slotAnalyzeCentralityEccentricity())); cPowerAct = new QAction(tr("Gil and Schmidt Power Centrality (PC)"), this); cPowerAct-> setShortcut(Qt::CTRL + Qt::Key_7); cPowerAct->setStatusTip(tr("Compute Power Centrality indices (aka Gil-Schmidt Power Centrality) for every actor 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(slotAnalyzeCentralityPower())); cInformationAct = new QAction(tr("Information Centrality (IC)"), this); cInformationAct-> setShortcut(Qt::CTRL + Qt::Key_8); cInformationAct->setEnabled(true); cInformationAct->setStatusTip(tr("Compute 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\n" "This 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(slotAnalyzeCentralityInformation())); cEigenvectorAct = new QAction(tr("Eigenvector Centrality (EVC)"), this); cEigenvectorAct-> setShortcut(Qt::CTRL + Qt::Key_9); cEigenvectorAct->setEnabled(true); cEigenvectorAct->setStatusTip(tr("Compute Eigenvector Centrality indices and group Eigenvector Centralization")); cEigenvectorAct->setWhatsThis( tr("Eigenvector Centrality (EVC)\n\n" "Computes the Eigenvector centrality of each node in a social network " "which is defined as the ith element of the leading eigenvector " "of the adjacency matrix. The leading eigenvector is the " "eigenvector corresponding to the largest positive eigenvalue." "The Eigenvector Centrality, proposed by Bonacich (1989), is " "an extension of the simpler Degree Centrality because it gives " "each actor a score proportional to the scores of its neighbors. " "Thus, a node may be important, in terms of its EC, because it " "has lots of ties or it has fewer ties to important other nodes.")); connect(cEigenvectorAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityEigenvector())); cInDegreeAct = new QAction(tr("Degree Prestige (DP)"), this); cInDegreeAct->setStatusTip(tr("Compute Degree Prestige (InDegree) indices ")); cInDegreeAct-> setShortcut(Qt::CTRL + Qt::Key_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\n" "This 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(slotAnalyzePrestigeDegree())); cPageRankAct = new QAction(tr("PageRank Prestige (PRP)"), this); cPageRankAct-> setShortcut(Qt::CTRL + Qt::Key_K); cPageRankAct->setEnabled(true); cPageRankAct->setStatusTip(tr("Compute PageRank Prestige indices for every actor")); 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\n" "This 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(slotAnalyzePrestigePageRank())); cProximityPrestigeAct = new QAction(tr("Proximity Prestige (PP)"), this); cProximityPrestigeAct-> setShortcut(Qt::CTRL + Qt::Key_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\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(slotAnalyzePrestigeProximity())); /** Options menu actions */ optionsNodeNumbersVisibilityAct = new QAction( tr("Display Node Numbers"), this ); optionsNodeNumbersVisibilityAct->setStatusTip( tr("Toggle displaying of node numbers (this session only)")); optionsNodeNumbersVisibilityAct->setWhatsThis( tr("Display Node Numbers\n\n" "Enables or disables displaying of node numbers\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsNodeNumbersVisibilityAct->setCheckable (true); optionsNodeNumbersVisibilityAct->setChecked ( ( appSettings["initNodeNumbersVisibility"] == "true" ) ? true: false ); connect(optionsNodeNumbersVisibilityAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsNodeNumbersVisibility(bool))); optionsNodeNumbersInsideAct = new QAction(tr("Display Numbers Inside Nodes"), this ); optionsNodeNumbersInsideAct->setStatusTip( tr("Toggle displaying of numbers inside nodes (this session only)")); optionsNodeNumbersInsideAct->setWhatsThis( tr("Display Numbers Inside Nodes\n\n" "Enables or disables displaying node numbers inside nodes.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsNodeNumbersInsideAct->setCheckable (true); optionsNodeNumbersInsideAct->setChecked( ( appSettings["initNodeNumbersInside"] == "true" ) ? true: false ); connect(optionsNodeNumbersInsideAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsNodeNumbersInside(bool))); optionsNodeLabelsVisibilityAct= new QAction(tr("Display Node Labels"), this ); optionsNodeLabelsVisibilityAct->setStatusTip( tr("Toggle displaying of node labels (this session only)")); optionsNodeLabelsVisibilityAct->setWhatsThis( tr("Display Node Labels\n\n" "Enables or disables node labels.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsNodeLabelsVisibilityAct->setCheckable (true); optionsNodeLabelsVisibilityAct->setChecked( ( appSettings["initNodeLabelsVisibility"] == "true" ) ? true: false ); connect(optionsNodeLabelsVisibilityAct, SIGNAL(toggled(bool)), this, SLOT(slotOptionsNodeLabelsVisibility(bool))); optionsEdgesVisibilityAct = new QAction(tr("Display Edges"), this); optionsEdgesVisibilityAct->setStatusTip(tr("Toggle displaying edges (this session only)")); optionsEdgesVisibilityAct->setWhatsThis( tr("Display Edges\n\n" "Enables or disables displaying of edges" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgesVisibilityAct->setCheckable(true); optionsEdgesVisibilityAct->setChecked( (appSettings["initEdgesVisibility"] == "true") ? true: false ); connect(optionsEdgesVisibilityAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgesVisibility(bool)) ); optionsEdgeWeightNumbersAct = new QAction(tr("Display Edge Weights"), this); optionsEdgeWeightNumbersAct->setStatusTip( tr("Toggle displaying of numbers of edge weights (this session only)")); optionsEdgeWeightNumbersAct->setWhatsThis( tr("Display Edge Weights\n\n" "Enables or disables displaying edge weight numbers.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeWeightNumbersAct->setCheckable(true); connect(optionsEdgeWeightNumbersAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeWeightNumbersVisibility(bool)) ); optionsEdgeWeightConsiderAct = new QAction(tr("Consider Edge Weights in Calculations"), this); optionsEdgeWeightConsiderAct-> setStatusTip( tr("Toggle considering edge weights during calculations " "(i.e. distances, centrality, etc) (this session only)")); optionsEdgeWeightConsiderAct-> setWhatsThis( tr("Consider Edge Weights in Calculations\n\n" "Enables or disables considering edge weights during " "calculations (i.e. distances, centrality, etc).\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeWeightConsiderAct->setCheckable(true); optionsEdgeWeightConsiderAct->setChecked(false); connect(optionsEdgeWeightConsiderAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeWeightsDuringComputation(bool)) ); optionsEdgeLabelsAct = new QAction(tr("Display Edge Labels"), this); optionsEdgeLabelsAct->setStatusTip( tr("Toggle displaying of Edge labels, if any (this session only)")); optionsEdgeLabelsAct->setWhatsThis( tr("Display Edge Labes\n\n" "Enables or disables displaying edge labels.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeLabelsAct->setCheckable(true); optionsEdgeLabelsAct->setChecked( (appSettings["initEdgeLabelsVisibility"] == "true") ? true: false ); connect(optionsEdgeLabelsAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeLabelsVisibility(bool)) ); optionsEdgeArrowsAct = new QAction( tr("Display Edge Arrows"),this); optionsEdgeArrowsAct->setStatusTip( tr("Toggle displaying directional Arrows on edges (this session only)")); optionsEdgeArrowsAct->setWhatsThis( tr("Display edge Arrows\n\n" "Enables or disables displaying of arrows on edges.\n\n" "Useful if all links are reciprocal (undirected graph).\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeArrowsAct->setCheckable(true); optionsEdgeArrowsAct->setChecked( (appSettings["initEdgeArrows"]=="true") ? true: false ); connect(optionsEdgeArrowsAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeArrowsVisibility(bool)) ); optionsEdgeThicknessPerWeightAct = new QAction( tr("Edge Thickness reflects Weight"), this); optionsEdgeThicknessPerWeightAct->setStatusTip(tr("Draw edges as thick as their weights (if specified)")); optionsEdgeThicknessPerWeightAct->setWhatsThis( tr("Edge thickness reflects weight\n\n" "Click to toggle having all edges as thick as their weight (if specified)")); optionsEdgeThicknessPerWeightAct->setCheckable(true); optionsEdgeThicknessPerWeightAct->setChecked( (appSettings["initEdgeThicknessPerWeight"]=="true") ? true: false ); connect(optionsEdgeThicknessPerWeightAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeThicknessPerWeight(bool)) ); optionsEdgeThicknessPerWeightAct->setEnabled(false); drawEdgesBezier = new QAction( tr("Bezier Curves"), this); drawEdgesBezier->setStatusTip(tr("Draw Edges as Bezier curves")); drawEdgesBezier->setWhatsThis( tr("Edges Bezier\n\n" "Enable or disables drawing Edges as Bezier curves." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); drawEdgesBezier->setCheckable(true); drawEdgesBezier->setChecked ( (appSettings["initEdgeShape"]=="bezier") ? true: false ); drawEdgesBezier->setEnabled(false); connect(drawEdgesBezier, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgesBezier(bool)) ); changeBackColorAct = new QAction(QIcon(":/images/format_color_fill_48px.svg"), tr("Change Background Color"), this); changeBackColorAct->setStatusTip(tr("Change the canvasbackground color")); changeBackColorAct->setWhatsThis(tr("Background Color\n\n" "Changes the background color of the canvas")); connect(changeBackColorAct, SIGNAL(triggered()), this, SLOT(slotOptionsBackgroundColor())); backgroundImageAct = new QAction(QIcon(":/images/wallpaper_48px.svg"), tr("Background Image (this session)"), this); backgroundImageAct->setStatusTip( tr("Select and display a custom image in the background" "(for this session only)")); backgroundImageAct->setWhatsThis( tr("Background image\n\n" "Enable to select an image file from your computer, " "which will be displayed in the background instead of plain color." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); backgroundImageAct->setCheckable(true); backgroundImageAct->setChecked(false); connect(backgroundImageAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsBackgroundImageSelect(bool))); fullScreenModeAct = new QAction(QIcon(":/images/fullscreen_48px.svg"), tr("Full screen (this session)"), this); fullScreenModeAct->setShortcut(Qt::Key_F11); fullScreenModeAct->setStatusTip( tr("Toggle full screen mode (for this session only)")); fullScreenModeAct->setWhatsThis( tr("Full Screen Mode\n\n" "Enable to show application window in full screen mode. " "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); fullScreenModeAct->setCheckable(true); fullScreenModeAct->setChecked(false); connect(fullScreenModeAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsWindowFullScreen(bool))); openSettingsAct = new QAction(QIcon(":/images/settings_48px.svg"), tr("Settings"), this); openSettingsAct->setShortcut(Qt::CTRL + Qt::Key_Comma); openSettingsAct->setEnabled(true); openSettingsAct->setToolTip( tr("Open the Settings dialog where you can save your preferences " "for all future sessions")); openSettingsAct->setStatusTip( tr("Open the Settings dialog to save your preferences " "for all future sessions")); openSettingsAct->setWhatsThis( tr("Settings\n\n" "Opens the Settings dialog where you can edit and save settings " "permanently for all subsequent sessions.")); connect(openSettingsAct, SIGNAL(triggered()), this, SLOT(slotOpenSettingsDialog())); /** Help menu actions */ helpApp = new QAction(QIcon(":/images/help_48px.svg"), tr("Manual"), this); helpApp->setShortcut(Qt::Key_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(QIcon(":/images/tip_24px.svg"), 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(slotHelpTips())); helpCheckUpdatesApp = new QAction( QIcon(":/images/system_update_alt_48px.svg"), tr("Check for Updates"), this); helpCheckUpdatesApp->setStatusTip(tr("Open a browser to SocNetV website " "to check for a new version...")); helpCheckUpdatesApp->setWhatsThis(tr("Check Updates\n\n" "Open a browser to SocNetV website so " "that you can check yourself for updates")); connect(helpCheckUpdatesApp, SIGNAL(triggered()), this, SLOT(slotHelpCheckUpdateDialog())); helpSystemInfoAct = new QAction(QIcon(":/images/about_24px.svg"), tr("System Information"), this); helpSystemInfoAct->setEnabled(true); helpSystemInfoAct->setStatusTip(tr("Show information about your system")); helpSystemInfoAct->setWhatsThis( tr("

System Information

" "

Shows useful information about your system, " "which you can include in your bug reports.

")); connect(helpSystemInfoAct, SIGNAL(triggered()), this, SLOT(slotHelpSystemInfo())); helpAboutApp = new QAction(QIcon(":/images/about_24px.svg"), 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(QIcon(":/images/qt.png"), tr("About Qt"), this); helpAboutQt->setStatusTip(tr("About Qt")); helpAboutQt->setWhatsThis(tr("About\n\nAbout Qt")); connect(helpAboutQt, SIGNAL(triggered()), this, SLOT(slotAboutQt() ) ); qDebug()<< "MW::initActions() - Finished"; } /** * @brief Creates and populates the menu bar */ void MainWindow::initMenuBar() { qDebug()<< "MW::initMenuBar()"; /** menuBar entry networkMenu */ networkMenu = menuBar()->addMenu(tr("&Network")); networkMenu->addAction(networkNewAct); networkMenu->addAction(networkOpenAct); networkMenu->addSeparator(); recentFilesSubMenu = new QMenu(tr("Recent &files...")); recentFilesSubMenu ->setIcon(QIcon(":/images/recent_48px.svg")); for (int i = 0; i < MaxRecentFiles; ++i) recentFilesSubMenu->addAction(recentFileActs[i]); slotNetworkFileRecentUpdateActions(); networkMenu ->addMenu (recentFilesSubMenu ); networkMenu->addSeparator(); importSubMenu = new QMenu(tr("&Import ...")); importSubMenu->setIcon(QIcon(":/images/file_upload_48px.svg")); importSubMenu->addAction(networkImportGMLAct); importSubMenu->addAction(networkImportPajekAct); importSubMenu->addAction(networkImportAdjAct); importSubMenu->addAction(networkImportTwoModeSM); importSubMenu->addAction(networkImportListAct); importSubMenu->addAction(networkImportUcinetAct); importSubMenu->addAction(networkImportGraphvizAct); networkMenu ->addMenu (importSubMenu); networkMenu->addSeparator(); networkMenu->addAction (openTextEditorAct); networkMenu->addAction (networkViewFileAct); networkMenu->addSeparator(); networkMenu->addAction (networkViewSociomatrixAct); networkMenu->addAction (networkViewSociomatrixPlotAct); networkMenu->addSeparator(); networkMenu->addAction (networkDataSetSelectAct); networkMenu->addSeparator(); randomNetworkMenu = new QMenu(tr("Create &Random Network...")); randomNetworkMenu->setIcon(QIcon(":/images/random_48px.svg")); networkMenu ->addMenu (randomNetworkMenu); randomNetworkMenu->addAction (networkRandomScaleFreeAct); randomNetworkMenu->addAction (networkRandomSmallWorldAct); randomNetworkMenu->addAction (networkRandomErdosRenyiAct ); randomNetworkMenu->addAction (networkRandomLatticeAct); randomNetworkMenu->addAction (networkRandomRegularSameDegreeAct); randomNetworkMenu->addAction (networkRandomLatticeRingAct); // networkRandomGaussianAct->addTo(randomNetworkMenu); networkMenu->addSeparator(); networkMenu ->addAction(networkWebCrawlerAct); networkMenu ->addSeparator(); networkMenu ->addAction(networkSaveAct); networkMenu ->addAction(networkSaveAsAct); networkMenu ->addSeparator(); networkMenu->addAction (networkExportImageAct); networkMenu->addAction (networkExportPDFAct); networkMenu->addSeparator(); exportSubMenu = networkMenu ->addMenu(tr("Export to other...")); exportSubMenu->setIcon ( QIcon(":/images/file_download_48px.svg") ); exportSubMenu->addAction (networkExportSMAct); exportSubMenu->addAction (networkExportPajek); //exportSubMenu->addAction (networkExportList); //exportSubMenu->addAction (networkExportDL); //exportSubMenu->addAction (networkExportGW); networkMenu ->addSeparator(); networkMenu ->addAction(networkPrintAct); networkMenu ->addSeparator(); networkMenu ->addAction(networkCloseAct); networkMenu ->addAction(networkQuitAct); /** menuBar entry editMenu */ editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction (editRelationPreviousAct); editMenu->addAction (editRelationNextAct); editMenu->addAction (editRelationAddAct); editMenu->addAction (editRelationRenameAct); editMenu->addSeparator(); editMenu->addAction ( zoomInAct ); editMenu->addAction ( zoomOutAct ); editMenu->addSeparator(); editMenu->addAction ( editRotateLeftAct ); editMenu->addAction ( editRotateRightAct ); editMenu->addSeparator(); editMenu->addAction (editResetSlidersAct ); editMenu->addSeparator(); editNodeMenu = new QMenu(tr("Nodes...")); editNodeMenu->setIcon(QIcon(":/images/node_48px.svg")); editMenu->addMenu ( editNodeMenu ); editNodeMenu->addAction (editNodeSelectAllAct); editNodeMenu->addAction (editNodeSelectNoneAct); editNodeMenu->addSeparator(); editNodeMenu->addAction (editNodeFindAct); editNodeMenu->addAction (editNodeAddAct); editNodeMenu->addAction (editNodeRemoveAct); editNodeMenu->addSeparator(); editNodeMenu->addAction (editNodePropertiesAct); editNodeMenu->addSeparator(); editNodeMenu->addAction (editNodeSelectedToCliqueAct); editNodeMenu->addAction (editNodeSelectedToStarAct); editNodeMenu->addAction (editNodeSelectedToCycleAct); editNodeMenu->addAction (editNodeSelectedToLineAct); editNodeMenu->addSeparator(); editNodeMenu->addAction (editNodeColorAll); editNodeMenu->addAction (editNodeSizeAllAct); editNodeMenu->addAction (editNodeShapeAll); editNodeMenu->addSeparator(); editNodeMenu->addAction (editNodeNumbersSizeAct); editNodeMenu->addAction (editNodeNumbersColorAct); editNodeMenu->addSeparator(); editNodeMenu->addAction (editNodeLabelsSizeAct); editNodeMenu->addAction (editNodeLabelsColorAct); editEdgeMenu = new QMenu(tr("Edges...")); editEdgeMenu->setIcon(QIcon(":/images/edges_48px.svg")); editMenu-> addMenu (editEdgeMenu); editEdgeMenu->addAction(editEdgeAddAct); editEdgeMenu->addAction(editEdgeRemoveAct); editEdgeMenu->addSeparator(); editEdgeMenu->addAction (editEdgeUndirectedAllAct); editEdgeMenu->addSeparator(); editEdgeMenu->addAction (editEdgeSymmetrizeAllAct); editEdgeMenu->addSeparator(); editEdgeMenu->addAction (editEdgeSymmetrizeStrongTiesAct); editEdgeMenu->addAction (editEdgesCocitationAct); editEdgeMenu->addSeparator(); editEdgeMenu->addAction (editEdgeDichotomizeAct); editEdgeMenu->addSeparator(); editEdgeMenu->addAction(editEdgeLabelAct); editEdgeMenu->addAction(editEdgeColorAct); editEdgeMenu->addAction(editEdgeWeightAct); editEdgeMenu->addSeparator(); editEdgeMenu->addAction (editEdgeColorAllAct); // transformNodes2EdgesAct->addTo (editMenu); editMenu ->addSeparator(); filterMenu = new QMenu ( tr("Filter...")); filterMenu->setIcon(QIcon(":/images/filter_list_48px.svg")); editMenu ->addMenu(filterMenu); filterMenu->addAction(filterNodesAct ); filterMenu->addAction(editFilterNodesIsolatesAct ); filterMenu->addAction(editFilterEdgesByWeightAct ); filterMenu->addAction(editFilterEdgesUnilateralAct); /** menuBar entry: analyze menu */ analysisMenu = menuBar()->addMenu(tr("&Analyze")); matrixMenu = new QMenu(tr("Adjacency Matrix and Matrices...")); matrixMenu->setIcon(QIcon(":/images/sm.png")); analysisMenu->addMenu (matrixMenu); matrixMenu->addAction (networkViewSociomatrixAct); matrixMenu->addAction (networkViewSociomatrixPlotAct); matrixMenu->addSeparator(); matrixMenu->addAction (analyzeMatrixAdjInvertAct); matrixMenu->addSeparator(); matrixMenu->addAction(analyzeMatrixAdjTransposeAct); matrixMenu->addSeparator(); matrixMenu->addAction(analyzeMatrixAdjCocitationAct); matrixMenu->addSeparator(); matrixMenu->addAction (analyzeMatrixDegreeAct); matrixMenu->addAction (analyzeMatrixLaplacianAct); // analysisMenu->addAction (netDensity); analysisMenu->addSeparator(); cohesionMenu = new QMenu(tr("Cohesion...")); cohesionMenu->setIcon(QIcon(":/images/assessment_48px.svg")); analysisMenu->addMenu(cohesionMenu); cohesionMenu->addAction (analyzeGraphReciprocityAct); cohesionMenu->addAction (analyzeGraphSymmetryAct); cohesionMenu->addSection("Graph distances"); cohesionMenu->addAction (analyzeGraphDistanceAct); cohesionMenu->addAction (averGraphDistanceAct); cohesionMenu->addSeparator(); cohesionMenu->addAction (analyzeMatrixDistancesGeodesicAct); cohesionMenu->addAction (analyzeMatrixGeodesicsAct); cohesionMenu->addSeparator(); cohesionMenu->addAction (analyzeGraphEccentricityAct); cohesionMenu->addAction (analyzeGraphDiameterAct); cohesionMenu ->addSeparator(); cohesionMenu->addAction(analyzeGraphConnectednessAct); cohesionMenu ->addSeparator(); cohesionMenu->addAction (analyzeGraphWalksAct); cohesionMenu->addAction (analyzeGraphWalksTotalAct); cohesionMenu ->addSeparator(); cohesionMenu->addAction (analyzeMatrixReachabilityAct); cohesionMenu->addSeparator(); cohesionMenu->addAction (clusteringCoefAct); analysisMenu->addSeparator(); // CENTRALITIES centrlMenu = new QMenu(tr("Centrality and Prestige indices...")); centrlMenu->setIcon(QIcon(":/images/centrality_48px.svg")); analysisMenu->addMenu(centrlMenu); 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->addAction (cEigenvectorAct); centrlMenu->addSeparator(); centrlMenu->addAction (cInDegreeAct); centrlMenu->addAction (cPageRankAct); centrlMenu->addAction (cProximityPrestigeAct); analysisMenu->addSeparator(); // COMMUNITIES & SUBGROUPS communitiesMenu = new QMenu(tr("Communities and Subgroups...")); communitiesMenu->setIcon(QIcon(":/images/communities_48px.svg")); analysisMenu->addMenu(communitiesMenu); communitiesMenu->addAction (analyzeCommunitiesCliquesAct); communitiesMenu->addSeparator(); communitiesMenu->addAction (analyzeCommunitiesTriadCensusAct); analysisMenu->addSeparator(); // STRUCTURAL EQUIVALENCE strEquivalenceMenu = new QMenu(tr("Structural Equivalence...")); strEquivalenceMenu->setIcon(QIcon(":/images/similarity.png")); analysisMenu->addMenu (strEquivalenceMenu); strEquivalenceMenu->addAction (analyzeStrEquivalencePearsonAct); strEquivalenceMenu->addAction(analyzeStrEquivalenceMatchesAct); strEquivalenceMenu->addSeparator(); strEquivalenceMenu->addAction (analyzeStrEquivalenceTieProfileDissimilaritiesAct); strEquivalenceMenu->addSeparator(); strEquivalenceMenu->addAction (analyzeStrEquivalenceClusteringHierarchicalAct); /** 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...")); randomLayoutMenu ->setIcon(QIcon(":/images/random_48px.svg")); layoutMenu->addMenu (randomLayoutMenu ); randomLayoutMenu-> addAction(layoutRandomAct); randomLayoutMenu-> addAction( layoutRandomRadialAct ); layoutMenu->addSeparator(); layoutRadialProminenceMenu = new QMenu(tr("Radial by prominence index...")); layoutRadialProminenceMenu->setIcon(QIcon(":/images/radial_layout_48px.svg")); layoutMenu->addMenu (layoutRadialProminenceMenu); layoutRadialProminenceMenu->addAction (layoutRadialProminence_DC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_CC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_IRCC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_BC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_SC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_EC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_PC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_IC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_EVC_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_DP_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_PRP_Act); layoutRadialProminenceMenu->addAction (layoutRadialProminence_PP_Act); layoutMenu->addSeparator(); layoutLevelProminenceMenu = new QMenu (tr("On Levels by prominence index...")); layoutLevelProminenceMenu->setIcon(QIcon(":/images/layout_levels_24px.svg")); layoutMenu->addMenu (layoutLevelProminenceMenu); layoutLevelProminenceMenu->addAction (layoutLevelProminence_DC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_CC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_IRCC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_BC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_SC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_EC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_PC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_IC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_EVC_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_DP_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_PRP_Act); layoutLevelProminenceMenu->addAction (layoutLevelProminence_PP_Act); layoutMenu->addSeparator(); layoutNodeSizeProminenceMenu = new QMenu (tr("Node Size by prominence index...")); layoutNodeSizeProminenceMenu->setIcon(QIcon(":/images/node_size_48px.svg")); layoutMenu->addMenu (layoutNodeSizeProminenceMenu); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_DC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_CC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_IRCC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_BC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_SC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_EC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_PC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_IC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_EVC_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_DP_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_PRP_Act); layoutNodeSizeProminenceMenu->addAction (layoutNodeSizeProminence_PP_Act); layoutMenu->addSeparator(); layoutNodeColorProminenceMenu = new QMenu (tr("Node Color by prominence index...")); layoutNodeColorProminenceMenu->setIcon(QIcon(":/images/color_layout_48px.svg")); layoutMenu->addMenu (layoutNodeColorProminenceMenu); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_DC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_CC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_IRCC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_BC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_SC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_EC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_PC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_IC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_EVC_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_DP_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_PRP_Act); layoutNodeColorProminenceMenu->addAction (layoutNodeColorProminence_PP_Act); layoutMenu->addSeparator(); layoutForceDirectedMenu = new QMenu (tr("Force-Directed Placement...")); layoutForceDirectedMenu->setIcon(QIcon(":/images/force.png")); layoutMenu->addMenu (layoutForceDirectedMenu); layoutForceDirectedMenu->addAction (layoutFDP_KamadaKawai_Act); layoutForceDirectedMenu->addAction (layoutFDP_FR_Act); layoutForceDirectedMenu->addAction (layoutFDP_Eades_Act); layoutMenu->addSeparator(); layoutMenu->addAction (layoutGuidesAct); /** menuBar entry optionsMenu */ optionsMenu = menuBar()->addMenu(tr("&Options")); nodeOptionsMenu=new QMenu(tr("Nodes...")); nodeOptionsMenu->setIcon(QIcon(":/images/node_48px.svg")); optionsMenu->addMenu (nodeOptionsMenu); nodeOptionsMenu->addAction (optionsNodeNumbersVisibilityAct); nodeOptionsMenu->addAction (optionsNodeLabelsVisibilityAct); nodeOptionsMenu->addAction (optionsNodeNumbersInsideAct); edgeOptionsMenu=new QMenu(tr("Edges...")); edgeOptionsMenu->setIcon(QIcon(":/images/edges_48px.svg")); optionsMenu->addMenu (edgeOptionsMenu); edgeOptionsMenu->addAction (optionsEdgesVisibilityAct); edgeOptionsMenu->addSeparator(); edgeOptionsMenu->addAction (optionsEdgeWeightNumbersAct); edgeOptionsMenu->addAction (optionsEdgeWeightConsiderAct); edgeOptionsMenu->addAction (optionsEdgeThicknessPerWeightAct); edgeOptionsMenu->addSeparator(); edgeOptionsMenu->addAction (optionsEdgeLabelsAct); edgeOptionsMenu->addSeparator(); edgeOptionsMenu->addAction (optionsEdgeArrowsAct ); edgeOptionsMenu->addSeparator(); edgeOptionsMenu->addAction (drawEdgesBezier); viewOptionsMenu = new QMenu (tr("&Canvas...")); viewOptionsMenu->setIcon(QIcon(":/images/view.png")); optionsMenu->addMenu (viewOptionsMenu); viewOptionsMenu->addAction (changeBackColorAct); viewOptionsMenu->addAction (backgroundImageAct); optionsMenu->addSeparator(); optionsMenu->addAction(fullScreenModeAct); optionsMenu->addSeparator(); optionsMenu->addAction (openSettingsAct); /** menuBar entry helpMenu */ helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction (helpApp); helpMenu->addAction (tipsApp); helpMenu->addSeparator(); helpMenu->addAction (helpCheckUpdatesApp); helpMenu->addSeparator(); helpMenu->addAction(helpSystemInfoAct); helpMenu-> addAction (helpAboutApp); helpMenu-> addAction (helpAboutQt); qDebug()<< "MW::initMenuBar() - Finished"; } /** * @brief Initializes the toolbar */ void MainWindow::initToolBar(){ qDebug()<< "MW::initToolBar()"; toolBar = addToolBar("operations"); toolBar->addAction (networkNewAct); toolBar->addAction (networkOpenAct); toolBar->addAction (networkSaveAct); toolBar->addAction (networkPrintAct); toolBar->addSeparator(); toolBar->addAction (editDragModeSelectAct); toolBar->addAction (editDragModeScrollAct); toolBar->addSeparator(); //Create relation select widget // QLabel *labelRelationSelect= new QLabel; // labelRelationSelect ->setText(tr("Relations:")); // toolBar->addWidget (labelRelationSelect); toolBar->addAction (editRelationPreviousAct); editRelationChangeCombo = new QComboBox; editRelationChangeCombo ->setEditable(true); editRelationChangeCombo ->setInsertPolicy(QComboBox::InsertAtCurrent); editRelationChangeCombo->setMinimumWidth(180); editRelationChangeCombo->setCurrentIndex(0); editRelationChangeCombo->setToolTip( tr("

Current relation

" "

To rename the current relation, write new name and press Enter.

")); editRelationChangeCombo->setStatusTip( tr("

Name of the current relation.

" "

To rename it, write a new name and press Enter. To select another relation use Down arrow.

")); editRelationChangeCombo->setWhatsThis( tr("

Relations combo

" "

This combo box displays the current relation.

" "

To rename the current relation, write a new name and press Enter.

" "

To select another relation (if any), click the Down arrow.

")); toolBar->addWidget(editRelationChangeCombo); toolBar->addAction (editRelationNextAct); toolBar->addAction (editRelationAddAct); toolBar->addSeparator(); // QLabel *labelEditNodes= new QLabel; // labelEditNodes ->setText(tr("Nodes:")); // toolBar->addWidget (labelEditNodes); toolBar->addAction (editNodeAddAct); toolBar->addAction (editNodeRemoveAct); toolBar->addAction (editNodeFindAct); toolBar->addAction(editNodePropertiesAct ); toolBar->addSeparator(); // QLabel *labelEditEdges= new QLabel; // labelEditEdges ->setText(tr("Edges:")); // toolBar->addWidget (labelEditEdges); toolBar->addAction (editEdgeAddAct); toolBar->addAction (editEdgeRemoveAct); toolBar->addAction (editFilterEdgesByWeightAct); toolBar->addSeparator(); // QLabel *labelApplicationIcons = new QLabel; // labelApplicationIcons ->setText(tr("Settings:")); // toolBar->addWidget(labelApplicationIcons); toolBar->addAction(openSettingsAct); toolBar->addSeparator(); toolBar->addAction ( QWhatsThis::createAction (this)); toolBar->setIconSize(QSize(16,16)); qDebug()<< "MW::initToolBar() - Finished"; } /** * @brief Creates docked panels for instant access to main app functionalities * and displaying statistics */ void MainWindow::initPanels(){ qDebug()<< "MW::initPanels()"; /* * create widgets for the Control Panel */ QString helpMessage = ""; QLabel *toolBoxNetworkAutoCreateSelectLabel = new QLabel; toolBoxNetworkAutoCreateSelectLabel->setText(tr("Auto Create:")); toolBoxNetworkAutoCreateSelectLabel->setMinimumWidth(90); toolBoxNetworkAutoCreateSelectLabel->setStatusTip( tr("Create a network automatically (famous, random, or by using the web crawler).")); toolBoxNetworkAutoCreateSelect = new QComboBox; toolBoxNetworkAutoCreateSelect->setStatusTip( tr("Create a network automatically (famous, random, or by using the web crawler).")); helpMessage = tr("

Auto network creation

" "

Create a new network automatically.

" "

You may create a random network, recreate famous data-sets " "or use the built-in web crawler to create a network of webpages.

" ); toolBoxNetworkAutoCreateSelect->setToolTip( helpMessage ); toolBoxNetworkAutoCreateSelect->setWhatsThis( helpMessage ); toolBoxNetworkAutoCreateSelect->setToolTip( helpMessage); toolBoxNetworkAutoCreateSelect->setWhatsThis( helpMessage ); QStringList networkAutoCreateSelectCommands; networkAutoCreateSelectCommands << "Select" << "Famous data sets" << "Random scale-free" << "Random small-world" << "Random Erdős–Rényi" << "Random lattice" << "Random d-regular" << "Random ring-lattice" << "With Web Crawler"; toolBoxNetworkAutoCreateSelect->addItems(networkAutoCreateSelectCommands); toolBoxNetworkAutoCreateSelect->setMinimumWidth(90); QLabel *toolBoxEditNodeSubgraphSelectLabel = new QLabel; toolBoxEditNodeSubgraphSelectLabel->setText(tr("Subgraph:")); toolBoxEditNodeSubgraphSelectLabel->setMinimumWidth(90); toolBoxEditNodeSubgraphSelectLabel->setStatusTip( tr("Create a basic subgraph with selected nodes.")); toolBoxEditNodeSubgraphSelect = new QComboBox; toolBoxEditNodeSubgraphSelect->setStatusTip( tr("Create a basic subgraph with selected nodes.")); helpMessage = tr("

Subgraph creation

" "

Create a basic subgraph from selected nodes.

" "

Select some nodes with your mouse and then click on one of these" "options to create a basic subgraph with them.

" "

You can create a star, clique, line, etc subgraph.

" "

There must be some nodes selected!

"); toolBoxEditNodeSubgraphSelect->setToolTip( helpMessage ); toolBoxEditNodeSubgraphSelect->setWhatsThis( helpMessage ); toolBoxEditNodeSubgraphSelectLabel->setToolTip( helpMessage); toolBoxEditNodeSubgraphSelectLabel->setWhatsThis( helpMessage ); QStringList editNodeSubgraphCommands; editNodeSubgraphCommands << "Select" << "Clique" << "Star" << "Cycle" << "Line"; toolBoxEditNodeSubgraphSelect->addItems(editNodeSubgraphCommands); toolBoxEditNodeSubgraphSelect->setMinimumWidth(90); QLabel *toolBoxEdgeModeSelectLabel = new QLabel; toolBoxEdgeModeSelectLabel->setText(tr("Edge Mode:")); toolBoxEdgeModeSelectLabel->setMinimumWidth(90); toolBoxEditEdgeModeSelect = new QComboBox; toolBoxEditEdgeModeSelect->setStatusTip( tr("Select the edge mode: directed or undirected.")); helpMessage = tr("

Edge mode

" "

In social networks and graphs, edges can be directed or undirected " "(and the corresponding network is called directed or undirected as well).

" "

This option lets you choose what the kind of edges you want in your network.

" "

By selecting an option here, all edges of the network will change automatically.

" "

For instance, if the network is directed and and you select \"undirected\" " "then all the directed edges will become undirected

"); toolBoxEditEdgeModeSelect->setToolTip( helpMessage ); toolBoxEditEdgeModeSelect->setWhatsThis( helpMessage ); QStringList edgeModeCommands; edgeModeCommands << "Directed" << "Undirected"; toolBoxEditEdgeModeSelect->addItems(edgeModeCommands); toolBoxEditEdgeModeSelect->setMinimumWidth(120); QLabel *toolBoxEditEdgeTransformSelectLabel = new QLabel; toolBoxEditEdgeTransformSelectLabel->setText(tr("Transform:")); toolBoxEditEdgeTransformSelectLabel->setMinimumWidth(90); toolBoxEditEdgeTransformSelect = new QComboBox; toolBoxEditEdgeTransformSelect->setStatusTip( tr("Select a method to transform the network, i.e. transform all directed edges to undirected.")); helpMessage = tr("

Transform Network Edges

" "

Select a method to transform network edges. Available methods:

" "

Symmetrize Directed Edges:

" "

Makes all directed arcs in this relation reciprocal. " "That is, if there is an arc from node A to node B " "then a new arc from node B to node A is created " "with the same weight.

" "

Symmetrize Edges by examining Strong Ties:

" "

Creates a new symmetric relation by keeping strong ties only. " "In the new relation, a tie will exist between actor A and " "actor B only when both arcs A->B and B->A are present " "in the current or all relations.

" "

Symmetrize Edges by examining Cocitation:

" "

Creates a new symmetric relation by connecting actors " "that are cocitated by others. " "In the new relation, an edge will exist between actor i and " "actor j only if C(i,j) > 0, where C the Cocitation Matrix.

" "

Dichotomize Edges

" "

Creates a new binary relation in a valued network using " "edge dichotomization according to a given threshold value. " "In the new dichotomized relation, an edge will exist between actor i and " "actor j only if e(i,j) > threshold, where threshold is a user-defined value." "The process is also known as compression and slicing.

" ); toolBoxEditEdgeTransformSelect->setToolTip( helpMessage ); toolBoxEditEdgeTransformSelect->setWhatsThis( helpMessage ); QStringList edgeTransformCommands; edgeTransformCommands << "Select" << "Symmetrize All Directed Ties" << "Symmetrize Strong Ties" << "Cocitation Network" << "Edge Dichotomization"; toolBoxEditEdgeTransformSelect->addItems(edgeTransformCommands); toolBoxEditEdgeTransformSelect->setMinimumWidth(120); //create a grid layout for Edit buttons QGridLayout *editGrid = new QGridLayout; editGrid->addWidget(toolBoxNetworkAutoCreateSelectLabel, 0,0); editGrid->addWidget(toolBoxNetworkAutoCreateSelect, 0,1); editGrid->addWidget(toolBoxEditNodeSubgraphSelectLabel, 1,0); editGrid->addWidget(toolBoxEditNodeSubgraphSelect, 1,1); editGrid->addWidget(toolBoxEdgeModeSelectLabel,2,0); editGrid->addWidget(toolBoxEditEdgeModeSelect,2,1); editGrid->addWidget(toolBoxEditEdgeTransformSelectLabel,3,0); editGrid->addWidget(toolBoxEditEdgeTransformSelect,3,1); editGrid->setSpacing(5); editGrid->setContentsMargins(5, 5, 5, 5); //create a groupbox "Network" - Inside, display the grid layout of widgets QGroupBox *editGroupBox= new QGroupBox(tr("Network")); editGroupBox->setLayout(editGrid); editGroupBox->setMaximumWidth(255); editGroupBox->setMinimumHeight(130); //create widgets for the "Analysis" box QLabel *toolBoxAnalysisMatricesSelectLabel = new QLabel; toolBoxAnalysisMatricesSelectLabel->setText(tr("Matrix:")); toolBoxAnalysisMatricesSelectLabel->setMinimumWidth(90); toolBoxAnalysisMatricesSelect = new QComboBox; toolBoxAnalysisMatricesSelect->setStatusTip( tr("Select which matrix to compute and display, based on the " "adjacency matrix of the current network.")); helpMessage = tr("

Matrix Analysis

" "

Compute and display the adjacency matrix and other matrices " "based on the adjacency matrix of the current network. " "Available options:" "

Adjacency Matrix

" "

Adjacency Matrix Plot

" "

Inverse of Adjacency Matrix

" "

Transpose of Adjacency Matrix

" "

Cocitation Matrix

" "

Degree Matrix

" "

Laplacian Matrix

" ); toolBoxAnalysisMatricesSelect->setToolTip( helpMessage ); toolBoxAnalysisMatricesSelect->setWhatsThis( helpMessage ); QStringList graphMatricesList; graphMatricesList << "Select" << "Adjacency" << "Adjacency Plot" << "Adjacency Inverse" << "Adjacency Transpose" << "Cocitation Matrix" << "Degree Matrix" << "Laplacian Matrix"; toolBoxAnalysisMatricesSelect->addItems(graphMatricesList); toolBoxAnalysisMatricesSelect->setMinimumWidth(120); QLabel *toolBoxAnalysisCohesionSelectLabel = new QLabel; toolBoxAnalysisCohesionSelectLabel->setText(tr("Cohesion:")); toolBoxAnalysisCohesionSelectLabel->setMinimumWidth(90); toolBoxAnalysisCohesionSelect = new QComboBox; toolBoxAnalysisCohesionSelect->setStatusTip( tr("Select a graph-theoretic measure, i.e. distances, walks, graph diameter, eccentricity.")); helpMessage = tr("

Analyze Cohesion

" "

Reciprocity:

" "

Measures the likelihood that pairs of nodes in a directed network are mutually linked.

" "

Symmetry:

" "

Checks if the directed network is symmetric or not.

" "

Distances:

" "

Computes the matrix of geodesic distances between all pairs of nodes.

" "

Average Distance:

" "

Computes the average distance between all nodes.

" "

Graph Diameter:

" "

The maximum distance between any two nodes in the network.

" "

Walks:

" "

A walk is a sequence of edges and vertices (nodes), where " "each edge's endpoints are the two vertices adjacent to it. " "In a walk, vertices and edges may repeat." "

Eccentricity:

" "

The Eccentricity of each node is how far, at most, is from every other actor in the network.

" "

Reachability:

" "

Creates a matrix where an element (i,j) = 1 only if the actors i and j are reachable.

" "

Clustering Coefficient (CLC):

" "

The CLC score of each node is the proportion of actual links " "between its neighbors divided by the number of links that could " "possibly exist between them. " "Quantifies how close each actor and its neighbors are to form " "a complete subgraph (clique)

"); toolBoxAnalysisCohesionSelect->setToolTip( helpMessage ); toolBoxAnalysisCohesionSelect->setWhatsThis(helpMessage); QStringList graphPropertiesList; graphPropertiesList << "Select" << "Reciprocity" << "Symmetry" << "Distance" << "Average Distance" << "Distances Matrix" << "Geodesics Matrix" << "Eccentricity" << "Diameter" << "Connectedness" << "Walks of given length" << "Total Walks" << "Reachability Matrix" << "Clustering Coefficient"; toolBoxAnalysisCohesionSelect->addItems(graphPropertiesList); toolBoxAnalysisCohesionSelect->setMinimumWidth(120); QLabel *toolBoxAnalysisProminenceSelectLabel = new QLabel; toolBoxAnalysisProminenceSelectLabel->setText(tr("Prominence:")); toolBoxAnalysisProminenceSelectLabel->setMinimumWidth(90); toolBoxAnalysisProminenceSelect = new QComboBox; toolBoxAnalysisProminenceSelect->setStatusTip( tr("Select a prominence metric to compute for each actor " "and the whole network. ") ); helpMessage = tr("

Prominence Analysis

" "

Compute Centrality and Prestige indices, to measure how " "prominent (important) " "each actor (node) is inside the network.

" "

Centrality measures quantify how central is each node by examining " "its ties and its geodesic distances (shortest path lengths) to other nodes. " "Most Centrality indices were designed for undirected graphs.

" "

Prestige indices focus on \"choices received\" to a node. " "These indices measure the nominations or ties to each node from all others (or inLinks). " "Prestige indices are suitable (and can be calculated only) on directed graphs.

" "

Available measures:

" "

Degree Centrality (DC)

" "

The sum of outbound edges or the sum of weights of outbound " "edges from each node i to all adjacent nodes. Note: This is " "the outDegree Centrality. To compute inDegree Centrality, " "use the Degree Prestige measure.

" "

Closeness Centrality (CC):

" "The inverted sum of geodesic distances from each node u " "to all other nodes. " "

IR Closeness Centrality (IRCC):

" "

The ratio of the fraction of nodes reachable by each node u " "to the average distance of these nodes from u.

" "

Betweenness Centrality (BC):

" "

The sum of delta(s,t,u) for all s,t ∈ V where " "delta(s,t,u) is the ratio of all geodesics between nodes " "s and t which run through node u.

" "

Stress Centrality (SC):

" "

The sum of sigma(s,t,u) for all s,t ∈ V where " "sigma(s,t,u) is the number of geodesics between nodes " "s and t which run through node u.

" "

Eccentricity Centrality (EC):

" "

Also known as Harary Graph Centrality. The inverse maximum geodesic distance from node u to " "all other nodes in the network." "

Power Centrality (PC):

" "

The sum of the sizes of all Nth-order neighbourhoods " "of node u with weight 1/n.

" "

Information Centrality (IC):

" "

Measures the information flow through all paths between actors weighted by " "strength of tie and distance.

" "

Eigenvector Centrality (EVC):

" "

The EVC score of each node i is the ith element of the " "leading eigenvector of the adjacency matrix, that is the " "eigenvector corresponding to the largest positive eigenvalue. " "

Degree Prestige (DP):

" "

Also known as InDegree Centrality, it is the sum of inbound edges to a node u " "from all adjacent nodes.

" "

PageRank Prestige (PRP):

" "

For each node u counts all inbound links (edges) to it, but " "it normalizes each inbound link from another node v by the outDegree of v.

" "

Proximity Prestige (PP):

" "

The ratio of the proportion of nodes who can reach each node u " "to the average distance these nodes are from it. Similar to Closeness Centrality " "but it counts only inbound distances to each actor, thus it is a measure of actor prestige.

" ); toolBoxAnalysisProminenceSelect->setToolTip( helpMessage ); toolBoxAnalysisProminenceSelect->setWhatsThis( helpMessage); QStringList prominenceCommands; prominenceCommands << "Select" << prominenceIndexList; toolBoxAnalysisProminenceSelect->addItems(prominenceCommands); toolBoxAnalysisProminenceSelect->setMinimumWidth(120); QLabel *toolBoxAnalysisCommunitiesSelectLabel = new QLabel; toolBoxAnalysisCommunitiesSelectLabel->setText(tr("Communities:")); toolBoxAnalysisCommunitiesSelectLabel->setMinimumWidth(90); toolBoxAnalysisCommunitiesSelect = new QComboBox; toolBoxAnalysisCommunitiesSelect->setStatusTip( tr("Select a community detection measure / cohesive subgroup algorithm, i.e. cliques, triad census etc.")); helpMessage = tr("

Community Analysis

" "

Community detection measures and cohesive subgroup algorithms, " "to identify meaningful subgraphs in the graph.

" "

Available measures

" "

Clique Census:

" "

Computes aggregate counts of all maximal cliques of actors by size, " " actor by clique analysis, clique co-memberships

" "

Triad Census:

" "

Computes the Holland, Leinhardt and Davis triad census, which " "counts all different classes of triads coded according to their" "number of Mutual, Asymmetric and Non-existest dyads (M-A-N scheme)

" ); toolBoxAnalysisCommunitiesSelect->setToolTip( helpMessage ); toolBoxAnalysisCommunitiesSelect->setWhatsThis( helpMessage ); QStringList communitiesCommands; communitiesCommands << "Select" << "Cliques" << "Triad Census"; toolBoxAnalysisCommunitiesSelect->addItems(communitiesCommands); toolBoxAnalysisCommunitiesSelect->setMinimumWidth(120); QLabel *toolBoxAnalysisStrEquivalenceSelectLabel = new QLabel; toolBoxAnalysisStrEquivalenceSelectLabel->setText(tr("Equivalence:")); toolBoxAnalysisStrEquivalenceSelectLabel->setMinimumWidth(90); toolBoxAnalysisStrEquivalenceSelect = new QComboBox; toolBoxAnalysisStrEquivalenceSelect->setStatusTip( tr("Select a method to measure structural equivalence, " "i.e. Pearson Coefficients, tie profile similarities, " "hierarchical clustering, etc.")); helpMessage = tr("

Structural Equivalence Analysis

" "

Select one of the available structural equivalence " "measures and visualization algorithms.

" "

Available options

" "

Pearson Coefficients<.em>

" "

Tie profile similarities

" "

Dissimilarities

" "

Hierarchical Clustering Analysis

"); toolBoxAnalysisStrEquivalenceSelect->setToolTip( helpMessage ); toolBoxAnalysisStrEquivalenceSelect->setWhatsThis( helpMessage ); QStringList connectivityCommands; connectivityCommands << "Select" << "Pearson Coefficients" << "Similarities" << "Dissimilarities" << "Hierarchical Clustering"; toolBoxAnalysisStrEquivalenceSelect->addItems(connectivityCommands); toolBoxAnalysisStrEquivalenceSelect->setMinimumWidth(120); //create layout for analysis options QGridLayout *analysisGrid = new QGridLayout(); analysisGrid->addWidget(toolBoxAnalysisMatricesSelectLabel, 0,0); analysisGrid->addWidget(toolBoxAnalysisMatricesSelect, 0,1); analysisGrid->addWidget(toolBoxAnalysisCohesionSelectLabel, 1,0); analysisGrid->addWidget(toolBoxAnalysisCohesionSelect, 1,1); analysisGrid->addWidget(toolBoxAnalysisProminenceSelectLabel, 2,0); analysisGrid->addWidget(toolBoxAnalysisProminenceSelect, 2,1); analysisGrid->addWidget(toolBoxAnalysisCommunitiesSelectLabel, 3,0); analysisGrid->addWidget(toolBoxAnalysisCommunitiesSelect, 3,1); analysisGrid->addWidget(toolBoxAnalysisStrEquivalenceSelectLabel, 4,0); analysisGrid->addWidget(toolBoxAnalysisStrEquivalenceSelect, 4,1); analysisGrid->setSpacing(5); analysisGrid->setContentsMargins(5, 5, 5, 5); //create a box and set the above layout inside QGroupBox *analysisBox= new QGroupBox(tr("Analyze")); analysisBox->setMinimumHeight(180); analysisBox->setMaximumWidth(255); analysisBox->setLayout (analysisGrid ); //create widgets for the "Visualization By Index" box QLabel *toolBoxLayoutByIndexSelectLabel = new QLabel; toolBoxLayoutByIndexSelectLabel->setText(tr("Index:")); toolBoxLayoutByIndexSelectLabel->setMinimumWidth(70); toolBoxLayoutByIndexSelect = new QComboBox; toolBoxLayoutByIndexSelect->setStatusTip(tr("Select a prominence-based layout model")); helpMessage = tr("

Visualize by prominence index

" "

Apply a prominence-based layout model to the network.

" "

For instance, you can apply a degree centrality layout.

" "

Note: For each prominence index, you must select a layout type (below).

" "

Available measures:

" "

Degree Centrality (DC)

" "

The sum of outbound edges or the sum of weights of outbound " "edges from each node i to all adjacent nodes. Note: This is " "the outDegree Centrality. To compute inDegree Centrality, " "use the Degree Prestige measure.

" "

Closeness Centrality (CC):

" "The inverted sum of geodesic distances from each node u " "to all other nodes. " "

IR Closeness Centrality (IRCC):

" "

The ratio of the fraction of nodes reachable by each node u " "to the average distance of these nodes from u.

" "

Betweenness Centrality (BC):

" "

The sum of delta(s,t,u) for all s,t ∈ V where " "delta(s,t,u) is the ratio of all geodesics between nodes " "s and t which run through node u.

" "

Stress Centrality (SC):

" "

The sum of sigma(s,t,u) for all s,t ∈ V where " "sigma(s,t,u) is the number of geodesics between nodes " "s and t which run through node u.

" "

Eccentricity Centrality (EC):

" "

Also known as Harary Graph Centrality. The inverse maximum geodesic distance from node u to " "all other nodes in the network." "

Power Centrality (PC):

" "

The sum of the sizes of all Nth-order neighbourhoods " "of node u with weight 1/n.

" "

Information Centrality (IC):

" "

Measures the information flow through all paths between actors weighted by " "strength of tie and distance.

" "

Eigenvector Centrality (EVC):

" "

The EVC score of each node i is the ith element of the " "leading eigenvector of the adjacency matrix, that is the " "eigenvector corresponding to the largest positive eigenvalue. " "

Degree Prestige (DP):

" "

Also known as InDegree Centrality, it is the sum of inbound edges to a node u " "from all adjacent nodes.

" "

PageRank Prestige (PRP):

" "

For each node u counts all inbound links (edges) to it, but " "it normalizes each inbound link from another node v by the outDegree of v.

" "

Proximity Prestige (PP):

" "

The ratio of the proportion of nodes who can reach each node u " "to the average distance these nodes are from it. Similar to Closeness Centrality " "but it counts only inbound distances to each actor, thus it is a measure of actor prestige.

" ); toolBoxLayoutByIndexSelect->setToolTip( helpMessage ); toolBoxLayoutByIndexSelect->setWhatsThis( helpMessage ); QStringList layoutCommandsList; layoutCommandsList << "None" << "Random" << prominenceIndexList; toolBoxLayoutByIndexSelect->addItems(layoutCommandsList); toolBoxLayoutByIndexSelect->setMinimumHeight(20); toolBoxLayoutByIndexSelect->setMinimumWidth(100); QLabel *toolBoxLayoutByIndexTypeLabel = new QLabel; toolBoxLayoutByIndexTypeLabel->setText(tr("Type:")); toolBoxLayoutByIndexTypeLabel->setMinimumWidth(70); toolBoxLayoutByIndexTypeSelect = new QComboBox; toolBoxLayoutByIndexTypeSelect->setStatusTip( tr("Select layout type for the selected model")); helpMessage = tr("

Layout Type

" "

Select a layout type (radial, level, node size or node color) " "for the selected prominence-based model you want to apply to the " "network. Please note that node coloring works only for basic shapes " "(box, circle, etc) not for image icons.

"); toolBoxLayoutByIndexTypeSelect->setToolTip( helpMessage ); toolBoxLayoutByIndexTypeSelect->setWhatsThis( helpMessage ); QStringList layoutTypes; layoutTypes << "Radial" << "On Levels" << "Node Size"<< "Node Color"; toolBoxLayoutByIndexTypeSelect->addItems(layoutTypes); toolBoxLayoutByIndexTypeSelect->setMinimumHeight(20); toolBoxLayoutByIndexTypeSelect->setMinimumWidth(100); toolBoxLayoutByIndexApplyButton = new QPushButton(tr("Apply")); toolBoxLayoutByIndexApplyButton->setObjectName ("toolBoxLayoutByIndexApplyButton"); toolBoxLayoutByIndexApplyButton->setFocusPolicy(Qt::NoFocus); toolBoxLayoutByIndexApplyButton->setMinimumHeight(20); toolBoxLayoutByIndexApplyButton->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(toolBoxLayoutByIndexApplyButton, 2,1); layoutByIndexGrid->setSpacing(5); layoutByIndexGrid->setContentsMargins(5, 5, 5, 5); //create a box and set the above layout inside QGroupBox *layoutByIndexBox= new QGroupBox(tr("By Prominence Index")); layoutByIndexBox->setMinimumHeight(120); helpMessage = tr("

Visualize by prominence index

" "

Apply a prominence-based layout model to the network.

" "

For instance, you can apply a Degree Centrality layout.

" "

For each prominence index, you must select a layout type:

" "

Radial, Levels, NodeSize or NodeColor.

" "

Please note that node coloring works only for basic shapes " "(box, circle, etc) not for image icons.

"); layoutByIndexBox->setToolTip( helpMessage ); layoutByIndexBox->setMaximumWidth(255); layoutByIndexBox->setLayout (layoutByIndexGrid ); // create widgets for the "Force-Directed Models" Box QLabel *toolBoxLayoutForceDirectedSelectLabel = new QLabel; toolBoxLayoutForceDirectedSelectLabel->setText(tr("Model:")); toolBoxLayoutForceDirectedSelectLabel->setMinimumWidth(70); toolBoxLayoutForceDirectedSelect = new QComboBox; QStringList modelsList; modelsList << tr("None") << tr("Kamada-Kawai") << tr("Fruchterman-Reingold") << tr("Eades Spring Embedder") ; toolBoxLayoutForceDirectedSelect->addItems(modelsList); toolBoxLayoutForceDirectedSelect->setMinimumHeight(20); toolBoxLayoutForceDirectedSelect->setMinimumWidth(100); toolBoxLayoutForceDirectedSelect->setStatusTip ( tr("Select a Force-Directed layout model. ")); helpMessage = tr("

Visualize by a Force-Directed Placement layout model.

" "

Available models:

" "

Kamada-Kawai

" "

The best variant of the Spring Embedder family of models. " "

In this the graph is considered to be a dynamic system where " "every edge is between two actors is a 'spring' of a desirable " "length, which corresponds to their graph theoretic distance.

" "

In this way, the optimal layout of the graph \n" "is the state with the minimum imbalance. The degree of " "imbalance is formulated as the total spring energy: " "the square summation of the differences between desirable " "distances and real ones for all pairs of vertices.

" "

Fruchterman-Reingold:

" "

In this 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 Eades Spring " "Embedder, all vertices repel each other.

" "

Eades Spring Embedder:

" "

A spring-gravitational model, where each node is " "regarded as physical object (ring) repelling all other non-adjacent " "nodes, while springs between connected nodes attract them.

" ); toolBoxLayoutForceDirectedSelect->setToolTip ( helpMessage ); toolBoxLayoutForceDirectedSelect->setWhatsThis( helpMessage ); toolBoxLayoutForceDirectedApplyButton = new QPushButton(tr("Apply")); toolBoxLayoutForceDirectedApplyButton->setObjectName ("toolBoxLayoutForceDirectedApplyButton"); toolBoxLayoutForceDirectedApplyButton->setFocusPolicy(Qt::NoFocus); toolBoxLayoutForceDirectedApplyButton->setMinimumHeight(20); toolBoxLayoutForceDirectedApplyButton->setMaximumWidth(60); //create layout for dynamic visualisation QGridLayout *layoutForceDirectedGrid = new QGridLayout(); layoutForceDirectedGrid->addWidget(toolBoxLayoutForceDirectedSelectLabel, 0,0); layoutForceDirectedGrid->addWidget(toolBoxLayoutForceDirectedSelect, 0,1); layoutForceDirectedGrid->addWidget(toolBoxLayoutForceDirectedApplyButton, 1,1); layoutForceDirectedGrid->setSpacing(5); layoutForceDirectedGrid->setContentsMargins(5, 5, 5, 5); //create a box for dynamic layout options QGroupBox *layoutDynamicBox= new QGroupBox(tr("By Force-Directed Model")); layoutDynamicBox->setMinimumHeight(90); layoutDynamicBox->setMaximumWidth(255); layoutDynamicBox->setLayout (layoutForceDirectedGrid ); layoutDynamicBox->setContentsMargins(5, 5, 5, 5); //Parent box with vertical layout for all layout/visualization boxes QVBoxLayout *visualizationBoxLayout = new QVBoxLayout; visualizationBoxLayout->addWidget(layoutByIndexBox); visualizationBoxLayout->addWidget(layoutDynamicBox); visualizationBoxLayout->setContentsMargins(5,5,5,5); QGroupBox *visualizationBox= new QGroupBox(tr("Layout")); visualizationBox->setMaximumWidth(255); visualizationBox->setLayout (visualizationBoxLayout ); visualizationBox->setContentsMargins(5,5,5,5); //Parent box with vertical layout for all boxes of Controls QGridLayout *controlGrid = new QGridLayout; controlGrid->addWidget(editGroupBox, 0,0); controlGrid->addWidget(analysisBox, 1, 0); controlGrid->addWidget(visualizationBox, 2, 0); controlGrid->setRowStretch(3,1); //fix stretch controlGrid->setContentsMargins(5, 5, 5, 5); //create a box with title leftPanel = new QGroupBox(tr("Control Panel")); leftPanel->setMinimumWidth(220); leftPanel->setObjectName("leftPanel"); leftPanel->setLayout (controlGrid); // // Create widgets for Properties/Statistics group/tab // QLabel *rightPanelNetworkHeader = new QLabel; QFont labelFont = rightPanelNetworkHeader ->font(); labelFont.setWeight(QFont::Bold); rightPanelNetworkHeader-> setText (tr("Network")); rightPanelNetworkHeader->setFont(labelFont); QLabel *rightPanelNetworkTypeLabel = new QLabel; rightPanelNetworkTypeLabel-> setText ("Type:"); rightPanelNetworkTypeLabel->setStatusTip( tr("The type of the network: directed or undirected. " "Toggle the menu option Edit->Edges->Undirected Edges to change it")); rightPanelNetworkTypeLabel->setToolTip( tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "toggle the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLabel->setWhatsThis( tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "toggle the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLCD = new QLabel; rightPanelNetworkTypeLCD->setAlignment(Qt::AlignRight); rightPanelNetworkTypeLCD->setText (tr("Directed")); rightPanelNetworkTypeLCD->setStatusTip( tr("Directed data mode. " "Toggle the menu option Edit->Edges->Undirected Edges to change it")); rightPanelNetworkTypeLCD->setToolTip( tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "toggle the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLCD->setWhatsThis( tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "toggle the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLCD ->setMinimumWidth(75); QLabel *rightPanelNodesLabel = new QLabel; rightPanelNodesLabel->setText(tr("Nodes:")); rightPanelNodesLabel->setStatusTip( tr("The total number of actors (nodes or vertices) " "in this social network.")); rightPanelNodesLabel->setToolTip( tr("

Nodes

" "

Each actor in a social netwok is visualized as a node (or vertex) " "in a graph. This is total number of actors " "(nodes or vertices) in this social network.

")); rightPanelNodesLabel ->setMinimumWidth(80); rightPanelNodesLCD=new QLabel; rightPanelNodesLCD->setAlignment(Qt::AlignRight); rightPanelNodesLCD->setStatusTip( tr("The total number of actors (nodes or vertices) in the social network.")); rightPanelNodesLCD->setToolTip( tr("This is the total number of actors \n" "(nodes or vertices) in the social network.")); rightPanelEdgesLabel = new QLabel; rightPanelEdgesLabel->setText(tr("Arcs:")); rightPanelEdgesLabel->setStatusTip(tr("The total number of edges (links between actors) in the social network.")); rightPanelEdgesLabel->setToolTip(tr("This is the total number of (directed) edges \n" "(links between actors) in the social network.")); rightPanelEdgesLCD=new QLabel; rightPanelEdgesLCD->setAlignment(Qt::AlignRight); rightPanelEdgesLCD->setStatusTip(tr("The total number of directed edges in the social network.")); rightPanelEdgesLCD->setToolTip(tr("This is the total number of directed edges \n" "(links between actors) in the social network.")); QLabel *rightPanelDensityLabel = new QLabel; rightPanelDensityLabel->setText(tr("Density:")); rightPanelDensityLabel->setStatusTip(tr("The density d is the ratio of existing edges to all possible edges")); helpMessage = tr("

Density

" "

The density d of a social network is the ratio of " "existing edges to all possible edges ( n*(n-1) ) between the " "nodes of the network

."); rightPanelDensityLabel->setToolTip( helpMessage ); rightPanelDensityLabel->setWhatsThis( helpMessage ); rightPanelDensityLCD=new QLabel; rightPanelDensityLCD->setAlignment(Qt::AlignRight); rightPanelDensityLCD->setStatusTip(tr("The network density, the ratio of existing " "edges to all possible edges ( n*(n-1) ) between nodes.")); rightPanelDensityLCD->setToolTip( tr("

This is the density of the network. " "

The density of a network is the ratio of existing " "edges to all possible edges ( n*(n-1) ) between nodes.

")); QLabel *verticalSpaceLabel1 = new QLabel; verticalSpaceLabel1->setText (""); QLabel *rightPanelSelectedHeaderLabel = new QLabel; rightPanelSelectedHeaderLabel-> setText (tr("Selection")); rightPanelSelectedHeaderLabel->setFont(labelFont); QLabel *rightPanelSelectedNodesLabel = new QLabel; rightPanelSelectedNodesLabel->setText(tr("Nodes:")); rightPanelSelectedNodesLabel->setStatusTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedNodesLabel->setToolTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedNodesLCD=new QLabel; rightPanelSelectedNodesLCD->setAlignment(Qt::AlignRight); rightPanelSelectedNodesLCD->setText("0"); rightPanelSelectedNodesLCD->setStatusTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedNodesLCD->setToolTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedEdgesLabel = new QLabel; rightPanelSelectedEdgesLabel->setText(tr("Arcs:")); rightPanelSelectedEdgesLabel->setStatusTip(tr("The number of selected edges.")); rightPanelSelectedEdgesLabel->setToolTip(tr("The number of selected edges.")); rightPanelSelectedEdgesLCD=new QLabel; rightPanelSelectedEdgesLCD->setText("0"); rightPanelSelectedEdgesLCD->setAlignment(Qt::AlignRight); rightPanelSelectedEdgesLCD->setStatusTip(tr("The number of selected edges.")); rightPanelSelectedEdgesLCD->setToolTip(tr("The number of selected edges.")); QLabel *verticalSpaceLabel2 = new QLabel; verticalSpaceLabel2-> setText (""); rightPanelClickedNodeHeaderLabel = new QLabel; rightPanelClickedNodeHeaderLabel-> setText (tr("Clicked Node")); rightPanelClickedNodeHeaderLabel->setFont(labelFont); QLabel *rightPanelClickedNodeLabel = new QLabel; rightPanelClickedNodeLabel->setText (tr("Number:")); rightPanelClickedNodeLabel->setToolTip (tr("The node number of the last clicked node.")); rightPanelClickedNodeLabel->setStatusTip( tr("The node number of the last clicked node. Zero means no node clicked.")); rightPanelClickedNodeLCD = new QLabel; rightPanelClickedNodeLCD->setAlignment(Qt::AlignRight); rightPanelClickedNodeLCD->setToolTip (tr("This is the node number of the last clicked node. \n" "Becomes zero when you click on something other than a node.")); rightPanelClickedNodeLCD->setStatusTip( tr("The node number of the last clicked node. Zero if you clicked something else.")); QLabel *rightPanelClickedNodeInDegreeLabel = new QLabel; rightPanelClickedNodeInDegreeLabel->setText (tr("In-Degree:")); rightPanelClickedNodeInDegreeLabel->setToolTip (tr("The inDegree of a node is the sum of all inbound edge weights.")); rightPanelClickedNodeInDegreeLabel->setStatusTip (tr("The inDegree of a node is the sum of all inbound edge weights.")); rightPanelClickedNodeInDegreeLCD = new QLabel; rightPanelClickedNodeInDegreeLCD->setAlignment(Qt::AlignRight); rightPanelClickedNodeInDegreeLCD->setStatusTip (tr("The sum of all inbound edge weights of the last clicked node. " "Zero if you clicked something else.")); rightPanelClickedNodeInDegreeLCD->setToolTip (tr("This is the sum of all inbound edge weights of last clicked node. \n" "Becomes zero when you click on something other than a node.")); QLabel *rightPanelClickedNodeOutDegreeLabel = new QLabel; rightPanelClickedNodeOutDegreeLabel->setText (tr("Out-Degree:")); rightPanelClickedNodeOutDegreeLabel->setToolTip (tr("The outDegree of a node is the sum of all outbound edge weights.")); rightPanelClickedNodeOutDegreeLabel->setStatusTip (tr("The outDegree of a node is the sum of all outbound edge weights.")); rightPanelClickedNodeOutDegreeLCD=new QLabel; rightPanelClickedNodeOutDegreeLCD->setAlignment(Qt::AlignRight); rightPanelClickedNodeOutDegreeLCD->setStatusTip (tr("The sum of all outbound edge weights of the last clicked node. " "Zero if you clicked something else.")); rightPanelClickedNodeOutDegreeLCD->setToolTip (tr("This is the sum of all outbound edge weights of the last clicked node. \n" "Becomes zero when you click on something other than a node.")); QLabel *rightPanelClickedNodeClucofLabel = new QLabel; rightPanelClickedNodeClucofLabel->setText (tr("Clu.Coef.")); helpMessage = tr("

Clustering Coefficient of the active node.

" "

The Clustering Coefficient quantifies how close the clicked " "vertex and its neighbors are to being a clique. " "The value is the proportion of Edges between the vertices " "within the neighbourhood of the clicked vertex, " "divided by the number of Edges that could possibly exist " "between them. " "

This value is automatically calculated only if vertices < 500." "

If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient.

"); rightPanelClickedNodeClucofLabel->setWhatsThis( helpMessage ); rightPanelClickedNodeClucofLabel->setToolTip ( helpMessage ); rightPanelClickedNodeClucofLabel->setStatusTip( tr("The Clustering Coefficient of the last clicked node. " "Zero when you click on something else.")); rightPanelClickedNodeClucofLCD = new QLabel; rightPanelClickedNodeClucofLCD->setAlignment(Qt::AlignRight); rightPanelClickedNodeClucofLCD->setStatusTip( tr("The Clustering Coefficient of the last clicked node. " "Zero when you click on something else.")); rightPanelClickedNodeClucofLCD->setWhatsThis( helpMessage ); rightPanelClickedNodeClucofLCD ->setToolTip ( helpMessage ); QLabel *verticalSpaceLabel3 = new QLabel; verticalSpaceLabel3-> setText (""); QLabel * rightPanelClickedEdgeHeaderLabel = new QLabel; rightPanelClickedEdgeHeaderLabel-> setText (tr("Clicked Edge")); rightPanelClickedEdgeHeaderLabel->setFont(labelFont); rightPanelClickedEdgeNameLabel = new QLabel; rightPanelClickedEdgeNameLabel->setText (tr("Name:")); rightPanelClickedEdgeNameLabel->setToolTip (tr("The name of the last clicked edge.")); rightPanelClickedEdgeNameLabel->setStatusTip (tr("The name of the last clicked edge.")); rightPanelClickedEdgeNameLCD = new QLabel; rightPanelClickedEdgeNameLCD->setAlignment(Qt::AlignRight); rightPanelClickedEdgeNameLCD->setToolTip (tr("This is the name of the last clicked edge. \n" "Becomes zero when you click on somethingto other than an edge")); rightPanelClickedEdgeNameLCD->setStatusTip (tr("The name of the last clicked edge." "Zero when you click on something else.")); rightPanelClickedEdgeWeightLabel = new QLabel; rightPanelClickedEdgeWeightLabel->setText (tr("Weight:")); rightPanelClickedEdgeWeightLabel->setStatusTip (tr("The weight of the clicked edge.")); rightPanelClickedEdgeWeightLabel->setToolTip (tr("The weight of the clicked edge.")); rightPanelClickedEdgeWeightLCD =new QLabel; rightPanelClickedEdgeWeightLCD->setAlignment(Qt::AlignRight); rightPanelClickedEdgeWeightLCD->setToolTip (tr("This is the weight of the last clicked edge. \n" "Becomes zero when you click on something other than an edge")); rightPanelClickedEdgeWeightLCD->setStatusTip (tr("The weight of the last clicked edge. " "Zero when you click on something else.")); rightPanelClickedEdgeReciprocalWeightLabel = new QLabel; rightPanelClickedEdgeReciprocalWeightLabel->setText (tr("")); rightPanelClickedEdgeReciprocalWeightLabel->setToolTip (tr("The weight of the reciprocal edge.")); rightPanelClickedEdgeReciprocalWeightLabel->setStatusTip (tr("The weight of the reciprocal edge.")); rightPanelClickedEdgeReciprocalWeightLCD =new QLabel; rightPanelClickedEdgeReciprocalWeightLCD->setAlignment(Qt::AlignRight); rightPanelClickedEdgeReciprocalWeightLCD->setToolTip (tr("This is the reciprocal weight of the last clicked reciprocated edge. \n" "Becomes zero when you click on something other than an edge")); rightPanelClickedEdgeReciprocalWeightLCD->setStatusTip (tr("The reciprocal weight of the last clicked reciprocated edge. \n" "Becomes zero when you click on something other than an edge")); //create a grid layout QGridLayout *propertiesGrid = new QGridLayout(); propertiesGrid->setColumnMinimumWidth(0, 10); propertiesGrid->setColumnMinimumWidth(1, 10); propertiesGrid->addWidget(rightPanelNetworkHeader , 0,0); propertiesGrid->addWidget(rightPanelNetworkTypeLabel , 1,0); propertiesGrid->addWidget(rightPanelNetworkTypeLCD , 1,1); propertiesGrid->addWidget(rightPanelNodesLabel, 2,0); propertiesGrid->addWidget(rightPanelNodesLCD,2,1); propertiesGrid->addWidget(rightPanelEdgesLabel, 3,0); propertiesGrid->addWidget(rightPanelEdgesLCD,3,1); propertiesGrid->addWidget(rightPanelDensityLabel, 4,0); propertiesGrid->addWidget(rightPanelDensityLCD,4,1); propertiesGrid->addWidget(verticalSpaceLabel1, 5,0); propertiesGrid->addWidget(rightPanelSelectedHeaderLabel, 6,0,1,2); propertiesGrid->addWidget(rightPanelSelectedNodesLabel , 7,0); propertiesGrid->addWidget(rightPanelSelectedNodesLCD ,7,1); propertiesGrid->addWidget(rightPanelSelectedEdgesLabel, 8,0); propertiesGrid->addWidget(rightPanelSelectedEdgesLCD, 8,1); propertiesGrid->addWidget(verticalSpaceLabel2, 9,0); propertiesGrid->addWidget(rightPanelClickedNodeHeaderLabel, 10,0,1,2); propertiesGrid->addWidget(rightPanelClickedNodeLabel , 11,0); propertiesGrid->addWidget(rightPanelClickedNodeLCD ,11,1); propertiesGrid->addWidget(rightPanelClickedNodeInDegreeLabel, 12,0); propertiesGrid->addWidget(rightPanelClickedNodeInDegreeLCD,12,1); propertiesGrid->addWidget(rightPanelClickedNodeOutDegreeLabel, 13,0); propertiesGrid->addWidget(rightPanelClickedNodeOutDegreeLCD,13,1); propertiesGrid->addWidget(rightPanelClickedNodeClucofLabel, 14,0); propertiesGrid->addWidget(rightPanelClickedNodeClucofLCD,14,1); propertiesGrid->addWidget(verticalSpaceLabel3, 15,0); propertiesGrid->addWidget(rightPanelClickedEdgeHeaderLabel, 16,0,1,2); propertiesGrid->addWidget(rightPanelClickedEdgeNameLabel , 17,0); propertiesGrid->addWidget(rightPanelClickedEdgeNameLCD ,17,1); propertiesGrid->addWidget(rightPanelClickedEdgeWeightLabel , 18,0); propertiesGrid->addWidget(rightPanelClickedEdgeWeightLCD ,18,1); propertiesGrid->addWidget(rightPanelClickedEdgeReciprocalWeightLabel , 19,0); propertiesGrid->addWidget(rightPanelClickedEdgeReciprocalWeightLCD ,19,1); // Create our mini miniChart miniChart = new Chart(this); int chartHeight = 140; miniChart->setThemeSmallWidget(chartHeight,chartHeight); // Nothing else to do with miniChart. // MW::initApp() will populate it with a dummy point. propertiesGrid->addWidget(miniChart,20,0,1,2); propertiesGrid->setRowMinimumHeight(20, (int) floor( 1.5 * chartHeight ) ); propertiesGrid->setRowStretch(20,0); // We need some margin form the edge of the miniChart to the messageLabel below, // but setRowStretch is not enough. So, we add a spacer! QSpacerItem *spacer = new QSpacerItem (100, 10, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); propertiesGrid->addItem(spacer, 22,0,3,2); propertiesGrid->setRowStretch(22,1); //allow this row to stretch // Add the message label, this will be displayed in the down-right corner. QLabel *rightPanelMessageLabel = new QLabel; rightPanelMessageLabel-> setText ("https://socnetv.org"); propertiesGrid->addWidget(rightPanelMessageLabel, 25, 0, 1, 2); propertiesGrid->setRowStretch(25,0); // stop row from stretching // Create a panel with title rightPanel = new QGroupBox(tr("Statistics Panel")); rightPanel->setMaximumWidth(190); rightPanel->setObjectName("rightPanel"); rightPanel->setLayout (propertiesGrid); qDebug()<< "MW::initPanels() - Finished"; } /** * @brief Initializes the application window UI: * Creates helper widgets and sets the main layout of the MainWindow */ void MainWindow::initWindowLayout() { qDebug () << "MW::initWindowLayout()"; int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize); QSize iconSize(size, size); iconSize.setHeight(16); iconSize.setWidth(16); // // Zoom slider // zoomInBtn = new QToolButton; zoomInBtn->setShortcut(Qt::CTRL + Qt::Key_Plus); zoomInBtn->setToolTip(tr("Zoom in (Ctrl++)")); zoomInBtn->setStatusTip(tr("Zoom inside the actual network. Or press Cltr and use mouse wheel.")); zoomInBtn->setWhatsThis(tr("Zoom In.\n\n" "Zooms in the actual network" "You can also press Cltr and use mouse wheel.")); zoomInBtn->setAutoRepeat(true); zoomInBtn->setAutoRepeatInterval(33); zoomInBtn->setAutoRepeatDelay(0); zoomInBtn->setIcon(QPixmap(":/images/zoom_in_24px.svg")); zoomInBtn->setIconSize(iconSize); zoomOutBtn = new QToolButton; zoomOutBtn->setAutoRepeat(true); zoomOutBtn->setShortcut(Qt::CTRL + Qt::Key_Minus); zoomOutBtn->setToolTip(tr("Zoom out (Ctrl+-)")); zoomOutBtn->setStatusTip(tr("Zoom out of the actual network. Or press Cltr and use mouse wheel.")); zoomOutBtn->setWhatsThis(tr("Zoom out.\n\n" "Zooms out the actual network" "You can also press Cltr and use mouse wheel.")); zoomOutBtn->setAutoRepeat(true); zoomOutBtn->setAutoRepeatInterval(33); zoomOutBtn->setAutoRepeatDelay(0); zoomOutBtn->setIcon(QPixmap(":/images/zoom_out_24px.svg")); zoomOutBtn->setIconSize(iconSize); zoomSlider = new QSlider; zoomSlider->setMinimum(0); zoomSlider->setMaximum(500); zoomSlider->setValue(250); zoomSlider->setToolTip(tr("Zoom slider: Drag up to zoom in. \n" "Drag down to zoom out. ")); zoomSlider->setWhatsThis(tr("Zoom slider: Drag up to zoom in. \n" "Drag down to zoom out. ")); zoomSlider->setTickPosition(QSlider::TicksBothSides); // Zoom slider layout QVBoxLayout *zoomSliderLayout = new QVBoxLayout; zoomSliderLayout->addWidget(zoomInBtn); zoomSliderLayout->addWidget(zoomSlider); zoomSliderLayout->addWidget(zoomOutBtn); // // Rotate slider // rotateLeftBtn = new QToolButton; rotateLeftBtn->setAutoRepeat(true); rotateLeftBtn->setShortcut(Qt::CTRL + Qt::Key_Left); rotateLeftBtn->setIcon(QPixmap(":/images/rotate_left_48px.svg")); rotateLeftBtn->setToolTip(tr("Rotate counterclockwise (Ctrl+Left Arrow)")); rotateLeftBtn->setStatusTip(tr("Rotate counterclockwise (Ctrl+Left Arrow)")); rotateLeftBtn->setWhatsThis(tr("Rotates counterclockwise (Ctrl+Left Arrow)")); rotateLeftBtn->setIconSize(iconSize); rotateRightBtn = new QToolButton; rotateRightBtn->setAutoRepeat(true); rotateRightBtn->setShortcut(Qt::CTRL + Qt::Key_Right); rotateRightBtn ->setIcon(QPixmap(":/images/rotate_right_48px.svg")); rotateRightBtn->setToolTip(tr("Rotate clockwise (Ctrl+Right Arrow)")); rotateRightBtn->setStatusTip(tr("Rotate clockwise (Ctrl+Right Arrow)")); rotateRightBtn->setWhatsThis(tr("Rotates clockwise (Ctrl+Right Arrow)")); rotateRightBtn ->setIconSize(iconSize); rotateSlider = new QSlider; rotateSlider->setOrientation(Qt::Horizontal); rotateSlider->setMinimum(-180); rotateSlider->setMaximum(180); rotateSlider->setTickInterval(5); rotateSlider->setValue(0); rotateSlider->setToolTip(tr("Rotate slider: Drag to left to rotate clockwise. \n" "Drag to right to rotate counterclockwise. ")); rotateSlider->setWhatsThis(tr("Rotate slider: Drag to left to rotate clockwise. " "Drag to right to rotate counterclockwise. ")); rotateSlider->setTickPosition(QSlider::TicksBothSides); // Rotate slider layout QHBoxLayout *rotateSliderLayout = new QHBoxLayout; rotateSliderLayout->addWidget(rotateLeftBtn); rotateSliderLayout->addWidget(rotateSlider); rotateSliderLayout->addWidget(rotateRightBtn ); resetSlidersBtn = new QToolButton; resetSlidersBtn->setText(tr("Reset")); resetSlidersBtn->setShortcut(Qt::CTRL + Qt::Key_0); resetSlidersBtn->setStatusTip(tr("Reset zoom and rotation to zero (or press Ctrl+0)")); resetSlidersBtn->setToolTip(tr("Reset zoom and rotation to zero (Ctrl+0)")); resetSlidersBtn->setWhatsThis(tr("Reset zoom and rotation to zero (Ctrl+0)")); resetSlidersBtn->setIcon(QPixmap(":/images/refresh_48px.svg")); resetSlidersBtn ->setIconSize(iconSize); resetSlidersBtn->setEnabled(true); // Create a layout for the toolbox and the canvas. // This will be the layout of our MW central widget QGridLayout *layout = new QGridLayout; layout->addWidget(leftPanel, 0, 0, 2,1); layout->addWidget(graphicsWidget,0,1); layout->addLayout(zoomSliderLayout, 0, 2); layout->addWidget(rightPanel, 0, 3,2,1); layout->addLayout(rotateSliderLayout, 1, 1, 1, 1); layout->addWidget(resetSlidersBtn, 1, 2, 1, 1); //create a dummy widget, and set the above layout QWidget *widget = new QWidget; widget->setLayout(layout); //now set this as central widget of MW setCentralWidget(widget); // set panels visibility if ( appSettings["showRightPanel"] == "false") { slotOptionsWindowRightPanelVisibility(false); } if ( appSettings["showLeftPanel"] == "false") { slotOptionsWindowLeftPanelVisibility(false); } qDebug () << "MW::initWindowLayout - maximizing my window"; showMaximized(); qDebug () << "MW::initWindowLayout() - Finished"; } /** * @brief Connects signals & slots between various parts of the app: * - the GraphicsWidget and the Graph * - the GraphicsWidget and the MainWindow * This must be called after all widgets have been created. * */ void MainWindow::initSignalSlots() { qDebug ()<< "MW::initSignalSlots()"; // Signals between graphicsWidget and MainWindow connect( graphicsWidget, &GraphicsWidget::setCursor, this,&MainWindow::setCursor); connect( graphicsWidget, &GraphicsWidget::userClickOnEmptySpace, this, &MainWindow::slotEditClickOnEmptySpace ) ; connect( graphicsWidget, SIGNAL( userMiddleClicked(const int &, const int &) ), this, SLOT( slotEditEdgeCreate(const int &, const int &) ) ); connect( graphicsWidget, SIGNAL( openNodeMenu() ), this, SLOT( slotEditNodeOpenContextMenu() ) ) ; connect (graphicsWidget, &GraphicsWidget::openContextMenu, this, &MainWindow::slotEditOpenContextMenu); connect( graphicsWidget, SIGNAL(userNodeMoved(const int &, const int &, const int &)), this, SLOT( slotEditNodePosition(const int &, const int &, const int &) ) ); connect( graphicsWidget, SIGNAL(zoomChanged(const int &)), zoomSlider, SLOT( setValue(const int &)) ); connect(zoomSlider, SIGNAL(valueChanged(const int &)), graphicsWidget, SLOT(changeMatrixScale(const int &))); connect( zoomInBtn, SIGNAL(clicked()), graphicsWidget, SLOT( zoomIn() ) ); connect( zoomOutBtn, SIGNAL(clicked()), graphicsWidget, SLOT( zoomOut() ) ); connect( graphicsWidget, SIGNAL(rotationChanged(const int &)), rotateSlider, SLOT( setValue(const int &)) ); connect(rotateSlider, SIGNAL(valueChanged(const int &)), graphicsWidget, SLOT(changeMatrixRotation(const int &))); connect(rotateLeftBtn, SIGNAL(clicked()), graphicsWidget, SLOT(rotateLeft())); connect(rotateRightBtn, SIGNAL(clicked()), graphicsWidget, SLOT(rotateRight())); connect(resetSlidersBtn, SIGNAL(clicked()), graphicsWidget, SLOT(reset())); // //SIGNALS BETWEEN ACTIVEGRAPH AND MAINWINDOW // connect( activeGraph, &Graph::signalSelectionChanged, this, &MainWindow::slotEditSelectionChanged); connect( activeGraph, &Graph::signalNodeClickedInfo , this, &MainWindow::slotEditNodeInfoStatusBar ); connect ( activeGraph, &Graph::signalEdgeClicked, this, &MainWindow::slotEditEdgeClicked ); connect (activeGraph, &Graph::signalGraphModified, this, &MainWindow::slotNetworkChanged); connect (activeGraph, &Graph::signalGraphLoaded, this, &MainWindow::slotNetworkFileLoaded); connect( activeGraph, &Graph::signalGraphSavedStatus, this, &MainWindow::slotNetworkSavedStatus); connect( activeGraph, SIGNAL( statusMessage (QString) ), this, SLOT( statusMessage (QString) ) ) ; connect( activeGraph, SIGNAL( signalDatasetDescription (QString) ), this, SLOT( slotHelpMessageToUserInfo (QString) ) ) ; connect( editRelationChangeCombo , SIGNAL( activated(int) ) , activeGraph, SLOT( relationSet(int) ) ); connect( editRelationChangeCombo , SIGNAL( currentTextChanged(QString) ), activeGraph, SLOT( relationCurrentRename(QString) ) ); connect( this , &MainWindow::signalRelationAddAndChange, activeGraph, &Graph::relationAdd ); connect( editRelationNextAct, &QAction::triggered, activeGraph, &Graph::relationNext ); connect( editRelationPreviousAct, &QAction::triggered, activeGraph, &Graph::relationPrev ); connect ( activeGraph, &Graph::signalRelationChangedToMW, this, &MainWindow::slotEditRelationChange ); connect ( activeGraph, &Graph::signalRelationsClear, this, &MainWindow::slotEditRelationsClear ); connect ( activeGraph, &Graph::signalRelationAddToMW, this, &MainWindow::slotEditRelationAdd ); connect ( activeGraph, &Graph::signalRelationRenamedToMW, this, &MainWindow::slotEditRelationRename ); connect ( activeGraph, &Graph::signalProgressBoxCreate, this, &MainWindow::slotProgressBoxCreate); connect ( activeGraph, &Graph::signalProgressBoxKill, this, &MainWindow::slotProgressBoxDestroy); connect ( activeGraph, &Graph::signalPromininenceDistributionChartUpdate, this, &MainWindow::slotAnalyzeProminenceDistributionChartUpdate); // // Signals and slots inside MainWindow // connect( editDragModeSelectAct, &QAction::triggered, this, &MainWindow::slotEditDragModeSelection ); connect( editDragModeScrollAct, &QAction::triggered, this, &MainWindow::slotEditDragModeScroll ); connect( editRelationAddAct, SIGNAL(triggered()), this, SLOT(slotEditRelationAdd()) ); connect( editRelationRenameAct,SIGNAL(triggered()), this, SLOT(slotEditRelationRename()) ) ; connect(zoomInAct, SIGNAL(triggered()), graphicsWidget, SLOT( zoomIn()) ); connect(zoomOutAct, SIGNAL(triggered()), graphicsWidget, SLOT( zoomOut()) ); connect(editRotateLeftAct, SIGNAL(triggered()), graphicsWidget, SLOT( rotateLeft()) ); connect(editRotateRightAct, SIGNAL(triggered()), graphicsWidget, SLOT( rotateRight()) ); connect(editResetSlidersAct, SIGNAL(triggered()), graphicsWidget, SLOT( reset()) ); connect( layoutGuidesAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutGuides(bool))); connect(toolBoxNetworkAutoCreateSelect, static_cast(&QComboBox::currentIndexChanged), this, &MainWindow::toolBoxNetworkAutoCreateSelectChanged); connect(toolBoxEditNodeSubgraphSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxEditNodeSubgraphSelectChanged(int) ) ); connect(toolBoxEditEdgeModeSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(slotEditEdgeMode(int) ) ); connect(toolBoxEditEdgeTransformSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxEditEdgeTransformSelectChanged(int) ) ); connect(toolBoxAnalysisMatricesSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisMatricesSelectChanged(int) ) ); connect(toolBoxAnalysisCohesionSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisCohesionSelectChanged(int) ) ); connect(toolBoxAnalysisStrEquivalenceSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisStrEquivalenceSelectChanged(int) ) ); connect(toolBoxAnalysisCommunitiesSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisCommunitiesSelectChanged(int) ) ); connect(toolBoxAnalysisProminenceSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisProminenceSelectChanged(int) ) ); connect(toolBoxLayoutByIndexApplyButton, SIGNAL (clicked() ), this, SLOT(toolBoxLayoutByIndexApplyBtnPressed() ) ); connect(toolBoxLayoutForceDirectedApplyButton, SIGNAL (clicked() ), this, SLOT(toolBoxLayoutForceDirectedApplyBtnPressed() ) ); } /** * @brief Initializes the default network parameters. * Used on app start and especially when erasing a network to start a new one */ void MainWindow::initApp(){ qDebug()<<"MW::initApp() - START INITIALISATION ON THREAD" << thread(); statusMessage( tr("Application initialization. Please wait...")); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // first select none graphicsWidget->selectNone(); // Init basic variables inverseWeights=false; askedAboutWeights=false; previous_fileName=fileName; fileName=""; initFileCodec= "UTF-8"; networkSaveAct->setIcon(QIcon(":/images/file_download_48px.svg")); networkSaveAct->setEnabled(true); /** Clear previous network data */ activeGraph->clear(); activeGraph->setSocNetV_Version(VERSION); activeGraph->vertexShapeSetDefault(appSettings["initNodeShape"], appSettings["initNodeIconPath"]); activeGraph->vertexSizeInit(appSettings["initNodeSize"].toInt(0, 10)); activeGraph->vertexColorInit( appSettings["initNodeColor"] ); activeGraph->vertexNumberSizeInit(appSettings["initNodeNumberSize"].toInt(0,10)); activeGraph->vertexNumberColorInit(appSettings["initNodeNumberColor"]); activeGraph->vertexNumberDistanceInit(appSettings["initNodeNumberDistance"].toInt(0,10)); activeGraph->vertexLabelColorInit(appSettings["initNodeLabelColor"]); activeGraph->vertexLabelSizeInit(appSettings["initNodeLabelSize"].toInt(0,10)); activeGraph->vertexLabelDistanceInit(appSettings["initNodeLabelDistance"].toInt(0,10)); activeGraph->edgeColorInit(appSettings["initEdgeColor"]); activeGraph->edgeWeightNumbersVisibilitySet( (appSettings["initEdgeWeightNumbersVisibility"] == "true") ? true:false ); activeGraph->setReportsRealNumberPrecision(appSettings["initReportsRealNumberPrecision"].toInt()); activeGraph->setReportsLabelLength(appSettings["initReportsLabelsLength"].toInt()); activeGraph->setReportsChartType(appSettings["initReportsChartType"].toInt()); emit signalSetReportsDataDir(appSettings["dataDir"]); /** Clear graphicsWidget and reset settings and transformations **/ graphicsWidget->clear(); rotateSlider->setValue(0); zoomSlider->setValue(250); graphicsWidget->setInitZoomIndex(250); graphicsWidget->setInitNodeSize(appSettings["initNodeSize"].toInt(0, 10)); graphicsWidget->setNodeNumberVisibility( ( appSettings["initNodeNumbersVisibility"] == "true" ) ? true: false ); graphicsWidget->setNodeLabelsVisibility( (appSettings["initNodeLabelsVisibility"] == "true" ) ? true: false ); graphicsWidget->setNumbersInsideNodes( ( appSettings["initNodeNumbersInside"] == "true" ) ? true: false ); graphicsWidget->setEdgeHighlighting( ( appSettings["canvasEdgeHighlighting"] == "true" ) ? true: false ); if (appSettings["initBackgroundImage"] != "" && QFileInfo(appSettings["initBackgroundImage"]).exists()) { graphicsWidget->setBackgroundBrush(QImage(appSettings["initBackgroundImage"])); graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); statusMessage( tr("BackgroundImage on.") ); } else { graphicsWidget->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"])) ); } slotOptionsCanvasIndexMethod (appSettings["canvasIndexMethod"]) ; /** Clear Chart */ miniChart->resetToTrivial(); /** Clear LCDs **/ slotNetworkChanged(0, 0, 0, 0); rightPanelClickedNodeInDegreeLCD->setText("-"); rightPanelClickedNodeOutDegreeLCD->setText("-"); rightPanelClickedNodeClucofLCD->setText("-"); rightPanelClickedNodeLCD->setText("-"); rightPanelClickedEdgeNameLCD->setText("-"); rightPanelClickedEdgeWeightLCD->setText("-"); rightPanelClickedEdgeReciprocalWeightLCD->setText(""); /** Clear toolbox and menu checkboxes **/ toolBoxEditEdgeTransformSelect->setCurrentIndex(0); toolBoxEditEdgeModeSelect->setCurrentIndex(0); initComboBoxes(); toolBoxLayoutByIndexSelect->setCurrentIndex(0); toolBoxLayoutByIndexTypeSelect ->setCurrentIndex(0); toolBoxLayoutForceDirectedSelect->setCurrentIndex(0); optionsEdgeWeightNumbersAct->setChecked( (appSettings["initEdgeWeightNumbersVisibility"] == "true") ? true:false ); optionsEdgeWeightConsiderAct->setChecked( false ) ; optionsEdgeArrowsAct->setChecked( (appSettings["initEdgeArrows"] == "true") ? true: false ); optionsEdgeLabelsAct->setChecked ( (appSettings["initEdgeLabelsVisibility"] == "true") ? true: false ); editFilterNodesIsolatesAct->setChecked(false); // re-init orphan nodes menu item editFilterEdgesUnilateralAct->setChecked(false); //editRelationChangeCombo->clear(); qDebug()<<"MW::initApp() - Clearing my" <close(); delete ed; } m_textEditors.clear(); /** set window title **/ setWindowTitle(tr("Social Network Visualizer ")+VERSION); QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor(); setCursor(Qt::ArrowCursor); statusMessage( tr("Ready")); qDebug()<< "MW::initApp() - END INITIALISATION ON THREAD" << thread(); } void MainWindow::initComboBoxes() { toolBoxAnalysisCommunitiesSelect->setCurrentIndex(0); toolBoxAnalysisStrEquivalenceSelect->setCurrentIndex(0); toolBoxAnalysisCohesionSelect->setCurrentIndex(0); toolBoxAnalysisProminenceSelect->setCurrentIndex(0); toolBoxAnalysisMatricesSelect->setCurrentIndex(0); toolBoxNetworkAutoCreateSelect->setCurrentIndex(0); toolBoxEditNodeSubgraphSelect->setCurrentIndex(0); } /** * @brief Updates the Recent Files QActions in the menu */ void MainWindow::slotNetworkFileRecentUpdateActions() { int numRecentFiles = qMin(recentFiles.size(), (int)MaxRecentFiles); for (int i = 0; i < numRecentFiles; ++i) { QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(recentFiles[i]).fileName()); recentFileActs[i]->setText(text); recentFileActs[i]->setData(recentFiles[i]); recentFileActs[i]->setVisible(true); } for (int j = numRecentFiles; j < MaxRecentFiles; ++j) recentFileActs[j]->setVisible(false); //separatorAct->setVisible(numRecentFiles > 0); } /** * @brief Convenience method to show a message in the status bar, with the given duration * Slot called by Graph::statusMessage to display some message to the user * @param message */ void MainWindow::statusMessage(const QString message){ statusBar()->showMessage( message, appSettings["initStatusBarDuration"].toInt(0)); } /** * @brief Helper function to display a useful info message * @param text */ void MainWindow::slotHelpMessageToUserInfo(const QString text) { slotHelpMessageToUser(USER_MSG_INFO,tr("Useful information"), text ); } /** * @brief Helper function to display a useful error message * @param text */ void MainWindow::slotHelpMessageToUserError(const QString text) { slotHelpMessageToUser(USER_MSG_CRITICAL ,tr("Error"), text ); } /** * @brief Convenience method * @param message */ int MainWindow::slotHelpMessageToUser(const int type, const QString statusMsg, const QString text, const QString info, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defBtn, const QString btn1, const QString btn2 ) { int response=0; QMessageBox msgBox; QPushButton *pbtn1, *pbtn2; switch (type) { case USER_MSG_INFO: if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText(text); if (!info.isNull()) msgBox.setInformativeText(info); msgBox.setIcon(QMessageBox::Information); if (buttons==QMessageBox::NoButton) { msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); } else { msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defBtn); } msgBox.setDefaultButton(defBtn); response = msgBox.exec(); break; case USER_MSG_CRITICAL: if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText(text); if (!info.isNull()) msgBox.setInformativeText(info); //msgBox.setTextFormat(Qt::RichText); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; case USER_MSG_CRITICAL_NO_NETWORK: statusMessage( tr("Nothing to do! Load or create a social network first") ); msgBox.setText( tr("No network! \n" "Load social network data or create a new social network first. \n") ); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; case USER_MSG_CRITICAL_NO_EDGES: statusMessage( tr("Nothing to do! Load social network data or create edges first") ); msgBox.setText( tr("No edges! \n" "Load social network data or create some edges first. \n") ); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; case USER_MSG_QUESTION: if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText( text ); if (!info.isNull()) msgBox.setInformativeText(info); if (buttons==QMessageBox::NoButton) { msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Yes); } else { msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defBtn); } msgBox.setIcon(QMessageBox::Question); response = msgBox.exec(); break; case USER_MSG_QUESTION_CUSTOM: // a custom question with just two buttons if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText( text ); if (!info.isNull()) msgBox.setInformativeText(info); pbtn1 = msgBox.addButton(btn1, QMessageBox::ActionRole); pbtn2 = msgBox.addButton(btn2, QMessageBox::ActionRole); msgBox.setIcon(QMessageBox::Question); response = msgBox.exec(); if (msgBox.clickedButton() == pbtn1 ) { response=1; } else if (msgBox.clickedButton() == pbtn2 ) { response=2; } break; default: //just for sanity if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText( text ); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; } return response; } /** * @brief Called from MW, when user selects something in the Network Auto Create * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxNetworkAutoCreateSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxNetworkAutoCreateSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: // famous data-sets slotNetworkDataSetSelect(); break; case 2: // scale-free slotNetworkRandomScaleFreeDialog(); break; case 3: // sw slotNetworkRandomSmallWorldDialog(); break; case 4: // erdos slotNetworkRandomErdosRenyiDialog(); break; case 5: // lattice slotNetworkRandomLatticeDialog(); break; case 6: // d-regular slotNetworkRandomRegularDialog(); break; case 7: // ring lattice slotNetworkRandomRingLattice(); break; case 8: // web crawler slotNetworkWebCrawlerDialog(); break; }; qDebug()<< "MW::toolBoxEditNodeSubgraphSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects something in the Subgraph from Selected * Nodes selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxEditNodeSubgraphSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxEditNodeSubgraphSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotEditNodeSelectedToClique(); break; case 2: slotEditNodeSelectedToStar(); break; case 3: slotEditNodeSelectedToCycle(); break; case 4: slotEditNodeSelectedToLine(); break; }; qDebug()<< "MW::toolBoxEditNodeSubgraphSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects something in the Edge Transform * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxEditEdgeTransformSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxEditEdgeTransformSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotEditEdgeSymmetrizeAll(); break; case 2: slotEditEdgeSymmetrizeStrongTies(); break; case 3: slotEditEdgeSymmetrizeCocitation(); break; case 4: slotEditEdgeDichotomizationDialog(); break; }; } /** * @brief Called from MW, when user selects something in the Matrices * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxAnalysisMatricesSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxAnalysisMatricesSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotNetworkViewSociomatrix(); break; case 2: slotNetworkViewSociomatrixPlotText(); break; case 3: slotAnalyzeMatrixAdjacencyInverse(); break; case 4: slotAnalyzeMatrixAdjacencyTranspose(); break; case 5: slotAnalyzeMatrixAdjacencyCocitation(); break; case 6: slotAnalyzeMatrixDegree(); break; case 7: slotAnalyzeMatrixLaplacian(); break; }; qDebug()<< "MW::toolBoxAnalysisMatricesSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects something in the Cohesion * selectbox of the toolbox to compute basic graph theoretic / network properties * @param selectedIndex */ void MainWindow::toolBoxAnalysisCohesionSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxAnalysisCohesionSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotAnalyzeReciprocity(); break; case 2: slotAnalyzeSymmetryCheck(); break; case 3: slotAnalyzeDistance(); break; case 4: slotAnalyzeDistanceAverage(); break; case 5: slotAnalyzeMatrixDistances(); break; case 6: slotAnalyzeMatrixGeodesics(); break; case 7: slotAnalyzeEccentricity(); break; case 8: slotAnalyzeDiameter(); break; case 9: slotAnalyzeConnectedness(); break; case 10: slotAnalyzeWalksLength(); break; case 11: slotAnalyzeWalksTotal(); break; case 12: slotAnalyzeReachabilityMatrix(); break; case 13: slotAnalyzeClusteringCoefficient(); break; }; qDebug()<< "MW::toolBoxAnalysisCohesionSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects something in the Communities selectbox * of the toolbox * @param selectedIndex * */ void MainWindow::toolBoxAnalysisCommunitiesSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxAnalysisCommunitiesSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: qDebug()<< "Cliques"; slotAnalyzeCommunitiesCliqueCensus(); break; case 2: qDebug() << "Triad Census"; slotAnalyzeCommunitiesTriadCensus(); break; }; qDebug()<< "MW::toolBoxAnalysisCommunitiesSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects something in the Structural Equivalence * selectbox of the toolbox * @param selectedIndex * */ void MainWindow::toolBoxAnalysisStrEquivalenceSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxAnalysisStrEquivalenceSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: qDebug()<< "Pearson"; slotAnalyzeStrEquivalencePearsonDialog(); break; case 2: qDebug()<< "Similarities"; slotAnalyzeStrEquivalenceSimilarityMeasureDialog(); break; case 3: qDebug() << "Dissimilarities"; slotAnalyzeStrEquivalenceDissimilaritiesDialog(); break; case 4: qDebug() << "Hierarchical Clustering"; slotAnalyzeStrEquivalenceClusteringHierarchicalDialog(); break; }; qDebug()<< "MW::toolBoxAnalysisStrEquivalenceSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects something in the Prominence selectbox * of the toolbox * @param selectedIndex * */ void MainWindow::toolBoxAnalysisProminenceSelectChanged(const int &selectedIndex) { qDebug()<< "MW::toolBoxAnalysisProminenceSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotAnalyzeCentralityDegree(); break; case 2: slotAnalyzeCentralityCloseness(); break; case 3: slotAnalyzeCentralityClosenessIR(); break; case 4: slotAnalyzeCentralityBetweenness(); break; case 5: slotAnalyzeCentralityStress(); break; case 6: slotAnalyzeCentralityEccentricity(); break; case 7: slotAnalyzeCentralityPower(); break; case 8: slotAnalyzeCentralityInformation(); break; case 9: slotAnalyzeCentralityEigenvector(); break; case 10: slotAnalyzePrestigeDegree(); break; case 11: slotAnalyzePrestigePageRank(); break; case 12: slotAnalyzePrestigeProximity(); break; }; qDebug()<< "MW::toolBoxAnalysisProminenceSelectChanged() - initComboBoxes() "; initComboBoxes(); } /** * @brief Called from MW, when user selects a Prominence index in the Layout selectbox * of the Control Panel . */ void MainWindow::toolBoxLayoutByIndexApplyBtnPressed(){ qDebug()<<"MW::toolBoxLayoutByIndexApplyBtnPressed()"; int selectedIndex = toolBoxLayoutByIndexSelect->currentIndex(); QString selectedIndexText = toolBoxLayoutByIndexSelect->currentText(); int selectedLayoutType = toolBoxLayoutByIndexTypeSelect ->currentIndex(); qDebug()<<"MW::toolBoxLayoutByIndexApplyBtnPressed() - selected index is " << selectedIndexText << " : " << selectedIndex << " selected layout type is " << selectedLayoutType; switch(selectedIndex) { case 0: break; case 1: if (selectedLayoutType==0) slotLayoutRadialRandom(); else if (selectedLayoutType==1) slotLayoutRandom(); break; default: if (selectedLayoutType==0) { // radial slotLayoutRadialByProminenceIndex(selectedIndexText); } else if (selectedLayoutType==1) { // on levels slotLayoutLevelByProminenceIndex(selectedIndexText); } else if (selectedLayoutType==2) { // node size slotLayoutNodeSizeByProminenceIndex(selectedIndexText); // re-init other options for node sizes... } else if (selectedLayoutType==3){ // node color slotLayoutNodeColorByProminenceIndex(selectedIndexText); } break; }; } /** * @brief Called from MW, when user selects a model in the Layout by Force Directed * selectbox of left panel. */ void MainWindow::toolBoxLayoutForceDirectedApplyBtnPressed(){ qDebug()<<"MW::toolBoxLayoutForceDirectedApplyBtnPressed()"; int selectedModel = toolBoxLayoutForceDirectedSelect->currentIndex(); QString selectedModelText = toolBoxLayoutForceDirectedSelect->currentText(); qDebug() << " selected index is " << selectedModelText << " : " << selectedModel; switch(selectedModel) { case 0: break; case 1: slotLayoutGuides(false); slotLayoutKamadaKawai(); break; case 2: slotLayoutGuides(false); slotLayoutFruchterman(); break; case 3: slotLayoutGuides(false); slotLayoutSpringEmbedder(); break; default: toolBoxLayoutForceDirectedSelect->setCurrentIndex(0); break; }; } /** * @brief Creates a new network */ void MainWindow::slotNetworkNew() { slotNetworkClose(); } /** * @brief Returns the last path used by user to open/save something */ QString MainWindow::getLastPath() { if ( appSettings["lastUsedDirPath"] == "socnetv-initial-none") { appSettings["lastUsedDirPath"] = appSettings["dataDir"]; } qDebug()<< "MW::getLastPath()" << appSettings["lastUsedDirPath"] ; return appSettings["lastUsedDirPath"] ; } /** * @brief Sets the last path used by user to open/save a network and adds the file * to recent files... * @param filePath */ void MainWindow::setLastPath(const QString &filePath) { qDebug()<< "MW::setLastPath() for " << filePath; QString currentPath = QFileInfo(filePath).dir().absolutePath(); QDir::setCurrent(currentPath); appSettings["lastUsedDirPath"] = currentPath; if ( !QFileInfo(filePath).completeSuffix().toLower().contains( "bmp" ) && !QFileInfo(filePath).completeSuffix().toLower().contains( "jpg" ) && !QFileInfo(filePath).completeSuffix().toLower().contains( "png" ) && !QFileInfo(filePath).completeSuffix().toLower().contains( "pdf" ) ) { recentFiles.removeAll(filePath); recentFiles.prepend(filePath); while(recentFiles.size() > MaxRecentFiles ) recentFiles.removeLast(); } slotNetworkFileRecentUpdateActions(); saveSettings(); qDebug() << appSettings["lastUsedDirPath"]; } /** * @brief If m_fileName is empty, opens a file selection dialog * Then calls slotNetworkFilePreview() * Called on application loading from command line with filename parameter * Called from slotNetworkImport* methods * Called from slotNetworkFileLoadRecent * @param m_fileName * @param m_fileFormat * @param checkSelectFileType */ void MainWindow::slotNetworkFileChoose(QString m_fileName, int m_fileFormat, const bool &checkSelectFileType) { qDebug() << "MW::slotNetworkFileChoose() - " << " m_fileName: " << m_fileName << " m_fileFormat " << m_fileFormat << " checkSelectFileType " << checkSelectFileType; previous_fileName=fileName; QString fileType_filter; /* * CASE 1: No filename provided. This happens when: * - User clicked Open Network File or * - User clicked Import Network * * Prepare known filetypes and * Open a file selection dialog for the user * */ if (m_fileName.isNull() || m_fileName.isEmpty() ) { fileType=m_fileFormat; // prepare supported filetype extensions switch (fileType){ case FileType::GRAPHML: fileType_filter = tr("GraphML (*.graphml *.xml);;All (*)"); break; case FileType::PAJEK: fileType_filter = tr("Pajek (*.net *.paj *.pajek);;All (*)"); break; case FileType::ADJACENCY: fileType_filter = tr("Adjacency (*.csv *.sm *.adj *.txt);;All (*)"); break; case FileType::GRAPHVIZ: fileType_filter = tr("GraphViz (*.dot);;All (*)"); break; case FileType::UCINET: fileType_filter = tr("UCINET (*.dl *.dat);;All (*)"); break; case FileType::GML: fileType_filter = tr("GML (*.gml);;All (*)"); break; case FileType::EDGELIST_WEIGHTED: fileType_filter = tr("Weighted Edge List (*.txt *.list *.edgelist *.lst *.wlst);;All (*)"); break; case FileType::EDGELIST_SIMPLE: fileType_filter = tr("Simple Edge List (*.txt *.list *.edgelist *.lst);;All (*)"); break; case FileType::TWOMODE: fileType_filter = tr("Two-Mode Sociomatrix (*.2sm *.aff);;All (*)"); break; default: //All fileType_filter = tr("GraphML (*.graphml *.xml);;" "GML (*.gml *.xml);;" "Pajek (*.net *.pajek *.paj);;" "UCINET (*.dl *.dat);;" "Adjacency (*.csv *.adj *.sm *.txt);;" "GraphViz (*.dot);;" "Weighted Edge List (*.txt *.edgelist *.list *.lst *.wlst);;" "Simple Edge List (*.txt *.edgelist *.list *.lst);;" "Two-Mode Sociomatrix (*.2sm *.aff);;" "All (*)"); break; } //prepare the filedialog QFileDialog *fileDialog = new QFileDialog(this); fileDialog->setFileMode(QFileDialog::ExistingFile); fileDialog->setNameFilter(fileType_filter); fileDialog->setViewMode(QFileDialog::Detail); fileDialog->setDirectory(getLastPath()); //connect its signals to our slots connect ( fileDialog, &QFileDialog::filterSelected, this, &MainWindow::slotNetworkFileDialogFilterSelected); connect ( fileDialog, &QFileDialog::fileSelected, this, &MainWindow::slotNetworkFileDialogFileSelected); connect ( fileDialog, &QFileDialog::rejected , this, &MainWindow::slotNetworkFileDialogRejected); //open the filedialog statusMessage( tr("Choose a network file...")); if (fileDialog->exec()) { m_fileName = (fileDialog->selectedFiles()).at(0); qDebug() << "MW::slotNetworkFileChoose() - m_fileName " << m_fileName; } else { //display some error statusMessage( tr("Nothing to do...")); } return; } /* * CASE 2: Filename provided. This happens when: * - Application starts from command line with filename parameter or * - User selects a Recent File or * - User selects a file in a previous slotNetworkFileChoose call * * If checkSelectFileType==TRUE (that is on app start or Recent File), * it tries to understand fileType by file extension. If file has unknown * file extension or an ambiguous file extension used by many different file * formats, then it asks the user to provide the fileType. Then it loads the * file * * If checkSelectFileType==FALSE, then it loads the file with given fileType. * */ if (checkSelectFileType || m_fileFormat==FileType::UNRECOGNIZED) { // This happens only on application startup or on loading a recent file. if ( ! m_fileName.endsWith(".graphml",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".net",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".paj",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".pajek",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".dl",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".dat",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".gml",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".wlist",Qt::CaseInsensitive )&& ! m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".sm",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".csv",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".aff",Qt::CaseInsensitive )) { //ambigious file type. Open an input dialog for the user to choose // what kind of network file this is. tempFileNameNoPath=m_fileName.split ("/"); QStringList fileTypes; fileTypes << tr("GraphML") << tr("GML") << tr("Pajek") << tr("UCINET") << tr("Adjacency") << tr("GraphViz") << tr("Edge List (weighted)") << tr("Edge List (simple, non-weighted)") << tr("Two-mode sociomatrix") ; bool ok; QString userFileType= QInputDialog::getItem( this, tr("Selected file has ambiguous file extension!"), tr("You selected: %1 \n" "The name of this file has either an unknown extension \n" "or an extension used by different network file formats.\n\n" "SocNetV supports the following social network file " "formats. \nIn parentheses the expected extension. \n" "- GraphML (.graphml or .xml)\n" "- GML (.gml or .xml)\n" "- Pajek (.paj or .pajek or .net)\n" "- UCINET (.dl .dat) \n" "- GraphViz (.dot)\n" "- Adjacency Matrix (.sm or .adj or .csv or .txt)\n" "- Simple Edge List (.list or .lst)\n" "- Weighted Edge List (.wlist or .wlst)\n" "- Two-Mode / affiliation (.2sm or .aff) \n\n" "If you are sure the file is of a supported format, please \n" "select the right format from the list below."). arg(tempFileNameNoPath.last()), fileTypes, 0, false, &ok); if (ok && !userFileType.isEmpty()) { if (userFileType == "GraphML") { m_fileFormat=FileType::GRAPHML; } else if (userFileType == "GraphML") { m_fileFormat=FileType::PAJEK; } else if (userFileType == "GML") { m_fileFormat=FileType::GML; } else if (userFileType == "UCINET") { m_fileFormat=FileType::UCINET; } else if (userFileType == "Adjacency") { m_fileFormat=FileType::ADJACENCY; } else if (userFileType == "GraphViz") { m_fileFormat=FileType::GRAPHVIZ; } else if (userFileType == "Edge List (weighted)") { m_fileFormat=FileType::EDGELIST_WEIGHTED; } else if (userFileType == "Edge List (simple, non-weighted)") { m_fileFormat=FileType::EDGELIST_SIMPLE; } else if (userFileType == "Two-mode sociomatrix") { m_fileFormat=FileType::TWOMODE; } } else { statusMessage( tr("Opening network file aborted.")); //if a file was previously opened, get back to it. if (activeGraph->graphLoaded()) { fileName=previous_fileName; } return; } } else if (m_fileName.endsWith(".graphml",Qt::CaseInsensitive ) || m_fileName.endsWith(".xml",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::GRAPHML; } else if (m_fileName.endsWith(".net",Qt::CaseInsensitive ) || m_fileName.endsWith(".paj",Qt::CaseInsensitive ) || m_fileName.endsWith(".pajek",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::PAJEK; } else if (m_fileName.endsWith(".dl",Qt::CaseInsensitive ) || m_fileName.endsWith(".dat",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::UCINET; } else if (m_fileName.endsWith(".sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".csv",Qt::CaseInsensitive ) || m_fileName.endsWith(".adj",Qt::CaseInsensitive ) || m_fileName.endsWith(".txt",Qt::CaseInsensitive )) { m_fileFormat=FileType::ADJACENCY; } else if (m_fileName.endsWith(".dot",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::GRAPHVIZ; } else if (m_fileName.endsWith(".gml",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::GML; } else if (m_fileName.endsWith(".list",Qt::CaseInsensitive ) || m_fileName.endsWith(".lst",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::EDGELIST_SIMPLE; } else if (m_fileName.endsWith(".wlist",Qt::CaseInsensitive ) || m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::EDGELIST_WEIGHTED; } else if (m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".aff",Qt::CaseInsensitive ) ) { m_fileFormat=FileType::TWOMODE; } else m_fileFormat=FileType::UNRECOGNIZED; } qDebug()<<"MW::slotNetworkFileChoose() - Calling slotNetworkFilePreview" << "with m_fileName" << m_fileName << "and m_fileFormat " << m_fileFormat; slotNetworkFilePreview(m_fileName, m_fileFormat ); } void MainWindow::slotNetworkFileDialogRejected() { qDebug() << "MW::slotNetworkFileDialogRejected() - if a file was previously opened, get back to it."; statusMessage( tr("Opening aborted")); } /** * @brief Called when user selects a file filter (i.e. GraphML) in the fileDialog * @param filter */ void MainWindow::slotNetworkFileDialogFilterSelected(const QString &filter) { qDebug() << "MW::slotNetworkFileDialogFilterSelected() - filter" << filter; if (filter.startsWith("GraphML",Qt::CaseInsensitive ) ) { fileType=FileType::GRAPHML; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::GRAPHML"; } else if (filter.contains("PAJEK",Qt::CaseInsensitive ) ) { fileType=FileType::PAJEK; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::PAJEK"; } else if (filter.contains("DL",Qt::CaseInsensitive ) || filter.contains("UCINET",Qt::CaseInsensitive ) ) { fileType=FileType::UCINET; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::UCINET"; } else if (filter.contains("Adjacency",Qt::CaseInsensitive ) ) { fileType=FileType::ADJACENCY; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::ADJACENCY"; } else if (filter.contains("GraphViz",Qt::CaseInsensitive ) ) { fileType=FileType::GRAPHVIZ; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::GRAPHVIZ"; } else if (filter.contains("GML",Qt::CaseInsensitive ) ) { fileType=FileType::GML; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::GML"; } else if (filter.contains("Simple Edge List",Qt::CaseInsensitive ) ) { fileType=FileType::EDGELIST_SIMPLE; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::EDGELIST_SIMPLE"; } else if (filter.contains("Weighted Edge List",Qt::CaseInsensitive ) ) { fileType=FileType::EDGELIST_WEIGHTED; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::EDGELIST_WEIGHTED"; } else if (filter.contains("Two-Mode",Qt::CaseInsensitive ) ) { fileType=FileType::TWOMODE; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::TWOMODE"; } else { fileType=FileType::UNRECOGNIZED; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FileType::UNRECOGNIZED"; } } /** * @brief Called when user selects a file in the fileDialog * Calls slotNetworkFileChoose() again. * @param fileName * */ void MainWindow::slotNetworkFileDialogFileSelected(const QString &fileName) { qDebug() << "MW::slotNetworkFileDialogFileSelected() - filename " << fileName << "calling slotNetworkFileChoose() with fileType" << fileType; slotNetworkFileChoose( fileName, fileType, ( (fileType==FileType::UNRECOGNIZED) ? true : false ) ); } /** * @brief Saves the network to a file by calling Graph::graphSave(). * First, it checks if a fileName is currently set * If not, calls slotNetworkSaveAs (which prompts for a fileName before returning here) * If a fileName is set, it checks if fileFormat is supported and saves the network. * If not supported, or the file is new, just tries to save in GraphML * For other exporting options the user is informed to use the export menu. */ void MainWindow::slotNetworkSave(const int &fileFormat) { statusMessage( tr("Saving file...")); if (activeNodes() == 0) { statusMessage( QString(tr("Nothing to save. There are no vertices.")) ); } if (activeGraph->graphSaved()) { statusMessage( QString(tr("Graph already saved.")) ); } if ( fileName.isEmpty() ) { slotNetworkSaveAs(); return; } QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); // if the specified format is one of the supported ones, just save it. if ( activeGraph->graphFileFormatExportSupported( fileFormat ) ) { activeGraph->graphSave(fileName, fileFormat ) ; } // else if it is GraphML or new file not saved yet, just save it. else if (activeGraph->graphFileFormat()==FileType::GRAPHML || ( activeGraph->graphSaved() && !activeGraph->graphLoaded() ) ) { activeGraph->graphSave(fileName, FileType::GRAPHML); } // else check whether Graph thinks this is supported and save it else if ( activeGraph->graphFileFormatExportSupported( activeGraph->graphFileFormat() ) ) { activeGraph->graphSave(fileName, activeGraph->graphFileFormat() ) ; } // In any other case, save in GraphML. // First, inform the user that we will save in that format. else { switch( slotHelpMessageToUser (USER_MSG_QUESTION, tr("Save to GraphML?"), tr("Default File Format: GraphML "), tr("This network will be saved in GraphML format " "which is the default file format of SocNetV. \n\n" "Is this OK? \n\n" "If not, press Cancel, then go to Network > Export menu " "to see other supported formats to export your data to.") ) ) { case QMessageBox::Yes: fileName = QFileInfo(fileName).absolutePath() + "/" + QFileInfo(fileName).baseName(); fileName.append(".graphml"); fileNameNoPath = QFileInfo (fileName).fileName(); setLastPath(fileName); // store this path activeGraph->graphSave(fileName, FileType::GRAPHML); break; case QMessageBox::Cancel: case QMessageBox::No: statusMessage( tr("Save aborted...") ); break; } } } /** * @brief Saves the network in a new GraphML file. * Always uses the GraphML format and extension. */ void MainWindow::slotNetworkSaveAs() { qDebug() << "MW::slotNetworkSaveAs()"; statusMessage( tr("Enter or select a filename to save the network...")); QString fn = QFileDialog::getSaveFileName( this, tr("Save Network to GraphML File Named..."), getLastPath(), tr("GraphML (*.graphml *.xml);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ fn.append(".graphml"); slotHelpMessageToUser ( USER_MSG_INFO, tr("Appending .graphml"), tr("Missing Extension. \n" "Appended the standard .graphml extension to the given filename.\n" "Final Filename: ") + QFileInfo(fn).fileName() ); } else if ( !QFileInfo(fn).suffix().contains("graphml", Qt::CaseInsensitive) && !QFileInfo(fn).suffix().contains("xml", Qt::CaseInsensitive) ) { fn = QFileInfo(fn).absolutePath() + "/" + QFileInfo(fn).baseName(); fn.append(".graphml"); slotHelpMessageToUser ( USER_MSG_INFO, tr("Appending .graphml"), tr("Wrong Extension. \n" "Appended a standard .graphml to the given filename. \n" "Final Filename: ") + QFileInfo(fn).fileName() ); } fileName=fn; QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); setLastPath(fileName); // store this path slotNetworkSave(FileType::GRAPHML); } else { statusMessage( tr("Saving aborted")); return; } } /** * @brief Called from Graph to update the 'save' status of the network * Updates Save icon and window title (if saved) * status > 0 means network has been saved * status = 0 means network has changed and needs saving * status < 0 means network has changed but there was an error saving it. * @param status */ void MainWindow::slotNetworkSavedStatus (const int &status) { if (status < 0) { statusMessage( tr("Error! Could not save this file: %1").arg (fileNameNoPath)); networkSaveAct->setEnabled(true); } else if (status == 0) { // Network needs saving // UX: Maybe change it to a more prominent color for the user to see? // networkSaveAct->setIcon(QIcon(":/images/file_download_48px.svg")); networkSaveAct->setEnabled(true); } else { // Network is saved. networkSaveAct->setEnabled(false); setWindowTitle( fileNameNoPath ); statusMessage( tr("Network saved under filename: %1").arg (fileNameNoPath)); } } /** * @brief Closes the network. Saves it if necessary. Used by createNew. */ void MainWindow::slotNetworkClose() { qDebug()<<"MW::slotNetworkClose()"; statusMessage( tr("Closing network file...")); if (!activeGraph->graphSaved()) { switch ( slotHelpMessageToUser ( USER_MSG_QUESTION, tr("Closing Network..."), tr("Network has not been saved. \n" "Do you want to save before closing it?") ) ) { case QMessageBox::Yes: slotNetworkSave(); break; case QMessageBox::No: break; case QMessageBox::Cancel: return; break; } } statusMessage( tr("Erasing old network data....")); initApp(); statusMessage( tr("Ready.")); } /** * @brief Sends the active network to the printer */ void MainWindow::slotNetworkPrint() { statusMessage( tr("Printing...")); QPrintDialog dialog(printer, this); if ( dialog.exec() == QDialog::Accepted ) { QPainter painter(printer); graphicsWidget->render(&painter); }; statusMessage( tr("Ready.")); } /** * @brief Imports a network from a GraphML formatted file */ void MainWindow::slotNetworkImportGraphML(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString(), FileType::GRAPHML, m_checkSelectFileType); } /** * @brief Imports a network from a GML formatted file */ void MainWindow::slotNetworkImportGML(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString(), FileType::GML, m_checkSelectFileType); } /** * @brief Imports a network from a Pajek-like formatted file */ void MainWindow::slotNetworkImportPajek(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString(), FileType::PAJEK, m_checkSelectFileType); } /** * @brief Imports a network from a Adjacency matrix formatted file */ void MainWindow::slotNetworkImportAdjacency(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString(), FileType::ADJACENCY, m_checkSelectFileType); } /** * @brief Imports a network from a Dot (GraphViz) formatted file */ void MainWindow::slotNetworkImportGraphviz(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString() ,FileType::GRAPHVIZ, m_checkSelectFileType); } /** * @brief Imports a network from a UCINET formatted file */ void MainWindow::slotNetworkImportUcinet(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString(), FileType::UCINET, m_checkSelectFileType); } /** * @brief Imports a network from a simple List or weighted List formatted file */ void MainWindow::slotNetworkImportEdgeList(){ bool m_checkSelectFileType = false; switch( slotHelpMessageToUser(USER_MSG_QUESTION_CUSTOM, tr("Select type of edge list format..."), tr("Select type of edge list format"), tr("SocNetV can parse two kinds of edgelist formats: \n\n" "A. Edge lists with edge weights, " "where each line has exactly 3 columns: " "source target weight, i.e.:\n" "1 2 1 \n" "2 3 1 \n" "3 4 2 \n" "4 5 1 \n\n" "B. Simple edge lists without weights, where each line " "has two or more columns in the form: source, target1, target2, ... , i.e.:\n" "1 2 3 4 5 6\n" "2 3 4 \n" "3 5 8 7\n\n" "Please select the appropriate type of edge list format of " "the file you want to load:"), QMessageBox::NoButton, QMessageBox::NoButton, tr("Weighted"), tr("Simple non-weighted") ) ) { case 1: qDebug() << "*** MW::slotNetworkImportEdgeList - Weighted list selected! " ; slotNetworkFileChoose( QString(), FileType::EDGELIST_WEIGHTED, m_checkSelectFileType); break; case 2: qDebug() << "*** MW: slotNetworkImportEdgeList - Simple list selected! " ; slotNetworkFileChoose( QString(), FileType::EDGELIST_SIMPLE, m_checkSelectFileType); break; } } /** * @brief Imports a network from a two mode sociomatrix formatted file */ void MainWindow::slotNetworkImportTwoModeSM(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString(), FileType::TWOMODE, m_checkSelectFileType); } /** * @brief Setup a list of all text codecs supported by OS */ void MainWindow::slotNetworkAvailableTextCodecs() { 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(); } /** * @brief Called from slotNetworkFileChoose() * Opens a window to preview the selected file where the user * can select an appropriate text codec * @param m_fileName * @param m_fileFormat * @return */ bool MainWindow::slotNetworkFilePreview(const QString &m_fileName, const int &m_fileFormat ){ qDebug() << "MW::slotNetworkFilePreview() - file: "<< m_fileName; if (!m_fileName.isEmpty()) { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); QFile file(m_fileName); if (!file.open(QFile::ReadOnly)) { slotHelpMessageToUserError( tr("Cannot read file %1:\n%2") .arg(m_fileName) .arg(file.errorString()) ); return false; } qDebug() << "MW::slotNetworkFilePreview() - reading file... " ; QByteArray data = file.readAll(); m_dialogPreviewFile->setEncodedData(data,m_fileName, m_fileFormat); QApplication::restoreOverrideCursor(); m_dialogPreviewFile->exec(); } return true; } /** * @brief Called on click on any file entry in "Recent Files" menu * Calls slotNetworkFileChoose() which checks file type and calls slotNetworkFilePreview */ void MainWindow::slotNetworkFileLoadRecent() { QAction *action = qobject_cast(sender()); if (action) { slotNetworkFileChoose(action->data().toString() ); } } /** * @brief Main network file loader method * Called from m_dialogPreviewFile and slotNetworkDataSetRecreate * Calls initApp to init to default values. * Then calls activeGraph::graphLoad to actually load the network... * @param m_fileName * @param m_codecName * @param m_fileFormat */ void MainWindow::slotNetworkFileLoad(const QString m_fileName, const QString m_codecName, const int m_fileFormat ) { qDebug() << "MW::slotNetworkFileLoad() : "<< m_fileName << " m_codecName " << m_codecName << " m_fileFormat " << m_fileFormat; initApp(); userSelectedCodecName = m_codecName; //var for future use in a Settings dialog QString delimiter=QString(); int two_sm_mode = 0; if ( m_fileFormat == FileType::TWOMODE ) { switch( slotHelpMessageToUser ( USER_MSG_QUESTION_CUSTOM, tr("Two-mode sociomatrix. Select mode..."), tr("Two-mode sociomatrix"), tr("If this file is in two-mode sociomatrix format, " "please specify which mode to open \n\n" "1st mode: rows are nodes \n" "2nd mode: columns are nodes"), QMessageBox::NoButton, QMessageBox::Ok, tr("1st Mode"),tr("2nd mode") ) ) { case 1: two_sm_mode = 1; break; case 2: two_sm_mode = 2; break; } } if ( m_fileFormat == FileType::EDGELIST_SIMPLE || m_fileFormat == FileType::EDGELIST_WEIGHTED ) { bool ok; QString delimiter = QInputDialog::getText( this, tr("Column delimiter in Edgelist file "), tr("SocNetV supports edge list formatted files " "with arbitrary column delimiters. \n" "The default delimiter is one or more spaces.\n\n" "If the column delimiter in this file is " "other than simple space or TAB, \n" "please enter it below.\n\n" "For instance, if the delimiter is a " "comma or pipe enter \",\" or \"|\" respectively.\n\n" "Leave empty to use space or TAB as delimiter."), QLineEdit::Normal, QString(""), &ok); if (!ok || delimiter.isEmpty() || delimiter.isNull() ) { delimiter=" "; } qDebug()<<"MW::slotNetworkFileLoad() - delimiter" << delimiter; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MW::slotNetworkFileLoad() - Calling activeGraph->graphLoad()" << "MW thread is:" << thread(); activeGraph->graphLoad ( m_fileName, m_codecName, m_fileFormat, two_sm_mode, delimiter ); } /** * @brief 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 */ void MainWindow::slotNetworkFileLoaded (const int &type, const QString &fName, const QString &netName, const int &totalNodes, const int &totalEdges, const QString &message) { qDebug()<< "MW::slotNetworkFileLoaded() - type " << type; if (type > 0) { // We have loaded a file with success. // Update our window and save path in settings fileName=fName; previous_fileName=fileName; QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); Q_ASSERT_X( !fileNameNoPath.isEmpty(), "not empty filename ", "empty filename " ); setWindowTitle("SocNetV "+ VERSION +" - "+fileNameNoPath); setLastPath(fileName); // store this path and file } else { qDebug()<< "MW::slotNetworkFileLoaded() - UNRECOGNIZED FILE. " "Message from Parser: " << message << "Calling initApp()"; statusMessage( tr("Error loading requested file. Aborted.")); slotHelpMessageToUser(USER_MSG_CRITICAL, tr("Error loading network file"), tr("Error loading network file"), tr("Sorry, the selected file is not in a supported format or encoding, " "or contains formatting errors. \n\n" "The error message was: \n\n" "%1" "\n\n" "What now? Review the message above to see if it helps you to fix the data file. " "Try a different codec in the preview window " "or if the file is of a legacy format (i.e. Pajek, UCINET, GraphViz, etc), " "please use the options in the Import sub menu. \n").arg(message) ); initApp(); return; } switch( type ) { case 0: break; case 1: statusMessage( tr("GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 2: statusMessage( tr("Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges )); break; case 3: statusMessage( tr("Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 4: statusMessage( tr("GraphViz (Dot) formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 5: statusMessage( tr("UCINET formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 6: statusMessage( tr("GML formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 7: statusMessage( tr("Weighted list formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 8: statusMessage( tr("Simple list formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 9: statusMessage( tr("Two-mode affiliation network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; default: // just for sanity QMessageBox::critical(this, "Error","Unrecognized format. \nPlease specify" " which is the file-format using Import Menu.","OK",0); break; } networkSaveAct->setIcon(QIcon(":/images/file_download_48px.svg")); networkSaveAct->setEnabled(false); QApplication::restoreOverrideCursor(); } /** * @brief Called from editDragModeSelectAct to toggle the drag mode (select or scroll). */ void MainWindow::slotEditDragModeSelection(bool checked){ qDebug() << "MW::slotEditDragModeSelection() - checked" << checked; editDragModeScrollAct -> setChecked(false); if (editDragModeSelectAct ->isChecked()) { graphicsWidget->setDragMode(QGraphicsView::RubberBandDrag); graphicsWidget->setInteractive(true); } else { graphicsWidget->setDragMode(QGraphicsView::NoDrag); graphicsWidget->setInteractive(false); } } /** * @brief Called from editDragModeSelectAct to toggle the drag mode (select or scroll). */ void MainWindow::slotEditDragModeScroll(bool checked){ qDebug() << "MW::slotEditDragModeScroll() - checked" << checked; editDragModeSelectAct ->setChecked(false); graphicsWidget->setInteractive(false); if ( editDragModeScrollAct -> isChecked() ) { graphicsWidget->setDragMode(QGraphicsView::ScrollHandDrag); } else { graphicsWidget->setDragMode(QGraphicsView::NoDrag); } } /** * @brief Called from Graph::relationsClear() to clear the relations combo. */ void MainWindow::slotEditRelationsClear(){ qDebug() << "MW::slotEditRelationsClear() - clearing combo"; editRelationChangeCombo->clear(); } /** * @brief Called from MW when user clicks New Relation btn * or when the user creates the first edge visually. * Called from activeGraph::relationAdd(QString) * via signal Graph::signalRelationChangedToMW() when the parser or a * Graph method demands a new relation to be added in the Combobox. */ void MainWindow::slotEditRelationAdd(QString newRelationName, const bool &changeRelation){ int comboItemsBefore = editRelationChangeCombo->count(); int relationsCounter=activeGraph->relations(); qDebug() << "MW::slotEditRelationAdd() - adding relation:" << newRelationName <<"to relations combo. Before this, combo items:" << comboItemsBefore << "and currentIndex:" <currentIndex() << "relationsCounter:" <addItem(newRelationName); if (changeRelation) { if ( comboItemsBefore == 0 ) { // only at startup slotEditRelationChange(0); } else { slotEditRelationChange(); } } qDebug() << "MW::slotEditRelationAdd() - added relation:" << newRelationName <<"now combo items:" << editRelationChangeCombo->count() << "now currentIndex:" <currentIndex() << "relationsCounter" <setCurrentIndex( ( editRelationChangeCombo->count()-1 ) ); } else { qDebug() << "MW::slotEditRelationChange(int) - to index" << relIndex; editRelationChangeCombo->setCurrentIndex(relIndex); } } /** * @brief Renames a relation * @param newName */ void MainWindow::slotEditRelationRename(QString newName) { qDebug()<<"MW::slotEditRelationRename() -" << newName; bool ok=false; if (newName.isNull() || newName.isEmpty()) { qDebug()<<"MW::slotEditRelationRename() - prompt to enter new name"; newName = QInputDialog::getText( this, tr("Rename current relation"), tr("Enter a new name for this relation."), QLineEdit::Normal, QString(), &ok ); if ( newName.isEmpty() || !ok ){ slotHelpMessageToUser(USER_MSG_CRITICAL, tr("Not a valid name."), tr("Error"), tr("You did not enter a valid name for this relation.") ); return; } else { activeGraph->relationCurrentRename(newName, true); } } else { qDebug()<<"MW::slotEditRelationRename() - current text " << editRelationChangeCombo->currentText(); qDebug()<<"MW::slotEditRelationRename() - updating combo name to" << newName; editRelationChangeCombo->setCurrentText(newName); } } /** * @brief Obsolete - Exports the network to a PNG image * @return * */ bool MainWindow::slotNetworkExportPNG(){ qDebug()<< "MW::slotNetworkExportPNG"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); 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 ("/"); QString name = tempFileNameNoPath.last(); name.truncate(name.lastIndexOf(".")); qDebug("slotExportPNG: grabbing canvas"); QPixmap picture; picture=graphicsWidget->grab(graphicsWidget->rect()); qDebug("slotExportPNG: adding logo"); QPainter p; p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); if (appSettings["printLogo"]=="true") { QImage logo(":/images/socnetv-logo.png"); p.drawImage(5,5, logo); p.drawText(7,47,name); } else p.drawText(5,15,name); 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; } /** * @brief Opens the Export to Image Dialog */ void MainWindow::slotNetworkExportImageDialog() { qDebug() << "MW::slotNetworkExportImageDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Opening Image export dialog. ")); m_dialogExportImage = new DialogExportImage(this); connect( m_dialogExportImage, &DialogExportImage::userChoices, this, &MainWindow::slotNetworkExportImage); m_dialogExportImage->exec(); } /** * @brief Exports the network to a an image * @return */ void MainWindow::slotNetworkExportImage( const QString &filename, const QByteArray &format, const int &quality, const int &compression ) { qDebug() << "slotNetworkExportImage()"; if (filename.isEmpty()) { statusMessage( tr("Saving aborted") ); return; } setLastPath(filename); // store this path tempFileNameNoPath=filename.split ("/"); QString name = tempFileNameNoPath.last(); name.truncate(name.lastIndexOf(".")); // Grab network from canvas qDebug() << "slotNetworkExportImage(): grabbing canvas"; QPixmap picture; picture = graphicsWidget->grab(graphicsWidget->viewport()->rect()); QPainter p; qDebug() << "slotNetworkExportImage(): adding name (and logo)"; p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); if (appSettings["printLogo"]=="true") { QImage logo(":/images/socnetv-logo.png"); p.drawImage(5,5, logo); p.drawText(7,47,name); } else p.drawText(5,15,name); p.end(); qDebug() << "slotNetworkExportImage(): saving to file"; QImageWriter imgWriter; imgWriter.setFormat(format); imgWriter.setQuality(quality); imgWriter.setCompression(compression); imgWriter.setFileName(filename); if ( imgWriter.write(picture.toImage()) ) { QMessageBox::information(this, tr("Export to image..."), tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); statusMessage( tr("Image exporting completed.") ); } else { slotHelpMessageToUser(USER_MSG_CRITICAL, "Error", "error exporing image", imgWriter.errorString()); } } /** * @brief Opens the Export to PDF Dialog */ void MainWindow::slotNetworkExportPDFDialog() { qDebug() << "MW::slotNetworkExportPDFDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Opening PDF export dialog. ")); m_dialogExportPDF = new DialogExportPDF(this); connect( m_dialogExportPDF, &DialogExportPDF::userChoices, this, &MainWindow::slotNetworkExportPDF); m_dialogExportPDF->exec(); } /** * @brief Exports the visible part of the network to a PDF Document * @return * */ void MainWindow::slotNetworkExportPDF(QString &pdfName, const QPageLayout::Orientation &orientation, const int &dpi, const QPrinter::PrinterMode printerMode=QPrinter::ScreenResolution, const QPageSize &pageSize = QPageSize(QPageSize::A4) ){ qDebug()<< "MW::slotNetworkExportPDF()"; Q_UNUSED(dpi); if (pdfName.isEmpty()) { statusMessage( tr("Saving aborted")); return; } else { setLastPath(pdfName); // store this path tempFileNameNoPath=pdfName.split ("/"); QString name = tempFileNameNoPath.last(); name.truncate(name.lastIndexOf(".")); printerPDF = new QPrinter(printerMode); printerPDF->setOutputFormat(QPrinter::PdfFormat); printerPDF->setOutputFileName(pdfName); printerPDF->setPageOrientation(orientation); printerPDF->setPageSize(pageSize); // printerPDF->setResolution(dpi); QPainter p; p.begin(printerPDF); graphicsWidget->render(&p, QRect(0, 0, printerPDF->width(), printerPDF->height()), graphicsWidget->viewport()->rect()); p.setFont(QFont ("Helvetica", 8, QFont::Normal, false)); if (appSettings["printLogo"]=="true") { QImage logo(":/images/socnetv-logo.png"); p.drawImage(5,5, logo); p.drawText(7,47,name); } else p.drawText(5,15,name); p.end(); delete printerPDF; } qDebug()<< "Exporting PDF to "<< pdfName; tempFileNameNoPath=pdfName.split ("/"); setLastPath(pdfName); QMessageBox::information(this, tr("Export to PDF..."), tr("File saved as: ")+tempFileNameNoPath.last() , "OK",0); statusMessage( tr("Exporting completed") ); } /** * @brief Exports the network to a Pajek-formatted file * Calls the relevant Graph method. */ void MainWindow::slotNetworkExportPajek() { qDebug () << "MW::slotNetworkExportPajek"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); 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! \n" "Appending a standard .paj to the given filename."), "OK",0); fn.append(".paj"); } fileName=fn; setLastPath(fileName); QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); } else { statusMessage( tr("Saving aborted")); return; } activeGraph->graphSave(fileName, FileType::PAJEK); } /** * @brief Exports the network to a adjacency matrix-formatted file * Calls the relevant Graph method. */ void MainWindow::slotNetworkExportSM(){ qDebug("MW: slotNetworkExportSM()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); 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! \n" "Appending a standard .adj to the given filename."), "OK",0); fn.append(".adj"); } fileName=fn; setLastPath(fileName); QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); } else { statusMessage( tr("Saving aborted")); return; } bool saveEdgeWeights=false; if (activeGraph->graphIsWeighted() ) { switch ( slotHelpMessageToUser(USER_MSG_QUESTION, tr("Weighted graph. Social network with valued/weighted edges"), tr("Social network with valued/weighted edges"), tr("This social network includes valued/weighted edges " "(the depicted graph is weighted). " "Do you want to save the edge weights in the adjacency file?\n" "Select Yes if you want to save edge values " "in the resulting file. \n" "Select No, if you don't want edge values " "to be saved. In the later case, all non-zero values will be truncated to 1.") ) ) { case QMessageBox::Yes: saveEdgeWeights = true; break; case QMessageBox::No: saveEdgeWeights = false; break; case QMessageBox::Cancel: statusMessage( tr("Save aborted...") ); return; break; } } activeGraph->graphSave(fileName, FileType::ADJACENCY, saveEdgeWeights ) ; } /** * @brief TODO Exports the network to a DL-formatted file * @return */ bool MainWindow::slotNetworkExportDL(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); 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; } /** TODO: Exports the network to a GW-formatted file */ bool MainWindow::slotNetworkExportGW(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); 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; } /** TODO: Exports the network to a list-formatted file */ bool MainWindow::slotNetworkExportList(){ 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; } /** * @brief 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::slotNetworkFileView(){ qDebug() << "slotNetworkFileView() : " << fileName.toLatin1(); if ( activeGraph->graphLoaded() && activeGraph->graphSaved() ) { //network unmodified, read loaded file again. QFile f( fileName ); if ( !f.open( QIODevice::ReadOnly ) ) { qDebug ("Error in open!"); return; } TextEditor *ed = new TextEditor(fileName,this,false); QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); ed->setWindowTitle( fileNameNoPath ); ed->show(); m_textEditors << ed; statusMessage( tr("Displaying network data file %1" ).arg(fileNameNoPath)); } else if (!activeGraph->graphSaved() ) { if ( !activeGraph->graphLoaded() ) { // new network, not saved yet int response = slotHelpMessageToUser( USER_MSG_QUESTION, tr("New network not saved yet. You might want to save it first."), tr("This new network you created has not been saved yet."), tr("Do you want to open a file dialog to save your work " "(then I will display the file)?"), QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes ); if ( response == QMessageBox::Yes ) { slotNetworkSaveAs(); } else { return; } } else { // loaded network, but modified int response = slotHelpMessageToUser( USER_MSG_QUESTION, tr("Current network has been modified. Save to the original file?"), tr("Current social network has been modified since last save."), tr("Do you want to save it to the original file?"), QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes ); if ( response == QMessageBox::Yes ){ slotNetworkSave(); }else if (response ==QMessageBox::No ) { slotNetworkSaveAs(); } else { // user pressed Cancel return; } } slotNetworkFileView(); } else { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); } } /** * @brief Opens the embedded text editor */ void MainWindow::slotNetworkTextEditor(){ qDebug() << "slotNetworkTextEditor() : "; TextEditor *ed = new TextEditor("", this,false); ed->setWindowTitle(tr("New Network File")); ed->show(); m_textEditors << ed; statusMessage( tr("Enter your network data here" ) ); } /** * @brief Displays the adjacency matrix of the network. * It uses a different method for writing the matrix to a file. * While slotNetworkExportSM uses << operator of Matrix class * (via adjacencyMatrix of Graph class), this is using directly the * writeMatrixAdjacency method of Graph class */ void MainWindow::slotNetworkViewSociomatrix(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-adjacency-"+dateTime+".html"; qDebug () << "MW::slotNetworkViewSociomatrix() - dataDir" << appSettings["dataDir"] << "fn" <writeMatrixAdjacency(fn) ; //AVOID THIS, no preserving of node numbers when nodes are deleted. // activeGraph->writeMatrix(fn,MATRIX_ADJACENCY) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { qDebug () << "MW::slotNetworkViewSociomatrix() - " "calling QDesktopServices::openUrl for" << QUrl::fromLocalFile(fn) ; QDesktopServices::openUrl( QUrl::fromLocalFile(fn) ); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Adjacency matrix saved as ") + QDir::toNativeSeparators(fn)); } /** * @brief Displays a text-only plot of the network adjacency matrix */ void MainWindow::slotNetworkViewSociomatrixPlotText(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int N=activeNodes(); statusMessage(tr("Creating plot of adjacency matrix of %1 nodes.").arg(N )); QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-adjacency-plot-"+dateTime+".html"; bool simpler = false; if ( N > 999 ) { qreal MB = (N * N * 10)/(1024*1024); switch ( slotHelpMessageToUser( USER_MSG_QUESTION,tr("Very large network to plot!"), tr("Warning: Really large network"), tr("To plot a %1 x %1 matrix arranged in HTML table, " "I will need time to write a very large .html file , circa %2 MB in size. " "Instead, I can create a simpler / smaller HTML file without table. " "Press Yes to continue with simpler version, " "Press No to create large file with HTML table.").arg(N).arg( MB ) ) ) { case QMessageBox::Yes: simpler = true; break; case QMessageBox::No: simpler = false; break; default: return; break; } } activeGraph->writeMatrixAdjacencyPlot(fn, simpler); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Visual form of adjacency matrix saved as ") + QDir::toNativeSeparators(fn)); } /** * @brief Calls the m_datasetSelectionDialog to display the dataset selection dialog */ void MainWindow::slotNetworkDataSetSelect(){ qDebug()<< "MW::slotNetworkDataSetSelect()"; m_datasetSelectDialog = new DialogDataSetSelect(this); connect( m_datasetSelectDialog, SIGNAL( userChoices( QString) ), this, SLOT( slotNetworkDataSetRecreate(QString) ) ); m_datasetSelectDialog->exec(); } /** * @brief MainWindow::slotNetworkDataSetRecreate * @param m_fileName * Recreates some of the most famous and widely used data sets in * network analysis studies */ void MainWindow::slotNetworkDataSetRecreate (const QString m_fileName) { int m_fileFormat=0; qDebug()<< "MW::slotNetworkDataSetRecreate() fileName: " << m_fileName; //initApp(); qDebug()<< "MW::slotNetworkDataSetRecreate() datadir+fileName: " << appSettings["dataDir"]+m_fileName; activeGraph->writeDataSetToFile(appSettings["dataDir"], m_fileName); if (m_fileName.endsWith(".graphml")) { m_fileFormat=FileType::GRAPHML; } else if (m_fileName.endsWith(".pajek") || m_fileName.endsWith(".paj") || m_fileName.endsWith(".net")) { m_fileFormat=FileType::PAJEK; } else if (m_fileName.endsWith(".sm") || m_fileName.endsWith(".adj")) { m_fileFormat=FileType::ADJACENCY; } else if (m_fileName.endsWith(".dot")) { m_fileFormat=FileType::GRAPHVIZ; } else if (m_fileName.endsWith(".dl")) { m_fileFormat=FileType::UCINET; } else if (m_fileName.endsWith(".gml")) { m_fileFormat=FileType::GML; } else if (m_fileName.endsWith(".wlst")) { m_fileFormat=FileType::EDGELIST_WEIGHTED; } else if (m_fileName.endsWith(".lst")) { m_fileFormat=FileType::EDGELIST_SIMPLE; } else if (m_fileName.endsWith(".2sm")) { m_fileFormat=FileType::TWOMODE; } slotNetworkFileLoad(appSettings["dataDir"]+m_fileName, "UTF-8", m_fileFormat); // if ( slotNetworkFileLoad(appSettings["dataDir"]+m_fileName, "UTF-8", m_fileFormat) ) { // qDebug() << "slotNetworkDataSetRecreate() 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."); // } } /** * @brief MainWindow::slotNetworkRandomErdosRenyiDialog * Shows the Erdos-Renyi network creation dialog */ void MainWindow::slotNetworkRandomErdosRenyiDialog(){ statusMessage( tr("Generate a random Erdos-Renyi network. ")); m_randErdosRenyiDialog = new DialogRandErdosRenyi( this, appSettings["randomErdosEdgeProbability"].toFloat(0)); connect( m_randErdosRenyiDialog, &DialogRandErdosRenyi::userChoices, this, &MainWindow::slotNetworkRandomErdosRenyi ); m_randErdosRenyiDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomErdosRenyi * @param newNodes * @param model * @param edges * @param eprob * @param mode * @param diag * Calls activeGraph->slotNetworkRandomErdosRenyi () to create a symmetric network * Edge existance is controlled by a user specified possibility. */ void MainWindow::slotNetworkRandomErdosRenyi( const int newNodes, const QString model, const int edges, const qreal eprob, const QString mode, const bool diag) { qDebug() << "MW::slotNetworkRandomErdosRenyi()"; initApp(); statusMessage( tr("Creating Erdos-Renyi Random Network. Please wait... ") ); appSettings["randomErdosEdgeProbability"] = QString::number(eprob); activeGraph->randomNetErdosCreate ( newNodes, model, edges, eprob, mode, diag); setWindowTitle("Untitled Erdos-Renyi random network"); double threshold = log(newNodes)/newNodes; //qreal clucof=activeGraph->clusteringCoefficient(); if ( (eprob ) > threshold ) QMessageBox::information( this, "New Erdos-Renyi Random Network", tr("Random network created. \n")+ //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 Erdos-Renyi Random Network", tr("Random network created. \n")+ //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( tr("Erdos-Renyi Random Network created. ") ) ; } /** * @brief MainWindow::slotNetworkRandomScaleFreeDialog */ void MainWindow::slotNetworkRandomScaleFreeDialog() { qDebug() << "MW::slotNetworkRandomScaleFreeDialog()"; statusMessage( tr("Generate a random Scale-Free network. ")); m_randScaleFreeDialog = new DialogRandScaleFree(this); connect( m_randScaleFreeDialog, &DialogRandScaleFree::userChoices, this, &MainWindow::slotNetworkRandomScaleFree); m_randScaleFreeDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomScaleFree * @param nodes * @param power * @param initialNodes * @param edgesPerStep * @param zeroAppeal * @param mode */ void MainWindow::slotNetworkRandomScaleFree ( const int &newNodes, const int &power, const int &initialNodes, const int &edgesPerStep, const qreal &zeroAppeal, const QString &mode) { qDebug() << "MW::slotNetworkRandomScaleFree()"; initApp(); activeGraph->randomNetScaleFreeCreate( newNodes, power, initialNodes, edgesPerStep, zeroAppeal, mode); setWindowTitle("Untitled scale-free network"); //qreal avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //qreal clucof=activeGraph->clusteringCoefficient(); QMessageBox::information(this, "New scale-free network", tr("Scale-free random network created.\n") // +tr("\nNodes: ")+ QString::number(nodesSelected)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr("Scale-Free Random Network created. ") ); } /** * @brief MainWindow::slotNetworkRandomSmallWorldDialog */ void MainWindow::slotNetworkRandomSmallWorldDialog() { qDebug() << "MW::slotNetworkRandomSmallWorldDialog()"; statusMessage( tr("Generate a random Small-World network. ")); m_randSmallWorldDialog = new DialogRandSmallWorld(this); connect( m_randSmallWorldDialog, &DialogRandSmallWorld::userChoices, this, &MainWindow::slotNetworkRandomSmallWorld); m_randSmallWorldDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomSmallWorld * @param nodes * @param degree * @param beta * @param mode * @param diag */ void MainWindow::slotNetworkRandomSmallWorld(const int &newNodes, const int °ree, const qreal &beta, const QString &mode, const bool &diag) { Q_UNUSED(diag); qDebug() << "MW::slotNetworkRandomSmallWorld()"; initApp(); activeGraph->randomNetSmallWorldCreate(newNodes, degree, beta, mode); setWindowTitle("Untitled small-world network"); //qreal avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //qreal clucof=activeGraph->clusteringCoefficient(); QMessageBox::information(this, "New Small World network", tr("Small world network created.\n") // +tr("\nNodes: ")+ QString::number(nodeCount)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr("Small World Random Network created. ") ); } /** * @brief MainWindow::slotNetworkRandomRegularDialog */ void MainWindow::slotNetworkRandomRegularDialog() { qDebug() << "MW::slotRandomRegularDialog()"; statusMessage( tr("Generate a d-regular random network. ")); m_randRegularDialog = new DialogRandRegular(this); connect( m_randRegularDialog, &DialogRandRegular::userChoices, this, &MainWindow::slotNetworkRandomRegular); m_randRegularDialog->exec(); } /** * @brief Creates a pseudo-random k-regular network where every node has the same degree * @param newNodes * @param degree * @param mode * @param diag */ void MainWindow::slotNetworkRandomRegular(const int &newNodes, const int °ree, const QString &mode, const bool &diag){ initApp(); activeGraph->randomNetRegularCreate (newNodes,degree, mode, diag); setWindowTitle("Untitled d-regular network"); //qreal avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //qreal clucof=activeGraph->clusteringCoefficient(); QMessageBox::information(this, "New d-Regular network", tr("d-Regular network created.\n") // +tr("\nNodes: ")+ QString::number(nodeCount)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr( "d-regular network created. " ) ); } void MainWindow::slotNetworkRandomGaussian(){ } /** * @brief MainWindow::slotNetworkRandomRingLattice * Creates a lattice network, i.e. a connected network where every node has the same degree and is connected with its neighborhood. */ void MainWindow::slotNetworkRandomRingLattice(){ 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. \n" "This 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; } initApp(); activeGraph->randomNetRingLatticeCreate(newNodes, degree, true ); setWindowTitle("Untitled ring-lattice network"); //qreal avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //qreal clucof=activeGraph->clusteringCoefficient(); QMessageBox::information(this, "New Ring Lattice", tr("Ring lattice network created.\n") // +tr("\nNodes: ")+ QString::number(activeNodes())+ // tr("\nEdges: ")+ QString::number( activeEdges() ) // + tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr("Ring lattice random network created: " )); } /** * @brief Called from DialogRandLattice */ void MainWindow::slotNetworkRandomLatticeDialog() { qDebug() << "MW::slotNetworkRandomLatticeDialog()"; statusMessage( tr("Generate a lattice network. ")); m_randLatticeDialog = new DialogRandLattice(this); connect( m_randLatticeDialog, &DialogRandLattice::userChoices, this, &MainWindow::slotNetworkRandomLattice); m_randLatticeDialog->exec(); } /** * @brief Creates a lattice network, i.e. a connected network whose drawing * forms a regular tiling Lattices are also known as meshes or grids. * @param newNodes * @param degree * @param mode * @param diag */ void MainWindow::slotNetworkRandomLattice(const int &newNodes, const int &length, const int &dimension, const int &nei, const QString &mode, const bool &circular){ qDebug() << "MW::slotNetworkRandomLattice()"; initApp(); activeGraph->randomNetLatticeCreate (newNodes, length, dimension, nei, mode, circular); setWindowTitle("Untitled lattice network"); //qreal avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //qreal clucof=activeGraph->clusteringCoefficient(); QMessageBox::information(this, "Lattice network", tr("Lattice network created.\n") // +tr("\nNodes: ")+ QString::number(nodeCount)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr( "Lattice network created. " ) ); } /** * @brief MainWindow::slotNetworkWebCrawlerDialog * Shows a dialog where enters a website url * and the app creates a new network by crawling it */ void MainWindow::slotNetworkWebCrawlerDialog() { qDebug () << "MW: slotNetworkWebCrawlerDialog() - canvas Width & Height already sent"; m_WebCrawlerDialog = new DialogWebCrawler(this); connect (m_WebCrawlerDialog, &DialogWebCrawler::userChoices, this, &MainWindow::slotNetworkWebCrawler); m_WebCrawlerDialog->exec() ; } /** * @brief Called from m_WebCrawlerDialog. Clears the loaded network then * passes parameters to Graph::webCrawl function * @param seed * @param maxNodes * @param maxRecursion * @param extLinks * @param intLinks */ void MainWindow::slotNetworkWebCrawler (const QString &urlSeed, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &intLinks, const bool &childLinks, const bool &parentLinks, const bool &selfLinks, const bool &extLinksIncluded, const bool &extLinksCrawl, const bool &socialLinks, const bool &delayedRequests ) { slotNetworkClose(); qDebug () << "MW::slotNetworkWebCrawler() - urlPatternsIncluded" << urlPatternsIncluded; qDebug () << "MW::slotNetworkWebCrawler() - linkClasses" << linkClasses; activeGraph->webCrawl( urlSeed, urlPatternsIncluded, urlPatternsExcluded, linkClasses, maxNodes, maxLinksPerPage, intLinks, childLinks, parentLinks, selfLinks, extLinksIncluded, extLinksCrawl, socialLinks, delayedRequests) ; } /** * @brief Called by Graph when the network has changed a lot. * Makes the networkSave icon active and refreshes any LCD values. * Also called from activeGraph and graphicsWidget. */ void MainWindow::slotNetworkChanged( const bool &directed, const int &vertices, const int &edges, const qreal &density){ qDebug()<<"MW::slotNetworkChanged()" << "directed" << directed << "vertices" << vertices << "edges" << edges << "density"<< density; // networkSaveAct->setIcon(QIcon(":/images/file_download_48px.svg")); networkSaveAct->setEnabled(true); rightPanelNodesLCD->setText (QString::number(vertices)); if ( !directed ) { rightPanelEdgesLCD->setStatusTip(tr("Shows the total number of undirected edges in the network.")); rightPanelEdgesLCD->setToolTip(tr("The total number of undirected edges in the network.")); rightPanelNetworkTypeLCD->setStatusTip(tr("Undirected data mode. Toggle the menu option Edit->Edges->Undirected Edges to change it")); rightPanelNetworkTypeLCD->setToolTip(tr("The loaded network, if any, is undirected and \n" "any edge you add between nodes will be undirected.\n" "If you want to work with directed edges and/or \n" "transform the loaded network (if any) to directed \n" "disable the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLCD->setWhatsThis(tr("The loaded network, if any, is undirected and \n" "any edge you add between nodes will be undirected.\n" "If you want to work with directed edges and/or \n" "transform the loaded network (if any) to directed \n" "disable the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); if (toolBoxEditEdgeModeSelect->currentIndex()==0) { toolBoxEditEdgeModeSelect->setCurrentIndex(1); } rightPanelNetworkTypeLCD-> setText ("Undirected"); rightPanelEdgesLabel->setText(tr("Edges:")); rightPanelEdgesLabel->setStatusTip( tr("Shows the total number of undirected edges in the network.") ); rightPanelEdgesLabel->setToolTip(tr("The total number of undirected edges in the network.")); rightPanelSelectedEdgesLabel->setText( tr("Edges:")); editEdgeUndirectedAllAct->setChecked(true); } else { rightPanelEdgesLCD->setStatusTip(tr("Shows the total number of directed edges in the network.")); rightPanelEdgesLCD->setToolTip(tr("The total number of directed edges in the network.")); rightPanelNetworkTypeLCD->setStatusTip(tr("Directed data mode. Toggle the menu option Edit->Edges->Undirected Edges to change it")); rightPanelNetworkTypeLCD->setToolTip(tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "enable the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLCD->setWhatsThis(tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "enable the option Edit->Edges->Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLCD-> setText ("Directed"); if (toolBoxEditEdgeModeSelect->currentIndex()==1) { toolBoxEditEdgeModeSelect->setCurrentIndex(0); } rightPanelEdgesLabel->setText(tr("Arcs:")); rightPanelEdgesLabel->setStatusTip(tr("Shows the total number of directed edges (arcs) in the network.")); rightPanelEdgesLabel->setToolTip(tr("The total number of directed edges (arcs) in the network.")); rightPanelSelectedEdgesLabel->setText( tr("Arcs:") ); editEdgeUndirectedAllAct->setChecked(false); } rightPanelEdgesLCD->setText(QString::number(edges)); rightPanelDensityLCD->setText(QString::number(density )); qDebug()<<"MW::slotNetworkChanged() - finished updating mainwindow !"; } /** * @brief MainWindow::slotEditOpenContextMenu * Popups a context menu with some options when the user right-clicks on the scene * @param mPos */ void MainWindow::slotEditOpenContextMenu( const QPointF &mPos) { Q_UNUSED(mPos); QMenu *contextMenu = new QMenu(" Menu",this); Q_CHECK_PTR( contextMenu ); //displays "out of memory" if needed int nodesSelected = activeGraph->graphSelectedVerticesCount(); contextMenu->addAction( "## Selected nodes: " + QString::number( nodesSelected ) + " ## "); contextMenu->addSeparator(); if (nodesSelected > 0) { contextMenu->addAction(editNodePropertiesAct ); contextMenu->addSeparator(); contextMenu->addAction(editNodeRemoveAct ); if (nodesSelected > 1 ){ editNodeRemoveAct->setText(tr("Remove ") + QString::number(nodesSelected) + tr(" nodes")); contextMenu->addSeparator(); contextMenu->addAction(editNodeSelectedToCliqueAct); contextMenu->addAction(editNodeSelectedToStarAct); contextMenu->addAction(editNodeSelectedToCycleAct); contextMenu->addAction(editNodeSelectedToLineAct); } else { editNodeRemoveAct->setText(tr("Remove ") + QString::number(nodesSelected) + tr(" node")); } contextMenu->addSeparator(); } contextMenu->addAction( editNodeAddAct ); contextMenu->addSeparator(); contextMenu->addAction( editEdgeAddAct ); contextMenu->addSeparator(); QMenu *options=new QMenu("Options", this); contextMenu->addMenu(options ); options->addAction (openSettingsAct ); options->addSeparator(); options->addAction (editNodeSizeAllAct ); options->addAction (editNodeShapeAll ); options->addAction (editNodeColorAll ); options->addAction (optionsNodeNumbersVisibilityAct); options->addAction (optionsNodeLabelsVisibilityAct); options->addSeparator(); options->addAction (editEdgeColorAllAct ); options->addSeparator(); options->addAction (changeBackColorAct ); options->addAction (backgroundImageAct ); //QCursor::pos() is good only for menus not related with node coordinates contextMenu->exec(QCursor::pos() ); delete contextMenu; } /** * @brief MainWindow::slotEditClickOnEmptySpace * Called from GW when the user clicks on empty space. */ void MainWindow::slotEditClickOnEmptySpace(const QPointF &p) { qDebug() << "MW::slotEditClickOnEmptySpace()"; rightPanelClickedNodeLCD->setText ("0"); rightPanelClickedNodeInDegreeLCD->setText ("0"); rightPanelClickedNodeOutDegreeLCD->setText("0"); rightPanelClickedNodeClucofLCD->setText("0"); activeGraph->vertexClickedSet(0); activeGraph->edgeClickedSet(0,0); statusMessage( tr("Position (%1,%2): Double-click to create a new node." ) .arg(p.x()) .arg(p.y()) ); } /** * @brief MainWindow::slotEditNodeSelectAll */ void MainWindow::slotEditNodeSelectAll(){ qDebug() << "MW::slotEditNodeSelectAll()"; graphicsWidget->selectAll(); statusMessage( tr("Selected nodes: %1") .arg( activeGraph->graphSelectedVerticesCount() ) ); } /** * @brief MainWindow::slotEditNodeSelectNone */ void MainWindow::slotEditNodeSelectNone(){ qDebug() << "MainWindow::slotEditNodeSelectNone()"; graphicsWidget->selectNone(); statusMessage( QString(tr("Selection cleared") ) ); } /** * @brief MainWindow::slotEditNodePosition * Called from GraphicsWidget when a node moves to update vertex coordinates * in Graph * @param nodeNumber * @param x * @param y */ void MainWindow::slotEditNodePosition(const int &nodeNumber, const int &x, const int &y){ qDebug("MW::slotEditNodePosition() for %i with x %i and y %i", nodeNumber, x, y); activeGraph->vertexPosSet(nodeNumber, x, y); if (!activeGraph->graphSaved()) { networkSaveAct->setIcon(QIcon(":/images/file_download_48px.svg")); networkSaveAct->setEnabled(true); } } /** * @brief MainWindow::slotEditNodeAdd * Calls Graph::vertexCreate method to add a new RANDOM node into the activeGraph-> * Called when "Add Node" button is clicked on the Main Window. */ void MainWindow::slotEditNodeAdd() { qDebug() << "MW::slotEditNodeAdd() - calling Graph::vertexCreateAtPosRandom "; activeGraph->vertexCreateAtPosRandom(true); statusMessage( tr("New random positioned node (numbered %1) added.") .arg(activeGraph->vertexNumberMax()) ); } /** * @brief Opens the node find dialog */ void MainWindow::slotEditNodeFindDialog(){ qDebug() << "MW::slotEditNodeFindDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } //@TODO - prominenceIndexList should be either // the list of all computes indices // or the last computed indice // or empty if the user has not computed any index yet. m_nodeFindDialog = new DialogNodeFind(this, prominenceIndexList) ; connect( m_nodeFindDialog, &DialogNodeFind::userChoices, this, &MainWindow::slotEditNodeFind); m_nodeFindDialog->exec(); statusMessage( tr("Node properties dialog opened. Ready. ") ); return; } /** * @brief MainWindow::slotEditNodeFind * @param list */ void MainWindow::slotEditNodeFind(const QStringList &list, const QString &searchType, const QString &indexStr) { qDebug() << "MW::slotEditNodeFind() - nodes:" << list << "search type:"<< searchType << "indexStr"<vertexFindByNumber(list); } else if (searchType == "labels"){ activeGraph->vertexFindByLabel(list); } else if (searchType == "score"){ indexType = activeGraph->getProminenceIndexByName(indexStr); activeGraph->vertexFindByIndexScore(indexType, list); } return; } /** * @brief MainWindow::slotEditNodeRemove * Deletes a node and the attached objects (edges, etc). * If user has clicked on a node (signaled from GW or set by another function) * it deletes it * Else it asks for a nodeNumber to remove. The nodeNumber is doomedJim. * Called from nodeContextMenu */ void MainWindow::slotEditNodeRemove() { qDebug() << "MW::slotEditNodeRemove()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); 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 deleted node." "You cannot remove nodes in multirelational networks."), "OK",0); statusMessage( tr("Nothing to remove.") ); return; } // if there are already multiple nodes selected, erase them int nodesSelected = activeGraph->graphSelectedVerticesCount(); if ( nodesSelected > 0) { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); int removeCounter = 0; qDebug() << "MW::slotEditNodeRemove() multiple selected to remove"; foreach (int nodeNumber, activeGraph->graphSelectedVertices() ) { activeGraph->vertexRemove(nodeNumber); ++removeCounter ; } editNodeRemoveAct->setText(tr("Remove Node")); statusMessage( tr("Removed ") + nodesSelected + tr(" nodes. Ready. ") ); QApplication::restoreOverrideCursor(); } else { int nodeNumber=-1, min=-1, max=-1; bool ok=false; min = activeGraph->vertexNumberMin(); max = activeGraph->vertexNumberMax(); 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 { nodeNumber = QInputDialog::getInt( this, tr("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::slotEditNodeRemove() - removing vertex with number %i from Graph", nodeNumber); activeGraph->vertexRemove(nodeNumber); qDebug("MW::slotEditNodeRemove() - Completed. Node %i removed completely.",nodeNumber); statusMessage( tr("Node removed completely. Ready. ") ); } } /** * @brief Opens Node Properties dialog for the selected nodes. * If no nodes are selected, prompts the user for a node number */ void MainWindow::slotEditNodePropertiesDialog() { qDebug() << "MW::slotEditNodePropertiesDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int min=-1, max=-1, size = appSettings["initNodeSize"].toInt(0, 10); int nodeNumber = 0; int selectedNodesCount = activeGraph->graphSelectedVerticesCount(); QColor color = QColor(appSettings["initNodeColor"]); QString shape= appSettings["initNodeShape"]; QString iconPath = QString(); QString label=""; bool ok=false; if ( selectedNodesCount == 0) { min = activeGraph->vertexNumberMin(); max = activeGraph->vertexNumberMax(); qDebug() << "MW::slotEditNodePropertiesDialog() - no node selected" << "min node number " << min << "max node number " << max << "opening inputdialog"; if (min==-1 || max==-1 ) { qDebug("ERROR in finding min max nodeNumbers. Abort"); return; } nodeNumber = 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; } label = activeGraph->vertexLabel( nodeNumber ); color = activeGraph->vertexColor( nodeNumber ); shape = activeGraph->vertexShape( nodeNumber); size = activeGraph->vertexSize ( nodeNumber); iconPath = activeGraph->vertexShapeIconPath ( nodeNumber); } else { qDebug() << "MW::slotEditNodePropertiesDialog() - " "selectedNodesCount" << selectedNodesCount; foreach (nodeNumber, activeGraph->graphSelectedVertices() ) { qDebug() << "MW::slotEditNodePropertiesDialog() " "reading properties of selected node" << nodeNumber; if ( selectedNodesCount > 1 ) { color = activeGraph->vertexColor( nodeNumber ); shape = activeGraph->vertexShape( nodeNumber); iconPath = activeGraph->vertexShapeIconPath ( nodeNumber); size = activeGraph->vertexSize ( nodeNumber); } else { label = activeGraph->vertexLabel( nodeNumber ); color = activeGraph->vertexColor( nodeNumber ); shape = activeGraph->vertexShape( nodeNumber); iconPath = activeGraph->vertexShapeIconPath ( nodeNumber); size = activeGraph->vertexSize ( nodeNumber); } } } //@todo add some grouping function here? qDebug() << "MW::slotEditNodePropertiesDialog() - opening DialogNodeEdit." << "label"<