socnetv-2.4/0000775000175000017500000000000013245521146013277 5ustar dimitrisdimitrissocnetv-2.4/.travis.yml0000664000175000017500000000235013132076156015411 0ustar dimitrisdimitrislanguage: cpp compiler: gcc sudo: require dist: trusty before_install: - sudo add-apt-repository ppa:beineri/opt-qt58-trusty -y - sudo apt-get update -qq install: - sudo apt-get -y install qt58base - source /opt/qt58/bin/qt58-env.sh script: - qmake PREFIX=/usr - make -j4 - sudo make install # Does not write to /usr - find . - mkdir -p appdir - sudo mv ./usr appdir/ ; sudo chown -R $USER . # Workaround for non-functional make install - cd appdir ; find . - mkdir -p ./usr/share/applications/ # Workaround for missing desktop file in make install - cp ../socnetv.desktop ./usr/share/applications/ - cp ../socnetv.desktop . - cp ./usr/share/pixmaps/socnetv.png . - find . - cd .. - wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" - chmod a+x linuxdeployqt*.AppImage - unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage - curl --upload-file ./SocNetV*.AppImage https://transfer.sh/SocNetV-git.$(git rev-parse --short HEAD)-x86_64.AppImage socnetv-2.4/socnetv.spec0000664000175000017500000002141313245520745015641 0ustar dimitrisdimitris# spec file for package socnetv # # Copyright (c) 2016 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.4 %define release 1 %define prefix /usr/local %define lastrev %(LANG=en_US.UTF-8 && date +"%a %b %e %Y") %define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) %define is_fedora %(test -e /etc/fedora-release && echo 1 || echo 0) %define qmake qmake %define lrelease lrelease #BEGIN BUILDSERVICE COMMANDS %if 0%{?fedora_version} %define is_suse 0 %define is_fedora 1 %endif %if 0%{?suse_version} %define is_suse 1 %define is_fedora 0 %endif #END BUILDSERVICE COMMANDS %if %{is_fedora} %define distr Fedora %define breqr qt5-qtbase,qt5-qtbase-devel, qt5-qttools, fedora-release, desktop-file-utils %define qmake /usr/bin/qmake-qt5 %define lrelease /usr/bin/lrelease %endif %if %{is_suse} %define distr SUSE # %(head -1 /etc/SuSE-release) %define breqr libqt5-qtbase, libqt5-qtbase-devel, libqt5-qttools, 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: http://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) 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 # %prep ## %setup %setup -q -n app-master ## because master.zip unpacks to app-master/ chmod -R a-x+X COPYING changelog.gz INSTALL NEWS README.md TODO man nets src chmod 644 nets/* find . -type f -name '*~' -delete find . -type f -name '*.bak' -delete rm -f config.log config.status Makefile socnetv.spec socnetv.mak sed -i -e 's/INSTALLPATH = \//INSTALLPATH = ./g' socnetv.pro # #MAKE SECTION # %build %{qmake} %__make # #INSTALL SECTION # %install %if %{is_fedora} desktop-file-validate %{name}.desktop #desktop-file-install --add-category="Math" --delete-original --dir=%{buildroot}%{_datadir}/applications %{buildroot}/%{_datadir}/applnk/Edutainment/%{name}.desktop %endif %make_install mkdir -p %{buildroot}%{_bindir} mkdir -p %{buildroot}%{_datadir}/pixmaps/ mkdir -p %{buildroot}%{_datadir}/applications/ mkdir -p %{buildroot}%{_mandir}/man1/ cp -r socnetv %{buildroot}%{_bindir}/%{name} cp -r src/images/socnetv.png %{buildroot}%{_datadir}/pixmaps/%{name}.png cp -r socnetv.desktop %{buildroot}%{_datadir}/applications/ cp -r man/socnetv.1.gz %{buildroot}%{_mandir}/man1 rm -rf %{buildroot}/%{_datadir}/doc/%{name} %clean [ -d %{buildroot} -a "%{buildroot}" != "" ] && %__rm -rf %{buildroot} # #FILES SECTION # %files %defattr(-,root,root) %{_bindir}/%{name} %{_datadir}/applications/%{name}.desktop %{_datadir}/pixmaps/%{name}.png %{_mandir}/man1/* %doc changelog.gz NEWS README.md TODO COPYING AUTHORS INSTALL # #CHANGELOG SECTION # %changelog * 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 socnetv-2.4/nets/0000775000175000017500000000000013245521146014250 5ustar dimitrisdimitrissocnetv-2.4/nets/4-actors-sm.csv0000644000175000017500000000003713014570726017036 0ustar dimitrisdimitris0,1,1,2 1,0,2,1 0,0,0,1 1,0,0,0socnetv-2.4/nets/sampson.dl0000644000175000017500000001631313014570726016255 0ustar dimitrisdimitrisDL N=18 NM=10 FORMAT = FULLMATRIX DIAGONAL PRESENT ROW LABELS: ROMUL_10 BONAVEN_5 AMBROSE_9 BERTH_6 PETER_4 LOUIS_11 VICTOR_8 WINF_12 JOHN_1 GREG_2 HUGH_14 BONI_15 MARK_7 ALBERT_16 AMAND_13 BASIL_3 ELIAS_17 SIMP_18 COLUMN LABELS: ROMUL_10 BONAVEN_5 AMBROSE_9 BERTH_6 PETER_4 LOUIS_11 VICTOR_8 WINF_12 JOHN_1 GREG_2 HUGH_14 BONI_15 MARK_7 ALBERT_16 AMAND_13 BASIL_3 ELIAS_17 SIMP_18 LEVEL LABELS: SAMPLK1 SAMPLK2 SAMPLK3 SAMPDLK SAMPES SAMPDES SAMPIN SAMPNIN SAMPPR SAMNPR DATA: 0 0 2 0 3 0 0 0 0 0 0 0 0 1 0 0 0 0 3 0 0 0 0 0 2 0 0 0 0 0 0 1 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 3 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 1 0 0 0 0 0 1 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 0 0 3 0 0 3 2 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 2 0 0 0 0 0 0 0 0 3 0 0 1 0 0 0 0 0 2 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 1 2 0 0 2 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 1 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 0 3 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 2 0 0 1 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 3 2 0 0 0 0 1 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 3 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 3 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 1 0 0 3 1 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 1 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 2 3 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 3 0 0 0 0 1 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 0 0 0 0 0 0 0 0 0 2 0 0 0 3 0 0 0 3 0 0 0 0 0 0 0 0 1 0 2 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 1 2 0 0 0 0 3 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 3 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 0 0 0 2 0 0 0 0 0 3 0 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 3 1 0 0 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 1 0 0 0 0 3 2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 0 3 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 1 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 3 2 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 2 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 2 0 0 0 1 0 0 0 0 0 0 0 3 1 0 2 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 1 0 0 0 0 0 0 0 0 1 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 2 2 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 2 2 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 1 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 3 0 1 1 0 0 0 0 3 1 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 3 2 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 2 2 1 0 0 0 3 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 2 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 2 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 2 1 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 1 2 0 0 0 0 0 0 0 1 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 3 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 0 1 1 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 2 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 1 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 3 0 0 0 0 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 2 1 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 3 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 2 2 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 2 3 0 0 0 0 3 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.4/nets/5-actors-plain-list.lst0000644000175000017500000000002713014570726020502 0ustar dimitrisdimitris1 2 3 4 2 3 4 5 3 4 5 1socnetv-2.4/nets/lion_share.dot0000644000175000017500000001332713014570726017111 0ustar dimitrisdimitris##"A few people in the field of genetics are using dot to draw "marriage node diagram" pedigree drawings. Here is one I have done of a test pedigree from the FTREE pedigree drawing package (Lion Share was a racehorse)." Contributed by David Duffy. ##Command to get the layout: "dot -Tpng thisfile > thisfile.png" digraph Ped_Lion_Share { # page = "8.2677165,11.692913" ; ratio = "auto" ; mincross = 2.0 ; label = "Pedigree Lion_Share" ; "001" [shape=box , regular=1,style=filled,fillcolor=white ] ; "002" [shape=box , regular=1,style=filled,fillcolor=white ] ; "003" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "004" [shape=box , regular=1,style=filled,fillcolor=white ] ; "005" [shape=box , regular=1,style=filled,fillcolor=white ] ; "006" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "007" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "009" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "014" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "015" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "016" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "ZZ01" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "ZZ02" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "017" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "012" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "008" [shape=box , regular=1,style=filled,fillcolor=white ] ; "011" [shape=box , regular=1,style=filled,fillcolor=white ] ; "013" [shape=box , regular=1,style=filled,fillcolor=white ] ; "010" [shape=box , regular=1,style=filled,fillcolor=white ] ; "023" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "020" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "021" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "018" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "025" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "019" [shape=box , regular=1,style=filled,fillcolor=white ] ; "022" [shape=box , regular=1,style=filled,fillcolor=white ] ; "024" [shape=box , regular=1,style=filled,fillcolor=white ] ; "027" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "026" [shape=box , regular=1,style=filled,fillcolor=white ] ; "028" [shape=box , regular=1,style=filled,fillcolor=grey ] ; "marr0001" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "001" -> "marr0001" [dir=none,weight=1] ; "007" -> "marr0001" [dir=none,weight=1] ; "marr0001" -> "017" [dir=none, weight=2] ; "marr0002" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "001" -> "marr0002" [dir=none,weight=1] ; "ZZ02" -> "marr0002" [dir=none,weight=1] ; "marr0002" -> "012" [dir=none, weight=2] ; "marr0003" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0003" [dir=none,weight=1] ; "003" -> "marr0003" [dir=none,weight=1] ; "marr0003" -> "008" [dir=none, weight=2] ; "marr0004" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0004" [dir=none,weight=1] ; "006" -> "marr0004" [dir=none,weight=1] ; "marr0004" -> "011" [dir=none, weight=2] ; "marr0005" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0005" [dir=none,weight=1] ; "ZZ01" -> "marr0005" [dir=none,weight=1] ; "marr0005" -> "013" [dir=none, weight=2] ; "marr0006" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "004" -> "marr0006" [dir=none,weight=1] ; "009" -> "marr0006" [dir=none,weight=1] ; "marr0006" -> "010" [dir=none, weight=2] ; "marr0007" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0007" [dir=none,weight=1] ; "015" -> "marr0007" [dir=none,weight=1] ; "marr0007" -> "023" [dir=none, weight=2] ; "marr0008" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0008" [dir=none,weight=1] ; "016" -> "marr0008" [dir=none,weight=1] ; "marr0008" -> "020" [dir=none, weight=2] ; "marr0009" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0009" [dir=none,weight=1] ; "012" -> "marr0009" [dir=none,weight=1] ; "marr0009" -> "021" [dir=none, weight=2] ; "marr0010" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "008" -> "marr0010" [dir=none,weight=1] ; "017" -> "marr0010" [dir=none,weight=1] ; "marr0010" -> "018" [dir=none, weight=2] ; "marr0011" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "011" -> "marr0011" [dir=none,weight=1] ; "023" -> "marr0011" [dir=none,weight=1] ; "marr0011" -> "025" [dir=none, weight=2] ; "marr0012" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "013" -> "marr0012" [dir=none,weight=1] ; "014" -> "marr0012" [dir=none,weight=1] ; "marr0012" -> "019" [dir=none, weight=2] ; "marr0013" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "010" -> "marr0013" [dir=none,weight=1] ; "021" -> "marr0013" [dir=none,weight=1] ; "marr0013" -> "022" [dir=none, weight=2] ; "marr0014" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "019" -> "marr0014" [dir=none,weight=1] ; "020" -> "marr0014" [dir=none,weight=1] ; "marr0014" -> "024" [dir=none, weight=2] ; "marr0015" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "022" -> "marr0015" [dir=none,weight=1] ; "025" -> "marr0015" [dir=none,weight=1] ; "marr0015" -> "027" [dir=none, weight=2] ; "marr0016" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "024" -> "marr0016" [dir=none,weight=1] ; "018" -> "marr0016" [dir=none,weight=1] ; "marr0016" -> "026" [dir=none, weight=2] ; "marr0017" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "026" -> "marr0017" [dir=none,weight=1] ; "027" -> "marr0017" [dir=none,weight=1] ; "marr0017" -> "028" [dir=none, weight=2] ; } socnetv-2.4/nets/mexico-power-network.lst0000644000175000017500000000050013014570726021074 0ustar dimitrisdimitris18 8 10 23 21 19 11 21 29 5 9 10 23 8 9 18 11 4 7 6 8 20 5 21 5 4 29 20 7 6 8 9 26 21 6 5 7 4 20 21 8 7 4 6 5 8 20 21 9 5 8 23 29 20 21 11 10 8 18 23 4 5 6 7 21 24 26 25 9 10 37 20 10 18 29 8 11 9 20 25 26 11 19 23 9 10 25 21 36 20 4 5 6 7 8 9 10 24 8 26 26 5 8 24 10 21 19 4 5 6 7 8 9 11 18 36 37 11 37 8 36 25 10 11 8 socnetv-2.4/nets/network.dl0000644000175000017500000000065213014570726016265 0ustar dimitrisdimitrisDL N=4 FORMAT = FULLMATRIX DIAGONAL PRESENT LABELS: On the normalization and visualization of author co-citation data:Salton's cosine versus the Jaccard index Caveats for the use of citation indicators in research and journalevaluations Should co-occurrence data be normalized? A rejoinder Home on the range - What and where is the middle in science andtechnology studies? DATA: 0 0 0.158114 0 0.201234 0 1 0 1 0 0 0 0.1 1 1 0socnetv-2.4/nets/4actors-line-properties.dot0000644000175000017500000000051113014570726021453 0ustar dimitrisdimitris graph graphname { // The label attribute can be used to change the label of a node a [label="Foo"]; // Here, the node shape is changed. b [shape=box]; // These edges both have different line properties a -- b -- c [color=blue]; b -- d [style=dotted]; c -- d [color=#ddd, style=dotted]; }socnetv-2.4/nets/jin2-sm.txt0000644000175000017500000000040113014570726016263 0ustar dimitrisdimitris0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.4/nets/network.gw0000644000175000017500000000253713014570726016307 0ustar dimitrisdimitrisLEDA.GRAPH unknown unknown 5 |{1 0 1}| |{2 0 1}| |{3 0 1}| |{4 0 1}| |{5 0 1}| 4 1 2 0 |{edge 3 1 3 1}| 2 3 0 |{edge 3 1 3 1}| 3 4 0 |{edge 3 1 3 1}| 4 5 0 |{edge 3 1 3 1}| # version string GraphWin 1.400000 # scaling wxmin wymin wxmax wymax 0.9821777 -274.8779 -281.2244 274.8779 283.8025 # node label font and size 0 15 # edge label font and size 0 13 # node index format %d # edge index format %d # multi-edge distance 4.793233 # # node infos # x y shape bclr(r,g,b) bwidth r1 r2 clr(r,g,b) ltype lclr(r,g,b) lpos lstr -146.6016 65.93628 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 1 59.04785 107.677 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 2 54.97559 -44.01489 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 3 -145.5835 -44.01489 6 0 0 0 1.018519 20.36133 10.18066 160 0 0 1 255 255 228 4 4 -73.30078 -148.8757 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 5 # # edge infos # width clr(r,g,b) shape style dir ltype lclr(r,g,b) lpos sanch tanch poly lstr 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (-136.6243,67.96136) (49.07063,105.6519) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (58.77464,97.5) (55.24879,-33.8379) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (44.79492,-44.01489) (-125.2222,-44.01489) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (-140.3645,-51.58607) (-79.07878,-140.4936) 1 socnetv-2.4/nets/Freemans_EIES-2_n48.lst0000644000175000017500000001432513014570726020176 0ustar dimitrisdimitris1 2 4 1 3 2 1 6 2 1 8 2 1 10 2 1 11 2 1 13 3 1 14 3 1 18 2 1 19 3 1 20 2 1 21 3 1 22 2 1 23 2 1 24 2 1 25 2 1 26 3 1 27 2 1 31 2 1 32 2 1 33 2 1 35 2 1 36 2 1 37 2 1 38 2 1 39 3 1 40 2 1 41 2 1 42 3 1 43 2 1 44 4 1 45 3 1 46 3 2 1 4 2 3 2 2 6 2 2 8 1 2 10 2 2 11 2 2 13 3 2 14 4 2 18 2 2 19 3 2 21 2 2 22 2 2 23 2 2 24 2 2 25 2 2 26 2 2 27 2 2 32 2 2 33 2 2 35 2 2 36 2 2 37 2 2 38 2 2 39 2 2 40 2 2 41 2 2 42 2 2 43 3 2 44 4 2 45 4 2 46 2 3 1 3 3 2 1 3 6 4 3 8 1 3 13 2 3 18 2 3 19 4 3 20 4 3 22 4 3 23 1 3 24 2 3 25 2 3 26 2 3 27 1 3 31 1 3 32 2 3 33 2 3 35 2 3 36 4 3 37 2 3 39 2 3 41 1 3 42 1 3 43 1 6 1 2 6 2 2 6 3 2 6 8 2 6 10 2 6 13 2 6 14 2 6 18 3 6 19 2 6 20 2 6 21 1 6 22 2 6 23 2 6 24 2 6 26 2 6 27 4 6 31 1 6 32 2 6 33 2 6 35 2 6 36 2 6 37 2 6 38 2 6 39 2 6 40 2 6 41 2 6 42 2 6 43 2 6 44 2 6 46 2 8 1 3 8 6 2 8 13 2 8 14 3 8 18 2 8 19 2 8 20 1 8 22 2 8 23 1 8 24 2 8 25 2 8 27 1 8 32 2 8 33 2 8 35 2 8 37 2 8 38 1 8 40 1 8 41 2 8 42 2 8 44 2 8 45 2 10 1 4 10 2 2 10 13 3 10 18 2 10 19 2 10 22 2 10 23 2 10 24 2 10 27 2 10 31 2 10 33 2 10 37 3 10 39 2 10 40 2 10 41 2 10 42 3 10 44 4 10 45 2 10 46 3 11 1 3 11 2 2 11 3 1 11 13 2 11 14 2 11 19 1 11 21 3 11 41 2 13 1 3 13 2 2 13 3 2 13 6 2 13 8 2 13 10 2 13 11 1 13 14 1 13 18 2 13 19 4 13 20 1 13 21 2 13 22 2 13 23 2 13 24 2 13 25 2 13 26 2 13 27 2 13 32 2 13 33 2 13 35 2 13 36 2 13 37 2 13 38 2 13 40 2 13 41 1 13 42 2 13 43 2 13 44 2 13 45 4 13 46 2 14 1 3 14 2 4 14 8 2 14 13 2 14 19 2 14 21 2 14 22 1 14 24 1 14 25 2 14 33 2 14 35 2 14 40 3 14 42 1 14 44 2 14 45 4 14 46 2 18 1 3 18 3 2 18 6 3 18 8 2 18 11 1 18 13 2 18 14 1 18 19 2 18 20 3 18 21 2 18 22 1 18 23 2 18 24 2 18 25 2 18 26 2 18 27 2 18 31 2 18 32 4 18 33 2 18 35 2 18 36 4 18 37 2 18 38 2 18 40 2 18 41 2 18 42 3 18 43 2 18 44 2 18 45 1 19 1 3 19 2 2 19 3 2 19 6 2 19 8 2 19 10 2 19 13 4 19 14 2 19 18 2 19 21 2 19 22 2 19 23 2 19 24 2 19 25 2 19 26 2 19 27 2 19 31 2 19 32 2 19 33 2 19 35 2 19 36 1 19 37 2 19 38 2 19 40 2 19 41 2 19 42 2 19 44 3 19 45 3 19 46 2 20 1 2 20 3 1 20 6 2 20 11 1 20 13 1 20 18 3 20 22 2 20 23 1 20 24 1 20 27 2 20 31 2 20 32 3 20 33 2 20 35 1 20 36 1 20 37 1 20 38 2 20 41 1 20 43 1 20 44 2 20 45 2 20 46 2 21 1 3 21 2 3 21 3 1 21 6 2 21 8 1 21 11 3 21 13 3 21 14 2 21 18 1 21 19 2 21 22 1 21 23 1 21 24 2 21 26 2 21 27 2 21 31 1 21 32 1 21 33 2 21 35 2 21 36 1 21 39 2 21 40 4 21 41 2 21 42 2 21 43 2 21 44 3 21 45 3 22 1 3 22 2 2 22 3 4 22 6 3 22 8 3 22 13 3 22 18 2 22 19 2 22 20 3 22 21 2 22 23 3 22 24 4 22 25 4 22 26 2 22 27 3 22 31 3 22 32 3 22 33 3 22 35 4 22 36 3 22 37 3 22 38 3 22 39 2 22 40 2 22 41 3 22 42 4 22 43 3 22 44 3 22 45 2 22 46 2 23 1 3 23 2 2 23 3 2 23 6 3 23 8 1 23 13 2 23 14 2 23 18 2 23 19 2 23 20 2 23 22 3 23 24 2 23 25 2 23 27 2 23 31 4 23 32 1 23 33 2 23 35 2 23 36 2 23 37 2 23 38 2 23 40 1 23 42 3 23 44 3 23 45 1 24 1 2 24 2 2 24 3 2 24 6 2 24 8 3 24 10 2 24 13 3 24 14 1 24 18 2 24 19 2 24 22 3 24 23 2 24 25 2 24 27 2 24 31 2 24 32 2 24 33 4 24 35 3 24 37 2 24 38 2 24 39 2 24 40 1 24 41 1 24 42 2 24 44 2 24 45 2 24 46 2 25 1 3 25 2 2 25 3 3 25 6 1 25 8 2 25 13 3 25 14 2 25 18 1 25 19 3 25 20 1 25 21 1 25 22 3 25 23 2 25 24 3 25 26 1 25 27 1 25 32 3 25 33 3 25 35 3 25 37 2 25 39 1 25 40 2 25 41 1 25 42 2 25 43 2 25 44 2 25 45 2 25 46 1 26 1 4 26 2 2 26 3 2 26 11 1 26 13 2 26 19 2 26 21 1 26 22 2 26 39 2 26 40 2 26 42 2 26 43 2 26 44 4 26 45 1 26 46 2 27 1 2 27 3 2 27 6 4 27 8 1 27 13 2 27 18 2 27 20 2 27 22 2 27 23 2 27 24 1 27 32 2 27 33 2 27 35 3 27 36 2 27 37 2 27 38 2 27 39 2 27 41 2 27 42 2 27 43 1 27 44 2 27 46 2 31 1 1 31 3 2 31 6 1 31 8 1 31 18 2 31 19 2 31 20 2 31 22 2 31 23 2 31 24 2 31 32 1 31 35 3 31 36 1 31 37 3 31 38 2 31 42 1 32 1 2 32 2 2 32 3 2 32 6 2 32 8 2 32 13 2 32 18 3 32 19 2 32 20 2 32 22 3 32 23 1 32 24 2 32 25 2 32 27 2 32 31 1 32 33 3 32 35 4 32 36 2 32 37 3 32 38 3 32 41 2 32 42 3 32 43 1 33 1 3 33 2 3 33 3 2 33 6 2 33 8 2 33 13 3 33 14 1 33 18 2 33 19 3 33 20 2 33 22 2 33 23 3 33 24 4 33 25 3 33 27 2 33 31 2 33 32 2 33 35 3 33 36 2 33 37 2 33 38 3 33 40 1 33 41 2 33 42 2 33 43 1 33 45 1 35 1 2 35 2 2 35 3 2 35 6 3 35 13 2 35 14 3 35 18 2 35 19 2 35 22 3 35 23 2 35 24 3 35 25 2 35 27 3 35 32 3 35 33 3 35 37 4 35 38 2 35 41 2 35 42 4 35 45 2 36 1 3 36 2 2 36 3 4 36 6 3 36 13 2 36 18 4 36 20 1 36 22 3 36 23 1 36 24 1 36 27 3 36 31 1 36 32 2 36 33 1 36 35 1 36 37 2 36 38 2 36 41 2 36 42 3 36 43 2 36 44 2 36 46 2 37 1 3 37 2 2 37 3 2 37 6 2 37 8 3 37 10 2 37 13 3 37 14 2 37 18 2 37 19 3 37 20 2 37 21 2 37 22 3 37 23 2 37 24 3 37 25 2 37 26 2 37 27 2 37 31 4 37 32 3 37 33 3 37 35 4 37 36 2 37 38 3 37 40 2 37 41 3 37 42 3 37 43 2 37 44 2 37 45 2 37 46 2 38 1 2 38 2 2 38 3 2 38 6 3 38 8 1 38 13 3 38 18 3 38 19 2 38 20 2 38 22 3 38 23 2 38 24 3 38 27 2 38 31 3 38 32 3 38 33 3 38 35 3 38 36 3 38 37 3 38 41 1 38 42 2 39 1 4 39 2 1 39 3 2 39 6 1 39 8 1 39 11 1 39 13 1 39 18 1 39 19 1 39 20 1 39 21 2 39 22 2 39 23 1 39 24 1 39 26 3 39 27 2 39 32 1 39 33 1 39 35 2 39 36 1 39 37 2 39 38 1 39 41 2 39 42 2 39 44 3 39 46 1 40 1 3 40 2 2 40 3 2 40 6 2 40 8 2 40 10 2 40 13 3 40 14 3 40 18 2 40 19 2 40 21 4 40 22 1 40 23 2 40 24 2 40 25 2 40 26 2 40 27 2 40 32 1 40 33 2 40 35 2 40 36 1 40 37 2 40 42 2 40 43 2 40 44 2 40 45 2 40 46 2 41 1 3 41 2 2 41 6 3 41 13 2 41 18 1 41 19 1 41 21 2 41 22 2 41 23 2 41 24 2 41 27 3 41 32 2 41 33 2 41 35 3 41 37 2 41 38 1 41 39 2 41 40 1 41 42 2 41 44 2 41 45 2 41 46 2 42 1 3 42 2 2 42 3 2 42 6 3 42 8 2 42 10 2 42 13 3 42 18 3 42 19 2 42 20 3 42 21 2 42 22 4 42 23 3 42 24 2 42 25 2 42 26 2 42 27 2 42 32 3 42 33 2 42 35 4 42 36 2 42 37 4 42 39 2 42 40 2 42 41 2 42 43 2 42 44 3 42 45 2 42 46 4 43 1 3 43 2 3 43 3 1 43 6 2 43 10 2 43 13 3 43 18 2 43 19 2 43 21 2 43 22 2 43 24 2 43 25 2 43 26 2 43 27 2 43 32 2 43 33 2 43 35 2 43 37 2 43 38 2 43 40 3 43 41 2 43 42 3 43 44 3 43 45 3 43 46 2 44 1 4 44 2 4 44 3 2 44 6 2 44 8 2 44 10 3 44 11 2 44 13 2 44 14 2 44 18 2 44 19 3 44 20 2 44 21 3 44 22 2 44 23 3 44 24 2 44 25 2 44 26 3 44 27 2 44 31 2 44 32 2 44 33 2 44 35 2 44 36 2 44 37 2 44 38 2 44 39 2 44 40 2 44 41 2 44 42 3 44 43 2 44 45 4 44 46 3 45 1 4 45 2 4 45 6 2 45 8 2 45 10 2 45 13 4 45 14 4 45 18 2 45 19 3 45 21 2 45 22 1 45 24 3 45 25 2 45 32 1 45 33 2 45 35 3 45 36 1 45 37 1 45 39 2 45 40 2 45 41 1 45 42 3 45 43 2 45 44 4 45 46 3 46 1 3 46 2 2 46 6 1 46 8 1 46 10 2 46 13 3 46 14 2 46 18 2 46 19 2 46 24 2 46 25 2 46 27 1 46 37 2 46 42 4 46 44 3 46 45 3socnetv-2.4/nets/11actors-pajek-matrix.paj0000644000175000017500000000216513014570726020777 0ustar dimitrisdimitris*Vertices 11 1 "minister1" 0.2912 0.2004 ellipse 2 "pminister" 0.4875 0.0153 diamond 3 "minister2" 0.3537 0.3416 ellipse 4 "minister2" 0.3537 0.3416 ellipse 5 "minister3" 0.4225 0.5477 ellipse 6 "minister4" 0.4538 0.1603 ellipse 7 "minister5" 0.4900 0.3836 ellipse 8 "minister6" 0.6212 0.5038 ellipse 9 "minister7" 0.6450 0.2023 ellipse 10 "advisor1" 0.6488 0.6031 box 11 "advisor2" 0.3212 0.5515 box 12 "advisor3" 0.7188 0.4218 box *Matrix 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0socnetv-2.4/nets/jin1-sm.txt0000644000175000017500000000157313014570726016275 0ustar dimitrisdimitris0 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.4/nets/network-edgelist.dl0000644000175000017500000000061413014570726020061 0ustar dimitrisdimitrisDL N=4 FORMAT = edgelist LABELS: On the normalization and visualization of author co-citation data:Salton's cosine versus the Jaccard index Caveats for the use of citation indicators in research and journalevaluations Should co-occurrence data be normalized? A rejoinder Home on the range - What and where is the middle in science andtechnology studies? DATA: 1 2 1 1 3 5 1 4 2 2 3 1 2 4 1 3 4 1socnetv-2.4/nets/11actors-pajek-matrix.adj0000644000175000017500000000216513014570726020763 0ustar dimitrisdimitris*Vertices 11 1 "minister1" 0.2912 0.2004 ellipse 2 "pminister" 0.4875 0.0153 diamond 3 "minister2" 0.3537 0.3416 ellipse 3 "minister2" 0.3537 0.3416 ellipse 4 "minister3" 0.4225 0.5477 ellipse 5 "minister4" 0.4538 0.1603 ellipse 6 "minister5" 0.4900 0.3836 ellipse 7 "minister6" 0.6212 0.5038 ellipse 8 "minister7" 0.6450 0.2023 ellipse 9 "advisor1" 0.6488 0.6031 box 10 "advisor2" 0.3212 0.5515 box 11 "advisor3" 0.7188 0.4218 box *Matrix 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0socnetv-2.4/nets/Freemans_EIES-3-messages_n48.lst0000644000175000017500000000714213014570726022003 0ustar dimitrisdimitris1 1 24 1 2 488 1 3 28 1 4 65 1 5 20 1 6 65 1 7 45 1 8 346 1 9 82 1 10 52 1 11 177 1 12 28 1 13 24 1 14 49 1 15 81 1 16 77 1 17 77 1 18 73 1 19 33 1 20 31 1 21 22 1 22 46 1 23 31 1 24 128 1 25 38 1 26 89 1 27 95 1 28 25 1 29 388 1 30 71 1 31 212 1 32 185 2 1 364 2 2 6 2 3 17 2 4 17 2 5 15 2 7 30 2 8 20 2 9 35 2 10 20 2 11 22 2 12 15 2 13 15 2 14 15 2 15 15 2 16 50 2 17 25 2 18 8 2 20 15 2 21 15 2 22 15 2 23 15 2 25 15 2 26 15 2 27 10 2 28 24 2 29 89 2 30 23 2 31 163 2 32 39 3 1 4 3 2 5 3 8 5 4 1 52 4 2 30 4 4 4 4 6 2 4 8 32 4 9 21 4 10 34 4 11 9 4 16 5 4 17 4 4 18 2 4 19 35 4 24 12 4 27 12 4 28 5 4 29 20 4 30 4 4 31 19 4 32 33 5 1 26 5 2 4 5 3 4 5 4 4 5 6 4 5 7 8 5 8 4 5 9 4 5 10 4 5 11 4 5 12 4 5 13 4 5 14 4 5 15 4 5 16 4 5 17 4 5 18 4 5 19 4 5 21 4 5 22 8 5 23 4 5 24 14 5 25 4 5 27 4 5 29 4 5 30 7 5 31 4 5 32 4 6 1 72 6 2 23 6 4 2 6 6 34 6 8 16 6 10 7 6 11 15 6 15 8 6 16 7 6 17 6 6 24 14 6 27 7 6 28 3 6 29 34 6 30 3 6 31 22 7 1 14 7 31 6 8 1 239 8 2 82 8 3 5 8 4 37 8 5 3 8 6 34 8 7 5 8 8 10 8 9 12 8 10 18 8 11 164 8 12 18 8 16 30 8 17 53 8 18 27 8 19 20 8 20 4 8 22 5 8 23 4 8 24 55 8 26 9 8 27 34 8 29 146 8 30 216 8 31 88 8 32 288 9 1 24 9 2 25 9 4 2 9 8 8 9 9 16 9 11 15 9 13 10 9 17 5 9 27 15 9 29 10 9 31 30 9 32 44 10 1 43 10 2 15 10 4 32 10 6 12 10 8 14 10 10 5 10 11 25 10 12 2 10 16 10 10 17 10 10 19 20 10 20 15 10 22 5 10 23 20 10 24 29 10 26 4 10 27 10 10 29 47 10 30 6 10 31 22 10 32 19 11 1 178 11 2 36 11 4 11 11 6 19 11 7 10 11 8 172 11 9 39 11 10 28 11 11 29 11 13 4 11 16 23 11 17 15 11 18 24 11 21 8 11 24 29 11 25 10 11 26 11 11 27 22 11 29 46 11 31 119 11 32 34 12 2 5 12 8 5 12 12 3 12 19 5 12 29 53 12 31 5 12 32 9 13 1 5 13 11 5 13 31 5 14 1 12 14 3 9 14 14 2 14 16 12 14 19 5 14 29 35 14 31 8 15 1 120 15 6 4 15 12 5 15 15 78 15 27 8 15 29 58 15 31 32 16 1 58 16 2 25 16 4 10 16 8 20 16 10 5 16 11 10 16 14 5 16 16 15 16 17 10 16 21 5 16 24 5 16 29 35 16 31 10 17 1 63 17 2 18 17 3 9 17 4 7 17 6 6 17 8 36 17 10 5 17 11 9 17 12 5 17 14 5 17 16 5 17 20 5 17 21 2 17 27 15 17 29 10 17 30 9 17 31 15 17 32 9 18 1 58 18 2 8 18 3 5 18 4 4 18 8 4 18 10 5 18 11 18 18 18 4 18 27 20 18 29 8 18 30 10 18 31 48 19 1 5 19 2 5 19 4 25 19 8 10 19 14 5 19 19 5 19 23 5 19 31 10 20 21 4 20 29 4 21 1 9 21 11 3 21 16 5 21 29 5 22 1 10 22 24 40 22 29 15 22 32 5 23 1 5 23 2 5 23 3 5 23 10 19 23 19 5 23 29 14 23 31 5 24 1 89 24 2 17 24 3 4 24 4 14 24 5 14 24 6 18 24 7 8 24 8 41 24 9 4 24 10 19 24 11 31 24 12 4 24 13 4 24 14 9 24 15 4 24 16 14 24 17 4 24 18 9 24 19 4 24 20 4 24 21 4 24 22 58 24 23 4 24 24 5 24 25 18 24 26 14 24 27 9 24 28 4 24 29 156 24 30 4 24 31 56 24 32 10 25 1 32 25 2 5 25 14 15 25 22 10 25 24 23 25 25 10 25 30 9 25 31 15 26 1 35 26 2 5 26 10 5 26 29 10 26 31 13 27 1 50 27 2 28 27 4 13 27 8 19 27 9 29 27 10 5 27 11 8 27 13 33 27 15 4 27 17 10 27 18 15 27 24 10 27 28 3 27 29 32 27 31 13 27 32 33 28 1 9 28 2 6 28 6 3 28 28 3 28 32 6 29 1 559 29 2 132 29 3 5 29 4 24 29 5 21 29 6 29 29 8 155 29 9 15 29 10 98 29 11 69 29 12 89 29 13 37 29 14 76 29 15 80 29 16 63 29 17 15 29 18 4 29 19 9 29 20 18 29 21 43 29 22 108 29 23 29 29 24 218 29 26 15 29 27 66 29 29 6 29 30 14 29 31 91 29 32 126 30 1 39 30 2 21 30 4 6 30 5 3 30 6 3 30 8 140 30 10 7 30 12 2 30 17 9 30 18 5 30 27 2 30 29 18 30 30 2 30 31 20 30 32 8 31 1 82 31 2 125 31 3 10 31 4 22 31 5 10 31 6 15 31 7 18 31 8 70 31 9 35 31 10 23 31 11 114 31 12 20 31 13 16 31 14 15 31 15 24 31 16 30 31 17 28 31 18 49 31 19 30 31 20 5 31 21 5 31 22 15 31 23 8 31 24 53 31 25 25 31 26 8 31 27 21 31 28 8 31 29 65 31 30 28 31 32 67 32 1 239 32 2 99 32 4 27 32 5 3 32 8 268 32 9 101 32 10 18 32 11 35 32 12 4 32 17 7 32 22 14 32 24 5 32 27 50 32 28 6 32 29 71 32 30 7 32 31 107 32 32 219socnetv-2.4/nets/10actors-colors.dot0000644000175000017500000000022013014570726017705 0ustar dimitrisdimitrisdigraph mydot { node [color=red, shape=box]; a -> b -> c ->d node [color=pink, shape=circle]; d->e->a->f->j->k->l->o [weight=1, color=gray]; } socnetv-2.4/nets/jin3-sm.txt0000644000175000017500000000037513014570726016276 0ustar dimitrisdimitris0 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.4/nets/Freemans_EIES-1_n48.lst0000644000175000017500000001226013014570726020171 0ustar dimitrisdimitris1 2 4 1 3 2 1 6 2 1 8 2 1 10 2 1 11 2 1 13 2 1 14 2 1 18 2 1 19 2 1 20 2 1 21 2 1 22 2 1 23 2 1 24 2 1 25 2 1 26 3 1 27 2 1 31 2 1 32 2 1 33 2 1 35 2 1 36 2 1 37 2 1 38 2 1 39 3 1 40 2 1 41 2 1 42 2 1 43 2 1 44 4 1 45 2 1 46 2 2 1 4 2 3 2 2 8 1 2 11 3 2 13 3 2 14 4 2 18 1 2 19 3 2 21 2 2 22 2 2 23 2 2 24 3 2 25 2 2 27 1 2 32 2 2 33 3 2 35 2 2 37 2 2 40 2 2 41 1 2 42 2 2 43 3 2 44 4 2 45 4 2 46 2 3 1 3 3 2 1 3 6 4 3 8 1 3 13 2 3 18 2 3 19 4 3 20 4 3 22 4 3 23 1 3 24 2 3 25 2 3 26 2 3 27 1 3 31 1 3 32 2 3 33 2 3 35 2 3 36 4 3 37 2 3 39 2 3 41 1 3 42 1 3 43 1 6 1 2 6 3 2 6 8 2 6 13 2 6 14 2 6 18 2 6 19 2 6 20 2 6 21 2 6 22 2 6 23 2 6 24 1 6 27 4 6 31 1 6 32 2 6 33 2 6 35 2 6 36 2 6 37 2 6 38 2 6 40 2 6 41 2 6 42 2 6 44 2 8 1 3 8 6 2 8 13 2 8 14 3 8 18 2 8 19 2 8 20 1 8 22 2 8 23 1 8 24 2 8 25 2 8 27 1 8 32 2 8 33 2 8 35 2 8 37 2 8 38 1 8 40 1 8 41 2 8 42 2 8 44 2 8 45 2 10 1 3 10 13 2 10 22 2 10 24 1 10 27 2 10 33 1 10 40 2 10 42 2 10 44 2 11 1 3 11 2 2 11 3 1 11 13 2 11 14 2 11 19 1 11 21 3 11 41 2 13 1 2 13 2 2 13 3 2 13 6 2 13 8 2 13 14 1 13 19 2 13 21 2 13 22 2 13 23 2 13 24 2 13 25 2 13 27 1 13 32 2 13 33 2 13 35 1 13 36 1 13 37 2 13 38 2 13 40 2 13 42 2 13 43 2 14 1 3 14 2 4 14 8 2 14 13 2 14 19 1 14 21 2 14 22 1 14 33 1 14 35 3 14 40 3 14 45 4 18 1 2 18 2 1 18 3 3 18 6 3 18 8 2 18 11 1 18 13 2 18 14 2 18 19 2 18 20 3 18 22 1 18 23 2 18 24 2 18 25 2 18 27 2 18 31 2 18 32 3 18 33 2 18 35 2 18 36 4 18 37 2 18 38 2 18 41 2 18 42 2 18 43 2 19 1 1 19 2 3 19 3 2 19 6 1 19 8 1 19 13 3 19 14 1 19 18 1 19 22 2 19 23 1 19 24 2 19 25 2 19 27 1 19 31 2 19 32 2 19 33 2 19 35 2 19 36 1 19 37 2 19 38 2 19 40 2 19 41 1 19 42 1 19 44 1 20 1 1 20 3 1 20 6 2 20 13 1 20 18 3 20 22 2 20 24 1 20 27 2 20 32 2 20 33 2 20 35 2 20 38 2 20 42 2 20 43 2 21 1 3 21 2 3 21 3 1 21 6 2 21 8 1 21 11 3 21 13 3 21 14 2 21 18 1 21 19 1 21 22 1 21 23 1 21 24 1 21 27 2 21 31 1 21 32 1 21 33 1 21 35 1 21 36 1 21 39 2 21 40 4 21 41 2 21 42 2 21 43 2 21 44 3 21 45 3 22 1 3 22 2 2 22 3 4 22 6 2 22 8 3 22 13 3 22 14 2 22 18 1 22 19 2 22 20 3 22 21 1 22 23 3 22 24 4 22 25 3 22 26 2 22 27 3 22 31 2 22 32 3 22 33 3 22 35 4 22 36 3 22 37 3 22 38 3 22 39 2 22 40 1 22 41 2 22 42 4 22 43 3 22 44 2 22 46 1 23 1 3 23 2 2 23 3 2 23 6 3 23 8 1 23 11 1 23 13 2 23 14 2 23 18 2 23 19 2 23 20 1 23 22 3 23 24 2 23 25 2 23 27 2 23 31 4 23 32 1 23 33 2 23 35 1 23 36 2 23 37 2 23 38 2 23 42 3 23 44 2 23 46 1 24 1 2 24 2 2 24 3 2 24 6 1 24 8 3 24 13 3 24 14 1 24 19 2 24 22 3 24 23 2 24 25 3 24 27 1 24 31 2 24 32 2 24 33 4 24 35 3 24 37 3 24 38 2 24 42 2 25 1 3 25 2 2 25 3 3 25 8 2 25 13 3 25 14 2 25 18 1 25 19 2 25 22 3 25 23 2 25 24 2 25 27 1 25 32 3 25 33 3 25 35 3 25 37 2 25 41 1 25 42 1 25 44 2 25 46 1 26 1 4 26 2 1 26 3 2 26 19 2 26 22 2 26 23 1 26 27 1 26 37 1 26 39 2 26 40 2 26 41 1 26 42 2 26 43 2 26 44 4 26 46 2 27 1 2 27 3 2 27 6 4 27 8 1 27 13 2 27 18 2 27 20 2 27 22 2 27 23 2 27 24 1 27 32 1 27 33 2 27 35 3 27 36 2 27 37 2 27 38 2 27 39 2 27 41 2 27 42 2 27 43 1 27 44 2 27 46 2 31 1 1 31 3 2 31 6 1 31 8 1 31 18 2 31 19 2 31 20 2 31 22 2 31 23 2 31 24 2 31 32 1 31 35 3 31 36 1 31 37 3 31 38 2 31 42 1 32 1 2 32 2 2 32 3 2 32 6 2 32 8 2 32 13 2 32 18 3 32 19 2 32 20 2 32 22 3 32 23 1 32 24 2 32 25 2 32 27 2 32 31 1 32 33 3 32 35 4 32 36 2 32 37 3 32 38 3 32 41 2 32 42 3 32 43 1 33 1 3 33 2 3 33 3 2 33 6 2 33 8 2 33 13 3 33 14 1 33 18 2 33 19 3 33 20 2 33 22 2 33 23 3 33 24 4 33 25 3 33 27 2 33 31 2 33 32 2 33 35 3 33 36 2 33 37 2 33 38 3 33 40 1 33 41 2 33 42 2 33 43 1 33 45 1 35 1 2 35 2 2 35 3 2 35 6 3 35 13 2 35 14 3 35 18 2 35 19 2 35 22 3 35 24 3 35 25 2 35 27 3 35 32 3 35 33 3 35 37 4 35 38 2 35 41 2 35 42 4 36 1 2 36 3 4 36 6 3 36 18 4 36 20 1 36 22 2 36 23 1 36 24 1 36 27 2 36 31 1 36 32 2 36 33 2 36 35 1 36 37 1 36 38 2 36 41 1 36 42 2 37 1 2 37 2 2 37 3 2 37 6 2 37 8 2 37 13 3 37 14 2 37 18 2 37 19 2 37 20 2 37 22 3 37 23 2 37 24 3 37 25 2 37 27 3 37 31 4 37 32 3 37 33 3 37 35 4 37 36 2 37 38 3 37 40 2 37 41 2 37 42 4 38 1 2 38 2 2 38 3 2 38 6 2 38 8 1 38 13 2 38 18 3 38 19 2 38 20 2 38 22 3 38 23 2 38 24 3 38 27 2 38 31 2 38 32 4 38 33 3 38 35 3 38 36 3 38 37 4 38 41 1 39 1 4 39 2 1 39 3 2 39 6 1 39 8 1 39 11 1 39 13 1 39 18 1 39 19 1 39 20 1 39 21 2 39 22 2 39 23 1 39 24 1 39 26 3 39 27 2 39 32 1 39 33 1 39 35 2 39 36 1 39 37 2 39 38 1 39 41 2 39 42 2 39 44 3 39 46 1 40 1 2 40 2 2 40 3 1 40 6 2 40 8 1 40 13 2 40 14 2 40 18 1 40 19 1 40 21 4 40 22 1 40 23 1 40 24 1 40 25 1 40 27 1 40 32 1 40 33 1 40 43 2 41 1 3 41 2 2 41 6 3 41 18 1 41 19 1 41 21 1 41 22 2 41 23 2 41 24 2 41 27 3 41 32 2 41 33 2 41 35 3 41 37 2 41 38 1 41 39 2 41 40 1 41 42 2 41 44 2 42 1 2 42 2 2 42 3 2 42 6 2 42 8 2 42 13 2 42 18 2 42 20 2 42 22 3 42 23 2 42 24 2 42 26 2 42 27 2 42 32 2 42 33 2 42 35 4 42 36 2 42 37 3 42 39 2 42 40 2 42 41 2 42 43 2 42 44 2 42 46 3 43 1 3 43 2 4 43 3 1 43 13 4 43 18 2 43 21 2 43 22 2 43 24 2 43 25 2 43 26 2 43 27 2 43 32 2 43 33 2 43 35 2 43 40 2 43 41 1 43 42 2 43 45 2 43 46 1 44 1 4 44 2 4 44 3 2 44 6 2 44 8 2 44 10 2 44 11 1 44 13 2 44 14 2 44 19 2 44 21 2 44 22 2 44 23 2 44 24 1 44 25 2 44 26 3 44 27 2 44 33 1 44 35 2 44 36 2 44 37 2 44 39 2 44 40 2 44 41 2 44 42 2 44 43 2 44 46 1 45 1 3 45 2 3 45 6 1 45 8 2 45 13 3 45 14 4 45 19 1 45 21 2 45 22 1 45 24 1 45 27 1 45 32 1 45 33 1 45 40 2 45 43 3 45 44 3 45 46 1 46 1 2 46 2 2 46 42 3socnetv-2.4/nets/jin4-sm.txt0000644000175000017500000000040113014570726016265 0ustar dimitrisdimitris0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.4/nets/6actors-plain.dot0000644000175000017500000000014413014570726017441 0ustar dimitrisdimitrisdigraph mydot { "test-1" -> "test-2" -> "test-3" -> "test-4" -> "test-5" -> "test-6" -> "test-1"; } socnetv-2.4/AUTHORS0000664000175000017500000000053013014570726014347 0ustar dimitrisdimitrisDeveloper: Dimitris Kalamaras See my blog at: http://dimitris.apeiro.gr Translators: - Greek: None - German: Daniel Pinto dos Santos Debian packagers: - Serafeim Zanikolas (>0.43) - Alejandro Garrido Mota (<0.44) Gentoo packager: - Markos Chandras Arch packager: - Tom Tryfonidis socnetv-2.4/socnetv.desktop0000664000175000017500000000036513131662212016351 0ustar dimitrisdimitris[Desktop Entry] X-SuSE-translate=true Name=SocNetV GenericName=Social Network Visualizer Comment=Social Network Analysis and Visualization software Exec=socnetv Icon=socnetv Categories=Education;Science;Math;Qt; Terminal=false Type=Application socnetv-2.4/INSTALL0000664000175000017500000000502213044107013014315 0ustar dimitrisdimitrisInstallation ============ Binaries SocNetV binary packages are available for Windows, Mac OS X and Linux distros. You can download a binary package for your Operating System from the project webpage at: http://socnetv.org/downloads If there is no package for your OS, please download and compile the source code. Windows To run SocNetV in Windows, download the latest SocNetV zip for Windows from the Downloads page, unzip it, and double-click on the socnetv executable. The program will run immediately. Mac OS X If you are a Mac user, you can download and run SocNetV from a disk image (dmg file). From the Downloads page, download the Mac OS .dmg file. Once downloaded, double click on it and a new window will appear. To run the application, double click on the SocNetV icon holding down the META key. To install SocNetV, drag the SocNetV icon to your Applications. Linux SocNetV is available in most Linux distributions, although not the latest 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 published under the GPL. Qt5 is preinstalled in most Linux distributions and it is available for Windows and Mac OS X. If you do not have Qt5 installed, please download and install it from https://www.qt.io/developers Once you have Qt5 installed in your OS, you are ready to compile SocNetV from source. All you have to do is to 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 2) cd socnetv-2.X 3) qmake 4) make 5) sudo make install Probably you have already done the first 2 steps, so just type in 'qmake' or 'qmake-qt5'. When you finish compiling and installing, run the application typing: socnetv or go to Start Menu > Mathematics > SocNetV. Questions ========= For any questions, email us at: info@socnetv.org socnetv-2.4/README.md0000664000175000017500000001626613245330735014573 0ustar dimitrisdimitris[![socnetv](/src/images/socnetv.png)](http://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 field data from a file in a supported format (GraphML, GraphViz, EdgeList, GML, Adjacency, Edgelist, Pajek, UCINET, etc.) 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. Main 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, ring 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 for Windows, Linux and MacOS. The program is Free Software, licensed under the GNU General Public License 3 (GPL3). You can copy it as many times as you wish, or modify it, provided you keep the same license. The documentation is also Free, licensed under the Free Documentation License (FDL). 2. Availability & License ------------------------- Official Website: http://socnetv.org Email: info@socnetv.org Author: Dimitris V. Kalamaras Blog: http://dimitris.apeiro.gr SocNetV is a cross-platform application, developed in C++ language using the Qt5 cross-platform libraries and tools. Qt is an open source development toolkit published under the GPL. This means you can compile and run SocNetV on any OS supported by Qt. Binary packages and executables for Linux, Mac and Windows are available from the project's website. See section 3 (installation) below. SocNetV is Free Software, distributed under the General Public Licence Version 3 (see the COPYING file for details). The application is not a "finished" product. Therefore, there is no warranty of efficiency, correctness or usability. Nevertheless, we are looking forward to help you if you experience any problems with SocNetV. See section 6 (bug reporting) below. 3. Installation --------------- You can install SocNetV : - using binary packages or - compiling it from source ## a) Install a binary package or executable (Linux/Mac/Windows) SocNetV binary packages are available for Windows, MacOS and Linux. You can download a binary package for your Operating System from the project's webpage at: http://socnetv.org/downloads If there is no package for your OS, please download and compile the source code. Windows To run SocNetV in Windows, download the latest SocNetV Windows installer from the project's Downloads page and double-click on the executable to start the installation. The program will be installed in the usual Windows Program Files directory and a new Start Menu shortcut will be created. Click on that shortcut to start SocNetV. MacOS If you are a Mac user, download the latest SocNetV disk image (.dmg file) from the Downloads page. Once downloaded, double click on the .dmg file and a new window will appear. Drag the SocNetV icon into your Applications folder to install it. To run the application, double click on the SocNetV icon in Applications holding down the META key. Linux SocNetV packages are available in the repositories of most Linux distributions, although these are usually not the latest 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, 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: 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 ## b) Compile from Source Code To compile and install SocNetV from source you need the Qt5 toolkit development libraries. Qt5 is preinstalled in most Linux distributions and it is also available for Windows and MacOS. If you do not have Qt5 installed, please download and install it from https://www.qt.io/developers Once you have Qt5 installed in your OS, 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 All you have to do is to 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 2) cd socnetv-2.X 3) qmake 4) make 5) 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. # 4. 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. # 5. Usage & documentation For usage documentation, 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. # 6. 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 socnetv-2.4/TODO0000664000175000017500000000060213127123037013761 0ustar dimitrisdimitrisKNOWN BUGS ========== 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. 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 socnetv-2.4/socnetv.pro0000775000175000017500000000743313245334571015520 0ustar dimitrisdimitrislessThan(QT_VERSION, 5.0) { error("SocNetV requires at least Qt 5.0!") } # START added for ArchLinux / openSUSE compatibility INSTALLPATH = / target.path = $$[INSTALLPATH]usr/bin TARGET = socnetv pixmap.path = $$[INSTALLPATH]usr/share/pixmaps pixmap.files = src/images/socnetv.png documentation.path = $$[INSTALLPATH]usr/share/doc/socnetv documentation.files = manual manpage.path = $$[INSTALLPATH]usr/share/man/man1 manpage.files = man/socnetv.1.gz translations.path = $$[INSTALLPATH]usr/share/socnetv translations.files = translations doc.path = $$[INSTALLPATH]usr/share/doc/socnetv doc.files = license changelog.gz NEWS README.md TODO COPYING AUTHORS INSTALL INSTALLS += target pixmap documentation manpage translations doc # END TEMPLATE = app CONFIG += qt thread warn_on release #CONFIG += qt thread warn_on debug LANGUAGE = C++ # support QT += xml QT += network QT += widgets QT += printsupport INCLUDEPATH += ./src FORMS += src/forms/dialogfilteredgesbyweight.ui \ src/forms/dialogwebcrawler.ui \ src/forms/dialognodeedit.ui \ src/forms/dialogdatasetselect.ui \ src/forms/dialograndsmallworld.ui \ src/forms/dialograndscalefree.ui \ src/forms/dialogranderdosrenyi.ui \ src/forms/dialograndregular.ui \ src/forms/dialogsettings.ui \ src/forms/dialogsimilaritypearson.ui \ src/forms/dialogsimilaritymatches.ui \ src/forms/dialogdissimilarities.ui \ src/forms/dialogclusteringhierarchical.ui HEADERS += 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/graph.h \ src/graphvertex.h \ src/mainwindow.h \ src/matrix.h \ src/texteditor.h \ src/parser.h \ src/dialogfilteredgesbyweight.h \ src/dialogwebcrawler.h \ src/webcrawler.h \ src/dialogdatasetselect.h \ src/dialogpreviewfile.h \ src/dialognodeedit.h \ src/dialogranderdosrenyi.h \ src/dialograndsmallworld.h \ src/dialograndscalefree.h \ src/dialograndregular.h \ src/dialogsettings.h \ src/dialogsimilaritypearson.h \ src/dialogsimilaritymatches.h \ src/dialogdissimilarities.h \ src/dialogclusteringhierarchical.h SOURCES += 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/graph.cpp \ src/graphvertex.cpp \ src/main.cpp \ src/mainwindow.cpp \ src/matrix.cpp \ src/texteditor.cpp \ src/parser.cpp \ src/dialogfilteredgesbyweight.cpp \ src/dialogwebcrawler.cpp \ src/webcrawler.cpp \ src/dialogdatasetselect.cpp \ src/dialogpreviewfile.cpp \ src/dialognodeedit.cpp \ src/dialogranderdosrenyi.cpp \ src/dialograndsmallworld.cpp \ src/dialograndregular.cpp \ src/dialograndscalefree.cpp \ src/dialogsettings.cpp \ src/dialogsimilaritypearson.cpp \ src/dialogsimilaritymatches.cpp \ src/dialogdissimilarities.cpp \ src/dialogclusteringhierarchical.cpp # Extra optimization flags #win32 { # QMAKE_CXXFLAGS += -msse -mfpmath=sse -ffast-math #} #unix:!macx{ # QMAKE_CXXFLAGS += -ffast-math #} #macx { # QMAKE_CXXFLAGS += -msse -ffast-math #} RESOURCES = src/src.qrc win32 { RC_FILE = src/icon.rc } macx:ICON = src/images/socnetv.icns TRANSLATIONS = translations/socnetv_es.ts \ translations/socnetv_el.ts socnetv-2.4/man/0000775000175000017500000000000013245521146014052 5ustar dimitrisdimitrissocnetv-2.4/man/socnetv.1.gz0000664000175000017500000000557413245520654016252 0ustar dimitrisdimitris‹¬¡–Zsocnetv.1…X]oÛ8}÷¯ ,6d¥`w‹A±n⤞qã vÚ-¶û@K´Í %jDÊŽûã÷ÜKJ–æ¡©E‘—÷óÜs•.? g³Rùx+.~nŒøáÍÛ¿_ˆ‹…Íî•ÿ<ú!ý1¹øz?XLƒôCw^ ~ƒt*ÖÚ¨+ƒ÷X:Ù èéÛè³øþ~íTí´-¿¿i7m•©øÊ›Éâúqú°œÎïh—¸Œ í„YmUFúµ­‹D4Ø3Z×Z•¹9YUFgÒ“x/üV«† vÑ!a‹]‹Ó‹] Ý¿h¿ñVq°Èdù&MÄ·ÿ¬šÿŠ7µÜŸ»Qìéœkµ´À ®¸²ö¸“¤ì¤K„±2‡K•ÉE.=ö×¶ cp²ÐtÀ5Uek¯r2¡^\ÞÕ²Ú~š%‚|Ö¿'b’oÔL;5z1Ε™*³CxcøÍƒüU='âézz?Y&Bù, x%ƒö†}£K¯j ¼Åª’^Ñý'v‘“2[–*#öjUÉréуI®½™·uð±×ÊAzm›ÍVTwŒ°=NƒåVAY+ÞTH8 4µ ’a€×%BšP:¼;É‚…Ç6YçÁ–¤ØZyñéÖœÌâRªP¾ÖŒrMÃÈUé´GÄr-ñRÕ‰Ø(›cwÈEYfäˆ.Ð*C°IŸÏLãp\—ìSë5–ñ6{iž]ˆ{/\Ÿ$¾tÖ½;推ŒÅ±™Dy!$P™·&âFmj¥ºÇk̳§_ ç;ÒAõu“Á—l¡¤c§REž»+ÎÖ)l"éì‚ ç¼ÞP¦æž—:U©PX*wŠrŽ·eÆ:E®éO5~¯Tùj]—¡°(*ýõÊî8'+µ}ÑE§ 2¿–ås§Õ0é™~m‹¢)ioŽ`f,]š­ ÅÑ@¸õK\Cq5ú·FugÞ\¨~kôN„éˆh„~ô­†Zu¶ò™^2$¡(…ƒFB ­º%Ëqcý÷0YÉÚ‘cŽ™ôZ±Gˆju9NÐÁéÀљԹu£GUt"¾HïÝ–Øô.%[©DpÊÊQ3êÕóRÌ@¹·P)î•1£çÒîËód"8uÊ}ý …ÞÿÕ‰[ƒÒ,)áÅZÂ\} a3Œ~¸V”€+<#+௧Éý§©˜é:kà>»Ú)jßæPË¡ïBСŸÓ.ƒ ÂHÁLr]`-Pø¦•9‘9…âÑß"–…,áMøÊšœÝ4äRB]•X$TêL†U€:~§âkh€¡mɬ¼m¡Û ’T!Zi.ô™ØQºVR½(vLHòü{GÛÚ÷ýÐhãGÀg´ŸÐ½,¥1vO PÆ´KUˆ—wݙ۬Ñ%ý´ .ç^»Ñ;Uâ'Œ…žgéiE£Ò·Ó;TŠÍš‘Œ™¾²èö¶4”RdË~KåT7%Å$#Iè5rR½Z5LTO±ÛW´©×ë6¤"°Djsâ ]ÊX-³gêÄœ _“v”™é²yaŸd6_ åª5¿~ú4¹_މl ê|çæ@S؉D„kT N·„ù7ýmÏ»ÄåíÍl˜0#ÛA=¹‚é *?m½¯Þ]]Eê—Úzs…{ÜÔS/éÖæ}Ôhμtsùèá‘=Mü<¸Ñ¾:8V¥}ßÐ#Š-· †~<-Æw“A¡­äKtAÌìŒAø‰˜ë”ÈÕ­îi:L]³Ò œ®Ö›­"ŒP/^»ç(’Wm–5•F4€Gy(ä’IÕ]jÚD`W9%òŽëû1jÍ7h´vÏ™H#.ϲþ(V÷Tx—(È«ZéNRÑ[ ÚFÂWÍ®o\Ìê<¸=YLÈ@£ç%_îmuj]üª—©õReÙ3–3ôQÜ,²CBjÄóvT ½o ]Üf×IôMŒuçŒÁ`iûx´öEzo4ÈáØolÐ=x›¬¿|A¨E§/¢ßhf8—NÎ:J/tžŸKwÂ,¸94eàLûÎË}‹ßAøØI‡kÑ^G¡¯•¢K[¥ÞùPèD} Ç9Ž2܉̢ÌJµ¨Ÿ×r³¡à¯øBÑmœªGíe›Ô¼zñ!€ÁÓýmÝE§Õ5¦“q>›meI¬Í:³èÍ ¹R&á¦I­›º<ýO;ÜVVpD[sq>rà£åA@÷œ;[Å[ó¢  ÏCÏD±mIÝÓ]9Ò}ÛDÖ!Æ-I ¦´¶P2õ\ z. _GDXtóÚ-ÓJGù@ŽWk‰æÐ$ŽrÖÈ“®Í:¹£ŸgIG¾“)ÀŽ3Lü[S:„m;P7‹ì·ÌSÖQ…K§hlB‡ÆàwM<Šf½?iS€§ØK¿3ìfácA~«b¼‡ðcóÁÑ{1eÕ¨0^ñÕ¶­»OMZ™§CV«íeÊSVa¨ Ó—Â {=ñ.løƒ—qhÆŽÜúáÉ´ÒN>a´ÁW$"•ù¯,(s;ÑßÏ56AkìªdØUÑêÉ(&nÐÎ2k†$lFtÿ‰bׂFu¼1øKÂ豿狢ÌCnѰ°¦ûvç>l 9øŠÀ=/³G÷KÎ*Š’1秎髈œxº&Bç®RÕ›C Ç¡tÜŒzÄë¡Ï«W5OêrÃ,db«­ ÕÇu›ëõZ95êéÔ²àT¹•ß69®Kd …ßvm;6³k\Átüì†-{S\þ™hÛ6¯!åp[=Dè¯v„Z® (qVZ¬âVeÏ+û¢\»Âð,î­gµ_.¢6½=Ô¼é‹J_îç_îҧ»E s]]R‚TŒ”ûöÍ›¿`ìÄXˆ~{H §à¡ðq¾-¨"ËB†ãõ)Hp¿©¥Ûž[h3.‰;kÉ£¨f»V‡ü(hºG¹;˜±/ψí}¥aJÅÈ~ÆkŽ£øF`œý›q'åüûö"ºiü´ü8!Ä_C‘_¤‘…„%â§<®¥ÏíÚ¿6qmøã}8 ÷ŠÇÉÃüq¹<ò7't× (Ät¨î)ЬèLËz¯`ݦêFQ#˜¢©×̨Q„ŠHLnƒ´ø1+¡Ñ¾”>vNñË M€àü& ŠÛ†ÏS )þª%cÝ….#Ñp£^+Èû±2ûZÖ%Op¹âÝ-gjJûy Y!£/¹&¢2Ta‡M’Ä|!þÍR~×(:$Û‹ÊO v2[ I´ÌË clERg(ì /諆ÃѪÝ&F#JO0ÁUÒÛþI¥$#>_Ͼ>Nï>.˾+Ïtœà(X®EÇ»û'qGŽà=€íê¬çþFˆ_ÑšÛ†¡£›ÿví÷ôô–J¶ Õ~¿O7eÃc^œÝÕ¦2aÐC¾©@ VíøÎcù4Lµ]¦µzD ïfüÝÃQþ@ ÌLk2ãÙbÎeÂt`Gmý-ºàÿÞMs~Asocnetv-2.4/src/0000775000175000017500000000000013245521146014066 5ustar dimitrisdimitrissocnetv-2.4/src/dialograndscalefree.cpp0000664000175000017500000001126313245520654020556 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt randscalefreeddialog.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); ui.initialNodesSpinBox-> setEnabled(true); ui.undirectedRadioButton->setChecked(false); ui.directedRadioButton->setEnabled(true); ui.directedRadioButton->setChecked(true); ui.diagCheckBox->setText("No, set zero"); ui.diagCheckBox ->setChecked(false); ui.diagCheckBox -> setEnabled(false); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &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); // } //gatherData(); } void DialogRandScaleFree::gatherData() { qDebug() << "DialogRandScaleFree::gatherData() " ; nodes = ui.nodesSpinBox->value(); power = ui.powerSpinBox->value(); initialNodes = ui.initialNodesSpinBox->value(); edgesPerStep = ui.edgesPerStepSpinBox ->value(); zeroAppeal = ui.zeroAppealSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); // diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "initialNodes " << initialNodes; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, power, initialNodes, edgesPerStep,zeroAppeal, mode); } socnetv-2.4/src/main.cpp0000775000175000017500000000646613245520654015540 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt main.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 //for text translations #include #include //used for cout #include "mainwindow.h" //main application window using namespace std; int main(int argc, char *argv[]) { Q_INIT_RESOURCE(src); QApplication app(argc, argv); QTranslator tor( 0 ); QLocale locale; // set the location where your .qm files are in load() below as the last parameter instead of "." // for development, use "/" to use the english original as // .qm files are stored in the base project directory. tor.load( QString("socnetv.") + locale.name(), "." ); app.installTranslator( &tor ); //Check if a filename is passed when this program is called. QString option; if ( argc > 1 ) { option = argv[1]; if (option=="--help" || option=="-h" || option=="--h" || 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"; } } MainWindow *socnetv=new MainWindow(option); socnetv->show(); return app.exec(); } socnetv-2.4/src/dialograndscalefree.h0000664000175000017500000000460213245520654020222 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt randscalefreeddialog.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0); public slots: void checkErrors(); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int &nodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode); private: QString mode; int nodes; // n int initialNodes; // m0 int edgesPerStep; //m int power; float zeroAppeal; // a bool diag; Ui::DialogRandScaleFree ui; }; #endif socnetv-2.4/src/matrix.cpp0000775000175000017500000024026713245520654016117 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt matrix - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 "matrix.h" #define TINY 1.0e-20 #include //allows the use of RAND_MAX macro #include #include //needed for fabs, qFloor etc #include /** * @brief Matrix::Matrix * Default constructor - creates a Matrix of given dimension (0x0) * Use resize(m,n) or zeromatrix(m,n) to resize it * @param Actors */ Matrix::Matrix (int rowDim, int colDim) : m_rows (rowDim), m_cols(colDim) { row = new (nothrow) MatrixRow[ m_rows ]; Q_CHECK_PTR( row ); for (int i=0;i 0){ qDebug() << "Matrix::clear() deleting old rows"; m_rows=0; m_cols=0; delete [] row; } } /** * @brief Resizes this matrix to m x n * Called before every operation on new matrices. * Every MatrixRow object holds max_int=32762 * @param Actors */ void Matrix::resize (const int m, const int n) { qDebug() << "Matrix: resize() "; clear(); m_rows = m; m_cols = n; row = new (nothrow) MatrixRow [ m_rows ]; Q_CHECK_PTR( row ); qDebug() << "Matrix: resize() -- resizing each row"; for (int i=0;i max) { max = item(r,c) ; } if ( item(r,c) < min){ min = item(r,c) ; } } } } /** * @brief Like Matrix::findMinMaxValues only it skips r==c * * @param min value. If (r,c) = minimum, it mean that neighbors r and c are the nearest in the matrix/network * @param max value * Complexity: O(n^2) */ void Matrix::NeighboursNearestFarthest (float &min, float & max, int &imin, int &jmin, int &imax, int &jmax){ max=0; min=RAND_MAX; for (int r = 0; r < rows(); ++r) { for (int c = 0; c < cols(); ++c) { if (r==c) continue; if ( item(r,c) > max) { max = item(r,c) ; imax = r; jmax=c; } if ( item(r,c) < min){ min = item(r,c) ; imin = r; jmin=c; } } } } /** * @brief Makes this square matrix the identity square matrix I * @param dim */ void Matrix::identityMatrix(int dim) { qDebug() << "Matrix::identityMatrix() -- deleting old rows"; clear(); m_rows=dim; m_cols=dim; row = new (nothrow) MatrixRow [m_rows]; Q_CHECK_PTR( row ); //qDebug() << "Matrix: resize() -- resizing each row"; for (int i=0;i=m_rows, corner case (will be deleted). Settting to RAND_MAX." // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } else if (i=erased) { setItem( i, j, item(i,j+1) ) ; // qDebug() << "Matrix:deleteRowColumn() -" // <<"j>=erased, shifting column left" // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } else if (i>=erased && j=erased, shifting rows up." // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } else if (i>=erased && j>=erased) { setItem( i, j, item(i+1,j+1) ) ; // qDebug() << "Matrix:deleteRowColumn() -" // <<"both i,j>=erased, shifting row up and column left." // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } } row[i].setSize(m_cols); } qDebug() << "Matrix:deleteRowColumn() - finished, new matrix:"; //printMatrixConsole(true); // @TODO comment out to release } /** * @brief Fills a matrix with a given value * @param value */ void Matrix::fillMatrix(float value ) { for (int i=0;i< rows() ; i++) for (int j=0;j< cols(); j++) setItem(i,j, value); } /** * @brief Subtracts this matrix from I and returns * * @return I-this to this matrix */ Matrix& Matrix::subtractFromI () { for (int i=0;i< rows();i++) for (int j=0;jsetItem(i,j, item(i,j)+b.item(i,j)); return *S; } /** * @brief Matrix subtraction, operator - * Subtract this matrix - B of the same dim and returns the result S * Allows S = A-B * @param b * @return Matrix S */ Matrix& Matrix::operator -(Matrix & b) { Matrix *S = new Matrix(rows(), cols() ); qDebug()<< "Matrix::operator -"; for (int i=0;i< rows();i++) for (int j=0;jsetItem(i,j, item(i,j)-b.item(i,j)); return *S; } /** * @brief Matrix multiplication, operator * * Multiplies (right) this matrix with given matrix b. * Allows P = A * B where A,B of same dimension * and returns product as a reference to the calling object * @param b * @param symmetry * @return */ Matrix& Matrix::operator *(Matrix & b) { qDebug()<< "Matrix::operator *"; Matrix *P = new Matrix(rows(), cols()); if ( cols() != b.rows() ) { qDebug()<< "Matrix::product() - ERROR! Non compatible input matrices:" " this(" << rows() << "," << cols() << ") and b(" << b.rows() << ","<< b.cols(); return *P; } for (int i=0;i< rows();i++) for (int j=0;jsetItem(i,j,0); for (int k=0;k< cols();k++) { P->setItem(i,j, P->item(i,j) + item(i,k)*b.item(k,j) ); } } return *P; } /** * @brief Multiplies (right) this m x n matrix with given n x p matrix b * and returns the product in the calling matrix which becomes an m x p matrix. * This convenience operator *= allows A *= B * @param b * @param symmetry * @return */ void Matrix::operator *=(Matrix & b) { qDebug()<< "Matrix::operator *"; if ( cols() != b.rows() ) { qDebug()<< "Matrix::product() - ERROR! Non compatible input matrices:" " this(" << rows() << "," << cols() << ") and b(" << b.rows() << ","<< b.cols(); return; } Matrix *P = new Matrix(rows(), b.cols()); for (int i=0;i< rows();i++) { for (int j=0;jsetItem(i,j,0); for (int k=0;k < cols();k++) { P->setItem(i,j, P->item(i,j) + item(i,k)*b.item(k,j) ); } } } *this = *P; } /** * @brief Matrix Multiplication. Given two matrices A (mxn) and B (nxp) * computes their product and stores it to the calling matrix which becomes * an m x p matrix * Allows P.product(A, B) * @param A * @param B * @param symmetry * @return i x k matrix */ void Matrix::product(Matrix &A, Matrix & B, bool symmetry) { qDebug()<< "Matrix::product() - symmetry" << symmetry; if (A.cols() != B.rows() ) { qDebug()<< "Matrix::product() - ERROR! Non compatible input matrices:" " a(" << A.rows() << "," << A.cols() << ") and b(" << B.rows() << ","<< B.cols(); return; } Matrix *P = new Matrix(A.rows(), B.cols()); float prod = 0; for (int i=0;i< A.rows();i++) { for (int j=0;j j ) continue; prod = 0; for (int k=0;ksetItem(i,j, prod); if (symmetry) { P->setItem(j,i, prod ); } } } // qDebug() << "Matrix::product() - this"; *this = *P; //this->printMatrixConsole(); } /** * @brief Takes two ( N x N ) matrices (symmetric) and outputs an upper triangular matrix * @param a * @param b * @return */ Matrix& Matrix::productSym( Matrix &a, Matrix & b) { for (int i=0;i=j) continue; for (int k=0;k j ) { if (a.item(i,k)!=0 && b.item(j,k)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(j,k)); } else //k <= j && ik ) { if (a.item(k,i)!=0 && b.item(k,j)!=0) setItem(i,j, item(i,j)+a.item(k,i)*b.item(k,j)); } else { if (a.item(i,k)!=0 && b.item(k,j)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(k,j)); } } return *this; } /** * @brief Returns the n-nth power of this matrix * @param n * @param symmetry * @return Matrix */ Matrix& Matrix::pow (int n, bool symmetry) { if (rows()!= cols()) { qDebug()<< "Matrix::pow() - Error. This works only for square matrix"; return *this; } qDebug()<< "Matrix::pow() "; Matrix X, Y; //auxilliary matrices qDebug()<< "Matrix::pow() - creating X = this"; X=*this; //X = this //X.printMatrixConsole(true); qDebug()<< "Matrix::pow() - creating Y = I"; Y.identityMatrix( rows() ); // y=I //Y.printMatrixConsole(true); return expBySquaring2 (Y, X, n, symmetry); } /** * @brief Recursive algorithm implementing "Exponentiation by squaring". * Also known as Fast Modulo Multiplication, this algorithm allows * fast computation of a large power n of square matrix X * @param Y must be the Identity matrix on first call * @param X the matrix to be powered * @param n the power * @param symmetry * @return Matrix& * On first call, parameters must be: Y=I, X the orginal matrix to power and n the power. * Returns the power of matrix X to this object. * For n > 4 it is more efficient than naively multiplying the base with itself repeatedly. */ Matrix& Matrix::expBySquaring2 (Matrix &Y, Matrix &X, int n, bool symmetry) { if (n==1) { qDebug() <<"Matrix::expBySquaring2() - n = 1. Computing PM = X*Y where " "X = " ; //X.printMatrixConsole(); //qDebug() <<"Matrix::expBySquaring2() - n = 1. And Y = "; //Y.printMatrixConsole(); Matrix *PM = new Matrix(rows(), cols()); PM->product(X, Y, symmetry); //qDebug()<<"Matrix::expBySquaring2() - n = 1. PM = X*Y =" ; //PM->printMatrixConsole(); return *PM; } else if ( n%2 == 0 ) { //even qDebug()<<"Matrix::expBySquaring2() - even n =" << n << "Computing PM = X * X"; Matrix PM(rows(), cols()); PM.product(X,X,symmetry); //qDebug()<<"Matrix::expBySquaring2() - even n =" << n << ". PM = X * X = " ; //PM.printMatrixConsole(); return expBySquaring2 ( Y, PM, n/2 ); } else { //odd qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << "First compute PM = X * Y"; Matrix PM(rows(), cols()); Matrix PM2(rows(), cols()); PM.product(X,Y,symmetry); //qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << ". PM = X * Y = " ; //PM.printMatrixConsole(); qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << "Now compute PM2 = X * X"; PM2.product(X,X,symmetry); //qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << ". PM2 = X * X = " ; //PM2.printMatrixConsole(); return expBySquaring2 ( PM, PM2, (n-1)/2 ); } } /** * @brief Calculates the matrix-by-vector product Ax of this matrix * Default product: Ax * if leftMultiply=true then it returns the left product xA * @param in input array/vector * @param out output array * @param leftMultiply */ void Matrix::productByVector (float in[], float out[], const bool &leftMultiply) { int n = rows(); int m = cols(); for(int i = 0; i < n; i++) { out[i] = 0; for (int j = 0; j < m; j++) { if (leftMultiply) { // dot product of row vector b with j-th column in A out[i] += item (j, i) * in[j]; } else { // dot product of i-th row in A with the column vector b out[i] += item (i, j) * in[j]; } } } } /** * @brief Helper function, takes to vectors and returns their * Manhattan distance (also known as l1 norm, Taxicab or L1 distance) * which is the sum of the absolute differences * of their coordinates. * @param x * @param y * @return */ float Matrix::distanceManhattan(float x[], float y[], int n) { float norm = 0; for(int i = 0; i < n; i++) { norm += fabs (x[i] - y[i]); } return norm; } /** * @brief Helper function, computes the Euclideian length (also known as L2 distance) * of a vector: if x = (x1 x2 ... xn), then ||x|| = square_root(x1*x1 + x2*x2 + ... + xn*xn) * @param x * @param n * @return */ float Matrix::distanceEuclidean(float x[], int n) { float norm = 0; for (int i = 0; i < n; i++) { norm += x[i] * x[i]; } norm = sqrt(norm); return norm; } /** * @brief Implementation of the Power method which computes the * leading eigenvector (eigenvector centrality) of this matrix * and returns it as vector x. * @param n * @param x */ void Matrix::powerIteration (float x[], float &xsum, float &xmax, int &xmaxi, float &xmin, int &xmini, const float eps, const int &maxIter) { qDebug() << "Matrix::powerIteration() - maxIter" << maxIter <<"initial x" < vec(n); do { // calculate the matrix-by-vector product Ax and // store the result to vector tmp productByVector(x, tmp, false); // calculate the euclidean length of the resulting vector norm = distanceEuclidean(tmp, n); // normalize tmp to unit vector for next iteration xsum = 0; for(int i = 0; i < n; i++) { tmp[i] = tmp[i] / norm; } // calculate the manhattan distance between the new and prev vectors distance = distanceManhattan (tmp, x, n); vec.clear(); xmax = 0 ; xmin = RAND_MAX; for(int i = 0; i < n; i++) { vec.append(tmp[i]); x[i] = tmp[i]; xsum += x[i]; if (x[i] > xmax) { xmax = x[i] ; xmaxi = i+1; } if (x[i] < xmin) { xmin = x[i] ; xmini = i+1; } } qDebug() << "Matrix::powerIteration() - iteration" << iter << endl << "x" << vec << endl << "distance from previous x " << distance << "sum" << xsum << "xmax" << xmax << "xmin" << xmin; iter ++; if (iter > maxIter) break; } while ( distance > eps); delete [] tmp; } /** * @brief Returns the Transpose of this matrix * Allows T = A.transpose() * @param b * @return Matrix T */ Matrix& Matrix::transpose() { Matrix *T = new Matrix(cols(), rows()); //T->zeroMatrix(cols(), rows()); qDebug()<< "Matrix::transpose()"; for (int i=0;i< cols();i++) { for (int j=0;jsetItem(i,j, item(j,i)); } } return *T; } /** * @brief Returns the Cocitation Matrix of this matrix (C = A * A^T) * Allows T = A.cocitationMatrix() * @param b * @return Matrix T */ Matrix& Matrix::cocitationMatrix() { Matrix *T = new Matrix(cols(), rows()); qDebug()<< "Matrix::cocitationMatrix() this transpose"; //this->transpose().printMatrixConsole(); T->product(this->transpose(),*this, true); return *T; } /** * @brief Returns the Degree Matrix of this matrix. * The Degree Matrix is diagonal matrix which contains information about the degree * of each graph vertex (row of the adjacency matrix) * Allows S = A.degreeMatrix() * @param b * @return Matrix S */ Matrix& Matrix::degreeMatrix() { Matrix *S = new Matrix(rows(), cols()); qDebug()<< "Matrix::degreeMatrix()"; float degree=0; for (int i=0;i< rows();i++) { degree = 0; for (int j=0;jsetItem(i,i, degree); } return *S; } /** * @brief Returns the Laplacian of this matrix. * The Laplacian is a NxN matrix L = D - A where D is the degree matrix of A * Allows S = A.laplacianMatrix() * @param b * @return Matrix S */ Matrix& Matrix::laplacianMatrix() { Matrix *S = new Matrix(rows(), cols()); //S->zeroMatrix(rows(), cols()); qDebug()<< "Matrix::laplacianMatrix()"; *S = (this->degreeMatrix()) - *this; return *S; } /** * @brief Inverts given matrix A by Gauss Jordan elimination Input: matrix A Output: matrix A becomes unit matrix *this becomes the invert of A and is returned back. * @param A * @return inverse matrix of A */ Matrix& Matrix::inverseByGaussJordanElimination(Matrix &A){ qDebug()<< "Matrix::inverseByGaussJordanElimination()"; int n=A.cols(); qDebug()<<"Matrix::inverseByGaussJordanElimination() - build I size " << n << " This will become A^-1 in the end"; identityMatrix( n ); int l=0, m_pivotLine=0; float m_pivot=0, temp_pivot=0, elim_coef=0; for ( int j=0; j< n; j++) { // for n, it is the last diagonal element of A l=j+1; m_pivotLine=-1; m_pivot = A.item(j,j); qDebug() << "inverseByGaussJordanElimination() at column " << j+1 << " Initial pivot " << m_pivot ; for ( int i=l; i qFabs ( m_pivot ) ) { qDebug() << " A("<< i+1 << ","<< j+1 << ") = " << temp_pivot << " absolutely larger than current pivot "<< m_pivot << ". Marking new pivot line: " << i+1; m_pivotLine=i; m_pivot = temp_pivot ; } } if ( m_pivotLine != -1 ) { A.swapRows(m_pivotLine,j); swapRows(m_pivotLine,j); } qDebug()<<" multiplyRow() "<< j+1 << " by value " << 1/m_pivot ; for ( int k=0; k< rows(); k++) { A.setItem ( j, k, (1/m_pivot) * A.item (j, k) ); setItem ( j, k, (1/m_pivot) * item (j, k) ); qDebug()<<" A.item("<< j+1 << ","<< k+1 << ") = " << A.item(j,k); qDebug()<<" item("<< j+1 << ","<< k+1 << ") = " << item(j,k); } qDebug() << "eliminate variables FromRowsBelow()" << j+1 ; for ( int i=0; i< rows(); i++) { qDebug()<<" Eliminating item("<< i+1 << ","<< j+1 << ") = " << A.item(i,j) << " while at column j="<(1,n); float *vv; // vv stores the implicit scaling of each row vv=new (nothrow) float [n]; Q_CHECK_PTR( vv ); // QTextStream stream(stdout); // stream << "a = LU = " << a ; d=1.0; // No row interchanges yet. qDebug () << "Matrix::ludcmp() - loop over row to get scaling info" ; for (i=0;i big) big=temp; } if (big == 0) // No nonzero largest element. { qDebug() << "Matrix::ludcmp() - Singular matrix in routine ludcmp"; return false; } vv[i]=1.0/big; // Save the scaling. qDebug () << "Matrix::ludcmp() - big element in row i " << i+1 << " is "<< big << " row scaling vv[i] " << vv[i]; } qDebug () << "Matrix::ludcmp() - Start Crout's LOOP over columns"; for (j=0;j big) { // Is the figure of merit for the pivot better than the best so far? big=temp; imax=i; qDebug () << "Matrix::ludcmp() - found new largest pivot at row " << imax+1 << " big " << temp; } } qDebug () << "Matrix::ludcmp() - check for row interchange "; if (j != imax) // Do we need to interchange rows? { qDebug () << "Matrix::ludcmp() - interchanging rows " << imax+1 << " and " << j+1; for ( k=0; k=0;i--) { // Now we do the backsubstitution, equation (2.3.7). sum=b[i]; qDebug() << "Matrix::lubksb() backsubstitution: "<< "i " << i << " b[i] " << b[i] << "sum " << sum ; for ( j=i+1;jrows(), this->cols()); *A = *this; int n=rows(); float d; int indx[n]; qDebug () << "Matrix::solve() - solving A x - size " << n; if (n==0) { return false; } if ( ! ludcmp(*A,n,indx,d) ) { // Decompose the matrix just once. qDebug () << "Matrix::solve() - matrix a singular - RETURN"; return false ; } qDebug () << "Matrix::solve() - call lubksb"; lubksb(*A, n, indx, b); qDebug () << "Matrix::solve() - finished!"; return true; } /** * @brief Computes the dissimilarities matrix of the variables (rows, columns, both) * of this matrix using the user defined metric * @param metric * @param varLocation * @param diagonal * @param considerWeights * @return */ Matrix& Matrix::distancesMatrix(const int &metric, const QString varLocation, const bool &diagonal, const bool &considerWeights) { Q_UNUSED(considerWeights); Matrix *T = new Matrix(cols(), rows()); qDebug()<< "Matrix::distancesMatrix() -" <<"metric"<< metric << "varLocation"<< varLocation << "diagonal"< mean (N,0); // holds mean values qDebug()<< "Matrix::distancesMatrix() - input matrix:"; //this->printMatrixConsole(); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { distTemp = 0; ties = 0; max = 0; for (int j = 0 ; j < N ; j++ ) { // qDebug() << "(i,j)" << i<< ","< max ) ? distTemp : max; distTemp = max; } break; default: break; } } switch (metric) { case METRIC_JACCARD_INDEX: if (ties!=0) distance = 1 - distTemp/ ( ties ) ; else distance = 1; break; case METRIC_HAMMING_DISTANCE: distance = distTemp; break; case METRIC_EUCLIDEAN_DISTANCE: distance = (distTemp == RAND_MAX) ? distTemp : sqrt(distTemp); break; case METRIC_MANHATTAN_DISTANCE: distance = distTemp ; break; case METRIC_CHEBYSHEV_MAXIMUM: distance = distTemp ; break; default: break; } // qDebug() << "distTemp("< mean (N,0); // holds mean values qDebug()<< "Matrix::distancesMatrix() -" <<"input matrix"; //printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { distTemp = 0; ties = 0; max = 0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (metric) { case METRIC_JACCARD_INDEX: if (item(j,i) == item(j,k) && (item(j,i) != 0 && item(j,i) != RAND_MAX)) { distTemp++; } if ( ( item(j,i) != 0 && item(j,i) != RAND_MAX ) || ( item(j,k) != 0 && item(j,k) != RAND_MAX )) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (item(j,i) != item(j,k) ) { distTemp++; } break; case METRIC_EUCLIDEAN_DISTANCE: if (item(j,i) == RAND_MAX || item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; } else { distTemp += ( item(j,i) - item(j,k) )*( item(j,i) - item(j,k) ); //compute (x - y)^2 } break; case METRIC_MANHATTAN_DISTANCE: if (item(j,i) == RAND_MAX || item(j,k) == RAND_MAX || distTemp == RAND_MAX ) { distTemp = RAND_MAX; } else { distTemp += fabs( item(j,i) - item(j,k) ); //compute |x - y| } break; case METRIC_CHEBYSHEV_MAXIMUM: if (item(j,i) == RAND_MAX || item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; max = RAND_MAX; } else { distTemp = fabs( item(j,i) - item(j,k) ); max = ( distTemp > max ) ? distTemp : max; distTemp = max; } break; default: break; } } switch (metric) { case METRIC_JACCARD_INDEX: if (ties!=0) distance = 1 - distTemp/ ( ties ) ; else distance = 1; break; case METRIC_HAMMING_DISTANCE: distance = distTemp; break; case METRIC_EUCLIDEAN_DISTANCE: distance = (distTemp == RAND_MAX) ? distTemp : sqrt(distTemp); break; case METRIC_MANHATTAN_DISTANCE: distance = distTemp ; break; case METRIC_CHEBYSHEV_MAXIMUM: distance = distTemp ; break; default: break; } // qDebug() << "distTemp("< mean (N,0); // holds mean values //create augmented matrix (concatenated rows and columns) from input matrix for (int i = 0 ; i < N ; i++ ) { for (int j = 0 ; j < N ; j++ ) { CM.setItem(j,i, item(i,j)); CM.setItem(j + N,i, item(j,i)); } } qDebug()<< "Matrix::distancesMatrix() -" <<"input matrix"; //CM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { distTemp = 0; ties = 0; max = 0; for (int j = 0 ; j < M ; j++ ) { if (!diagonal) { if ( (i==j || k==j )) continue; if ( j>=N && ( (i+N)==j || (k+N)==j )) continue; } switch (metric) { case METRIC_JACCARD_INDEX: if (CM.item(j,i) == CM.item(j,k) && (CM.item(j,i) != 0 && CM.item(j,i) != RAND_MAX)) { distTemp++; } if ( ( CM.item(j,i) != 0 && CM.item(j,i) != RAND_MAX ) || ( CM.item(j,k) != 0 && CM.item(j,k) != RAND_MAX )) { ties++; } break; case METRIC_HAMMING_DISTANCE: if ( CM.item(j,i) != CM.item(j,k) ) { distTemp++; } break; case METRIC_EUCLIDEAN_DISTANCE: if ( CM.item(j,i) == RAND_MAX || CM.item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; } else { distTemp += ( CM.item(j,i) - CM.item(j,k) )*( CM.item(j,i) - CM.item(j,k) ); //compute (x - y)^2 } break; case METRIC_MANHATTAN_DISTANCE: if ( CM.item(j,i) == RAND_MAX || CM.item(j,k) == RAND_MAX || distTemp == RAND_MAX ) { distTemp = RAND_MAX; } else { distTemp += fabs( CM.item(j,i) - CM.item(j,k) ); //compute |x - y| } break; case METRIC_CHEBYSHEV_MAXIMUM: if ( CM.item(j,i) == RAND_MAX || CM.item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; max = RAND_MAX; } else { distTemp = fabs( CM.item(j,i) - CM.item(j,k) ); max = ( distTemp > max ) ? distTemp : max; distTemp = max; } break; default: break; } } switch (metric) { case METRIC_JACCARD_INDEX: if (ties!=0) distance = 1 - distTemp/ ( ties ) ; else distance = 1; break; case METRIC_HAMMING_DISTANCE: distance = distTemp; break; case METRIC_EUCLIDEAN_DISTANCE: distance = (distTemp == RAND_MAX) ? distTemp : sqrt(distTemp); break; case METRIC_MANHATTAN_DISTANCE: distance = distTemp ; break; case METRIC_CHEBYSHEV_MAXIMUM: distance = distTemp ; break; default: break; } // qDebug() << "distTemp("<printMatrixConsole(); return *T; } /** * @brief Computes the pair-wise matching score of the rows, columns * or both of the given matrix AM, based on the given matching measure * and returns the similarity matrix. * @param AM Matrix * @return Matrix nxn with matching scores for every pair of rows/columns of AM */ Matrix& Matrix::similarityMatrix(Matrix &AM, const int &measure, const QString varLocation, const bool &diagonal, const bool &considerWeights){ Q_UNUSED(considerWeights); qDebug()<< "Matrix::similarityMatrix() -" <<"measure"<< measure << "varLocation"<< varLocation; int N = 0; float sum = 0; float matchRatio = 0; float matches = 0; float ties = 0; float magn_i=0, magn_k=0; if (varLocation=="Rows") { N = AM.rows() ; this->zeroMatrix(N,N); QVector mean (N,0); // holds mean values qDebug()<< "Matrix::similarityMatrix() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { matches = 0; ties = 0; magn_i=0; magn_k=0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (measure) { case METRIC_SIMPLE_MATCHING : if (AM.item(i,j) == AM.item(k,j) ) { matches++; } ties++; break; case METRIC_JACCARD_INDEX: if (AM.item(i,j) == AM.item(k,j) && AM.item(i,j) != 0) { matches++; } if (AM.item(i,j) != 0 || AM.item(k,j) ) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (AM.item(i,j) != AM.item(k,j) ) { matches++; } break; case METRIC_COSINE_SIMILARITY: matches += AM.item(i,j) * AM.item(k,j); //compute x * y magn_i += AM.item(i,j) * AM.item(i,j); //compute |x|^2 magn_k += AM.item(k,j) * AM.item(k,j); //compute |y|^2 break; case METRIC_EUCLIDEAN_DISTANCE: matches += ( AM.item(i,j) - AM.item(k,j) )*( AM.item(i,j) - AM.item(k,j) ); //compute (x - y)^2 break; default: break; } } switch (measure) { case METRIC_SIMPLE_MATCHING : matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_JACCARD_INDEX: matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_HAMMING_DISTANCE: matchRatio = matches; break; case METRIC_COSINE_SIMILARITY: // sigma(i,j) = cos(theta) = x * y / |x| * |y| if ( !magn_i || ! magn_k ) { // Note that cosine similarity is undefined when // one or both vertices has degree zero. By convention, // in this case we take sigma(i,j) = 0 matchRatio = 0; } else matchRatio = matches / sqrt( magn_i * magn_k ); break; case METRIC_EUCLIDEAN_DISTANCE: matchRatio = sqrt(matches); break; default: break; } qDebug() << "matches("<zeroMatrix(N,N); QVector mean (N,0); // holds mean values qDebug()<< "Matrix::similarityMatrix() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { matches = 0; ties = 0; magn_i=0; magn_k=0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (measure) { case METRIC_SIMPLE_MATCHING : if (AM.item(j,i) == AM.item(j,k) ) { matches++; } ties++; break; case METRIC_JACCARD_INDEX: if (AM.item(j,i) == AM.item(j,k) && AM.item(j,i) != 0) { matches++; } if (AM.item(j,i) != 0 || AM.item(j,k) !=0 ) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (AM.item(j,i) != AM.item(j,k) ) { matches++; } break; case METRIC_COSINE_SIMILARITY: matches += AM.item(j,i) * AM.item(j,k); //compute x * y magn_i += AM.item(j,i) * AM.item(j,i); //compute |x|^2 magn_k += AM.item(j,k) * AM.item(j,k); //compute |y|^2 break; case METRIC_EUCLIDEAN_DISTANCE: matches += ( AM.item(j,i) - AM.item(j,k) )*( AM.item(j,i) - AM.item(j,k) ); //compute (x - y)^2 break; default: break; } } switch (measure) { case METRIC_SIMPLE_MATCHING : matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_JACCARD_INDEX: matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_HAMMING_DISTANCE: matchRatio = matches; break; case METRIC_COSINE_SIMILARITY: // sigma(i,j) = cos(theta) = x * y / |x| * |y| if ( !magn_i || ! magn_k ) { // Note that cosine similarity is undefined when // one or both vertices has degree zero. By convention, // in this case we take sigma(i,j) = 0 matchRatio = 0; } else matchRatio = matches / sqrt( magn_i * magn_k ); break; case METRIC_EUCLIDEAN_DISTANCE: matchRatio = sqrt(matches); break; default: break; } qDebug() << "matches("<zeroMatrix(N,N); CM.zeroMatrix(M,N); QVector mean (N,0); // holds mean values //create augmented matrix (concatenated rows and columns) from input matrix for (int i = 0 ; i < N ; i++ ) { for (int j = 0 ; j < N ; j++ ) { CM.setItem(j,i, AM.item(i,j)); CM.setItem(j + N,i, AM.item(j,i)); } } qDebug()<< "Matrix::similarityMatrix() -" <<"input matrix"; //CM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { matches = 0; ties = 0; magn_i=0; magn_k=0; for (int j = 0 ; j < M ; j++ ) { if (!diagonal) { if ( (i==j || k==j )) continue; if ( j>=N && ( (i+N)==j || (k+N)==j )) continue; } switch (measure) { case METRIC_SIMPLE_MATCHING : if (CM.item(j,i) == CM.item(j,k) ) { matches++; } ties++; break; case METRIC_JACCARD_INDEX: if (CM.item(j,i) == CM.item(j,k) && CM.item(j,i) != 0) { matches++; } if (CM.item(j,i) != 0 || CM.item(j,k) !=0 ) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (CM.item(j,i) != CM.item(j,k) ) { matches++; } break; case METRIC_COSINE_SIMILARITY: matches += CM.item(j,i) * CM.item(j,k); //compute x * y magn_i += CM.item(j,i) * CM.item(j,i); //compute |x|^2 magn_k += CM.item(j,k) * CM.item(j,k); //compute |y|^2 break; case METRIC_EUCLIDEAN_DISTANCE: matches += ( CM.item(j,i) - CM.item(j,k) )*( CM.item(j,i) - CM.item(j,k) ); //compute (x - y)^2 break; default: break; } } switch (measure) { case METRIC_SIMPLE_MATCHING : matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_JACCARD_INDEX: matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_HAMMING_DISTANCE: matchRatio = matches; break; case METRIC_COSINE_SIMILARITY: // sigma(i,j) = cos(theta) = x * y / |x| * |y| if ( !magn_i || ! magn_k ) { // Note that cosine similarity is undefined when // one or both vertices has degree zero. By convention, // in this case we take sigma(i,j) = 0 matchRatio = 0; } else matchRatio = matches / sqrt( magn_i * magn_k ); break; case METRIC_EUCLIDEAN_DISTANCE: matchRatio = sqrt(matches); break; default: break; } qDebug() << "matches("<zeroMatrix(N,N); QVector mean (N,0); // holds mean values QVector sigma(N,0); qDebug()<< "Matrix::pearsonCorrelationCoefficients() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { qDebug() << "comparing rows i"<zeroMatrix(N,N); QVector mean (N,0); // holds mean values QVector sigma(N,0); qDebug()<< "Matrix::pearsonCorrelationCoefficients() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { qDebug() << "comparing columns i"<zeroMatrix(N,N); CM.zeroMatrix(M,N); QVector mean (N,0); // holds mean values QVector sigma(N,0); //create augmented matrix (concatenated rows and columns) from input matrix for (int i = 0 ; i < N ; i++ ) { for (int j = 0 ; j < N ; j++ ) { CM.setItem(j,i, AM.item(i,j)); CM.setItem(j + N,i, AM.item(j,i)); } } qDebug()<< "Matrix::pearsonCorrelationCoefficients() -" <<"input matrix"; //CM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { //a column for (int k = i ; k < N ; k++ ) { // next column qDebug() << "comparing augmented columns i"<=N && ( (i+N)==j || (k+N)==j )) { qDebug() << "skipping because j>=N and i"< fabs(maxVal) ) ? fabs(minVal) : fabs(maxVal) ; os << qSetFieldWidth(0) << endl ; os << "- Values: " << ( (hasRealNumbers) ? ("real numbers (printed decimals 3)") : ("integers only" ) ) << endl; os << "- Max value: "; if ( maxVal==RAND_MAX ) os << infinity << " (=not connected nodes, in distance matrix)"; else os << maxVal; os << qSetFieldWidth(0) << endl ; os << "- Min value: "; if ( minVal==RAND_MAX ) os << infinity; else os << minVal; os << qSetFieldWidth(0) << endl << endl; os << qSetFieldWidth(7) << fixed << right << "v"<< qSetFieldWidth(3) << "" ; os << ( (hasRealNumbers) ? qSetRealNumberPrecision(3) : qSetRealNumberPrecision(0) ) ; // Note: In the case of Distance Matrix, // if there is DM(i,j)=RAND_MAX (not connected), we always use fieldWidth = 13 if ( maxAbsVal > 999) fieldWidth = 13 ; else if ( maxAbsVal > 99) fieldWidth = 10 ; else if ( maxAbsVal > 9 ) fieldWidth = 9 ; else fieldWidth = 8 ; // print first/header row for (int r = 0; r < m.cols(); ++r) { actorNumber = r+1; if ( actorNumber > 999) os << qSetFieldWidth(fieldWidth-3) ; else if ( actorNumber > 99) os << qSetFieldWidth(fieldWidth-2) ; else if ( actorNumber > 9) os << qSetFieldWidth(fieldWidth-1) ; else os << qSetFieldWidth(fieldWidth) ; os << fixed << actorNumber; } os << qSetFieldWidth(0) << endl; os << qSetFieldWidth(7)<< endl; // print rows for (int r = 0; r < m.rows(); ++r) { actorNumber = r+1; if ( actorNumber > 999) os << qSetFieldWidth(4) ; else if ( actorNumber > 99) os << qSetFieldWidth(5) ; else if ( actorNumber > 9) os << qSetFieldWidth(6) ; else os << qSetFieldWidth(7) ; os << fixed << actorNumber << qSetFieldWidth(3) <<"" ; for (int c = 0; c < m.cols(); ++c) { element = m(r,c) ; os << qSetFieldWidth(fieldWidth) << fixed << right; if ( element == RAND_MAX) // we print inf symbol instead of RAND_MAX (distances matrix). os << fixed << right << qSetFieldWidth(fieldWidth) << infinity ; else { if ( element > 999) os << qSetFieldWidth(fieldWidth-3) ; else if ( element > 99) os << qSetFieldWidth(fieldWidth-2) ; else if ( element > 9) os << qSetFieldWidth(fieldWidth-1) ; else os << qSetFieldWidth(fieldWidth) ; os << element; } } os << qSetFieldWidth(0) << endl; } return os; } /** * @brief Prints this matrix as HTML table * This has the problem that the real actorNumber != elementLabel i.e. when we * have deleted a node/vertex * @param os * @param debug * @return */ bool Matrix::printHTMLTable(QTextStream& os, const bool markDiag, const bool &plain, const bool &printInfinity){ qDebug() << "Matrix::printHTMLTable()"; int elementLabel=0, rowCount = 0; float maxVal, minVal, element; bool hasRealNumbers=false; findMinMaxValues(minVal, maxVal, hasRealNumbers); //maxAbsVal = ( fabs(minVal) > fabs(maxVal) ) ? fabs(minVal) : fabs(maxVal) ; os << ( (hasRealNumbers) ? qSetRealNumberPrecision(3) : qSetRealNumberPrecision(0) ) ; if (plain) { os << "
";

        // print first/header row
        os << "" << qSetFieldWidth(5) << right << "A/A";
        os <<  fixed << qSetFieldWidth(10) << right ;
        for (int r = 0; r < cols(); ++r) {
            elementLabel = r+1;
            os << elementLabel;
        }
        os << qSetFieldWidth(0) << ""<< endl;

        for (int r = 0; r < rows(); ++r) {
            elementLabel = r+1;
            rowCount++;

            os << "" << qSetFieldWidth(5) << right;
            os << elementLabel;
            os << qSetFieldWidth(0) << "";

            for (int c = 0; c < cols(); ++c) {
                element = item(r,c) ;
                os << fixed << qSetFieldWidth(10) << right;
                if (  element == RAND_MAX)  // print inf symbol instead of RAND_MAX (distances matrix).
                    os << infinity;
                else {
                    os << element ;

                }
               // os << "";
            }

            os << qSetFieldWidth(0) << endl;
        }

        os << "
"; return true; } os << "" << "" << "" << ""; // print first/header row for (int r = 0; r < cols(); ++r) { elementLabel = r+1; os << ""; } os << "" << "" << ""; // print rows rowCount = 0; for (int r = 0; r < rows(); ++r) { elementLabel = r+1; rowCount++; os << ""; os <<""; for (int c = 0; c < cols(); ++c) { element = item(r,c) ; os << fixed << right; os <<"" : ">"); if ( ( element == RAND_MAX ) && printInfinity) { // print inf symbol instead of RAND_MAX (distances matrix). os << infinity; } else { os << element ; } os << ""; } os <<""; } os << "
" << ("Actor/Actor") << "" << elementLabel << "
" << elementLabel << "
"; os << qSetFieldWidth(0) << endl ; os << "

" << "" << ("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 ) ) << "

"; return true; } /** * @brief Prints this matrix to stderr or stdout * @return */ bool Matrix::printMatrixConsole(bool debug){ qDebug() << "Matrix::printMatrixConsole() - debug " << debug << "matrix rows" << rows()<< "cols"<< cols(); QTextStream out ( (debug ? stderr : stdout) ); for (int r = 0; r < rows(); ++r) { for (int c = 0; c < cols(); ++c) { if ( item(r,c) < RAND_MAX ) { out << qSetFieldWidth(12) << qSetRealNumberPrecision(3) << forcepoint << fixed<. * ********************************************************************************/ #include #include #include #include #include //used for qDebug messages #include #include "graphicswidget.h" #include "graphicsedge.h" #include "graphicsnode.h" #include "graphicsedgeweight.h" #include "graphicsedgelabel.h" static const int EDGE_DIRECTED = 0; static const int EDGE_RECIPROCATED = 1; static const int EDGE_UNDIRECTED = 2; static const double Pi = 3.14159265; static double TwoPi = 2.0 * Pi; GraphicsEdge::GraphicsEdge(GraphicsWidget *gw, GraphicsNode *from, GraphicsNode *to, const float &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=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 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); //setCacheMode (QGraphicsItem::ItemCoordinateCache); 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=str; prepareGeometryChange(); } QString GraphicsEdge::color() const{ return m_color; } /** * @brief Called from Graph::graphSaveToPajekFormat() * @return */ QString GraphicsEdge::colorToPajek() { if (m_color.startsWith("#")) { return ("RGB"+m_color.right( m_color.size()-1 )).toUpper() ; } return m_color; } /** * @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 float &w) { qDebug() << "GraphicsEdge::setWeight() " << w; prepareGeometryChange(); m_weight = w; if (m_drawWeightNumber) weightNumber->setPlainText (QString::number(w)); } /** * @brief Returns the weight/value of this edge * @return */ float 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(); } /** * @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(mapFromItem(source, 0, 0), mapFromItem(target, 0, 0)); QLineF line(source->x(), source->y(), target->x(), target->y()); QPointF edgeOffset; line_length = line.length(); line_dx = line.dx(); line_dy = line.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 ; 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"; } } 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 = TwoPi - 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 - Pi / 3) * m_arrowSize, cos(angle - Pi / 3) * m_arrowSize); QPointF destArrowP2 = targetPoint + QPointF(sin(angle - Pi + Pi / 3) * m_arrowSize, cos(angle - Pi + Pi / 3) * m_arrowSize); // qDebug() << "*** GraphicsEdge::paint() destArrowP1 " // << destArrowP1.x() << "," << destArrowP1.y() // << " destArrowP2 " << destArrowP2.x() << "," << destArrowP2.y(); path.addPolygon ( QPolygonF() << targetPoint << destArrowP1 << destArrowP2 << targetPoint ); if (m_edgeDirType == EDGE_UNDIRECTED || m_edgeDirType == EDGE_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 +Pi / 3) * m_arrowSize, cos(angle +Pi / 3) * m_arrowSize); QPointF srcArrowP2 = sourcePoint + QPointF(sin(angle +Pi - Pi / 3) * m_arrowSize, cos(angle +Pi - Pi / 3) * m_arrowSize); 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==EDGE_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 { switch (m_state) { case EDGE_STATE_REGULAR: //qDebug() << "GraphicsEdge::pen() - returning pen for state REGULAR" ; if (m_weight < 0 ){ return QPen(QColor(m_color), width(), Qt::DashLine, Qt::RoundCap, Qt::RoundJoin); } return QPen(QColor(m_color), width(), style(), Qt::RoundCap, Qt::RoundJoin); break; case EDGE_STATE_HIGHLIGHT: // selected //qDebug() << "GraphicsEdge::pen() - returning pen for state HIGHLIGHTED" ; return QPen(QColor("red"), width(), style(), Qt::RoundCap, Qt::RoundJoin); case EDGE_STATE_HOVER: // hover //qDebug() << "GraphicsEdge::pen() - returning pen for state HOVER" ; return QPen(QColor("red"), width()+1, style(), Qt::RoundCap, Qt::RoundJoin); default: //qDebug() << "GraphicsEdge::pen() - returning pen for state DEFAULT" ; return QPen(QColor(m_color), width(), 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()"; //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()); painter->drawPath(m_path); } /** * @brief Returns the width of the edge as a function of edge weight * @return */ float GraphicsEdge::width() const{ if ( fabs(m_weight) > 1 ) { return 1+log(fabs(m_weight)) ; } return fabs(m_weight) ; } /** * @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::highlight(const bool &flag) { //qDebug()<< "GraphicsEdge::highlight() - " << flag; if (flag && m_hoverHighlighting) { prepareGeometryChange(); setState(EDGE_STATE_HIGHLIGHT); } else { prepareGeometryChange(); setState(EDGE_STATE_REGULAR); } } /** * @brief Toggles edge highlighting * @param toggle */ void GraphicsEdge::setHighlighting(const bool &toggle) { m_hoverHighlighting = toggle; } void GraphicsEdge::setClicked(const bool &toggle) { qDebug()<<"GraphicsEdge::setClicked()"; if (!toggle) { qDebug()<<"GraphicsEdge::setClicked() - restoring connected nodes"; //unselect them, restore their color source->setSelected(false); target->setSelected(false); //restore their size source->setSize(sourceOrigSize); target->setSize(targetOrigSize); qDebug()<<"GraphicsEdge::setClicked() - Restored source and target nodes"; } else { qDebug()<<"GraphicsEdge::setClicked() - making connected nodes larger"; // tell nodes to change their color source->setSelected(true); target->setSelected(true); // save their original size sourceOrigSize=source->size(); targetOrigSize=target->size(); //now, make them larger source->setSize(2*sourceOrigSize-1); target->setSize(2*targetOrigSize-1); qDebug()<<"GraphicsEdge::setClicked() - Made connected nodes larger"; } } /** * @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); } socnetv-2.4/src/webcrawler.h0000775000175000017500000000721713245520654016411 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt webcrawler.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 WEBCRAWLER_H #define WEBCRAWLER_H #include #include class QNetworkAccessManager; class QNetworkRequest; using namespace std; class WebCrawler_Parser : public QObject { Q_OBJECT public: WebCrawler_Parser(); ~WebCrawler_Parser(); void load (const QString &seed, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &extLinks, const bool &intLinks, const bool &selfLinks); public slots: void parse(QNetworkReply *reply); void newLink(int s, QUrl target, bool enqueue_to_frontier); signals: void signalCreateNode(const int &no, const QString &url, const bool &signalMW=false); void signalCreateEdge (const int &source, const int &target); void startSpider(); void finished (QString); private: QByteArray ba; QMap knownUrls; QUrl m_seed; int m_maxNodes; int m_discoveredNodes; int m_maxLinksPerPage; bool m_extLinks, m_intLinks, m_selfLinks ; QStringList m_urlPatternsIncluded; QString urlPattern; QStringList m_urlPatternsExcluded; QStringList m_linkClasses; QStringList::const_iterator constIterator; bool m_urlPatternAllowed; bool m_urlPatternNotAllowed; bool m_linkClassAllowed; }; class WebCrawler_Spider : public QObject { Q_OBJECT public: WebCrawler_Spider(); ~WebCrawler_Spider(); void load (const QString &seed, const int &maxNodes, const bool &delayedRequests); public slots: void get(); void httpFinished(QNetworkReply *reply); signals: void parse(QNetworkReply *reply); void finished (QString); private: QNetworkAccessManager *http; QNetworkRequest request; QNetworkReply *reply; QUrl currentUrl ; QString m_seed; int m_maxNodes; int m_visitedNodes; int m_wait_msecs; bool m_delayedRequests; }; #endif socnetv-2.4/src/dialogwebcrawler.h0000775000175000017500000000535613245520654017573 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogwebcrawler.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://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 = 0); public slots: void checkErrors (); void gatherData (); 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 &extLinks, const bool &intLinks, const bool &selfLinks, const bool &delayedRequests ); void webCrawlerDialogError(QString); private: Ui::DialogWebCrawler ui; QString seedUrl ; int maxLinksPerPage, maxUrlsToCrawl; bool extLinks, intLinks; QStringList linkClasses; QStringList urlPatternsIncluded; QStringList urlPatternsExcluded; }; #endif socnetv-2.4/src/application.qrc0000775000175000017500000000040113014570727017101 0ustar dimitrisdimitris editcopy.xpm editcut.xpm filenew.xpm fileopen.xpm editpaste.xpm filesave.xpm socnetv-2.4/src/dialognodeedit.cpp0000664000175000017500000001226113245520654017552 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialognodeedit.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 #include #include "dialognodeedit.h" DialogNodeEdit::DialogNodeEdit(QWidget *parent, const QString &l, const int &s, const QColor &col, const QString &sh) : QDialog(parent), ui(new Ui::DialogNodeEdit) { ui->setupUi(this); nodeSize = s; nodeColor = col; nodeShape = sh; nodeLabel = l; ui->labelEdit->setText(nodeLabel); ui->sizeSpin->setValue(nodeSize); if ( nodeShape == "box" ){ ui->boxRadio->setChecked (true); } else if ( nodeShape == "circle" ){ ui->circleRadio->setChecked (true); } else if ( nodeShape == "diamond" ){ ui->diamondRadio->setChecked (true); } else if ( nodeShape == "ellipse" ){ ui->ellipseRadio->setChecked (true); } else if ( nodeShape == "triangle" ){ ui->triangleRadio->setChecked (true); } pixmap = QPixmap(60,20) ; pixmap.fill(nodeColor); ui->colorButton->setIcon(QIcon(pixmap)); connect ( ui->buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui->labelEdit)->setFocus(); connect (ui->labelEdit, &QLineEdit::editingFinished, this, &DialogNodeEdit::checkErrors); connect (ui->colorButton, &QToolButton::clicked, this, &DialogNodeEdit::selectColor); } void DialogNodeEdit::gatherData(){ qDebug()<< " DialogNodeEdit::gatherData()" ; nodeLabel = ui->labelEdit->text(); nodeSize = ui->sizeSpin->value(); nodeValue = ui->valueEdit->text(); nodeShape = "circle"; if ( ui->boxRadio->isChecked () ){ nodeShape = "box"; } else if ( ui->circleRadio->isChecked() ){ nodeShape = "circle"; } else if ( ui->diamondRadio->isChecked() ){ nodeShape = "diamond"; } else if ( ui->ellipseRadio->isChecked() ){ nodeShape = "ellipse"; } else if ( ui->triangleRadio->isChecked() ){ nodeShape = "triangle"; } else if ( ui->starRadio->isChecked() ){ nodeShape = "star"; } else { nodeShape = "box"; } emit userChoices(nodeLabel,nodeSize,nodeValue,nodeColor,nodeShape); } 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); } //gatherData(); } 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; } socnetv-2.4/src/graphicsnodelabel.h0000775000175000017500000000415413245520654017717 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsnodelabel.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 socnetv-2.4/src/texteditor.cpp0000775000175000017500000002642613245520654017005 0ustar dimitrisdimitris/**************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt texteditor.cpp ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 "texteditor.h" TextEditor::TextEditor(const QString &fileName, QWidget *parent, const bool &format) : QMainWindow(parent), formatHTML(format) { qDebug("TextEditor()"); textEdit = new QTextEdit; setCentralWidget(textEdit); createActions(); createMenus(); createToolBars(); createStatusBar(); readSettings(); connect(textEdit->document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); resize( 1024,768 ); setWindowState(Qt::WindowMaximized|Qt::WindowActive); if (!fileName.isEmpty()) loadFile(fileName); else setCurrentFile(""); } void TextEditor::closeEvent(QCloseEvent *event) { if (maybeSave()) { writeSettings(); event->accept(); } else { event->ignore(); } } void TextEditor::newFile() { if (maybeSave()) { textEdit->clear(); setCurrentFile(""); } } void TextEditor::open() { if (maybeSave()) { QString fileName = QFileDialog::getOpenFileName(this); if (!fileName.isEmpty()) loadFile(fileName); } } bool TextEditor::save() { if (curFile.isEmpty()) { return saveAs(); } else { return saveFile(curFile); } } bool TextEditor::saveAs() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save file"), curFile); if (fileName.isEmpty()) return false; return saveFile(fileName); } void TextEditor::documentWasModified() { setWindowModified(textEdit->document()->isModified()); } void TextEditor::createActions() { newAct = new QAction(QIcon(":/images/new.png"), tr("&New"), this); newAct->setShortcut(tr("Ctrl+N")); newAct->setStatusTip(tr("Create a new file")); connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this); openAct->setShortcut(tr("Ctrl+O")); openAct->setStatusTip(tr("Open an existing file")); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction(QIcon(":/images/save.png"), tr("&Save"), this); saveAct->setShortcut(tr("Ctrl+S")); saveAct->setStatusTip(tr("Save the document to disk")); connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); saveAsAct = new QAction(tr("Save &As..."), this); saveAsAct->setStatusTip(tr("Save the document under a new name")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q")); exitAct->setStatusTip(tr("Exit the application")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this); cutAct->setShortcut(tr("Ctrl+X")); cutAct->setStatusTip(tr("Cut the current selection's contents to the " "clipboard")); connect(cutAct, SIGNAL(triggered()), textEdit, SLOT(cut())); copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Copy"), this); copyAct->setShortcut(tr("Ctrl+C")); copyAct->setStatusTip(tr("Copy the current selection's contents to the " "clipboard")); connect(copyAct, SIGNAL(triggered()), textEdit, SLOT(copy())); pasteAct = new QAction(QIcon(":/images/paste.png"), tr("&Paste"), this); pasteAct->setShortcut(tr("Ctrl+V")); pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " "selection")); connect(pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste())); aboutAct = new QAction(tr("&About"), this); aboutAct->setStatusTip(tr("Show the application's About box")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); aboutQtAct = new QAction(tr("About &Qt"), this); aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); cutAct->setEnabled(false); copyAct->setEnabled(false); connect(textEdit, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool))); connect(textEdit, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool))); } void TextEditor::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAct); fileMenu->addAction(openAct); fileMenu->addAction(saveAct); fileMenu->addAction(saveAsAct); fileMenu->addSeparator(); fileMenu->addAction(exitAct); editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(cutAct); editMenu->addAction(copyAct); editMenu->addAction(pasteAct); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); } void TextEditor::createToolBars() { fileToolBar = addToolBar(tr("File")); fileToolBar->addAction(newAct); fileToolBar->addAction(openAct); fileToolBar->addAction(saveAct); editToolBar = addToolBar(tr("Edit")); editToolBar->addAction(cutAct); editToolBar->addAction(copyAct); editToolBar->addAction(pasteAct); } void TextEditor::createStatusBar() { statusBar()->showMessage(tr("Ready")); } void TextEditor::readSettings() { QSettings settings("SocNetV", "TextEditor"); QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); QSize size = settings.value("size", QSize(400, 400)).toSize(); resize(size); move(pos); } void TextEditor::writeSettings() { QSettings settings("SocNetV ", "TextEditor"); settings.setValue("pos", pos()); settings.setValue("size", size()); } bool TextEditor::maybeSave() { if (textEdit->document()->isModified()) { QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, tr("TextEditor"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (ret == QMessageBox::Save) return save(); else if (ret == QMessageBox::Cancel) return false; } return true; } void TextEditor::loadFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(this, tr("SocNetV Editor"), tr("Cannot read file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } QTextStream in(&file); QApplication::setOverrideCursor(Qt::WaitCursor); if (formatHTML) textEdit->setHtml(in.readAll()); else textEdit->setPlainText(in.readAll()); QApplication::restoreOverrideCursor(); setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); file.close(); } bool TextEditor::saveFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("SocNetV Editor"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } QTextStream outText(&file); outText.setCodec("UTF-8"); QApplication::setOverrideCursor(Qt::WaitCursor); if (formatHTML) outText << textEdit->toHtml(); else outText << textEdit->toPlainText(); QApplication::restoreOverrideCursor(); setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); file.close(); return true; } void TextEditor::setCurrentFile(const QString &fileName) { curFile = fileName; textEdit->document()->setModified(false); setWindowModified(false); QString shownName; if (curFile.isEmpty()) shownName = "untitled.txt"; else shownName = strippedName(curFile); setWindowTitle(tr("%1[*] - %2").arg(shownName).arg(tr("SocNetV Editor"))); } QString TextEditor::strippedName(const QString &fullFileName) { return QFileInfo(fullFileName).fileName(); } //bool TextEditor::canInsertFromMimeData( const QMimeData *source ) const // { // if (source->hasImage()) // return true; // else // return QTextEdit::canInsertFromMimeData(source); // } //void TextEditor::insertFromMimeData( const QMimeData *source ) //{ // if (source->hasImage()) // { // QImage image = qvariant_cast(source->imageData()); // QTextCursor cursor = this->textCursor(); // QTextDocument *document = this->document(); // document->addResource(QTextDocument::ImageResource, QUrl("image"), image); // cursor.insertImage("image"); // } //} void TextEditor::about() { QMessageBox::about( this, "SocNetV Editor", " Part of Social Network Visualizer" "

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

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

This program is free software; you can redistribute it and/or modify" "
it under the terms of the GNU General Public License as published by" "
the Free Software Foundation; either version 3 of the License, or" "
(at your option) any later version.

" "

This program is distributed in the hope that it will be useful," "
but WITHOUT ANY WARRANTY; without even the implied warranty of" "
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" "
GNU General Public License for more details.

" "

You should have received a copy of the GNU General Public License" "
along with this program; if not, write to the Free Software" "
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

"); } socnetv-2.4/src/webcrawler.cpp0000775000175000017500000005750513245520654016751 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt webcrawler.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 "webcrawler.h" #include #include #include #include #include #include /* * frontier is our Url buffer (global) */ QQueue frontier; /** * @brief spider's constructor - does nothing */ WebCrawler_Spider::WebCrawler_Spider() { qDebug() << " wc_spider::WebCrawler_Spider() - thread() - " << thread(); } /** * @brief Called from Graph to init variables. * Connects http NetworkManager signal to httpfinished() slot * which in turn emits http reply to WebCrawler_Parser * @param url * @param maxN * @param maxLinksPerPage * @param extLinks * @param intLinks */ void WebCrawler_Spider::load(const QString &url, const int &maxN, const bool &delayedRequests) { qDebug() << " wc_spider::load() - thread() - " << thread() << "Initializing vars ..."; m_seed=url; // the initial url/domain we will crawl m_maxNodes=maxN; // maximum urls we'll check == max nodes in the resulted network m_delayedRequests = delayedRequests; // controls if we will wait between requests m_visitedNodes = 0; qDebug() << " wc_spider::load() - Creating http object"; http = new QNetworkAccessManager(this); qDebug() << " wc_spider::load() - Connecting http finished signal"; connect ( http, &QNetworkAccessManager::finished, this, &WebCrawler_Spider::httpFinished ); qDebug () << " wc_spider::load() - http->thread() " << http->thread() ; } /** * @brief Spider main functionality * Takes urls from frontier queue and downloads their HTML source code. * When http signals finished(), the httpFinished() slot passes the response data * to wc_parser thread parse() method to parse them */ void WebCrawler_Spider::get(){ qDebug() << " wc_spider::get() - "; //repeat forever.... do { // or until we crawl all urls in frontier. if (frontier.size() ==0 ) { qDebug () <<"## wc_spider::get() - Frontier is empty. " "No more urls to crawl. Breaking loop. ##" ; break; } // or until we have reached m_maxNodes if (m_maxNodes>0) { if (m_visitedNodes == m_maxNodes ) { qDebug () <<"## wc_spider::get() - Reached m_maxNodes!" << "STOP ##" ; emit finished("message from spider: visitedNodes > maxnodes. "); break; } } qDebug() << " wc_spider::get() - frontier size " << frontier.size() << " - Take the first url from frontier "; currentUrl = frontier.dequeue(); qDebug() << " wc_spider::get() - url: " << currentUrl << endl << "url not visited. " << "Increasing visitedNodes to" << m_visitedNodes + 1 << "Downloading html..."; //request = new QNetworkRequest; request.setUrl(currentUrl); request.setRawHeader( "User-Agent", "SocNetV harmless spider - see http://socnetv.org"); if (m_delayedRequests) { m_wait_msecs = rand() %1000; qDebug() << " wc_spider::get() - Sleeping for" << m_wait_msecs << "msecs"; QThread::msleep(m_wait_msecs); } qDebug() << " wc_spider::get() - calling http->get() for url" << currentUrl; qDebug() << " wc_spider::get() - http->thread():" << http->thread() ; QNetworkReply *reply = http->get(request) ; Q_UNUSED(reply); m_visitedNodes++; } while ( 1 ); qDebug() << "== wc_spider::get() - Finished infinite loop!"; } /** * @brief Called when QNetworkAccessManager http emits finished() * Emits parse with the reply object to WebCrawler_Parser::parse() * @param reply */ void WebCrawler_Spider::httpFinished(QNetworkReply *reply){ qDebug() << " wc_spider::httpFinished()"; emit parse (reply); } WebCrawler_Spider::~WebCrawler_Spider() { qDebug() << " wc_spider::~WebCrawler_Spider() - deleting http object"; m_visitedNodes = 0; delete http; http=0; qDebug() << " wc_spider::~WebCrawler_Spider() - deleting reply object"; } /** * @brief constructor called from parent thread - does nothing */ WebCrawler_Parser::WebCrawler_Parser() { qDebug () << " wc_parser::WebCrawler_Parser() - thread:" << thread(); } /** * @brief Called from parent Graph thread. Inits variables. * @param url * @param maxN * @param maxLinksPerPage * @param extLinks * @param intLinks */ void WebCrawler_Parser::load(const QString &url, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxN, const int &maxLinksPerPage, const bool &extLinks, const bool &intLinks, const bool &selfLinks) { qDebug () << " wc_parser::load() - thread():" << thread() << "Initializing variables "; // Initialize user-defined control variables and limits m_seed=QUrl(url); // the initial url/domain we will crawl if (m_seed.path() == "/") { m_seed.setPath(""); } m_urlPatternsIncluded = urlPatternsIncluded; // list of url patterns to include m_urlPatternsExcluded = urlPatternsExcluded; // list of url patterns to include m_linkClasses = linkClasses; // list of link classes to include m_maxNodes=maxN; // max urls we'll check == max nodes in the social network m_maxLinksPerPage = maxLinksPerPage; // max links per page to search for m_extLinks = extLinks; m_intLinks = intLinks; m_selfLinks = selfLinks; //clear global variables frontier.clear(); // the Url buffer (global) ba.clear(); // the byte array which stores the network reply knownUrls.clear(); // a map of all known urls m_discoveredNodes=0; // a counter of discovered nodes m_discoveredNodes++; frontier.enqueue(m_seed); knownUrls[m_seed]=m_discoveredNodes; qDebug() << " wc_parser::load() - seed:" << m_seed << " seed_host: " << m_seed.host() << " Added seed to frontier queue and knownUrls map. " << " Node " << m_discoveredNodes << " should be already created. " << " m_maxNodes " << m_maxNodes << " m_maxLinksPerPage " << m_maxLinksPerPage << " m_extLinks " << m_extLinks << " m_intLinks " << m_intLinks; } /** * @brief Called from WebCrawler_Spider::parse() signal when http has finished. * This method does the actual parsing of each page's html source from the reply bytearray. * First, we start by reading all from http reply to a QString called page. * Then we parse the page string, searching for url substrings. * @param reply */ void WebCrawler_Parser::parse(QNetworkReply *reply){ qDebug () << " wc_parser::parse() - thread:" << this->thread(); // Find to which node the response HTML belongs to // Get this from the reply object request method QUrl requestUrl = reply->request().url(); QString requestUrlStr = requestUrl.toString(); QString locationHeader = reply->header(QNetworkRequest::LocationHeader).toString(); int sourceNode = knownUrls [ requestUrl ]; QString host = requestUrl.host(); QString path = requestUrl.path(); qDebug() << " wc_parser::parse() - HTML of url " << requestUrlStr << " sourceNode " << sourceNode; qDebug() << " wc_parser::parse() - host " << host << " path " << path; // qDebug () << " wc_parser::parse() - original locationHeader" // << reply->header(QNetworkRequest::LocationHeader) ; // qDebug () << " wc_parser::parse() - decoded locationHeader" << locationHeader ; // qDebug () << " wc_parser::parse() - encoded requestUrl " << requestUrl; // qDebug () << " wc_parser::parse() - decoded requestUrl " << requestUrlStr; // Check for redirects if ( locationHeader != "" && locationHeader != requestUrlStr ) { qDebug () << "&& wc_parser::parse() Location response header " << locationHeader << " differs from requestUrl " << requestUrlStr << " Creating node redirect - Creating edge - RETURN "; newLink( sourceNode, locationHeader , true ); return; } QUrl newUrl; QString newUrlStr; int start=-1, end=-1, equal=-1 , invalidUrlsInPage =0; // index=-1; int validUrlsInPage = 0; ba=reply->readAll(); // read all data from the reply into a bytearray QString page(ba); // construct a QString from the bytearray QString md5(QCryptographicHash::hash(ba,QCryptographicHash::Md5).toHex()); qDebug () << " wc_parser::parse() - MD5 sum:" << md5.toLocal8Bit(); // If there are no links inside the HTML source, return if (!page.contains ("href")) { //FIXME: Frameset pages are not parsed! See docs/manual.html for example. qDebug() << "## wc_parser::parse() - Empty or not useful html from " << requestUrl << " page size " << page.size() << " \npage contents: " << page << " RETURN ##"; return; } // qDebug() << " \npage contents: " << page << endl << endl; // Delete html head qDebug() << " wc_parser::parse() - Erasing "; start=page.indexOf (""); //Find head pos if ( start != -1 && end != -1 ) { page = page.remove(start, end); //erase head } else if ( start == -1 ) { qDebug() << " wc_parser::parse() - ERROR IN locating tag start"; } else if ( end == -1 ) { qDebug() << " wc_parser::parse() - ERROR IN locating tag end"; } // Delete all scripts from page source while (page.contains(""); //Find pos if ( start != -1 && end != -1 ) { page = page.remove(start, end); //erase head } } // Main Loop: While there are more links in the page, parse them while (page.contains("href")) { if (m_maxNodes>0) { if (m_discoveredNodes >= m_maxNodes ) { qDebug () <<"!! wc_parser::parse() - Reached m_maxNodes! STOP "; emit finished("message from parse() - discoveredNodes > maxNodes"); return; } } // remove whitespace from the start and the end // all whitespace sequence becomes single space page=page.simplified(); start=page.indexOf ("href"); //Find its pos page = page.remove(0, start); //erase everything up to href equal=page.indexOf ("="); // Find next equal sign (=) page = page.remove(0, equal+1); //Erase everything up to = if (page.startsWith("\"") ) { page.remove(0,1); end=page.indexOf ("\""); } else if (page.startsWith("\'") ){ page.remove(0,1); end=page.indexOf ("\'"); } else { //end=page.indexOf ("\'"); } newUrlStr=page.left(end); //Save new url to newUrl :) newUrlStr=newUrlStr.simplified(); newUrl = QUrl(newUrlStr); if (!newUrl.isValid()) { invalidUrlsInPage ++; qDebug() << " wc_parser::parse() - found INVALID newUrl " << newUrl.toString() << " in page " << requestUrlStr << " Will CONTINUE only if invalidUrlsInPage < 200"; if (invalidUrlsInPage > 200) { qDebug() << " wc_parser::parse() - INVALID newUrls > 200"; emit finished("invalidUrlsInPage > 200"); return; } continue; } // TODO - REMOVE LAST / FROM EVERY PATH NOT ONLY ROOT PATH if (newUrl.path() == "/") { newUrl.setPath(""); } qDebug() << "@@ wc_parser::parse() - found VALID newUrl: " << newUrl.toString(); newUrlStr = newUrl.toString(); // Skip css, favicon, rss, ping, etc if ( newUrlStr.startsWith("#", Qt::CaseInsensitive) || newUrlStr.endsWith("feed/", Qt::CaseInsensitive) || newUrlStr.endsWith("rss/", Qt::CaseInsensitive) || newUrlStr.endsWith("atom/", Qt::CaseInsensitive) || newUrl.fileName().endsWith("xmlrpc.php", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".xml", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".ico", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".gif", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".png", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".jpg", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".js", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".css", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".rsd", Qt::CaseInsensitive) ) { qDebug()<< "!! wc_parser::parse() - # newUrl " << " seems a page resource or anchor (rss, favicon, etc) " << "Skipping..."; continue; } // Check if newUrl is compatible with the url patterns the user asked for m_urlPatternAllowed = true; for (constIterator = m_urlPatternsIncluded.constBegin(); constIterator != m_urlPatternsIncluded.constEnd(); ++constIterator) { //qDebug() << (*constIterator).toLocal8Bit().constData() << endl; urlPattern = (*constIterator).toLocal8Bit().constData(); if (urlPattern.isEmpty()) continue; if ( newUrl.toString().contains( urlPattern ) ) { qDebug() << "-- wc_parser::parse() - newUrl in allowed url patterns:" << urlPattern <<"Parsing"; break; } else { qDebug() << "!! wc_parser::parse() - newUrl not in allowed url patterns. CONTINUE "; m_urlPatternAllowed = false; } } m_urlPatternNotAllowed = false; for (constIterator = m_urlPatternsExcluded.constBegin(); constIterator != m_urlPatternsExcluded.constEnd(); ++constIterator) { //qDebug() << (*constIterator).toLocal8Bit().constData() << endl; urlPattern = (*constIterator).toLocal8Bit().constData(); if (urlPattern.isEmpty()) continue; if ( newUrl.toString().contains( urlPattern ) ) { qDebug() << "!! wc_parser::parse() - newUrl in excluded url patterns:" << urlPattern << "CONTINUE "; m_urlPatternNotAllowed = true; break; } else { qDebug() << "-- wc_parser::parse() - newUrl not in excluded url patterns. Parsing"; } } if (m_urlPatternAllowed && !m_urlPatternNotAllowed) { if ( newUrl.isRelative() ) { newUrl = requestUrl.resolved(newUrl); newUrlStr = newUrl.toString(); qDebug() << " wc_parser::parse() - RELATIVE url" << " host: " << host << " resolved url " << newUrl.toString(); if (!m_intLinks ){ qDebug()<< " wc_parser::parse() - Internal urls forbidden." << " SKIPPING node creation"; continue; } if (requestUrl.path() == newUrl.path()) { if (m_selfLinks) { qDebug()<< " wc_parser::parse() - " << " requestUrl.path() = newUrl.path()" << " Creating self link"; newLink(sourceNode, newUrl, false); } else { qDebug()<< " wc_parser::parse() - " << "requestUrl.path() = newUrl.path()" << "Self links not allowed. CONTINUE."; } } else { qDebug()<< " wc_parser::parse() - Internal links allowed." << " Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } else { qDebug() << " wc_parser::parse() - ABSOLUTE url."; if ( newUrl.scheme() != "http" && newUrl.scheme() != "https" && newUrl.scheme() != "ftp" && newUrl.scheme() != "ftps") { qDebug() << " wc_parser::parse() - found INVALID newUrl SCHEME" << newUrl.toString(); continue; } if ( newUrl.host() != host ) { qDebug()<< " wc_parser::parse() - absolute newUrl EXTERNAL."; if ( !m_extLinks ) { qDebug()<< " wc_parser::parse() - External urls forbidden." <<" Creating new node but NOT ADDING it to frontier..."; newLink(sourceNode, newUrl, false); } else { qDebug()<< " wc_parser::parse() - External urls allowed." <<" Creating new node and ADDING it to frontier..."; newLink(sourceNode, newUrl, true); } } else { qDebug()<< " wc_parser::parse() - absolute newUrl INTERNAL."; if (!m_intLinks){ qDebug()<< " wc_parser::parse() - Internal urls forbidden." << " SKIPPING node creation"; continue; } if ( newUrl.path () == path ) { qDebug()<< " wc_parser::parse() - " << "requestUrl.path() = newUrl.path()" << "Self links not allowed. CONTINUE."; continue; } qDebug()<< " wc_parser::parse() - Internal urls allowed." <<" Creating new node and ADDING it to frontier..."; newLink(sourceNode, newUrl, true); } } } validUrlsInPage ++; qDebug() << " wc_parser::parse() - validUrlsInPage " << validUrlsInPage; // If the user has specified a maxLinksPerPage limit then, // if we have reached it, stop parsing this page if ( m_maxLinksPerPage != 0 ) { if ( validUrlsInPage > m_maxLinksPerPage ) { qDebug () <<"!! wc_spider::parse() Reached m_maxLinksPerPage " <0) { if (m_discoveredNodes >= m_maxNodes ) { qDebug () <<"## wc_parser::newLink() - We have reached m_maxNodes!" << " - STOP! ###" ; emit finished("maxpages from newLink"); return; } } // check if the new url has been discovered previously QMap::const_iterator index = knownUrls.find(target); if ( index!= knownUrls.end() ) { qDebug()<< "-- wc_parser::newLink() - target already discovered " << " in knownUrls as node:" << index.value(); if (s !=index.value()) { qDebug()<< "-- wc_parser::newLink() - creating link" << s << "->" << index.value() << "then RETURN."; emit signalCreateEdge (s, index.value() ); } else { qDebug()<< "-- wc_parser::newLink() - Self links not allowed. RETURN."; } return; } m_discoveredNodes++; knownUrls[target]=m_discoveredNodes; emit signalCreateNode( m_discoveredNodes, target.toString(), false); qDebug()<< "** wc_parser::newLink() - Creating node " << m_discoveredNodes << " url "<< target.toString(); if (enqueue_to_frontier) { frontier.enqueue(target); qDebug()<< "** wc_parser::newLink() - Enqueuing new node to frontier " << " frontier size: "<< frontier.size(); emit startSpider(); } else { qDebug()<< "## wc_parser::newLink() - NOT enqueuing to frontier"; } qDebug()<< "-- wc_parser::newLink() - Creating edge from " << s << " to " << m_discoveredNodes; emit signalCreateEdge (s, m_discoveredNodes); } WebCrawler_Parser::~WebCrawler_Parser() { qDebug() << " wc_parser::~WebCrawler_Parser() - clearing vars"; ba.clear(); frontier.clear(); knownUrls.clear(); m_urlPatternsIncluded.clear(); urlPattern=""; m_urlPatternsExcluded.clear(); m_linkClasses.clear(); m_discoveredNodes = 0; } socnetv-2.4/src/dialogsettings.cpp0000664000175000017500000006444113245520654017626 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogsettings.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 DialogSettings::DialogSettings( QMap &appSettings, QWidget *parent) : QDialog(parent), m_appSettings(appSettings), 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 ); //debugging ui->printDebugChkBox->setChecked( (appSettings["printDebug"] == "true") ? true:false ); ui->progressDialogChkBox->setChecked( (appSettings["showProgressBar"] == "true") ? true:false ); /** * 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)); if (m_appSettings["initNodeShape"] == "box") { ui->nodeShapeRadioBox->setChecked(true); } else if (m_appSettings["initNodeShape"] == "circle") { ui->nodeShapeRadioCircle->setChecked(true); } else if (m_appSettings["initNodeShape"] == "diamond") { ui->nodeShapeRadioDiamond->setChecked(true); } else if (m_appSettings["initNodeShape"] == "ellipse") { ui->nodeShapeRadioEllipse->setChecked(true); } else if (m_appSettings["initNodeShape"] == "triangle") { ui->nodeShapeRadioTriangle->setChecked(true); } else if (m_appSettings["initNodeShape"] == "star") { ui->nodeShapeRadioStar->setChecked(true); } else { // default ui->nodeShapeRadioCircle->setChecked(true); } 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->printLogoChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setPrintLogo); 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->nodeShapeRadioBox, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioCircle, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioDiamond, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioEllipse, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioTriangle, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioStar, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); 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; } } /** * @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 DialogSettings::getNodeShape */ void DialogSettings::getNodeShape(){ QString nodeShape; if ( ui->nodeShapeRadioBox->isChecked () ){ m_appSettings["initNodeShape"] = "box"; } else if ( ui->nodeShapeRadioCircle->isChecked() ){ m_appSettings["initNodeShape"] = "circle"; } else if ( ui->nodeShapeRadioDiamond->isChecked() ){ m_appSettings["initNodeShape"] = "diamond"; } else if ( ui->nodeShapeRadioEllipse->isChecked() ){ m_appSettings["initNodeShape"] = "ellipse"; } else if ( ui->nodeShapeRadioTriangle->isChecked() ){ m_appSettings["initNodeShape"] = "triangle"; } else if ( ui->nodeShapeRadioStar->isChecked() ){ m_appSettings["initNodeShape"] = "star"; } else { m_appSettings["initNodeShape"] = "box"; } qDebug()<< "DialogSettings::getNodeShape() - new default shape " << nodeShape; emit setNodeShape(m_appSettings["initNodeShape"], 0); } /** * @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(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; } socnetv-2.4/src/dialogranderdosrenyi.h0000664000175000017500000000455013245520654020456 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogranderdosrenyi.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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=0, const float eprob = 0); public slots: void checkErrors(); void gatherData(); void gnmModel(); void gnpModel(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int nodes, const QString model, const int edges, const float eprob, const QString mode, const bool diag); private: QString model; QString mode; int nodes, edges; bool diag; Ui::DialogRandErdosRenyi ui; }; #endif socnetv-2.4/src/parser.cpp0000775000175000017500000050014413245520654016100 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt parser.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 "parser.h" #include #include #include #include #include #include #include //used for qDebug messages #include #include #include #include #include // used as list listDummiesPajek #include //for priority queue #include "graph.h" //needed for setParent using namespace std; Parser::Parser() { qDebug() << "Parser::Parser() - running on thread " << this->thread() ; } Parser::~Parser () { qDebug()<< "**** Parser::~Parser() destructor " << this->thread() <<" clearing hashes... "; nodeHash.clear(); keyFor.clear(); keyName.clear(); keyType.clear(); keyDefaultValue.clear(); edgesMissingNodesHash.clear(); edgeMissingNodesList.clear(); edgeMissingNodesListData.clear(); firstModeMultiMap.clear(); secondModeMultiMap.clear(); if (xml!=0) { qDebug()<< "**** Parser::~Parser() clearing xml reader object " ; xml->clear(); delete xml; xml=0; } } /** * @brief Loads the network calling one of the load* methods */ void Parser::load(const QString fn, const QString m_codec, const int iNS, const QString iNC, const QString iNSh, const QString iNNC, const int iNNS, const QString iNLC, const int iNLS , const QString iEC, const int width, const int height, const int fFormat, const int sm_mode, const QString delim) { qDebug()<< "**** Parser::load() - On a new thread " << this->thread(); initNodeSize=iNS; initNodeColor=iNC; initNodeShape=iNSh; initNodeNumberColor=iNNC; initNodeNumberSize=iNNS; initNodeLabelColor=iNLC; initNodeLabelSize=iNLS; initEdgeColor=iEC; edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; fileName=fn; userSelectedCodecName = m_codec; networkName=(fileName.split ("/")).last(); gwWidth=width; gwHeight=height; randX=0; randY=0; fileFormat= fFormat; two_sm_mode = sm_mode; if (!delim.isNull() && !delim.isEmpty()) delimiter = delim; else delimiter=" "; xml=0; errorMessage=QString::null; qDebug()<< "**** Parser::load() - networkName "<< networkName << " fileFormat "<< fileFormat << "delim" << delim << "delimiter"<thread() << "Reached end. " "Emitting finished() calling loadFileError() if any" << " fileFormat now "<< fileFormat ; emit finished ("Parser::load() - reach end"); } /** * @brief Parser::loadFileError * @param errorMessage * Called when some Parser method cannot read or parse correctly a file. * It informs the Graph and then MW with an error message */ void Parser::loadFileError(const QString &errorMessage) { qDebug()<<"Parser::loadFileError() - errorMessage:" <0){ qDebug() << "Parser::loadDL() - we are at source 1. " "Checking relationList"; relation = relationsList[ relationCounter ]; qDebug() << "Parser::loadDL() - " "WE ARE THE FIRST DATASET/MATRIX" << " source node counter is " << source << " and relation to " << relation<< ": " << relationCounter; emit relationSet (relationCounter); } else if (source>totalNodes) { source=1; relationCounter++; relation = relationsList[ relationCounter ]; qDebug() << "Parser::loadDL() - " "LOOKS LIKE WE ENTERED A NEW DATASET/MATRIX " << " init source node counter to " << source << " and relation to " << relation << ": " << relationCounter; emit relationSet (relationCounter); } else { qDebug() << "Parser::loadDL() - source node counter is " << source; } for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { //qDebug()<< (*it1).toLatin1() ; if ( (*it1)!="0"){ edgeWeight=(*it1).toFloat(&floatOK); qDebug() << "Parser::loadDL() - relation " << relationCounter << " found edge from " << source << " to " << target << " weight " << edgeWeight << " emitting edgeCreate() to parent" ; emit edgeCreate( source, target, edgeWeight, initEdgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; qDebug() << "Parser::loadDL() - TotalLinks= " << totalLinks; } target++; } // end for source++; } else { // two-mode target=NR+1; qDebug() << "Parser::loadDL() - this is a two-mode fullmatrix file. " "Splitting str to elements:"; lineElement=str.split(QRegExp("\\s+"), QString::SkipEmptyParts); qDebug()<< "Parser::loadDL() - lineElement:" << lineElement; if (lineElement.count() != NC) { qDebug() << "Parser::loadDL() - Not a two-mode fullmatrix UCINET " "formatted file. Aborting!!"; file.close(); //emit something... errorMessage = tr("UCINET two-mode fullmatrix file declared ") + QString::number(NC) + tr(" columns initially, " "but I found a different number ") + QString::number(lineElement.count()) + tr(" of matrix columns"); return false; } for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { //qDebug()<< (*it1).toLatin1() ; if ( (*it1)!="0"){ edgeWeight=(*it1).toFloat(&floatOK); qDebug() << "Parser::loadDL() - relation " << relationCounter << " found edge from " << source << " to " << target << " weight " << edgeWeight << " emitting edgeCreate() to parent" ; emit edgeCreate( source, target, edgeWeight, initEdgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; qDebug() << "Parser::loadDL() - TotalLinks= " << totalLinks; } target++; } // end for source++; } } // END FULLMATRIX FORMAT READING if (edgelist1Format) { // read edges in edgelist1 format lineElement=str.split(QRegExp("\\s+"), QString::SkipEmptyParts); qDebug() << "Parser::loadDL() - edgelist str line:"<< str; qDebug() << "Parser::loadDL() - edgelist data element:"<< lineElement; if ( lineElement.count() != 3 ) { qDebug() << "Parser::loadDL() - Not an edgelist1 UCINET " "formatted file. Aborting!!"; file.close(); //emit something... errorMessage = tr("UCINET file declared as edgelist but I found " "a line which did not have 3 elements (source, target, weight)"); return false; } source = (lineElement[0]).toInt(&intOK); target = (lineElement[1]).toInt(&intOK); qDebug() << "Parser::loadDL() - source node " << source << " target node " << target; edgeWeight=(lineElement[2]).toDouble(&intOK); if (intOK) { qDebug() << "Parser::loadDL() -list file declares edge weight: " << edgeWeight; } else { edgeWeight=1.0; qDebug () << " list file NOT declaring edge weight. Setting default: " << edgeWeight; } qDebug() << "Parser::loadDL() - Creating link " << source << " -> "<< target << " weight= "<< edgeWeight << " TotalLinks= " << totalLinks+1; emit edgeCreate(source, target, edgeWeight, initEdgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; } // END edgelist1 format reading. } // end if data_flag } // end while there are more lines //sanity check if (!twoMode_flag && nodeSum != totalNodes) { qDebug()<< "Error: aborting"; //emit something errorMessage = tr("UCINET declared ") + QString::number(totalNodes) + tr(" actors initially, " "but I found a different number ") + QString::number(nodeSum) + tr(" of node labels"); return false; } if (relationsList.count() == 0) { emit addRelation("unnamed"); } //The network has been loaded. Tell MW the statistics and network type emit relationSet (0); qDebug() << "Parser::loadDL() - FINISHED - " "emitting networkFileLoaded() and clearing arrays"; emit networkFileLoaded(FILE_UCINET, fileName, networkName, totalNodes, totalLinks, EDGE_DIRECTED); lineElement.clear(); tempList.clear(); rowLabels.clear(); colLabels.clear(); relationsList.clear(); return true; } bool Parser::readDLKeywords(QStringList &strList, int &N, int &NM, int &NR, int &NC, bool &fullmatrixFormat, bool &edgelist1Format){ QStringList tempList; QString tempStr=QString::null; QString label=QString::null; QString value=QString::null; bool intOK=false; for (QStringList::Iterator it1 = strList.begin(); it1!=strList.end(); ++it1) { tempStr = (*it1); qDebug() << "Parser::readDLKeywords() - element:" << tempStr.toLatin1() ; if ( tempStr.startsWith("DL", Qt::CaseInsensitive )){ // remove DL tempStr.remove("DL",Qt::CaseInsensitive); tempStr=tempStr.simplified(); qDebug() << "Parser::readDLKeywords() - element contained DL. Removed it:" << tempStr; } // check if this element contains a "=" if ( tempStr.count() > 0 ) { if (tempStr.contains("=",Qt::CaseInsensitive)) { qDebug() << "Parser::readDLKeywords() - splitting element at = sign"; tempList = tempStr.split("=", QString::SkipEmptyParts); label = tempList[0].simplified(); value= tempList[1].simplified(); if ( label == "n" || label == "N" ) { qDebug() << "Parser::readDLKeywords() - N is declared to be : " << value ; N=value.toInt(&intOK,10); if (!intOK) { qDebug() << "Parser::loadDL() - N conversion error..." ; //emit something here... errorMessage = tr("Cannot convert N value to integer"); return false; } } else if ( label == "nm" || label == "NM" ) { qDebug() << "Parser::readDLKeywords() - NM is declared to be : " << value ; NM = value.toInt(&intOK,10); if (!intOK) { qDebug() << "Parser::readDLKeywords() - NM conversion error..." ; //emit something here... errorMessage = tr("Cannot convert NM value to integer"); return false; } } else if ( label == "nr" || label == "NR" ) { qDebug() << "Parser::readDLKeywords() - NR is declared to be : " << value ; NR = value.toInt(&intOK,10); if (!intOK) { qDebug() << "Parser::readDLKeywords() - NR conversion error..." ; //emit something here... errorMessage = tr("Cannot convert NR value to integer"); return false; } } else if ( label == "nc" || label == "NC" ) { qDebug() << "Parser::readDLKeywords() - NC is declared to be : " << value ; NC = value.toInt(&intOK,10); if (!intOK) { qDebug() << "Parser::readDLKeywords() - NC conversion error..." ; //emit something here... errorMessage = tr("Cannot convert NC value to integer"); return false; } } else if ( label == "format" || label == "FORMAT" ) { qDebug() << "Parser::readDLKeywords() - FORMAT is declared to be : " << value ; if (value.contains("FULLMATRIX",Qt::CaseInsensitive)) { fullmatrixFormat=true; qDebug() << "Parser::readDLKeywords() - FORMAT fullmatrix detected" ; } else if (value.contains("edgelist",Qt::CaseInsensitive) ){ edgelist1Format=true; qDebug() << "Parser::readDLKeywords() - FORMAT edgelist detected" ; } } // end format } // end if contains = else { return false; } } // end if > 0 } // end for lineElement return true; } /** Tries to load the file as Pajek-formatted network. If not it returns -1 */ bool Parser::loadPajek(){ qDebug ("\n\nParser: loadPajek"); QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) { errorMessage = tr("Cannot open Pajek file"); return false; } QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QString str, label, temp; nodeColor=""; edgeColor=""; nodeShape=""; initEdgeLabel = QString::null; QStringList lineElement; bool ok=false, intOk=false, check1=false, check2=false; bool has_arcs=false; bool nodes_flag=false, edges_flag=false, arcs_flag=false, arcslist_flag=false, matrix_flag=false; fileContainsNodeColors=false; fileContainsNodeCoords=false; fileContainsLinkColors=false; fileContainsLinkLabels=false; bool zero_flag=false; int i=0, j=0, miss=0, source= -1, target=-1, nodeNum, colorIndex=-1; int coordIndex=-1, labelIndex=-1; unsigned long int lineCounter=0; int pos=-1, relationCounter=0; float weight=1; QString relation; list listDummiesPajek; totalLinks=0; totalNodes=0; j=0; //counts how many real nodes exist in the file miss=0; //counts missing nodeNumbers. //if j + miss < nodeNum, it creates (nodeNum-miss) dummy nodes which are deleted in the end. relationsList.clear(); while ( !ts.atEnd() ) { str= ts.readLine(); str = str.simplified(); if ( isComment(str) ) continue; lineCounter++; qDebug()<< "*** Parser:loadPajek(): " << str; if (lineCounter==1) { if ( str.startsWith("graph",Qt::CaseInsensitive) || str.startsWith("digraph",Qt::CaseInsensitive) || str.startsWith("DL",Qt::CaseInsensitive) || str.startsWith("list",Qt::CaseInsensitive) || str.startsWith("graphml",Qt::CaseInsensitive) || str.startsWith(" 0) { qDebug () << "Parser::loadPajek() relationCounter " << relationCounter << "emitting relationSet"; emit relationSet(relationCounter); i=0; // reset the source node index } relationCounter++; } continue; } else if ( str.contains( "*arcslist", Qt::CaseInsensitive) ) { arcs_flag=false; edges_flag=false; arcslist_flag=true; matrix_flag=false; continue; } else if ( str.contains( "*matrix", Qt::CaseInsensitive) ) { qDebug() << str ; arcs_flag=false; edges_flag=false; arcslist_flag=false; matrix_flag=true; //check if row has label for matrix data, // and use it as relation name if ( (pos = str.indexOf(":")) != -1 ) { relation = str.right(str.size() - pos -1) ; relation = relation.simplified(); qDebug() << "Parser::loadPajek() - adding relation "<< relation << " to relationsList and emitting addRelation "; relationsList << relation; emit addRelation( relation ); if (relationCounter > 0) { qDebug () << "Parser::loadPajek() relationCounter " << relationCounter << "emitting relationSet"; emit relationSet(relationCounter); i=0; // reset the source node index } relationCounter++; } continue; } /** READING NODES */ if (!edges_flag && !arcs_flag && !arcslist_flag && !matrix_flag) { //qDebug("=== Reading nodes ==="); nodes_flag=true; nodeNum=lineElement[0].toInt(&intOk, 10); //qDebug()<<"node number: "< nodeNum ) { qDebug ("Error: This Pajek net declares this node with nodeNumber smaller than previous nodes. Aborting"); errorMessage = tr("Pajek-formatted file declares a node with " "nodeNumber smaller than previous nodes."); return false; } qDebug ()<<"emitting createNode()"; emit createNode( nodeNum,initNodeSize, nodeColor, initNodeNumberColor, initNodeNumberSize, label, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), nodeShape, false ); initNodeColor=nodeColor; } // NODES CREATED. CREATE EDGES/ARCS NOW. // first check that all nodes are already created else { if (j && j!=totalNodes) { //if there were more or less nodes than the file declared qDebug()<<"*** WARNING ***: The Pajek file declares " << totalNodes <<" nodes, but I found " << j << " nodes...." ; totalNodes=j; } else if (j==0) { //if there were no nodes at all, we need to create them now. qDebug()<< "The Pajek file declares "<< totalNodes<< " but I didnt found any nodes. I will create them...."; for (int num=j+1; num<= totalNodes; num++) { qDebug() << "Parser-loadPajek(): Creating node number i = "<< num; randX=rand()%gwWidth; randY=rand()%gwHeight; emit createNode( num,initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, QString::number(i), initNodeLabelColor,initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } j=totalNodes; } if (edges_flag && !arcs_flag) { /**EDGES */ qDebug("Parser-loadPajek(): ==== Reading edges ===="); qDebug()< 0."); return false; // i --> (i-1) internally } else if (source < 0 && target >0 ) { //weights come first... edgeWeight = lineElement[0].toFloat(&ok); source= lineElement[1].toInt(&ok, 10); if (lineElement.count()>2) { target = lineElement[2].toInt(&ok,10); } else { target = lineElement[1].toInt(&ok,10); //self link } } else if (lineElement.count()>2) edgeWeight =lineElement[2].toFloat(&ok); else edgeWeight =1.0; //qDebug()<<"Parser-loadPajek(): weight "<< weight; if (lineElement.contains("c", Qt::CaseSensitive ) ) { //qDebug("Parser-loadPajek(): file with link colours"); fileContainsLinkColors=true; colorIndex=lineElement.indexOf( QRegExp("[c]"), 0 ) + 1; if (colorIndex >= lineElement.count()) edgeColor=initEdgeColor; else edgeColor=lineElement [ colorIndex ]; if (edgeColor.contains (".") ) edgeColor=initEdgeColor; //qDebug()<< " current color "<< edgeColor; } else { //qDebug("Parser-loadPajek(): file with no link colours"); edgeColor=initEdgeColor; } if (lineElement.contains("l", Qt::CaseSensitive ) ) { qDebug("Parser-loadPajek(): file with link labels"); fileContainsLinkLabels=true; labelIndex=lineElement.indexOf( QRegExp("[l]"), 0 ) + 1; if (labelIndex >= lineElement.count()) edgeLabel=initEdgeLabel; else edgeLabel=lineElement [ labelIndex ]; if (edgeLabel.contains (".") ) edgeLabel=initEdgeLabel; qDebug()<< " edge label "<< edgeLabel; } else { //qDebug("Parser-loadPajek(): file with no link labels"); edgeLabel=initEdgeLabel; } arrows=false; bezier=false; qDebug()<< "Parser-loadPajek(): EDGES: Create edge between " << source << " - "<< target; emit edgeCreate(source, target, edgeWeight, edgeColor, EDGE_UNDIRECTED, arrows, bezier, edgeLabel); totalLinks=totalLinks+2; } //end if EDGES else if (!edges_flag && arcs_flag) { /** ARCS */ //qDebug("Parser-loadPajek(): === Reading arcs ==="); source= lineElement[0].toInt(&ok, 10); target = lineElement[1].toInt(&ok,10); if (source == 0 || target == 0 ) { errorMessage = tr("Pajek-formatted file declares arc " "with a zero source or target nodeNumber. " "Each node should have a nodeNumber > 0."); return false; // i --> (i-1) internally } else if (source < 0 && target >0 ) { //weights come first... edgeWeight = lineElement[0].toFloat(&ok); source= lineElement[1].toInt(&ok, 10); if (lineElement.count()>2) { target = lineElement[2].toInt(&ok,10); } else { target = lineElement[1].toInt(&ok,10); //self link } } else if (lineElement.count()>2) edgeWeight =lineElement[2].toFloat(&ok); else edgeWeight =1.0; if (lineElement.contains("c", Qt::CaseSensitive ) ) { //qDebug("Parser-loadPajek(): file with link colours"); edgeColor=lineElement.at ( lineElement.indexOf( QRegExp("[c]"), 0 ) + 1 ); fileContainsLinkColors=true; } else { //qDebug("Parser-loadPajek(): file with no link colours"); edgeColor=initEdgeColor; } if (lineElement.contains("l", Qt::CaseSensitive ) ) { qDebug("Parser-loadPajek(): file with link labels"); fileContainsLinkLabels=true; labelIndex=lineElement.indexOf( QRegExp("[l]"), 0 ) + 1; if (labelIndex >= lineElement.count()) edgeLabel=initEdgeLabel; else edgeLabel=lineElement.at ( labelIndex ); //if (edgeLabel.contains (".") ) edgeLabel=initEdgeLabel; qDebug()<< " edge label "<< edgeLabel; } else { //qDebug("Parser-loadPajek(): file with no link labels"); edgeLabel=initEdgeLabel; } arrows=true; bezier=false; has_arcs=true; qDebug()<<"Parser-loadPajek(): ARCS: Creating arc from node "<< source << " to node "<< target << " with weight "<< weight; emit edgeCreate(source, target, edgeWeight , edgeColor, EDGE_DIRECTED, arrows, bezier, edgeLabel); totalLinks++; } //else if ARCS else if (arcslist_flag) { /** ARCSlist */ //qDebug("Parser-loadPajek(): === Reading arcs list==="); if (lineElement[0].startsWith("-") ) lineElement[0].remove(0,1); source= lineElement[0].toInt(&ok, 10); fileContainsLinkColors=false; edgeColor=initEdgeColor; has_arcs=true; arrows=true; bezier=false; for (int index = 1; index < lineElement.size(); index++) { target = lineElement.at(index).toInt(&ok,10); qDebug()<<"Parser-loadPajek(): ARCS LIST: Creating ARC source "<< source << " target "<< target << " with weight "<< weight; emit edgeCreate(source, target, edgeWeight, edgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; } } //else if ARCSLIST else if (matrix_flag) { /** matrix */ //qDebug("Parser-loadPajek(): === Reading matrix of edges==="); i++; source= i; fileContainsLinkColors=false; edgeColor=initEdgeColor; has_arcs=true; arrows=true; bezier=false; for (target = 0; target < lineElement.size(); target ++) { if ( lineElement.at(target) != "0" ) { edgeWeight = lineElement.at(target).toFloat(&ok); qDebug()<<"Parser-loadPajek(): MATRIX: Creating arc source " << source << " target "<< target +1 << " with weight "<< weight; emit edgeCreate(source, target+1, edgeWeight, edgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; } } } //else if matrix } //end if BOTH ARCS AND EDGES } //end WHILE file.close(); if (j==0) { errorMessage = tr("Could not find node declarations in this Pajek-formatted file."); return false; } qDebug("Parser-loadPajek(): Removing all dummy nodes, if any"); if (listDummiesPajek.size() > 0 ) { qDebug("Trying to delete the dummies now"); for ( list::iterator it=listDummiesPajek.begin(); it!=listDummiesPajek.end(); it++ ) { emit removeDummyNode(*it); } } if (relationsList.count() == 0) { emit addRelation(networkName); } qDebug("Parser-loadPajek(): Clearing DumiesList from Pajek"); listDummiesPajek.clear(); relationsList.clear(); emit relationSet (0); //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_PAJEK, fileName, networkName, totalNodes, totalLinks, ( (has_arcs) ? EDGE_DIRECTED: EDGE_UNDIRECTED)); return true; } /** Tries to load the file as adjacency sociomatrix-formatted. If not it returns -1 */ bool Parser::loadAdjacency(){ qDebug("\n\nParser: loadAdjacency()"); QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) { return false; } QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QString str; QStringList lineElement; int i=0, j=0, newCount=0, lastCount=0; bool intOK=false; relationsList.clear(); totalNodes=0; edgeWeight=1.0; totalLinks=0; i=1; while ( i < 11 && !ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "*** Parser:loadAdjacency(): Not an Adjacency-formatted file. Aborting!!"; file.close(); errorMessage = tr("Not an Adjacency-formatted file. " "A non-comment line includes prohibited strings (i.e GraphML), " "not only numbers and delimiters as expected."); return false; } if ( str.contains (",")) newCount = (str.split(",")).count(); else newCount = (str.split(" ")).count(); qDebug() << str; qDebug() << "newCount "<1 ) || (newCount < i) ) { // line element count differ, therefore this can't be an adjacency matrix qDebug()<< "*** Parser:loadAdjacency(): Not an Adjacency-formatted file. Aborting!!"; file.close(); errorMessage = tr("Error reading Adjacency-formatted file. " "Matrix row %1 has different number of elements from previous row.").arg(i); return false; } lastCount=newCount; i++; } ts.reset(); ts.seek(0); i=0; while ( !ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); // transforms "/t", " ", etc to plain " ". if ( isComment(str) ) continue; if ( str.contains (",")) lineElement=str.split(","); else lineElement=str.split(" "); if (i == 0 ) { totalNodes=lineElement.count(); qDebug("Parser-loadAdjacency(): There are %i nodes in this file", totalNodes); for (j=0; j 0) { totalNodes=0; qDebug () << "Parser::readGraphMLElementGraph() - relations now " << relationCounter << "emitting relationSet to change to the new relation" << "and setting totalNodes to " < 1 ) { qDebug()<<"Parser::endGraphMLElementNode() - multirelational data" "skipping node creation. Node should have been created in earlier relation"; bool_node = false; return; } qDebug()<<"Parser::endGraphMLElementNode() - signal to create node " << " nodenumber "<< totalNodes << " id " << node_id << " label " << nodeLabel << " coords " <"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); missingNode=true; } if (!nodeHash.contains(edge_target)) { qDebug() << "Parser::readGraphMLElementEdge() - target node id " << edge_target << "for edge from " << edge_source << " to " << edge_target << " DOES NOT EXIST!" << "Inserting into edgesMissingNodesHash"; edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); missingNode=true; } if (missingNode) { return; } source = nodeHash [edge_source]; target = nodeHash [edge_target]; qDebug()<< "Parser::readGraphMLElementEdge() - source "<< edge_source << " num "<< source <<" - target "<< edge_target << " num "<< target << " edgeDirType " << edgeDirType; } // this method emits the edge creation signal. // called at the end of edge element void Parser::endGraphMLElementEdge(QXmlStreamReader &xml){ Q_UNUSED(xml); if (missingNode) { qDebug()<<"Parser::endGraphMLElementEdge() - missingNode true " << " postponing edge creation signal"; return; } qDebug()<<"Parser::endGraphMLElementEdge() - signal edgeCreate " << source << " -> " << target << " edgeDirType value " << edgeDirType; emit edgeCreate(source, target, edgeWeight, edgeColor, edgeDirType, arrows, bezier, edgeLabel); totalLinks++; bool_edge= false; } /* * this method reads data for edges and nodes * called at a data element (usually nested inside a node an edge element) */ void Parser::readGraphMLElementData (QXmlStreamReader &xml){ QXmlStreamAttributes xmlStreamAttr = xml.attributes(); key_id = xmlStreamAttr.value("key").toString(); key_value=xml.text().toString(); qDebug()<< "Parser::readGraphMLElementData() - key_id: " << key_id << " key_value "<< key_value; if (key_value.trimmed() == "") { qDebug()<< "Parser::readGraphMLElementData() - text: " << key_value; xml.readNext(); key_value=xml.text().toString(); qDebug()<< "Parser::readGraphMLElementData() - text: " << key_value; if ( key_value.trimmed() != "" ) { //if there's simple text after the StartElement, qDebug()<< "Parser::readGraphMLElementData() - key_id " << key_id << " value is simple text " <"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); } } else if ( ( keyName.value(key_id) == "value" || keyName.value(key_id) == "weight" ) && keyFor.value(key_id) == "edge" ) { conv_OK=false; edgeWeight= key_value.toFloat( &conv_OK ); if (!conv_OK) edgeWeight = 1.0; if (missingNode){ edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); } qDebug()<< "Parser::readGraphMLElementData() - Data found. Edge value: " << key_value << " Using "<< edgeWeight << " for this edge"; } else if ( keyName.value(key_id) == "size of arrow" && keyFor.value(key_id) == "edge" ) { conv_OK=false; float temp = key_value.toFloat( &conv_OK ); if (!conv_OK) arrowSize = 1; else arrowSize = temp; qDebug()<< "Parser::readGraphMLElementData() - Data found. Edge arrow size: " << key_value << " Using "<< arrowSize << " for this edge"; } else if (keyName.value(key_id) == "label" && keyFor.value(key_id) == "edge" ){ edgeLabel = key_value; if (missingNode){ edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); } qDebug()<< "Parser::readGraphMLElementData() - Data found. Edge label: " << edgeLabel << " for this edge"; } } /** * Reads node graphics data and properties: label, color, shape, size, coordinates, etc. */ void Parser::readGraphMLElementNodeGraphics(QXmlStreamReader &xml) { qDebug()<< "Parser::readGraphMLElementNodeGraphics() - element name " << xml.name().toString(); float tempX =-1, tempY=-1, temp=-1; QString color; QXmlStreamAttributes xmlStreamAttr = xml.attributes(); if ( xml.name() == "Geometry" ) { if ( xmlStreamHasAttribute ( xmlStreamAttr, "x") ) { conv_OK=false; tempX = xml.attributes().value("x").toString().toFloat (&conv_OK) ; if (conv_OK) randX = tempX; } if ( xmlStreamHasAttribute ( xmlStreamAttr, "y") ) { conv_OK=false; tempY = xml.attributes().value("y").toString().toFloat (&conv_OK) ; if (conv_OK) randY = tempY; } qDebug()<< "Parser::readGraphMLElementNodeGraphics() - Node Coordinates: " << tempX << " " << tempY << " Using coordinates" << randX<< " "< 0 ) { bool ok; edgeWeight = initEdgeWeight; edgeColor = initEdgeColor; edgeDirType=EDGE_DIRECTED; qDebug()<<"Parser::createMissingNodeEdges() - edges to create " << count; QHash::const_iterator it = edgesMissingNodesHash.constBegin(); while (it != edgesMissingNodesHash.constEnd()) { qDebug()<<"Parser::createMissingNodeEdges() - creating missing edge " << it.key() << " data " << it.value() ; edgeMissingNodesList = (it.key()).split("===>"); if (! ((edgeMissingNodesList[0]).isEmpty() ) && !((edgeMissingNodesList[1]).isEmpty()) ) { source = nodeHash.value(edgeMissingNodesList[0], -666); target = nodeHash.value(edgeMissingNodesList[1], -666); if (source == -666 || target == -666 ) { //emit something that this node has not been declared continue; } edgeMissingNodesListData = (it.value()).split("|"); if (!edgeMissingNodesListData[0].isEmpty() ){ edgeWeight = edgeMissingNodesListData[0].toInt(&ok, 10); } if (!edgeMissingNodesListData[1].isEmpty() ){ edgeColor = edgeMissingNodesListData[1]; } if (!edgeMissingNodesListData[2].isEmpty() ){ if ( (edgeMissingNodesListData[2]).contains("2") ) edgeDirType=EDGE_UNDIRECTED; } qDebug()<<"Parser::createMissingNodeEdges() - signal edgeCreate " << source << " -> " << target << " edgeDirType value " << edgeDirType; emit edgeCreate(source, target, edgeWeight, edgeColor, edgeDirType, arrows, bezier, edgeLabel); } ++it; } } else { qDebug()<<"Parser::createMissingNodeEdges() - nothing to do"; } } /** Tries to load a file as GML formatted network. If not it returns -1 */ bool Parser::loadGML(){ qDebug()<< "Parser::loadGML()"; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) { return false; } QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QRegularExpression onlyDigitsExp("^\\d+$"); QStringList tempList; QString str; int fileLine=0; bool floatOK= false; bool isPlanar = false, graphKey=false, graphicsKey=false, edgeKey=false, nodeKey=false, graphicsCenterKey=false; Q_UNUSED(isPlanar); relationsList.clear(); node_id= QString::null; arrows=true; bezier=false; edgeDirType=EDGE_UNDIRECTED; totalNodes=0; while (!ts.atEnd() ) { floatOK= false; fileContainsNodeCoords = false; nodeShape = initNodeShape; nodeColor = true; fileLine++; str= ts.readLine() ; str=str.simplified(); qDebug()<< "Parser::loadGML() - line" << fileLine <<":" << str; if ( isComment(str) ) continue; if ( fileLine == 1 && ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) ) { qDebug()<< "*** Parser::loadGML(): Not a GML-formatted file. Aborting!!"; errorMessage = tr("Not an GML-formatted file. " "Non-comment line %1 includes prohibited strings (i.e GraphML)") .arg(fileLine); file.close(); return false; } if ( str.startsWith("comment",Qt::CaseInsensitive) ) { qDebug()<< "Parser::loadGML() - This is a comment. Continue."; continue; } if ( str.startsWith("creator",Qt::CaseInsensitive) ) { qDebug()<< "Parser::loadGML() - This is a creator description. Continue."; continue; } else if ( str.startsWith("graph",Qt::CaseInsensitive) ) { //describe a graph qDebug()<< "Parser::loadGML() - graph description list start"; graphKey = true; } else if ( str.startsWith("directed",Qt::CaseInsensitive) ) { //graph attribute declarations if (graphKey) { if ( str.contains("1")) { qDebug()<< "Parser::loadGML() - graph directed 1. A directed graph."; edgeDirType=EDGE_DIRECTED; } else { qDebug()<< "Parser::loadGML() - graph directed 0. An undirected graph."; } } } else if ( str.startsWith("isPlanar",Qt::CaseInsensitive) ) { //key declarations if (graphKey) { if ( str.contains("1")) { qDebug()<< "Parser::loadGML() - graph isPlanar 1. Planar graph."; isPlanar = true; } else { isPlanar = false; } } } else if ( str.startsWith("node",Qt::CaseInsensitive) ) { //node declarations qDebug()<< "Parser::loadGML() - node description list starts"; nodeKey = true; } else if ( str.startsWith("id",Qt::CaseInsensitive) ) { //describes identification number for an object if ( nodeKey ) { totalNodes++; node_id = str.split(" ",QString::SkipEmptyParts).last(); if (!node_id.contains(onlyDigitsExp)) { errorMessage = tr("Not a proper GML-formatted file. " "Node id tag at line %1 has non-arithmetic value.") .arg(fileLine); return false; } qDebug()<< "Parser::loadGML() - id description " << "This node" << totalNodes <<"id"<< node_id; } } else if ( str.startsWith("label ",Qt::CaseInsensitive) ) { //describes label if ( nodeKey ) { nodeLabel = str.split(" ",QString::SkipEmptyParts).last().remove("\""); qDebug()<< "Parser::loadGML() - node label definition" << "node" << totalNodes <<"id"<< node_id << "label" << nodeLabel; //FIXME REMOVE ANY " } else if ( edgeKey ) { edgeLabel = str.split(" ",QString::SkipEmptyParts).last(); qDebug()<< "Parser::loadGML() - edge label definition" << "edge" << totalLinks << "label" << edgeLabel; } } else if ( str.startsWith("edge ",Qt::CaseInsensitive) ) { //edge declarations qDebug()<< "Parser::loadGML() - edge description list start"; edgeKey = true; totalLinks++; } else if ( str.startsWith("source ",Qt::CaseInsensitive) ) { if (edgeKey) { edge_source = str.split(" ",QString::SkipEmptyParts).last(); //if edge_source if (!edge_source.contains(onlyDigitsExp)) { errorMessage = tr("Not a proper GML-formatted file. " "Edge source tag at line %1 has non-arithmetic value.") .arg(fileLine); return false; } source = edge_source.toInt(0); qDebug()<< "Parser::loadGML() - edge source definition" << "edge source" << edge_source; } } else if ( str.startsWith("target ",Qt::CaseInsensitive) ) { if (edgeKey) { edge_target = str.split(" ",QString::SkipEmptyParts).last(); if (!edge_source.contains(onlyDigitsExp)) { errorMessage = tr("Not a proper GML-formatted file. " "Edge target tag at line %1 has non-arithmetic value.") .arg(fileLine); return false; } target = edge_target.toInt(0); qDebug()<< "Parser::loadGML() - edge target definition" << "edge target" << edge_target; } } else if ( str.startsWith("graphics",Qt::CaseInsensitive) ) { //Describes graphics which are used to draw a particular object. if (nodeKey) { qDebug()<< "Parser::loadGML() - node graphics description list start"; } else if (edgeKey) { qDebug()<< "Parser::loadGML() - edge graphics description list start"; } graphicsKey = true; } else if ( str.startsWith("center",Qt::CaseInsensitive) ) { if (graphicsKey && nodeKey) { qDebug()<< "Parser::loadGML() - node graphics center start"; if ( str.contains("[", Qt::CaseInsensitive) ) { if ( str.contains("]", Qt::CaseInsensitive) && str.contains("x", Qt::CaseInsensitive) && str.contains("y", Qt::CaseInsensitive)) { str.remove("center"); str.remove("["); str.remove("]"); str = str.simplified(); tempList = str.split(" ",QString::SkipEmptyParts); randX = (tempList.at(1)).toFloat(&floatOK); if (!floatOK) { errorMessage = tr("Not a proper GML-formatted file. " "Node center tag at line %1 cannot be converted to float.") .arg(fileLine); return false; } randY = tempList.at(3).toFloat(&floatOK); if (!floatOK) { errorMessage = tr("Not a proper GML-formatted file. " "Node center tag at line %1 cannot be converted to float.") .arg(fileLine); return false; } qDebug()<< "Parser::loadGML() - node graphics center" << "x" << randX << "y" << randY; fileContainsNodeCoords = true; } else { graphicsCenterKey = true; } } } } else if ( str.startsWith("center",Qt::CaseInsensitive) && nodeKey && graphicsKey && graphicsCenterKey ) { //this is the case where the bracker [ is below the center tag } else if ( str.startsWith("type",Qt::CaseInsensitive) ) { if (graphicsKey && nodeKey) { qDebug()<< "Parser::loadGML() - node graphics type start"; nodeShape = str.split(" ",QString::SkipEmptyParts).last(); if (nodeShape.isNull() || nodeShape.isEmpty() ) { errorMessage = tr("Not a proper GML-formatted file. " "Node type tag at line %1 has no value.") .arg(fileLine); return false; } nodeShape.remove("\""); } } else if ( str.startsWith("fill",Qt::CaseInsensitive) ) { if (graphicsKey && nodeKey) { qDebug()<< "Parser::loadGML() - node graphics fill start"; nodeColor = str.split(" ",QString::SkipEmptyParts).last(); if (nodeColor.isNull() || nodeColor.isEmpty() ) { errorMessage = tr("Not a proper GML-formatted file. " "Node fill tag at line %1 has no value.") .arg(fileLine); return false; } } } else if ( str.startsWith("]",Qt::CaseInsensitive) ) { if (nodeKey && graphicsKey && graphicsCenterKey ) { qDebug()<< "Parser::loadGML() - node graphics center ends"; graphicsCenterKey = false; } else if (graphicsKey) { qDebug()<< "Parser::loadGML() - graphics list ends"; graphicsKey = false; } else if (nodeKey && !graphicsKey) { qDebug()<< "Parser::loadGML() - node description list ends"; nodeKey = false; if (!fileContainsNodeCoords) { randX=rand()%gwWidth; randY=rand()%gwHeight; } qDebug()<<" *** Creating node "<< node_id << " at "<< randX <<","<< randY <<" label "<" + edge_target; } emit edgeCreate(source,target, edgeWeight, edgeColor, edgeDirType, arrows, bezier, edgeLabel); } else if (graphKey) { qDebug()<< "Parser::loadGML() - graph description list ends"; graphKey = false; } } } if (relationsList.count() == 0 ) { emit addRelation( "unnamed" ); } //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_GML, fileName, networkName, totalNodes, totalLinks, edgeDirType); qDebug() << "Parser-loadGML()"; return true; } /** Tries to load the file as Dot (Graphviz) formatted network. If not it returns -1 */ bool Parser::loadDot(){ qDebug("\n\nParser: loadDotNetwork"); int fileLine=0, aNum=-1; int start=0, end=0, next=0; QString label, node, nodeLabel, fontName, fontColor, edgeShape, edgeColor, edgeLabel, networkLabel; QString str, temp, prop, value ; QStringList lineElement; nodeColor="red"; edgeColor="black"; nodeShape=""; edgeWeight=1.0; float nodeValue=1.0; bool netProperties = false; QStringList labels; QList nodeSequence; //holds edges QList nodesDiscovered; //holds nodes; relationsList.clear(); edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; source=0, target=0; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); totalNodes=0; while (!ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); str=str.trimmed(); if ( isComment (str) ) continue; fileLine++; qDebug ()<<"Reading fileLine "<< fileLine; qDebug ()<< str; if ( fileLine == 1 ) { if ( str.contains("vertices",Qt::CaseInsensitive) //Pajek || str.contains("network",Qt::CaseInsensitive) //Pajek? || str.contains("[",Qt::CaseInsensitive) // GML || str.contains("DL",Qt::CaseInsensitive) //DL format || str.contains("list",Qt::CaseInsensitive) //list || str.startsWith("",Qt::CaseInsensitive) && str.contains ("=",Qt::CaseInsensitive) && !netProperties ) { qDebug()<< "* A node definition must be here: \n" << str; end=str.indexOf('['); if (end!=-1) { temp=str.right(str.size()-end-1); //keep the properties temp=temp.remove(']'); temp=temp.remove(';'); node=str.left(end-1); node=node.remove('\"'); qDebug()<<"node named "<",Qt::CaseInsensitive) && !str.contains ("=",Qt::CaseInsensitive) && !netProperties ) { qDebug()<< "* A node definition without properties must be here ..." << str; end=str.indexOf(';'); if (end!=-1) { node=str.left(str.size()-end); //keep the properties qDebug()<<"node named "<",Qt::CaseInsensitive) ){ //non directed = symmetric links if ( str.contains("--",Qt::CaseInsensitive) ) nodeSequence=str.split("--"); else nodeSequence=str.split("-"); edgeDirType=EDGE_UNDIRECTED; } else { //is directed nodeSequence=str.split("->"); edgeDirType=EDGE_DIRECTED; } //Create all nodes defined in nodeSequence for ( QList::iterator it=nodeSequence.begin(); it!=nodeSequence.end(); it++ ) { node=(*it).simplified(); qDebug () << " nodeSequence node "<< node; if ( (aNum=nodesDiscovered.indexOf( node ) ) == -1) { //node not discovered before totalNodes++; randX=rand()%gwWidth; randY=rand()%gwHeight; qDebug()<<" *** Creating node "<< totalNodes << " at "<< randX <<","<< randY <<" label "< 2 ) {//there is a node definition here node=str.left(start).remove('\"').simplified(); qDebug()<<"node label: "< void print_queue(T& q) { qDebug() << "print_queue() "; while(!q.empty()) { qDebug() << q.top().key << " value: " << q.top().value << " "; q.pop(); } qDebug() << endl; } /** * @brief A method to load a weighted edge list formatted file. * @param delimiter * @return * This method can read and parse edgelist formated files * where edge source and target are either named with numbers or with labels * That is the following formats can be parsed: # edgelist with node numbers 1 2 1 1 3 2 1 6 2 1 8 2 ... # edgelist with node labels actor1 actor2 1 actor2 actor4 2 actor1 actor3 1 actorX actorY 3 name othername 1 othername somename 2 .... */ bool Parser::loadEdgeListWeighed(const QString &delimiter){ qDebug() << "Parser::loadEdgeListWeighed() - column delimiter" << delimiter ; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QMap nodeMap; // use a minimum priority queue to order Actors by their value // so that we can create the discovered nodes by either their increasing nodeNumber // (if nodesWithLabels == true) or by their actual number in the file (if nodesWithLabels == false). priority_queue, CompareActors> nodeQ; QHash edgeList; QString str, edgeKey,edgeKeyDelimiter="====>" ; QStringList lineElement, edgeElement; // one or more digits QRegularExpression onlyDigitsExp("^\\d+$"); bool nodesWithLabels = false; bool floatOK = false; int fileLine = 1; totalNodes=0; edgeWeight=1.0; edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; relationsList.clear(); qDebug()<< "*** Parser::loadEdgeListWeighed() - Initial file parsing " "to test integrity and edge naming scheme"; while ( !ts.atEnd() ) { str= ts.readLine() ; qDebug()<< "Parser::loadEdgeListWeighed() - str" << str; str=str.simplified(); qDebug()<< "Parser::loadEdgeListWeighed() - simplified str" << str; if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "Parser::loadEdgeListWeighed() - " "Not a Weighted list-formatted file. Aborting!!"; file.close(); errorMessage = tr("Not an EdgeList-formatted file. " "A non-comment line includes prohibited strings (i.e GraphML)"); return false; } lineElement=str.split(delimiter); if ( lineElement.count() != 3 ) { qDebug()<< "*** Parser::loadEdgeListWeighed() - " "Not a Weighted list-formatted file. Aborting!!"; file.close(); errorMessage = tr("Not a properly EdgeList-formatted file. " "Row %1 has not 3 elements as expected (i.e. source, target, weight)") .arg(fileLine); return false; } edge_source = lineElement[0]; edge_target = lineElement[1]; edge_weight = lineElement[2]; qDebug()<< "Parser::loadEdgeListWeighed() - Dissecting line - " "source:" << edge_source << "target:" << edge_target << "weight:" << edge_weight; if (!edge_source.contains(onlyDigitsExp)) { qDebug()<< "Parser::loadEdgeListWeighed() - node named by non-digit only string. " "nodesWithLabels = true"; nodesWithLabels = true; } if (!edge_target.contains(onlyDigitsExp)) { qDebug()<< "Parser::loadEdgeListWeighed() - node named by non-digit only string. " "nodesWithLabels = true"; nodesWithLabels = true; } fileLine ++; } ts.seek(0); qDebug()<< "*** Parser::loadEdgeListWeighed() - Initial file parsing finished. " "This is really a weighted edge list. Proceed to main parsing"; while ( !ts.atEnd() ) { str= ts.readLine() ; qDebug()<< "Parser::loadEdgeListWeighed() - str" << str; str=str.simplified(); qDebug()<< "Parser::loadEdgeListWeighed() - simplified str" << str; if ( isComment(str) ) continue; lineElement=str.split(delimiter); edge_source = lineElement[0]; edge_target = lineElement[1]; edge_weight = lineElement[2]; qDebug()<< "Parser::loadEdgeListWeighed() - Dissecting line - " "source:" << edge_source << "target:" << edge_target << "weight:" << edge_weight; if ( ! nodeMap.contains(edge_source) ) { totalNodes++; Actor sourceActor; sourceActor.key = edge_source; if (nodesWithLabels) { sourceActor.value = totalNodes; // order by an increasing totalNodes index nodeQ.push( sourceActor ); nodeMap.insert(edge_source, totalNodes); } else { sourceActor.value = edge_source.toInt(); // order by the actual actor number in the file nodeQ.push( sourceActor ); nodeMap.insert(edge_source, edge_source.toInt() ); } qDebug()<< "Parser::loadEdgeListWeighed() - source, new node named" << edge_source << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListWeighed() - source already found, continue"; } if ( ! nodeMap.contains(edge_target) ) { totalNodes++; Actor targetActor; targetActor.key = edge_target; if (nodesWithLabels) { targetActor.value = totalNodes ; // order by an increasing totalNodes index nodeQ.push( targetActor ); nodeMap.insert(edge_target, totalNodes); } else { targetActor.value = edge_target.toInt(); // order by the actual actor number in the file nodeQ.push( targetActor ); nodeMap.insert(edge_target, edge_target.toInt() ); } qDebug()<< "Parser::loadEdgeListWeighed() - target, new node named" << edge_target << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListWeighed() - target already found, continue"; } edgeWeight = edge_weight.toFloat(&floatOK); if (floatOK) { qDebug()<< "Parser::loadEdgeListWeighed() - read edge weight" << edgeWeight; } else { edgeWeight = 1.0; qDebug()<< "Parser::loadEdgeListWeighed() - error reading edge weight." "Using default edgeWeight" << edgeWeight; } edgeKey = edge_source + edgeKeyDelimiter + edge_target; if ( ! edgeList.contains( edgeKey ) ) { qDebug()<< "Parser::loadEdgeListWeighed() - inserting edgeKey" << edgeKey << "in edgeList with weight" << edgeWeight; edgeList.insert( edgeKey, edgeWeight ); totalLinks++; } } //end ts.stream while here file.close(); qDebug() << "Parser::loadEdgeListWeighed() - finished reading file, " "start creating nodes and edges"; //print_queue(nodeQ); // create nodes one by one while (!nodeQ.empty()) { Actor node = nodeQ.top(); nodeQ.pop(); randX=rand()%gwWidth; randY=rand()%gwHeight; if (nodesWithLabels) { qDebug() << "Parser::loadEdgeListWeighed() - creating node named" << node.key << "numbered" << node.value << "at position" << QPointF(randX, randY); emit createNode( node.value, initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } else { qDebug() << "Parser::loadEdgeListWeighed() - creating node named" << node.key << "numbered" << node.key.toInt() << "at position" << QPointF(randX, randY); emit createNode( node.key.toInt(), initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } } //create edges one by one QHash::const_iterator edge = edgeList.constBegin(); while (edge!= edgeList.constEnd()) { qDebug() << "Parser::loadEdgeListWeighed() - creating edge named" << edge.key() << " weight " << edge.value(); edgeElement=edge.key().split(edgeKeyDelimiter); if (nodesWithLabels) { source = nodeMap.value( edgeElement[0] ) ; target = nodeMap.value( edgeElement[1] ) ; } else { source = edgeElement[0].toInt() ; target = edgeElement[1].toInt() ; } edgeWeight = edge.value(); emit edgeCreate(source, target, edgeWeight, initEdgeColor, edgeDirType, arrows, bezier); ++edge; } if (relationsList.count() == 0) { emit addRelation("unnamed"); } //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_EDGELIST_WEIGHTED, fileName, networkName, totalNodes, totalLinks, edgeDirType); qDebug() << "Parser::loadEdgeListWeighed() - END. Returning."; return true; } bool Parser::loadEdgeListSimple(const QString &delimiter){ qDebug() << "Parser::loadEdgeListSimple() - column delimiter" << delimiter ; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); // set the userselectedCodec QString str, edgeKey,edgeKeyDelimiter="====>" ; QStringList lineElement,edgeElement; int columnCount=0; int fileLine = 0; bool nodesWithLabels= false; //@TODO Always use nodesWithLabels= true QMap nodeMap; // use a minimum priority queue to order Actors by their value // so that we can create the discovered nodes by either their increasing nodeNumber // (if nodesWithLabels == true) or by their actual number in the file (if nodesWithLabels == false). priority_queue, CompareActors> nodeQ; QHash edgeList; QRegularExpression onlyDigitsExp("^\\d+$"); totalNodes = 0; initEdgeWeight=1.0; edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; relationsList.clear(); qDebug()<< "*** Parser::loadEdgeListSimple() - Initial file parsing " "to test integrity and edge naming scheme"; while ( !ts.atEnd() ) { fileLine++; str= ts.readLine() ; qDebug()<< "Parser::loadEdgeListSimple() - line " << fileLine << endl << str; str=str.simplified(); if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "*** Parser:loadEdgeListSimple(): Not an EdgeList-formatted file. Aborting!!"; errorMessage = tr("Not an EdgeList-formatted file. " "Non-comment line %1 includes prohibited strings (i.e GraphML)") .arg(fileLine); file.close(); return false; } lineElement=str.split(delimiter); for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { edge_source = (*it1); if (!edge_source.contains(onlyDigitsExp)) { qDebug()<< "Parser::loadEdgeListSimple() - node named by non-digit only string. " "nodesWithLabels = true"; nodesWithLabels = true; } if ( edge_source == "0" ) { nodesWithLabels = true; } } } ts.seek(0); fileLine = 0; qDebug () << "Parser::loadEdgeListSimple() - Reset and read lines. nodesWithLabels" << nodesWithLabels; while ( !ts.atEnd() ) { fileLine++; str= ts.readLine() ; str=str.simplified(); qDebug()<< "Parser::loadEdgeListSimple() - line" << fileLine << endl << str; if ( isComment(str) ) continue; lineElement=str.split(delimiter); columnCount = 0; for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { columnCount ++; if (columnCount == 1) { // source node edge_source = (*it1); qDebug()<< "Parser::loadEdgeListSimple() - Dissecting line - " "source node:" << edge_source; if ( ! nodeMap.contains(edge_source) ) { totalNodes++; Actor sourceActor; sourceActor.key = edge_source; if (nodesWithLabels) { sourceActor.value = totalNodes; // order by an increasing totalNodes index nodeQ.push( sourceActor ); nodeMap.insert(edge_source, totalNodes); } else { sourceActor.value = edge_source.toInt(); // order by the actual actor number in the file nodeQ.push( sourceActor ); nodeMap.insert(edge_source, edge_source.toInt() ); } qDebug()<< "Parser::loadEdgeListSimple() - source, new node named" << edge_source << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListWeighed() - source already found, continue"; } } else { // target nodes edge_target= (*it1); qDebug()<< "Parser::loadEdgeListSimple() - Dissecting line - " "target node:" << edge_target; if ( ! nodeMap.contains(edge_target) ) { totalNodes++; Actor targetActor; targetActor.key = edge_target; if (nodesWithLabels) { targetActor.value = totalNodes ; // order by an increasing totalNodes index nodeQ.push( targetActor ); nodeMap.insert(edge_target, totalNodes); } else { targetActor.value = edge_target.toInt(); // order by the actual actor number in the file nodeQ.push( targetActor ); nodeMap.insert(edge_target, edge_target.toInt() ); } qDebug()<< "Parser::loadEdgeListSimple() - target, new node named" << edge_target << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListSimple() - target already found, continue"; } } if (columnCount > 1) { edgeKey = edge_source + edgeKeyDelimiter + edge_target; if ( ! edgeList.contains( edgeKey ) ) { qDebug()<< "Parser::loadEdgeListSimple() - inserting edgeKey" << edgeKey << "in edgeList with initial weight" << initEdgeWeight; edgeList.insert( edgeKey, initEdgeWeight ); totalLinks++; } else { // if edge already discovered, then increase its weight by 1 edgeWeight = edgeList.value(edgeKey); edgeWeight = edgeWeight + 1; qDebug()<< "Parser::loadEdgeListSimple() - edgeKey" << edgeKey << "found before, adding in edgeList with increased weight" << edgeWeight; edgeList.insert( edgeKey, edgeWeight ); } } } // end for QStringList::Iterator } //end ts.stream while here file.close(); // create nodes one by one while (!nodeQ.empty()) { Actor node = nodeQ.top(); nodeQ.pop(); randX=rand()%gwWidth; randY=rand()%gwHeight; if (nodesWithLabels) { qDebug() << "Parser::loadEdgeListSimple() - creating node named" << node.key << "numbered" << node.value << "at position" << QPointF(randX, randY); emit createNode( node.value, initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } else { qDebug() << "Parser::loadEdgeListSimple() - creating node named" << node.key << "numbered" << node.key.toInt() << "at position" << QPointF(randX, randY); emit createNode( node.key.toInt(), initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } } //create edges one by one QHash::const_iterator edge = edgeList.constBegin(); while (edge!= edgeList.constEnd()) { qDebug() << "Parser::loadEdgeListWeighed() - creating edge named" << edge.key() << " weight " << edge.value(); edgeElement=edge.key().split(edgeKeyDelimiter); if (nodesWithLabels) { source = nodeMap.value( edgeElement[0] ) ; target = nodeMap.value( edgeElement[1] ) ; } else { source = edgeElement[0].toInt() ; target = edgeElement[1].toInt() ; } edgeWeight = edge.value(); emit edgeCreate(source, target, edgeWeight, initEdgeColor, edgeDirType, arrows, bezier); ++edge; } if (relationsList.count() == 0) { emit addRelation("unnamed"); } //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_EDGELIST_SIMPLE, fileName, networkName, totalNodes, totalLinks, edgeDirType); qDebug() << "Parser-loadEdgeListSimple() ending and returning..."; return true; } //Returns true if QString str is a comment inside the network file. bool Parser::isComment(QString str){ if ( str.startsWith("#", Qt::CaseInsensitive) || str.startsWith("/*", Qt::CaseInsensitive) || str.startsWith("%", Qt::CaseInsensitive) || str.startsWith("/*", Qt::CaseInsensitive) || str.startsWith("//", Qt::CaseInsensitive) || str.isEmpty() ) { qDebug () << "Parser::isComment() - Comment or an empty line was found. " "Skipping..."; return true; } return false; } socnetv-2.4/src/dialogdissimilarities.cpp0000664000175000017500000000602113245520654021152 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogdissimilarities.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData(){ 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->gatherData(); this->accept(); } void DialogDissimilarities::on_buttonBox_rejected() { this->reject(); } DialogDissimilarities::~DialogDissimilarities(){ metricList.clear(); variablesLocationList.clear(); } socnetv-2.4/src/dialogsimilaritymatches.h0000664000175000017500000000436613245520654021166 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogsimilaritymatches.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0); ~DialogSimilarityMatches(); public slots: void gatherData(); 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 socnetv-2.4/src/dialogsimilaritypearson.cpp0000664000175000017500000000561713245520654021544 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogsimilaritypearson.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData(){ 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->gatherData(); this->accept(); } void DialogSimilarityPearson::on_buttonBox_rejected() { this->reject(); } DialogSimilarityPearson::~DialogSimilarityPearson(){ matrixList.clear(); variablesLocationList.clear(); } socnetv-2.4/src/dialogdatasetselect.cpp0000775000175000017500000001300013245520654020577 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogdatasetselect.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData(){ 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->gatherData(); this->accept(); } void DialogDataSetSelect::on_buttonBox_rejected() { this->reject(); } DialogDataSetSelect::~DialogDataSetSelect(){ datasets_list.clear(); datasets_filenames.clear(); } socnetv-2.4/src/forms/0000775000175000017500000000000013245521146015214 5ustar dimitrisdimitrissocnetv-2.4/src/forms/dialogsimilaritymatches.ui0000664000175000017500000001757513041416261022500 0ustar dimitrisdimitris DialogSimilarityMatches true 0 0 550 360 0 0 550 360 600 600 10 Similarity: Matches 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 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> 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> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 20 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.4/src/forms/dialogwebcrawler.ui0000664000175000017500000003153713245330127021076 0ustar dimitrisdimitris DialogWebCrawler 0 0 518 655 0 0 500 650 650 800 10 Generate network from web links 0 0 500 130 600 500 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 Initial URL Qt::Horizontal 40 20 0 0 390 22 400 24 <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> <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 11 PreferAntialias Qt::Horizontal 40 20 50 0 <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 <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 11 PreferAntialias Qt::Horizontal 40 20 63 0 <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 Url Patterns to include Url patterns to exclude <html><head/><body><p><span style=" font-weight:600;">Crawl Internal Links </span></p><p>If enabled, the crawler will scan and map <span style=" font-weight:600;">internal links </span>(i.e. pages within the initial website). </p><p>If you do not want to crawl internal links, disable this option. </p><p>Default is to crawl internal links.</p></body></html> Crawl internal links true <html><head/><body><p><span style=" font-weight:600;">Crawl external links</span></p><p>If enabled, the crawler will map <span style=" font-weight:600;">external links</span>. </p><p>For instance, if you start crawling from www.supersyntages.gr and we find there a link to another domain, i.e. www.linuxinsider.gr, then we will go outside of supersyntages.gr and crawl linuxinsider.gr too. </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>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 true <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> Wait (delay) between requests true true Sans Serif 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogWebCrawler accept() 257 324 157 274 buttonBox rejected() DialogWebCrawler reject() 325 324 286 274 socnetv-2.4/src/forms/dialogfilteredgesbyweight.ui0000664000175000017500000001000613041416261022763 0ustar dimitrisdimitris DialogFilterEdgesByWeight 0 0 400 210 400 210 10 Filter edges Sans Serif 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 Weight Threshold Sans Serif 10 1 -100.000000000000000 Sans Serif Filter edges with weight over threshold Sans Serif Filter edges with weight below threshold Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogFilterEdgesByWeight accept() 248 254 157 274 buttonBox rejected() DialogFilterEdgesByWeight reject() 316 260 286 274 socnetv-2.4/src/forms/dialogdissimilarities.ui0000664000175000017500000001520413041416261022126 0ustar dimitrisdimitris DialogDissimilarities true 0 0 550 330 0 0 550 330 600 600 10 Tie profile dissimilarities 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 Distance metric: 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> 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.</p><p><br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 20 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.4/src/forms/dialogclusteringhierarchical.ui0000664000175000017500000002500713245330127023452 0ustar dimitrisdimitris DialogClusteringHierarchical true 0 0 550 465 0 0 550 460 600 600 10 Hierarchical Clustering 0 0 400 210 <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 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 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> 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 absoulute differences between tie/distance profiles.<br/></p></body></html> 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> Enable to include matrix diagonal in calculations Include input matrix diagonal Enable to include matrix diagonal in calculations Print dendrogram (avoid in large nets) Qt::Vertical 20 40 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.4/src/forms/dialognodeedit.ui0000664000175000017500000002575213041416261020534 0ustar dimitrisdimitris DialogNodeEdit 0 0 464 280 450 280 500 300 DejaVu Sans 11 Node Properties Sans Serif 10 Node label Sans Serif 10 Sans Serif 10 Node size (default 8) DejaVu Sans 11 Qt::Horizontal 40 20 Sans Serif 10 8 false Sans Serif 10 Node value (disabled) DejaVu Sans 11 Qt::Horizontal 40 20 false Sans Serif 10 Sans Serif 10 Node color (click the button to select) DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 ... 60 20 Sans Serif 10 Node shape DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 :/images/box.png:/images/box.png DejaVu Sans 11 :/images/circle.png:/images/circle.png DejaVu Sans 11 :/images/diamond.png:/images/diamond.png DejaVu Sans 11 :/images/ellipse.png:/images/ellipse.png DejaVu Sans 11 :/images/triangle.png:/images/triangle.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Triangle</span></p><p>Change all node shapes to &quot;triangle&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;triangle&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/star.png:/images/star.png Sans Serif 9 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogNodeEdit accept() 233 316 157 274 buttonBox rejected() DialogNodeEdit reject() 301 322 286 274 socnetv-2.4/src/forms/dialogsettings.ui0000664000175000017500000041677513245330127020614 0ustar dimitrisdimitris DialogSettings 0 0 469 616 Settings & Preferences 0 General Data Exporting <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 Qt::Horizontal 48 20 220 0 <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> 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> ... Image Exporting <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 Debugging and Progressing <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 <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 Window options <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 <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 <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 <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 Canvas <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 <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 11 Qt::Horizontal 40 20 0 0 35 0 DejaVu Sans 11 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 <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 Qt::Horizontal 30 28 200 0 <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 35 0 <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> ... Qt::Vertical 20 90 <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 <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 <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 <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 <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 <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 <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 Qt::Vertical 20 40 <html><head/><body><p><span style=" font-family:'Sans Serif'; font-size:9pt; font-weight:600;">Update Mode</span></p><p><br/></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><br/></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><br/></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Full</span></p><p><br/></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><br/></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Minimal</span></p><p><br/></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><br/></p><p><br/></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">Bounding Rectangle </span></p><p><br/></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><br/></p><p><span style=" font-family:'Sans Serif'; font-size:9pt; text-decoration: underline;">None</span></p><p><br/></p><p><span style=" font-family:'Sans Serif'; font-size:9pt;">The engine will never update th canvas when something changes; This mode disables all item changes. Normally, you dont want to use this!</span></p></body></html> Update mode DejaVu Sans 11 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:'DejaVu Sans'; 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;"><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 th canvas when something changes; This mode disables all item changes. Normally, you dont want to use this!</span></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Index Method</span></p><p><br/></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 canvasL</p><p><br/></p><p><span style=" font-style:italic;">BspTreeIndex: </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/><p/><p><span style=" font-style:italic;">NoIndex: </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 11 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:'DejaVu Sans'; 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;"><span style=" 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;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Select one of the indexing algorithms the graphics engine provides for managing positional information about items on the canvasL</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-style:italic;">BspTreeIndex: </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 style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </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-style:italic;">NoIndex: </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> Nodes <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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Square</span></p><p>Change all node shapes to &quot;square&quot; or &quot;box&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;box&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default node shape</span></p><p>Click on any shape to select a new node shape for all nodes. 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.</p></body></html> :/images/box.png:/images/box.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Circle</span></p><p>Change all node shapes to &quot;circle&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;circle&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/circle.png:/images/circle.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Diamond</span></p><p>Change all node shapes to &quot;diamond&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;diamond&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/diamond.png:/images/diamond.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Ellipse</span></p><p>Change all node shapes to &quot;ellipse&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;ellipse&quot; 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> :/images/ellipse.png:/images/ellipse.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Star</span></p><p>Change all node shapes to &quot;star&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;star&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/triangle.png:/images/triangle.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Triangle</span></p><p>Change all node shapes to &quot;triangle&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;triangle&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/star.png:/images/star.png <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 DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 Node Number settings <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 DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 <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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 <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 Node Label settings <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 Label color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 Label font size DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 Label distance DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 groupBox_3 groupBox_4 groupBox_7 Edges 9 9 431 518 Edge settings <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 <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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 <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 <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> Bezier Curves <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 DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 <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 DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 Edge offset from node DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 Weight number settings <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 Weight number color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 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 Weight number font size DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 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 Edge label settings <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 Label color DejaVu Sans 11 Qt::Horizontal 40 20 false 60 25 DejaVu Sans 11 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 Label font size DejaVu Sans 11 Qt::Horizontal 40 20 false DejaVu Sans 11 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 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogSettings accept() 227 281 157 274 buttonBox rejected() DialogSettings reject() 295 287 286 274 socnetv-2.4/src/forms/dialogsimilaritypearson.ui0000664000175000017500000001410713041416261022507 0ustar dimitrisdimitris DialogSimilarityPearson true 0 0 550 300 0 0 550 300 600 600 10 Pearson Correlations 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 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> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 40 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.4/src/forms/dialogranderdosrenyi.ui0000664000175000017500000004673513041416261021775 0ustar dimitrisdimitris DialogRandErdosRenyi 0 0 560 520 0 0 560 520 650 539 10 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 10 50 false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandErdosRenyi accept() 257 373 157 274 buttonBox rejected() DialogRandErdosRenyi reject() 325 373 286 274 socnetv-2.4/src/forms/dialograndscalefree.ui0000664000175000017500000004512413041416261021532 0ustar dimitrisdimitris DialogRandScaleFree 0 0 580 530 0 0 580 530 600 580 10 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 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandScaleFree accept() 257 373 157 274 buttonBox rejected() DialogRandScaleFree reject() 325 373 286 274 socnetv-2.4/src/forms/edgeeditdialog.ui0000664000175000017500000002210113014570727020504 0ustar dimitrisdimitris EdgeEditDialog 0 0 450 280 450 280 Node Properties 12 Enter node label 12 false 12 Enter node value (disabled) 12 Qt::Horizontal 40 20 false 12 12 Select line shape 12 Qt::Horizontal 40 20 12 :/images/box.png:/images/box.png 12 :/images/circle.png:/images/circle.png 12 :/images/diamond.png:/images/diamond.png 12 :/images/ellipse.png:/images/ellipse.png 12 :/images/triangle.png:/images/triangle.png 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 12 Select link color (click the button) 12 Qt::Horizontal 40 20 60 25 12 ... 60 20 12 Select the link weight (default 1) 12 Qt::Horizontal 40 20 12 8 buttonBox accepted() EdgeEditDialog accept() 233 316 157 274 buttonBox rejected() EdgeEditDialog reject() 301 322 286 274 socnetv-2.4/src/forms/dialograndregular.ui0000664000175000017500000003675413041416261021253 0ustar dimitrisdimitris DialogRandRegular 0 0 570 440 0 0 570 440 580 480 10 d-Regular network generator Sans Serif 10 PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 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>Note that 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><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 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>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> 4 9999 100 Qt::Horizontal 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 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 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 <span style=" font-style:italic;">n &gt;= d + 1 </span>and <span style=" font-style:italic;">nd </span>is even. Read more in the manual.</p></body></html> Qt::RichText true buttonBox accepted() DialogRandRegular accept() 257 373 157 274 buttonBox rejected() DialogRandRegular reject() 325 373 286 274 socnetv-2.4/src/forms/dialogdatasetselect.ui0000664000175000017500000000617313041416261021562 0ustar dimitrisdimitris DialogDataSetSelect true 0 0 490 200 0 0 460 200 490 215 10 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 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.4/src/forms/dialograndsmallworld.ui0000664000175000017500000003447213041416261021765 0ustar dimitrisdimitris DialogRandSmallWorld 0 0 570 420 0 0 570 400 580 420 10 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 10 PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandSmallWorld accept() 257 373 157 274 buttonBox rejected() DialogRandSmallWorld reject() 325 373 286 274 socnetv-2.4/src/dialogwebcrawler.cpp0000775000175000017500000002706713245520654020131 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogwebcrawler.cpp - description ------------------- copyright : (C) 2005-2018 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.patternsIncludedTextEdit->setToolTip("ALLOWED URL PATTERNS\n" "Enter, in separate lines, one or more " "url patterns to include while crawling. " "\nI.e. example.com/pattern/*" "\n\nDo not enter spaces." "\n\nLeave * to crawl all urls."); ui.patternsExcludedTextEdit->setText(""); ui.patternsExcludedTextEdit->setToolTip("NOT ALLOWED URL PATTERNS\n" "Enter, in separate lines, one or more " "url patterns to exclude while crawling. " "\nI.e. example.com/pattern/*" "\n\nDo not enter spaces." "\n\nLeave empty to crawl all urls."); extLinks=false; intLinks=true; ui.extLinksCheckBox->setChecked (extLinks); ui.intLinksCheckBox->setChecked (intLinks); 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.extLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect (ui.intLinksCheckBox, &QCheckBox::stateChanged, this, &DialogWebCrawler::checkErrors); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); } /** * @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.extLinksCheckBox->isChecked() && !ui.intLinksCheckBox->isChecked() ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); checkboxesError = true; } else { if (!patternsInError && !patternsExError && !urlError ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); checkboxesError = false; extLinks = ui.extLinksCheckBox->isChecked(); intLinks = ui.intLinksCheckBox->isChecked(); } } // 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::gatherData(){ qDebug()<< "DialogWebCrawler::gatherData() - 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, extLinks, intLinks, ui.selfLinksCheckBox->isChecked(), ui.waitCheckBox ->isChecked() ); } socnetv-2.4/src/dialogfilteredgesbyweight.h0000775000175000017500000000370013245520654021465 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogfilteredgesbyweight.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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: DialogFilterEdgesByWeight (QWidget *parent = 0); public slots: void gatherData (); signals: void userChoices( float, bool); private: Ui::DialogFilterEdgesByWeight ui; }; #endif socnetv-2.4/src/matrix.h0000775000175000017500000001616313245520654015560 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt matrix.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 MATRIX_H #define MATRIX_H #include #include //for static const QString declares below #include // std::pair, std::make_pair using namespace std; //or else compiler groans for nothrow class QTextStream; #ifdef Q_OS_WIN32 static const QString infinity = "\u221E" ; #else static const QString infinity = QString("\xE2\x88\x9E") ; #endif static const int METRIC_NONE = -1; static const int METRIC_SIMPLE_MATCHING = 0; static const int METRIC_JACCARD_INDEX = 1; static const int METRIC_HAMMING_DISTANCE = 2; static const int METRIC_COSINE_SIMILARITY = 3; static const int METRIC_EUCLIDEAN_DISTANCE = 4; static const int METRIC_MANHATTAN_DISTANCE= 5; static const int METRIC_PEARSON_COEFFICIENT = 6; static const int METRIC_CHEBYSHEV_MAXIMUM= 7; class MatrixRow { public: MatrixRow (int cols=0) { cell=new (nothrow) float [m_cols=cols]; Q_CHECK_PTR( cell ); for (int i=0;i. * ********************************************************************************/ #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 = 0); 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 socnetv-2.4/src/graph.cpp0000775000175000017500000264516513245520654015724 0ustar dimitrisdimitris/****************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graph.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://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 //used for qDebug messages #include #include #include #include #include //allows the use of RAND_MAX macro #include #include //for BFS queue Q #include // for randomizeThings static qreal Pi = 3.14159265; /** * @brief Graph::Graph * constructor */ Graph::Graph() { m_totalVertices=0; m_totalEdges=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; order=true; //returns true if the indexes of the list is ordered. graphModifiedFlag=false; m_graphName=""; m_curRelation=0; m_fileFormat=FILE_UNRECOGNIZED; m_undirected=false; m_isWeighted=false; m_graphDisconnected=false; m_symmetric=true; m_graphDensity = -1; fileName =""; calculatedGraphConnectedness = false; 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; calculatedCentralities=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; m_precision = 3; m_vertexClicked = 0; m_clickedEdge.v1=0; m_clickedEdge.v2=0; file_parser = 0; wc_parser = 0; wc_spider = 0; // edgesHash.reserve(40000); m_graphFileFormatExportSupported<< FILE_GRAPHML << FILE_PAJEK << FILE_ADJACENCY; randomizeThings(); htmlHead = QString("" "" "" "" "" "" "" "" "" "" "" "" "").arg(VERSION); htmlHeadLight = QString("" "" "" "" "" "" "" "" "" "" "").arg(VERSION); htmlEnd = ""; } /** * @brief Graph::~Graph */ Graph::~Graph() { qDebug()<<"Graph::~Graph() - Calling clear()"; clear("exit"); } /** Clears all vertices */ void Graph::clear(const QString &reason) { qDebug()<< "Graph::clear() - 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=FILE_UNRECOGNIZED; m_graphName=""; m_totalVertices=0; m_totalEdges=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; m_vertexClicked = 0; m_clickedEdge.v1=0; m_clickedEdge.v2=0; order=true; //returns true if the vpositions of the list is ordered. m_undirected=false; m_isWeighted=false; m_graphDisconnected=false; m_symmetric=true; m_graphDensity = -1; calculatedGraphConnectedness = false; 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; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; graphModifiedFlag=false; qDebug()<< "Graph::clear() - m_graph cleared. Now reports size" << m_graph.size() << "emitting graphModifiedSet()"; graphLoadedTerminateParserThreads("clear"); webCrawlTerminateThreads("clear"); if ( reason != "exit") { graphModifiedSet(graphModifiedFlag,true); } } /** * @brief Graph::canvasSizeSet * Called when MW and GraphicsWidget resizes to update canvasWidth and canvasHeight * @param w * @param h */ void Graph::canvasSizeSet(const int w, const int h){ float fX= (float)(w)/(float)(canvasWidth); float fY= (float)(h)/(float)(canvasHeight); float 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); graphModifiedSet(GRAPH_CHANGED_POSITIONS,false); } canvasWidth = w; canvasHeight= h; 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 */ float 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 { return qMin ( canvasWidth - 30.0 , qMax (30.0 , (double) (rand()%canvasWidth) ) ); } /** * @brief Graph::canvasRandomY * @return * Returns a random y-coordinate adjusted to be visible * inside the canvas usable area */ double Graph::canvasRandomY() const { return qMin ( canvasHeight - 30.0 , qMax (30.0 , (double) (rand()%canvasHeight) ) ); } /** * @brief Graph::relationSet * 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; 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 graphModifiedSet(GRAPH_CHANGED_EDGES)"; graphModifiedSet(GRAPH_CHANGED_EDGES); } } /** * @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" <name() // << " appended with vpos= "<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 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 long int &v1){ qDebug() << "Graph::vertexRemove() - v: " << m_graph[ vpos[v1] ]->name() << " vpos: " << vpos[v1] << " Removing all inbound and outbound edges "; long 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 ( (*it)->hasEdgeTo(v1) != 0) { qDebug()<< "Graph::vertexRemove() - vertex " << (*it)->name() << " has outbound Edge to "<< v1 << ". Removing it."; (*it)->edgeRemoveTo(v1); } if ( (*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 " ; long 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); graphModifiedSet(GRAPH_CHANGED_VERTICES); 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) ; graphModifiedSet(GRAPH_CHANGED_VERTICES); emit setVertexVisibility( (*it)-> name(), toggle ); } } } /** * @brief Graph::vertexIsolated * @param v1 * @return */ bool Graph::vertexIsolated(const long 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::vertexExists * Checks if there is a specific vertex in the graph. * Returns the vpos or -1 * Complexity: O(logN) for vpos retrieval * @param num * @return */ int Graph::vertexExists(const long int &v1){ qDebug () << "Graph: vertexExists() v: " << v1 << " with vpos " << vpos[v1] << " named " << m_graph[ vpos[v1] ] ->name(); if ( m_graph[ vpos[v1] ] ->name() == v1) return vpos[v1]; else return -1; } /** * @brief Graph::vertexExists * 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() == label) { // qDebug()<< "Graph: vertexExists() at pos %i" << i; return i; } i++; } return -1; } /** * @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 ); graphModifiedSet(GRAPH_CHANGED_POSITIONS,false); } /** * @brief Graph::vertexPos * @param v1 * @return */ QPointF Graph::vertexPos(const int &v1){ return m_graph[ vpos[v1] ]->pos(); } /** * @brief Graph::vertexClickedSet * @param v1 * 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. */ 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 long int size) { initVertexSize=size; } /** * @brief Graph::vertexSizeSet * Changes the size.of vertex v * Called from MW Node Properties * @param v * @param size */ void Graph::vertexSizeSet(const long int &v, const int &size) { m_graph[ vpos[v] ]->setSize(size); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); emit setNodeSize(v, size); } /** * @brief Graph::vertexSize * @param v * @return int */ int Graph::vertexSize(const long &v ) { return m_graph[ vpos[v] ]-> size(); } /** * @brief Graph::vertexSizeAllSet * Changes the size.of all vertices * @param size */ void Graph::vertexSizeAllSet(const int size) { qDebug()<< "Graph::vertexSizeAllSet() - 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); } } graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexShapeInit * @param shape */ void Graph::vertexShapeInit(const QString shape) { initVertexShape=shape; } /** * @brief Graph::vertexShapeSet * Changes the shape.of vertex v * @param v1 * @param shape */ void Graph::vertexShapeSet(const int v1, const QString shape){ m_graph[ vpos[v1] ]->setShape(shape); emit setNodeShape(v1, shape); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexShape * Returns the shape of this vertex * @param v1 * @return */ QString Graph::vertexShape(const int &v1){ return m_graph[ vpos[v1] ]->shape(); } /** * @brief Graph::vertexShapeAllSet * Changes the shape.of all vertices * @param shape */ void Graph::vertexShapeAllSet(const QString shape) { qDebug() << "Graph::vertexShapeAllSet - shape " <isEnabled() ){ continue; } else { (*it)->setShape(shape); emit setNodeShape((*it)->name(), shape); } } graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexColorSet * Changes the color of vertex v1 * @param v1 * @param color */ void Graph::vertexColorSet(const long int &v1, const QString &color){ qDebug()<< "Graph: vertexColorSet for "<< v1 << ", vpos " << vpos[v1]<< " with color "<< color; m_graph[ vpos[v1] ]->setColor ( color ); emit setNodeColor ( m_graph[ vpos[v1] ]-> name(), color ); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexColor * @param v1 * @return */ QColor Graph::vertexColor(const long int &v1){ 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 Graph::vertexColorAllSet * Changes the color of all vertices and updates default vertex color * @param color */ void Graph::vertexColorAllSet(const QString &color) { qDebug() << "*** Graph::vertexColorAllSet() " << " to " << color; vertexColorInit(color); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexColorAllSet() vertex " << (*it)->name() << " new color " << color; (*it)->setColor(color) ; emit setNodeColor ( (*it)-> name(), color ); } } graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexNumberColorInit * Changes the initial color of vertices numbers * @param color */ void Graph::vertexNumberColorInit (QString color) { initVertexNumberColor = color; } /** * @brief Graph::vertexNumberSizeInit * Changes the initial size of vertices numbers * @param size */ void Graph::vertexNumberSizeInit (const int &size) { initVertexNumberSize = size; } /** * @brief Graph::vertexNumberSizeSet * Changes the size.of vertex v number * @param v * @param size */ void Graph::vertexNumberSizeSet(const long int &v, const int &size) { m_graph[ vpos[v] ]->setNumberSize (size); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexNumberSizeSetAll * @param size */ void Graph::vertexNumberSizeSetAll(const int &size) { qDebug() << "*** Graph::vertexNumberSizeSetAll() " << " to " << size; vertexNumberSizeInit(size); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberSizeSetAll() vertex " << (*it)->name() << " new size " << size; (*it)->setNumberSize(size) ; emit setNodeNumberSize ( (*it)-> name(), size); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } //Changes the initial distance of vertices numbers void Graph::vertexNumberDistanceInit(const int &distance) { initVertexNumberDistance = distance; } /** * @brief Graph::vertexNumberDistanceSet * Changes the distance.of vertex v number from the vertex * @param v * @param size */ void Graph::vertexNumberDistanceSet(const long int &v, const int &newDistance) { m_graph[ vpos[v] ]->setNumberDistance (newDistance); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); emit setNodeNumberDistance(v, newDistance); } /** * @brief Graph::vertexNumberDistanceSetAll * Changes the distance.of all vertex number from their vertices * @param size */ void Graph::vertexNumberDistanceSetAll(const int &newDistance) { qDebug() << "*** Graph::vertexNumberDistanceSetAll() " << " to " << newDistance; vertexNumberDistanceInit(newDistance); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberDistanceSetAll() vertex " << (*it)->name() << " new distance " << newDistance; (*it)->setNumberDistance(newDistance) ; emit setNodeNumberDistance ( (*it)-> name(), newDistance); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelSet * Changes the label of a vertex v1 * @param v1 * @param label */ void Graph::vertexLabelSet(int v1, 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); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexLabel * Returns the label of a vertex v1 * @param v1 * @return */ QString Graph::vertexLabel(const long int &v1){ return m_graph[ vpos[v1] ]->label (); } /** * @brief Graph::vertexLabelSizeInit * Changes the default size of vertex labels * @param newSize */ void Graph::vertexLabelSizeInit(int newSize) { initVertexLabelSize = newSize; } /** * @brief Graph::vertexLabelSizeSet * Changes the label size of vertex v1 * @param v1 * @param size */ void Graph::vertexLabelSizeSet(const long int &v1, const int &size) { qDebug()<< "Graph: vertexLabelSizeSet for "<< v1 << ", vpos " << vpos[v1]<< " with size "<< size; m_graph[ vpos[v1] ] -> setLabelSize ( size ); emit setNodeLabelSize ( v1, size); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelSizeAllSet * Changes the label size of all vertices * @param size */ void Graph::vertexLabelSizeAllSet(const int &size) { qDebug() << "*** Graph::vertexLabelSizeAllSet() " << " to " << size; vertexLabelSizeInit(size); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelSizeAllSet() vertex " << (*it)->name() << " new size " << size; (*it)->setLabelSize(size) ; emit setNodeLabelSize ( (*it)-> name(), size); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelColorAllSet * Changes the label color of all vertices * @param size */ void Graph::vertexLabelColorAllSet(const QString &color) { qDebug() << "*** Graph::vertexLabelColorAllSet() " << " to " << color; vertexLabelColorInit(color); VList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelColorAllSet() vertex " << (*it)->name() << " new color" << color; (*it)->setLabelColor(color); emit setNodeLabelColor( (*it)-> name(), color); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelColorSet * Changes the label color of vertex v1 * @param v1 * @param color */ void Graph::vertexLabelColorSet(int v1, QString color){ m_graph[ vpos[v1] ]->setLabelColor(color); emit setNodeLabelColor(v1, color); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelColorInit * Changes the default vertex label color * @param color */ void Graph::vertexLabelColorInit(QString color){ initVertexLabelColor=color; } /** * @brief Graph::vertexLabelDistanceSet * Changes the distance.of vertex v label from the vertex * @param v * @param size */ void Graph::vertexLabelDistanceSet(const long int &v, const int &newDistance) { m_graph[ vpos[v] ]->setLabelDistance (newDistance); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); emit setNodeLabelDistance(v, newDistance); } /** * @brief Graph::vertexLabelDistanceAllSet * 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); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelDistanceInit * 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 GraphicsWidget::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 float &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 == EDGE_UNDIRECTED ) { qDebug()<< "-- Graph::edgeCreate() - Creating UNDIRECTED edge." << "Emitting drawEdge signal to GW"; edgeAdd ( v1, v2, weight, type, label, ( (weight==0) ? "blue" : color ) ); 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, EDGE_RECIPROCATED , label, color); emit signalDrawEdge(v1, v2, weight, label, color, EDGE_RECIPROCATED, drawArrows, bezier, initEdgeWeightNumbers); m_undirected = false; } else { qDebug()<< "-- Graph::edgeCreate() - Creating directed edge. Opposite arc does not exist." << "Emitting drawEdge to GW..."; edgeAdd ( v1, v2, weight, EDGE_DIRECTED, label, ( (weight==0) ? "blue" : color ) ); emit signalDrawEdge(v1, v2, weight, label, ( (weight==0) ? "blue" : color ), EDGE_DIRECTED, drawArrows, bezier, initEdgeWeightNumbers); m_undirected = false; m_symmetric=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; graphModifiedSet(GRAPH_CHANGED_EDGES, 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 ; float weight = 1.0; bool drawArrows=true; bool bezier=false; edgeCreate(source, target, weight, initEdgeColor, EDGE_DIRECTED, drawArrows, bezier); } /** * @brief Adds an edge between v1 and v2 * @param v1 * @param v2 * @param weight * @param label * @param color * @param type */ void Graph::edgeAdd (const int &v1, const int &v2, const float &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 ); m_graph [ target ]->edgeAddFrom(v1, weight); m_graph[ source ]->setOutLinkColor(v2, color); m_graph[ source ]->setOutEdgeLabel(v2, label); if ( weight != 1 && weight!=0) { m_isWeighted=true; //not binary graph } if (type == EDGE_RECIPROCATED ){ // make existing opposite edge reciprocal } else if (type == EDGE_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 long int &v1, const long 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 (graphUndirected() || removeOpposite ) { // remove opposite edge m_graph [ vpos[v2] ]->edgeRemoveTo(v1); m_graph [ vpos[v1] ]->edgeRemoveFrom(v2); m_symmetric=true; } else { if ( edgeExists(v2,v1) !=0 ) { m_symmetric=false; } } emit signalRemoveEdge(v1,v2, (graphUndirected() || removeOpposite )); graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * @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(float 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 ); } graphModifiedSet(GRAPH_CHANGED_EDGES); 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 ); } graphModifiedSet(GRAPH_CHANGED_EDGES); emit statusMessage(tr("Unilateral edges have been temporarily disabled.")); } /** * @brief Called from GraphicsWidget::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.v1=v1; m_clickedEdge.v2=v2; // Clear status bar message if (m_clickedEdge.v1 == 0 && m_clickedEdge.v2==0) { emit signalEdgeClicked(); } else { float weight = m_graph[ vpos[ m_clickedEdge.v1] ]->hasEdgeTo(m_clickedEdge.v2); qDebug() << "Graph::edgeClickedSet() - clicked edge weight:"<< weight; int type=EDGE_DIRECTED; // Check if the opposite tie exists. If yes, this is a reciprocated tie if ( edgeExists(m_clickedEdge.v2,m_clickedEdge.v1, false) ) { if (graphUndirected()) { type=EDGE_UNDIRECTED; } else { type=EDGE_RECIPROCATED; } } m_clickedEdge.type = type; emit signalEdgeClicked( m_clickedEdge.v1 ,m_clickedEdge.v2, weight, type, openMenu); } } /** * @brief Returns clicked edge * @return */ ClickedEdge 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 */ float Graph::edgeExists (const long int &v1, const long int &v2, const bool &checkReciprocal) { edgeWeightTemp = 0; edgeWeightTemp = m_graph[ vpos[v1] ]->hasEdgeTo(v2); if (!checkReciprocal) { return edgeWeightTemp; } else { //check if edge is reciprocal if ( edgeWeightTemp!=0 ) { edgeReverseWeightTemp = m_graph[ vpos[v2] ]->hasEdgeTo(v1); 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 long int &v1, const long 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... "; if ( !graphModified() && calculatedEdges ) { qDebug()<< "Graph::edgesEnabled() - Graph unchanged, edges: " << ((graphUndirected()) ? m_totalEdges / 2 : m_totalEdges); return (graphUndirected()) ? m_totalEdges / 2 : m_totalEdges; } 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; return (graphUndirected()) ? m_totalEdges / 2 : m_totalEdges; } /** * @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 long &v1, const long &v2, const float &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); graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * @brief Returns the weight of the edge v1 -> v2 * @param v1 * @param v2 * @return float */ float Graph::edgeWeight (const long &v1, const long &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; graphModifiedSet(GRAPH_CHANGED_EDGES_METADATA); return true; } /** * @brief Changes the color of edge v1 -> v2 * @param v1 * @param v2 * @param color */ void Graph::edgeColorSet(const long &v1, const long &v2, const QString &color){ qDebug()<< "Graph::edgeColorSet() - "<< v1 << " -> "<< v2 <<" vpos ("<< vpos[v1]<< " -> "<setOutLinkColor(v2, color); emit setEdgeColor(v1, v2, color); if (graphSymmetric()) { m_graph[ vpos[v2] ]->setOutLinkColor(v1, color); emit setEdgeColor(v2, v1, color); } graphModifiedSet(GRAPH_CHANGED_EDGES_METADATA); } /** * @brief Returns the color of the directed edge v1 -> v2 * @param v1 * @param v2 * @return */ QString Graph::edgeColor (const long &v1, const long &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 long &v1, const long &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); graphModifiedSet(GRAPH_CHANGED_EDGES_METADATA); } /** * @brief Returns the label of edge v1->v2 * @param v1 * @param v2 * @return */ QString Graph::edgeLabel (const long int &v1, const long 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 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 ( !graphModified() && 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() * Graph::graphConnectedness() * @return */ QList Graph::verticesListIsolated(){ if (!graphModified() && 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 (!graphModified() && !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 (!graphModified() && !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 weight; bool drawArrows = !graphUndirected(); int edgeType=graphUndirected() ? EDGE_UNDIRECTED : EDGE_RECIPROCATED; if (type == SUBGRAPH_CLIQUE) { for (int i=0; i < vList.size(); ++i ) { 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, EDGE_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 ) { 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, EDGE_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 ) { 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, EDGE_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 ) { 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, EDGE_UNDIRECTED, drawArrows); edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } else { edgeTypeSet( vList.value(i), vList.value(j), weight, edgeType ); } } } else { return; } } /** * @brief Sets the graph modification status. * @param graphChangedFlag * @param signalMW */ void Graph::graphModifiedSet(const int &graphNewStatus, const bool &signalMW){ if (graphNewStatus >0 && graphNewStatus < 10){ //minor changes, i.e. vertex positions, labels, etc graphModifiedFlag = (graphModifiedFlag > 10 ) ? graphModifiedFlag : graphNewStatus ; } else { graphModifiedFlag=graphNewStatus; } if (signalMW) { qDebug()<<"Graph::graphModifiedSet() - m_symmetric:" << m_symmetric << "graphModifiedFlag:" << graphModifiedFlag << "Emitting signal signalGraphModified()"; emit signalGraphModified(graphModifiedFlag, graphUndirected(), m_totalVertices, edgesEnabled(), graphDensity()); return; } qDebug()<<"Graph::graphModifiedSet() - m_symmetric " << m_symmetric << "graphModifiedFlag" << graphModifiedFlag << "Not emitting any signal to MW"; } /** * @brief Returns true of graph is modified (edges/vertices added/removed). * else false * @return */ bool Graph::graphModified() const { qDebug() << "Graph::graphModified() - graphModifiedFlag:" << graphModifiedFlag ; return (graphModifiedFlag > 10 ) ? true: false; } /** * @brief Returns true if the graph is saved. * @return */ bool Graph::graphSaved() const { qDebug() << "Graph::graphSaved() - graphModifiedFlag:" << graphModifiedFlag ; return (graphModifiedFlag == 0 ) ? true: false; } /** * @brief Graph::graphLoaded * @return */ bool Graph::graphLoaded() const { qDebug() << "Graph::graphLoaded() - " << (( graphFileFormat() != FILE_UNRECOGNIZED ) ? true: false ); return ( graphFileFormat() != FILE_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 */ float Graph::graphDensity() { qDebug()<< "Graph::graphDensity() - checking if graph modified... "; if (!graphModified() && calculatedGraphDensity) { qDebug()<< "Graph::graphDensity() - graph not modified and" "already calculated density. Returning last value:" << m_graphDensity; return m_graphDensity; } int V=vertices(); if (V!=0 && V!=1) { m_graphDensity = (graphUndirected()) ? (float) 2* edgesEnabled() / (float)(V*(V-1.0)) : (float) edgesEnabled() / (float)(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::graphWeighted(){ qDebug()<< "Graph::graphWeighted()"; if ( ! graphModified() && calculatedGraphWeighted ) { qDebug()<< "Graph::graphWeighted() - graph not modified. Return: " << m_isWeighted; return m_isWeighted; } float 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: graphWeighted() - true. Graph is edge-weighted."; m_isWeighted=true; break; } } if (m_isWeighted) break; } calculatedGraphWeighted = true; qDebug()<< "Graph::graphWeighted() - result" << m_isWeighted; emit signalProgressBoxKill(); return m_isWeighted; } /** Returns the sum of vertices having edgesOutbound */ int Graph::verticesWithOutboundEdges(){ return outboundEdgesVert; } /** Returns the sum of vertices having edgesInbound */ int Graph::verticesWithInboundEdges(){ return inboundEdgesVert; } /** Returns the sum of vertices having reciprocal edges */ 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..."; if (wc_spiderThread.isRunning() ) { // qDebug() << "Graph::webCrawlTerminateThreads() - deleting wc_spider pointer"; // delete wc_spider; // wc_spider= 0; // see why here: https://goo.gl/tQxpGA qDebug() << "Graph::webCrawlTerminateThreads() - wc_spiderThread running. " "Calling wc_spiderThread.quit()"; wc_spiderThread.quit(); } qDebug() << "Graph::webCrawlTerminateThreads() - Checking wc_parserThread..."; if (wc_parserThread.isRunning() ) { qDebug() << "Graph::webCrawlTerminateThreads() - wc_parserThread running. " "Calling wc_parserThread.quit()"; wc_parserThread.quit(); } } /** * @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 &urlSeed, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &extLinks, const bool &intLinks, const bool &selfLinks, const bool &delayedRequests){ relationCurrentRename(tr("web"), true); // TOFIX Due to multithreading, app crashes when crawler finishes its job. qDebug() << "Graph::webCrawl() - Graph thread:" << thread() << "seed url:" << urlSeed ; qDebug() << "Graph::webCrawl() - Creating wc_spider & wc_parser objects"; wc_parser = new WebCrawler_Parser(); wc_spider = new WebCrawler_Spider (); qDebug() << "Graph::webCrawl() - Moving wc_parser from thread:" << wc_parser->thread() << "and wc_spider from 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() - Connecting signals from/to parser & spider"; connect(&wc_parserThread, &QThread::finished, wc_parser, &QObject::deleteLater); connect(&wc_spiderThread, &QThread::finished, wc_spider, &QObject::deleteLater); connect(this, &Graph::operateSpider, wc_spider, &WebCrawler_Spider::get); connect(wc_parser, &WebCrawler_Parser::signalCreateNode, this, &Graph::vertexCreateAtPosRandomWithLabel); connect(wc_parser, &WebCrawler_Parser::signalCreateEdge, this, &Graph::edgeCreateWebCrawler); connect (wc_spider, &WebCrawler_Spider::finished, this, &Graph::webCrawlTerminateThreads); connect (wc_parser, &WebCrawler_Parser::finished, this, &Graph::webCrawlTerminateThreads); connect (wc_spider, &WebCrawler_Spider::parse, wc_parser, &WebCrawler_Parser::parse ); connect (wc_parser, &WebCrawler_Parser::startSpider, wc_spider, &WebCrawler_Spider::get ); qDebug() << "Graph::webCrawl() - Starting wc_parser & wc_spider threads!"; wc_parserThread.start(); wc_spiderThread.start(); qDebug() << "Graph::webCrawl() - loading wc_parser & wc_spider data!"; wc_parser->load(urlSeed, urlPatternsIncluded, urlPatternsExcluded, linkClasses, maxNodes, maxLinksPerPage, extLinks, intLinks, selfLinks); wc_spider->load (urlSeed, maxNodes, delayedRequests); qDebug() << "Graph::webCrawl() - Creating initial node 1, seed url:" << urlSeed; vertexCreateAtPosRandomWithLabel(1, urlSeed, 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 */ float Graph::graphReciprocity(){ qDebug() << "Graph::graphReciprocity() "; if (!graphModified() && calculatedGraphReciprocity){ qDebug() << "Graph::graphReciprocity() - graph not modified and " "already calculated reciprocity. Returning previous result: " << m_graphReciprocityArc; return m_graphReciprocityArc; } m_graphReciprocityArc=0; m_graphReciprocityDyad=0; m_graphReciprocityTiesReciprocated=0; m_graphReciprocityTiesNonSymmetric=0; m_graphReciprocityTiesTotal=0; m_graphReciprocityPairsReciprocated=0; m_graphReciprocityPairsTotal=0; float 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 = (float) m_graphReciprocityTiesReciprocated / (float) m_graphReciprocityTiesTotal; m_graphReciprocityPairsReciprocated = reciprocatedDyads.count(); m_graphReciprocityPairsTotal = totalDyads.count(); m_graphReciprocityDyad = (float) m_graphReciprocityPairsReciprocated / (float) 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) { Q_UNUSED(considerWeights); QTime computationTimer; computationTimer.start(); qDebug() << "Graph::writeReciprocity"; 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"); if (graphModified() || !calculatedGraphReciprocity){ qDebug() << "Graph::writeReciprocity() - graph modified or " "reciprocity not computed yet. Recomputing. "; m_graphReciprocityArc = graphReciprocity(); } int rowCount=0; int progressCounter=0; int N = vertices(); float tiesSym=0; float tiesNonSym=0; float tiesOutNonSym=0; float tiesInNonSym=0; float tiesOutNonSymTotalOut=0; float tiesInNonSymTotalIn=0; QString pMsg = tr("Writing Reciprocity to file. \nPlease wait..."); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N,pMsg); outText << htmlHead; outText.setRealNumberPrecision(m_precision); 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 =(float) (*it)->outEdgesReciprocated() / (float) ( (*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()) ? (float) (*it)->outEdgesNonSym() / (float) ((*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()) ? (float) (*it)->inEdgesNonSym() / (float) ((*it)->outEdgesNonSym() + (*it)->inEdgesNonSym()) : 0; // nonSym Out/Out. Proportion of non-symmetric outgoing ties to the total outgoing ties. tiesOutNonSymTotalOut = ( (*it)->outEdges() != 0) ? (float) (*it)->outEdgesNonSym() /(float) (*it)->outEdges() : 0; // nonSym In/In. Proportion of non-symmetric incoming ties to the total incoming ties. tiesInNonSymTotalIn = ( (*it)->inEdges() != 0) ? (float) (*it)->inEdgesNonSym() / (float) (*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(10) : "-" ) << "" << 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::graphSymmetric(){ qDebug() << "Graph::graphSymmetric() "; if (!graphModified() && calculatedGraphSymmetry){ qDebug() << "Graph::graphSymmetric() - graph not modified and " "already calculated symmetry. Returning previous result: " << m_symmetric; return m_symmetric; } m_symmetric=true; int v2=0, v1=0; float 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_symmetric=false; // qDebug() <<"Graph::graphSymmetric() - " // << " graph not symmetric because " // << v1 << " -> " << v2 << " weight " << weight // << " differs from " << v2 << " -> " << v1 ; break; } ++hit; } } //delete enabledOutEdges; qDebug() << "Graph: graphSymmetric() - Finished. Result:" << m_symmetric; calculatedGraphSymmetry = true; return m_symmetric; } /** * @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; float 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::null, false); } else { qDebug() << "Graph: graphSymmetrize(): v1 = " << v1 << " is already inLinked from v2 = " << v2 ; if (weight!= invertWeight ) edgeWeightSet(v2,v1,weight); } ++it1; } } //delete enabledOutEdges; m_symmetric=true; graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * @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, EDGE_UNDIRECTED, true, false, QString::null, false); } j++; } i++; } m_symmetric=true; graphModifiedSet(GRAPH_CHANGED_EDGES); qDebug()<< "Graph::graphCocitation()" << "final relations"< enabledOutEdges; QHash::const_iterator it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph::graphUndirectedSet() - iterate over edges of v1 " << v1; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges.cbegin(); while ( it1!=enabledOutEdges.cend() ){ v2 = it1.key(); weight = it1.value(); qDebug() << "Graph::graphUndirectedSet() - " << " v1 " << v1 << " -> " << v2 << " = " << " weight " << weight; edgeTypeSet(v1,v2, weight, EDGE_UNDIRECTED); ++it1; } } //delete enabledOutEdges; m_symmetric=m_undirected=true; graphModifiedSet(GRAPH_CHANGED_EDGES, signalMW); } /** * @brief Returns true if graph is undirected * @return */ bool Graph::graphUndirected() { qDebug() << "Graph::graphUndirected() -" < " << v2 << "edgeType" << dirType; if (dirType!=EDGE_DIRECTED) { // check for opposite edge float 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=EDGE_UNDIRECTED we add the opposite edge as EDGE_RECIPROCATED edgeAdd(v2,v1, weight, EDGE_RECIPROCATED, "", initEdgeColor); } else { if ( dirType == EDGE_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 EDGE_RECIPROCATED we don't need to equalize weights } } emit signalEdgeType( v1, v2, dirType ); } //graphModifiedSet(GRAPH_CHANGED_EDGES); //m_undirected = true; } /** * @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()"; graphDistanceGeodesicCompute(false); return ( m_graph[ vpos[v1] ] ->distance( v2) != RAND_MAX ) ? true: false; } /** * @brief Creates the reachability matrix XRM */ void Graph::graphMatrixReachabilityCreate() { qDebug() << "Graph::graphMatrixReachabilityCreate()"; if ( !calculatedDistances || graphModified() ) { graphDistanceGeodesicCompute(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 (lenght 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()"; graphDistanceGeodesicCompute(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()" ; graphDistanceGeodesicCompute(false, considerWeights, inverseWeights, false); return m_graphDiameter; } /** * @brief Returns the average distance of the graph * @param considerWeights * @param inverseWeights * @param dropIsolates * @return */ float Graph::graphDistanceGeodesicAverage(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ Q_UNUSED(considerWeights); Q_UNUSED(inverseWeights); graphConnectedness(); qDebug() <<"Graph::graphDistanceGeodesicAverage() - m_vertexPairsNotConnected " << m_vertexPairsNotConnected.count(); int N=vertices(dropIsolates);//TOFIX if (m_graphAverageDistance!=0) { if (m_vertexPairsNotConnected.count()==0) { return m_graphAverageDistance / ( N * ( N-1.0 ) ); } 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?) return m_graphAverageDistance / m_graphGeodesicsCount; } } else return 0; } /** * @brief Returns the number of geodesics (shortest-paths) in the graph. * @return */ int Graph::graphGeodesics() { qDebug()<< "Graph::graphGeodesics()"; graphDistanceGeodesicCompute(false, false,false,false); qDebug()<< "Graph::graphGeodesics() - geodesics:" << m_graphGeodesicsCount; return m_graphGeodesicsCount; } /** * @brief Excamines the Connectedness of the graph and returns an integer code. * @return int: * 2: strongly connected digraph (exists path from i to j and vice versa for every i,j) * 1: connected undirected graph * 0: not connected undirected graph no isolates * -1: not connected undirected graph with isolates * -2: unilaterally connected digraph (exists path only from i to j or from j to i, not both) * -3 disconnected digraph (there are unconnected pairs, with isolates). * -4 disconnected digraph (there are unconnected pairs, no isolates). * * Used by * MW::slotConnectedness() * MW::slotAnalyzeCentralityCloseness() * MW::slotLayoutRadialByProminenceIndex(QString ) * MW::slotLayoutNodeSizeByProminenceIndex(QString ) * MW::slotLayoutLevelByProminenceIndex(QString ) * */ int Graph::graphConnectedness(const bool updateProgress) { qDebug() << "Graph::graphConnectedness() "; if (calculatedGraphConnectedness && !graphModified()) { qDebug()<< "Graph::graphConnectedness() - graph unmodified. Returning:" << m_graphConnectedness; return m_graphConnectedness; } //initially if graph is undirected, assume it is connected. // if is directed, assume it is strongly connected. if ( graphUndirected() ) { m_graphConnectedness = 1; } else { m_graphConnectedness = 2; } graphDistanceGeodesicCompute(false, false,false,false); int progressCounter=0; int N = vertices(); VList::const_iterator it, jt; m_vertexPairsNotConnected.clear(); m_vertexPairsUnilaterallyConnected.clear(); // int isolatedVertices=verticesListIsolated().count(); bool isolatedVertices = false; if (updateProgress) { QString pMsg = tr("Computing Network Connectedness. \nPlease wait..."); emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); } for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if (updateProgress) { emit signalProgressBoxUpdate(++progressCounter); } for (jt=it; jt!=m_graph.cend(); ++jt) { if ( graphUndirected() ) { if ( (*it)->distance( (*jt)-> name() ) == RAND_MAX ) { // not connected because there is no path connecting (i,j) m_vertexPairsNotConnected.insertMulti((*it)-> name(),(*jt)-> name()); if ( (*it)->isIsolated() || (*jt)->isIsolated() ) { isolatedVertices = true; } } } else { if ( (*it)->distance( (*jt)-> name() ) != RAND_MAX ) { if ( (*jt)->distance( (*it)-> name() ) == RAND_MAX ) { // unilaterly connected because there is only a path i -> j m_vertexPairsUnilaterallyConnected.insertMulti((*it)-> name(),(*jt)-> name()); //m_vertexPairsNotConnected.insertMulti(j,i); } else { //strongly connected pair } } else { if ( (*jt)->distance( (*it)-> name() ) == RAND_MAX ) { // not connected because there is no path connecting (i,j) or (j,i) m_vertexPairsNotConnected.insertMulti( (*it)-> name(),(*jt)-> name() ); if ( (*it)->isIsolated() || (*jt)->isIsolated() ) { isolatedVertices = true; } } else { // unilaterly connected because there is only a path j -> i m_vertexPairsUnilaterallyConnected.insertMulti((*jt)-> name(),(*it)-> name() ); } } } } } if ( graphUndirected() ) { if ( m_vertexPairsNotConnected.count()>0) { if (isolatedVertices) m_graphConnectedness = -1; else m_graphConnectedness = 0; } else m_graphConnectedness = 1; } else { if (m_vertexPairsNotConnected.count()>0) { if (isolatedVertices) m_graphConnectedness = -3; else m_graphConnectedness = -4; } else if (m_vertexPairsUnilaterallyConnected.count() > 0) { m_graphConnectedness = -2; } else m_graphConnectedness = 2; } if (updateProgress) { emit signalProgressBoxKill(); } calculatedGraphConnectedness = true; return m_graphConnectedness; } /** * @brief Creates the shortest paths (geodesic) matrix SIGMA * Each SIGMA(i,j) element 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()"; if ( !calculatedDistances || graphModified() ) { graphDistanceGeodesicCompute(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 Geodesic Distances matrix * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::graphMatrixDistanceGeodesicCreate(const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::graphMatrixDistanceGeodesicCreate()"; if ( !calculatedDistances || graphModified() ) { graphDistanceGeodesicCompute(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::graphDistanceGeodesicCompute(const bool &computeCentralities, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::graphDistanceGeodesicCompute()" << "centralities" << computeCentralities << "considerWeights:"<::iterator it2; int w=0, u=0,s=0, i=0, si=0, ui=0, wi=0; int progressCounter=0; qDebug() << "Graph::graphDistanceGeodesicCompute() - Recomputing geodesic distances."; //drop isolated vertices from calculations (i.e. std C and group C). int N = vertices(dropIsolates,false); int E = edgesEnabled(); QString pMsg = tr("Computing geodesic distances. \nPlease wait..."); emit statusMessage ( pMsg ); emit signalProgressBoxCreate(N, pMsg ); m_symmetric = graphSymmetric(); qDebug() << "Graph::graphDistanceGeodesicCompute() - m_symmetric" << m_symmetric ; 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); } } m_graphDisconnected = true; } else { qDebug() << "Graph::graphDistanceGeodesicCompute() - Initializing variables"; float CC=0, BC=0, SC= 0, eccentricity=0, EC=0, PC=0; float SCC=0, SBC=0, SSC=0, SEC=0, SPC=0; float tempVarianceBC=0, tempVarianceSC=0,tempVarianceEC=0; float tempVarianceCC=0, tempVariancePC=0; float sigma_u=0, sigma_w=0; float delta_u=0, delta_w=0; float d_sw=0, d_su=0; m_graphDisconnected = false; H_f_i::const_iterator hfi ; // for Power Centrality qDebug() << "Graph: graphDistanceGeodesicCompute() - 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_graphGeodesicsCount = 0; //non zero distances m_verticesInfiniteEccentricity.clear(); // counts vertices with infinite eccentricity qDebug() << " m_graphDiameter "<< m_graphDiameter << " m_graphAverageDistance " <setDistance((*it1)->name(), RAND_MAX); // NOT NEEDED WE DO IT INSIDE GraphVertex::distance() (*it)->clearDistance(); // Set all pair-wise shortest-path counts (sigmas) to 0 // (*it)->setShortestPaths((*it1)->name(), 0); (*it)->clearShortestPaths(); } //Zero centrality indeces of each vertex if (computeCentralities) { qDebug() << " Graph:graphDistanceGeodesicCompute() -" "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)->setPC( 0.0 ); } } qDebug() << "Graph: graphDistanceGeodesicCompute() - " " initialising variables for max centrality scores"; if (m_symmetric) { 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_symmetric - 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); } 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]; 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(); } i=1; 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(); //initialize all sizeOfNthOrderNeighborhood to zero //sizeOfNthOrderNeighborhood.insert(i, 0); i++; } 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) { // Compute Closeness Centrality if ( (*it)->CC() != 0 ) { CC=1.0/(*it)->CC(); //Closeness centrality must be inverted } else { CC=0; } (*it)->setCC( CC ); 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); } 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() << "Ps[w]" ; for ( it2=lst.begin(); it2 != lst.end(); it2++ ){ qDebug() << (*it2); } 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.begin(); it2 != lst.end(); 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 ) * ( (float) sigma_u / (float)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); } 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 computeCentralities } // END for SSSP problem qDebug() << "*********** MAIN LOOP (SSSP problem): FINISHED."; // check if there are disconnected nodes qDebug() << "Checking if there are disconnected nodes"; m_graphDisconnected = false; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( ! (*it)->isEnabled() ) { qDebug()<< "actor i" << (*it)->name() << "disabled. SKIP/CONTINUE"; continue; } for ( it1=it; 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; } if ( (*it)-> distance ( (*it1)->name() ) == RAND_MAX) { m_verticesInfiniteEccentricity.append((*it)->name()); (*it)->setEccentricity( RAND_MAX ); qDebug()<< "actor i" << (*it)->name() << "has infinite eccentricity. " "There is no path from i to j" << (*it1)->name(); if ( (*it1)-> distance ( (*it)->name() ) == RAND_MAX) { m_verticesInfiniteEccentricity.append((*it1)->name()); m_graphDisconnected = true; (*it1)->setEccentricity( RAND_MAX ); qDebug()<< "actor j" << (*it1)->name() << "has also infinite eccentricity. " "There is no path from j to i" << (*it1)->name(); } } } 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 for disconnected checking if (computeCentralities) { qDebug() << "Graph: graphDistanceGeodesicCompute() - " "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_symmetric) { 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_symmetric){ (*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: graphDistanceGeodesicCompute() -" "Computing mean centrality values..."; // Compute mean values and prepare to compute variances meanSBC = sumSBC /(float) N ; varianceSBC=0; tempVarianceBC=0; meanSCC = sumSCC /(float) N ; varianceSCC=0; tempVarianceCC=0; meanSPC = sumSPC /(float) N ; varianceSPC=0; tempVariancePC=0; meanEC = sumEC /(float) N ; varianceEC=0; tempVarianceEC=0; qDebug() << "Graph: graphDistanceGeodesicCompute() - " "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 /= (float) N; varianceSCC /= (float) N; varianceSPC /= (float) N; varianceEC /= (float) N; // calculate SC mean value and prepare to compute variance meanSSC = sumSSC /(float) 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 /= (float) 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::graphDistanceGeodesicCompute()- 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 %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_graphAverageDistance += dist_w; m_graphGeodesicsCount++; qDebug()<< "== BFS - d(" << s <<"," << w <<")=" << m_graph[si]->distance(w) << " - inserting " << w << " to inflRange J of " << s << " - and " << s << " to inflDomain I of "<< 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; float weight=0, dist_u=0, 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"; 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 graphDistanceGeodesicCompute // 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 ) ; temp = m_graph[si]->shortestPaths(w) + m_graph[si]->shortestPaths(u); 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 < m_graph [ si ]->distance( w ) ) { qDebug() <<" --- dijkstra: dist_w =" << dist_w << " < d(s,w) =" << m_graph [ si ]->distance( 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_graphAverageDistance += dist_w; m_graphGeodesicsCount++; qDebug() << " --- dijkstra: " "Set d ( s=" << s << ", w="<< w << " ) = "<< dist_w << "="<< m_graph[si]->distance(w) << " m_graphAverageDistance =" << m_graphAverageDistance << "Inserting" << w << "to inflRange J of" << s << "and" << s << "to inflDomain I of"<< 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); m_graph [si]->setCC (m_graph [si]->CC() + dist_w); qDebug() << " --- dijkstra: Compute Centralities: " "For CC: sum of distances =" << m_graph [si]->CC() << " (will invert it l8r)"; 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; } qDebug() << " --- dijkstra: LOOP END over every edge ("<< u <<", w ) e E... "; } qDebug() << "### dijkstra: LOOP END. prQ is empty - Returning."; } /** * @brief Computes minimum and maximum centralities during graphDistanceGeodesicCompute() * @param C * @param v * @param max * @param min * @param maxNode * @param minNode */ void Graph::minmax(float C, GraphVertex *v, float &max, float &min, int &maxNode, int &minNode) { qDebug() << "MINMAX C = " << C << " max = " << max << " min = " << min << " name = " << v->name(); if (C > max ) { max=C; maxNode=v->name(); } if (C < min ) { min=C; minNode=v->name(); } } /** * @brief Calculates the number of discrete centrality classes of all vertices It stores that number in a QHash type where the centrality value is the key. Called from graphDistanceGeodesicCompute() * @param C * @param discreteClasses * @param classes */ void Graph::resolveClasses(float C, H_StrToInt &discreteClasses, int &classes){ H_StrToInt::iterator it2; it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), classes); } } /** * @brief Overloaded method. It only adds displaying current vertex for debugging purposes. * @param C * @param discreteClasses * @param classes * @param vertex */ void Graph::resolveClasses(float C, H_StrToInt &discreteClasses, int &classes, int vertex){ H_StrToInt::iterator it2; Q_UNUSED(vertex); it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), classes); } } /** * @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_precision); 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(10) : "-" ) << "" << ((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 && !graphModified()) { 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; float m_weight=0, weightSum=1, diagonalEntriesSum=0, rowSum=0; float IC=0, SIC=0; /* Note: isolated nodes must be dropped from the AM Otherwise, the 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) ; } float x=0; meanIC = sumIC /(float) n ; varianceIC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ x = ( (*it)->SIC() - meanIC ) ; x *=x; varianceIC += x; } varianceIC /= (float) n; calculatedIC = true; 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()"; QTime 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"); if (graphModified() || !calculatedIC ) { centralityInformation(considerWeights, inverseWeights); } 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 << "

"; 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(); } //Writes the eigenvector centralities to a file void Graph::writeCentralityEigenvector(const QString fileName, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates){ QTime 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"); if (graphModified() || !calculatedIC ) { emit statusMessage ( (tr("Calculating EVC scores...")) ); centralityEigenvector(considerWeights, inverseWeights,dropIsolates); } 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_precision); 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(10) : "-" ) << "" << (*it)->EVC() << "" << (*it)->SEVC() << "" << (*it)->EVC() / sumEVC << "" << (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 << "

"; 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 (!graphModified() && calculatedEVC ) { qDebug() << "Graph::centralityEigenvector() - graph not changed - returning"; return; } 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); float EVC[N]; graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); QString pMsg = tr("Computing Eigenvector Centrality scores. \nPlease wait...") ; emit statusMessage( pMsg ); emit signalProgressBoxCreate(N,pMsg); if (useDegrees) { 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 { 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 / (float) N; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (!(*it)->isIsolated() && dropIsolates) { continue; } (*it) -> setEVC( EVC[i]); (*it) -> setSEVC( EVC[i] / maxEVC); varianceEVC += (EVC[i]-meanEVC) * (EVC[i]-meanEVC) ; i++; } varianceEVC=varianceEVC/(float) 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. emit signalProgressBoxUpdate( N ); emit signalProgressBoxKill(); } //Calculates the degree (outDegree) centrality of each vertex - diagonal included void Graph::centralityDegree(const bool &weights, const bool &dropIsolates){ qDebug("Graph::centralityDegree()"); if (!graphModified() && calculatedDC ) { qDebug() << "Graph::centralityDegree() - graph not changed - returning"; return; } float DC=0, nom=0, denom=0, SDC=0; float weight; 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; H_StrToInt::iterator it2; 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 ) { // 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_symmetric = 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; it2 = discreteSDCs.find(QString::number(SDC)); if (it2 == discreteSDCs.end() ) { classesSDC++; // qDebug("This is a new DC class"); discreteSDCs.insert ( QString::number(SDC), 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 / (float) 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/(float) N; // qDebug() << "Graph::centralityDegree() - variance = " << varianceSDC; if (m_symmetric) { // 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 Graph::writeCentralityDegree * @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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 (!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) { QTime 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 (!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 Graph::centralityClosenessIR * 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 (!graphModified() && calculatedIRCC ) { qDebug() << "Graph::centralityClosenessIR() - " " graph not changed - returning"; return; } if (!calculatedDistances || graphModified()) { graphDistanceGeodesicCompute(false,considerWeights,inverseWeights,dropIsolates); } // calculate centralities VList::const_iterator it, jt; int progressCounter = 0; float IRCC=0,SIRCC=0; float Ji=0; float dist=0; float N=vertices(dropIsolates); 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); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit signalProgressBoxUpdate(++progressCounter); IRCC=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 ) { IRCC += dist; Ji ++; // compute |Ji| } } // sanity check for IRCC=0 (=> node is disconnected) if (IRCC != 0) { IRCC /= Ji; IRCC = ( Ji / (float) (N-1) ) / IRCC; } sumIRCC += IRCC; (*it) -> setIRCC ( IRCC ) ; (*it) -> setSIRCC ( IRCC ) ; // IRCC is a ratio, already std resolveClasses(IRCC, discreteIRCCs, classesIRCC); minmax( IRCC, (*it), maxIRCC, minIRCC, maxNodeIRCC, minNodeIRCC) ; } meanIRCC = sumIRCC / (float) 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/(float) N; calculatedIRCC=true; emit signalProgressBoxKill(); } //Writes the "improved" closeness centrality indices to a file void Graph::writeCentralityClosenessInfluenceRange(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime 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); 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_precision); 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 metric is similar to Closeness Centrality " "but it counts only outbound distances from each actor to other reachable nodes.
" "This metric 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(10) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 << "

"; 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) { QTime 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"); if (graphModified() || !calculatedCentralities ) { graphDistanceGeodesicCompute(true, considerWeights, inverseWeights, dropIsolates); } else { qDebug() << "Graph::writeCentralityBetweenness() -" "No need to recompute Distances/Centralities. Writing file."; } 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 (!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(); } //Writes the Stress centralities to a file void Graph::writeCentralityStress( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime 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"); if (graphModified() || !calculatedCentralities ) { graphDistanceGeodesicCompute(true, considerWeights, inverseWeights,dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 << "

"; 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 Graph::writeCentralityEccentricity * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityEccentricity(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime 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"); if (graphModified() || !calculatedCentralities ) { graphDistanceGeodesicCompute(true, considerWeights, inverseWeights,dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 << "

"; 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 Graph::writeCentralityPower * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityPower(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime 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"); if (graphModified() || !calculatedCentralities ) { graphDistanceGeodesicCompute(true, considerWeights, inverseWeights, dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 (!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 Graph::prestigeDegree * Calculates 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 (!graphModified() && 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; H_StrToInt::iterator it2; QHash *enabledInEdges = new QHash; QHash::const_iterator hit; float DP=0, SDP=0, nom=0, denom=0; float weight; classesSDP=0; sumSDP=0; sumDP=0; maxSDP=0; minSDP=N-1; discreteDPs.clear(); varianceSDP=0; meanSDP=0; m_symmetric = 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_symmetric=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 (); it2 = discreteDPs.find(QString::number(SDP)); if (it2 == discreteDPs.end() ) { classesSDP++; qDebug("This is a new DP class"); discreteDPs.insert ( QString::number(SDP), 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 / (float) 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/(float) N; if (m_symmetric) 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 Graph::writePrestigeDegree * @param fileName * @param considerWeights * @param dropIsolates */ void Graph::writePrestigeDegree (const QString fileName, const bool considerWeights, const bool dropIsolates) { QTime 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); VList::const_iterator it; int N = vertices(); float 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 (!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 (!graphModified() && calculatedPP ) { qDebug() << "Graph::prestigeProximity() - " " graph not changed - returning"; return; } graphDistanceGeodesicCompute(false,considerWeights, inverseWeights,inverseWeights); // calculate centralities VList::const_iterator it, jt; float PP=0; float dist=0; float Ii=0; float V=vertices(dropIsolates); classesPP=0; discretePPs.clear(); sumPP=0; maxPP=0; minPP=V-1; variancePP=0; meanPP=0; H_StrToInt::iterator it2; 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 it2 = discretePPs.find(QString::number(PP)); if (it2 == discretePPs.end() ) { classesPP++; qDebug() << "PP = " << (*it) -> PP() << " - this is a new PP class" ; discretePPs.insert ( QString::number(PP), classesPP ); } //qDebug("PP classes = %i ", classesPP); if (maxPP < PP ) { maxPP = PP ; maxNodePP=(*it)->name(); } if (minPP > PP ) { minPP = PP ; minNodePP=(*it)->name(); } } if (minPP == maxPP) maxNodePP=-1; meanPP = sumPP / 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) { QTime 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); 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 << "

"; 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 Graph::prestigePageRank * Calculates the PageRank Prestige of each vertex * @param dropIsolates */ void Graph::prestigePageRank(const bool &dropIsolates){ qDebug()<< "Graph::prestigePageRank()"; if (! graphModified() && 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; float PRP=0, oldPRP = 0; float SPRP=0; int iterations = 1; // a counter int referrer; float delta = 0.00001; // The delta where we will stop the iterative calculation float maxDelta = RAND_MAX; float sumInLinksPR = 0; // temp var for inlinks sum PR float transferedPRP = 0; float inLinks = 0; // temp var float outLinks = 0; // temp var float 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 / (float) 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) / (float) 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 / (float) 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 / (float) N; qDebug() << "PRP' Variance: " << variancePRP ; calculatedPRP= true; emit signalProgressBoxUpdate( N); emit signalProgressBoxKill(); return; } /** * @brief Graph::writePrestigePageRank * Writes the PageRank indices to a file * @param fileName * @param dropIsolates */ void Graph::writePrestigePageRank(const QString fileName, const bool dropIsolates){ QTime 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); 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_precision); 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(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*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 << "

"; 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 Graph::randomizeThings * 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 float &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") { graphUndirectedSet(true); } 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, 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, EDGE_UNDIRECTED, false, false, QString::null, false); } else { qDebug() << "Graph::randomNetErdosCreate() - " <<" create directed Edge no " << edgeCount; edgeCreate(i+1, j+1, 1, initEdgeColor, EDGE_DIRECTED, true, false, QString::null, 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, EDGE_UNDIRECTED, false, false, QString::null, false); } else { qDebug() << "Graph::randomNetErdosCreate() - create " << " directed Edge no " << edgeCount; edgeCreate(source, target, 1, initEdgeColor, EDGE_DIRECTED, true, false, QString::null, false); } emit signalProgressBoxUpdate(++progressCounter ); } while ( edgeCount != m ); } relationCurrentRename(tr("erdos-renyi"), true); emit signalProgressBoxUpdate((m != 0 ? m:N)); emit signalProgressBoxKill(); graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); } /** * @brief Graph::randomNetRingLatticeCreate * 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* Pi/ N ); graphUndirectedSet(true); 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, false); qDebug("Graph: createPhysicistLatticeNetwork, new node i=%i, at x=%i, y=%i", i+1, x,y); } int target = 0; for (int i=0;i " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EDGE_UNDIRECTED, false, false, QString::null, 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,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, EDGE_UNDIRECTED, false, false, QString::null, false); newEdges ++; } else { qDebug() << "Graph::randomNetScaleFreeCreate() -----> " "Creating pref.att. directed edge " << i+1 << " <-> " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EDGE_DIRECTED, true, false, QString::null, 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 " "graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES)"; graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); emit signalProgressBoxKill(); layoutVertexSizeByIndegree(); } /** * @brief Graph::randomNetSmallWorldCreate * Creates a small world 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") { graphUndirectedSet(true); } 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, EDGE_UNDIRECTED, false, false, QString::null, false); break; } } } else qDebug("Will not break link!"); } } emit signalProgressBoxUpdate( ++progressCounter ); } relationCurrentRename(tr("small-world"), true); emit signalProgressBoxKill(); layoutVertexSizeByIndegree(); graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); } /** * @brief Graph::randomNetRegularCreate * 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); m_undirected = (mode == "graph") ? true: false; int x = 0, y = 0 ; float progressCounter=0; float progressFraction =(m_undirected) ? 2/(float) degree : 1/(float) 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,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] ) || (m_undirected && m_edges.contains( secondEdgeVertices[1] + "->" + firstEdgeVertices[0]) )|| (m_undirected && 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() - 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, (m_undirected) ? EDGE_UNDIRECTED : EDGE_DIRECTED, (m_undirected) ? false:true, false, QString::null, 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(); graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); } /** * @brief Graph::walksBetween * 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(); } /** * 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). * This function is for digraphs only */ QList Graph::vertexinfluenceRange(int v1){ qDebug() << "Graph::vertexinfluenceRange() - vertex:"<< v1; if ( !calculatedDistances || graphModified() ) { graphDistanceGeodesicCompute(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::vertexinfluenceDomain() - target:" << target << "disabled. SKIP"; continue; } if ((*jt)->distance( v1 ) != RAND_MAX ) { qDebug() << "Graph::vertexinfluenceDomain() - v1 can reach:" << target; influenceRanges.insertMulti(v1,target); } } emit signalProgressBoxKill(); return influenceRanges.values(v1); } /** * @brief Graph::vertexinfluenceDomain * Returns the influence domain of vertex v1, namely the set of nodes who can * reach v1 * This function applies to digraphs only * @param v1 * @return */ QList Graph::vertexinfluenceDomain(int v1){ qDebug() << "Graph::vertexinfluenceDomain() - vertex:"<< v1; if ( !calculatedDistances || graphModified() ) { graphDistanceGeodesicCompute(false); } VList::const_iterator jt; 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 (jt=m_graph.cbegin(); jt!=m_graph.cend(); ++jt) { emit signalProgressBoxUpdate(++progressCounter); source = (*jt)->name(); if ( ! (*jt)->isEnabled() ) { qDebug() << "Graph::vertexinfluenceDomain() - " << source << "disabled. SKIP"; continue; } if ((*jt)->distance( v1 ) != RAND_MAX ) { qDebug() << "Graph::vertexinfluenceDomain() - v1 reachable from:" << source; influenceDomains.insertMulti(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(10) : "-" ) << "" << (*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) { QTime 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 (graphModified() || !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) duads in the triad. Possible values: 0, 1, 2, 3.
" "- The second character is the number of asymmetric (A) duads in the triad. Possible values: 0, 1, 2, 3.
" "- The third character is the number of null (N) duads in the triad. Possible values: 0, 1, 2, 3.
" "- The fourth character is infered from features or the nature of the triad, i.e. presence of cycle or transitivity. " "Possible values: none, D (\"Down\"), U (\"Up\"), C (\"Cyclic\"), T (\"Transitive\")") << "

"; 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 * Writes the number of cliques (maximal connected subgraphs) of each vertex into a given file. * @param fileName * @param considerWeights */ bool Graph::writeCliqueCensus(const QString &fileName, const bool considerWeights) { QTime 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; } long int N = vertices(); int cliqueCounter=0; int rowCounter = 0; int cliqueSize = 0; int actor2 = 0, actor1=0, index1=0, index2=0; float 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"; cliqueCensusRecursion = 0; 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_precision); 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 << "
"; 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/(float) cliqueSize) <<"
"; 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) <<"
"; 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.insertMulti(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() - upd. co-membership matrix CLQM" << "actor1:" << actor1 << "actor2:" << actor2 <<"old matrix element: (" << index1<<","< R, QSet P, QSet X) { cliqueCensusRecursion ++ ; qDebug () << "Graph::graphCliques() - check if we are at initialization step"; if (R.isEmpty() && P.isEmpty() && X.isEmpty()){ qDebug() << "Graph::graphCliques() - initialization step. R, X empty and P=V(G)"; int V = vertices() ; P.reserve( V ); R.reserve( V ); X.reserve( V ); P=verticesSet(); 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 ] = vertexNeighborhoodList(vertex).toSet(); (*it)->clearCliques(); } } qDebug() << "Graph::graphCliques() - check if P and X are both empty"; if (P.isEmpty() && X.isEmpty()) { qDebug() << "Graph::graphCliques() - P and X are both empty. MAXIMAL clique R=" << R; QList clique = R.toList(); graphCliqueAdd(clique); } int v; QSet N; QSet temp, temp1, temp2; QSet::iterator i = P.begin(); int counter = 0; while( i != P.end()) { counter ++ ; v = *i; qDebug() << "Graph::graphCliques() - v:" << v << " P:" << P << " P.count=" < addv; addv.insert(v); // dummy set with just v temp = R+addv; temp1 = P&N; temp2 = X&N; qDebug() << "Graph::graphCliques() - v:" << v << "Recursive call to graphCliques ( R ⋃ {v}, P ⋂ N(v), X ⋂ N(v) )" << endl << "N(v):" << N << endl << "R ⋃ {v}:" << temp << endl << "P ⋂ N(v):" << temp1 << endl << "X ⋂ N(v):" << temp2; if (cliqueCensusRecursion==1) { emit signalProgressBoxUpdate(counter); emit statusMessage ( tr("Finding cliques: Recursive backtracking for actor ") + QString::number(v)); } // find all clique extensions of R that contain v graphCliques( R+addv, P&N, X&N ); qDebug() << "Graph::graphCliques() - v:" << v << "Returned from recursive call. Moving v:"<< v <<" from P to X to be excluded in the future."; // P = P \ v i=P.erase(i); //P-=v; // X = X + v X.insert(v); qDebug() << "Graph::graphCliques() - v:" << v << "FINISHED" << " 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) { QTime 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; float 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; float 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); float min=RAND_MAX; float max=0; int imin, jmin, imax, jmax, mergedClusterIndex, deletedClusterIndex ; float 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) { QTime 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") { graphDistanceGeodesicCompute(); 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_precision); 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) { QTime 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") { graphDistanceGeodesicCompute(); graphMatrixSimilarityPearsonCreate(DM, PCC, varLocation,diagonal); } else { return; } emit statusMessage ( tr("Writing Pearson coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_precision); 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") { graphDistanceGeodesicCompute(); graphMatrixSimilarityPearsonCreate(DM, PCC, varLocation,diagonal); } else { return; } emit statusMessage ( tr("Writing Pearson coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_precision); 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. */ float Graph::numberOfTriples(int v1){ float totalDegree=0; if (graphSymmetric()){ 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 */ float Graph::clusteringCoefficientLocal(const long int &v1){ if ( !graphModified() && (m_graph[ vpos [v1] ] -> hasCLC() ) ) { float 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 graphIsSymmetric = false; if ( graphSymmetric() ) { graphIsSymmetric = true; } else { graphIsSymmetric = false; } float 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 ( graphIsSymmetric ) { 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 ( graphIsSymmetric ){ 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 */ float Graph::clusteringCoefficient (const bool updateProgress){ qDebug()<< "Graph::clusteringCoefficient()"; averageCLC=0; varianceCLC=0; maxCLC=0; minCLC=1; float temp=0; float x=0; float 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, SIGNAL( createNode (const int &,const int &, const QString &, const QString &, const int&, const QString &, const QString &, const int&, const QPointF&, const QString &, const bool &) ), this, SLOT( vertexCreate( const int &, const int &, const QString &, const QString &, const int &, const QString &, const QString &, const int &, const QPointF &, const QString &, const bool &) ) ) ; 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, SIGNAL( edgeCreate (const int&, const int&, const float&, const QString&, const int&, const bool&, const bool&, const QString&, const bool&)), this, SLOT( edgeCreate (const int&, const int&, const float&, const QString&, const int&, const bool&, const bool&, const QString&, const bool&) ) ); connect ( file_parser, SIGNAL(networkFileLoaded(int, QString, QString, int, int, bool, const QString &) ), this, SLOT(graphFileLoaded( const int &, const QString &, const QString &, const int &, const int&, const bool&, 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 Graph::graphFileLoaded * 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 bool &undirected, const QString &message) { if ( fileType == FILE_UNRECOGNIZED ) { qDebug() << "Graph::graphFileLoaded() - FILE_UNRECOGNIZED. " "Emitting signalGraphLoaded with error message " << message; emit signalGraphLoaded (fileType, QString::null, QString::null, 0, 0, message); return; } fileName = fName; if (netName != "") m_graphName=netName ; else m_graphName=(fileName.split("/").last()).split("/").first(); m_undirected = undirected; m_fileFormat = fileType; qDebug() << "Graph::graphFileLoaded() - " << " type " << fileType << " filename " << fileName << " name " << graphName() << " nodes " << totalNodes << " links " << totalLinks << " undirected " << undirected; graphModifiedSet(GRAPH_CHANGED_NEW); emit signalGraphLoaded (fileType, fileName, graphName(), totalNodes, totalLinks, message); graphModifiedSet(GRAPH_CHANGED_NONE); qDebug ()<< "Graph::graphFileLoaded() -check parser if running..."; } /** * @brief graphFileFormat * @return * Returns the format of the last file opened */ int Graph::graphFileFormat() const { return m_fileFormat; } /** * @brief Graph::graphFileFormatExportSupported * @param fileFormat * @return */ bool Graph::graphFileFormatExportSupported(const int &fileFormat) const { if (m_graphFileFormatExportSupported.contains(fileFormat)) { return true; } return false; } /** * @brief Graph::graphSave * Our almost universal graph saver. :) * Actually it just checks the requested file type and * calls the right 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 FILE_PAJEK : { qDebug() << "Graph::graphSave() - Pajek formatted file"; saved=graphSaveToPajekFormat(fileName, graphName(), canvasWidth, canvasHeight) ; break; } case FILE_ADJACENCY: { qDebug() << "Graph::graphSave() - Adjacency formatted file"; saved=graphSaveToAdjacencyFormat(fileName, saveEdgeWeights) ; break; } case FILE_GRAPHVIZ: { qDebug() << "Graph::graphSave() - GraphViz/Dot formatted file"; saved=graphSaveToDotFormat(fileName); } case FILE_GRAPHML: { // GraphML qDebug() << "Graph::graphSave() - GraphML formatted file"; saved=graphSaveToGraphMLFormat(fileName); break; } default: { m_fileFormat = FILE_UNRECOGNIZED; qDebug() << "Graph::graphSave() - Error! Unrecognized fileType"; break; } }; if (saved) { if (graphModified()) { calculatedGraphConnectedness = false; calculatedGraphReciprocity = false; calculatedGraphSymmetry = false; calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices = false; calculatedVerticesList=false; calculatedVerticesSet = false; calculatedIsolates = false; calculatedAdjacencyMatrix = false; calculatedDistances = false; calculatedCentralities = false; calculatedDP = false; calculatedDC = false; calculatedPP = false; calculatedIRCC = false; calculatedIC = false; calculatedPRP = false; } graphModifiedSet(GRAPH_CHANGED_NONE); signalGraphSaved(fileType); } else { signalGraphSaved(0); } } /** 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 ) { float 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; } bool Graph::graphSaveToGraphMLFormat (const QString &fileName, QString networkName, int maxWidth, int maxHeight) { float 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(); 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) ? 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 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"; 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 (graphUndirected()) 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 ( 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 (!graphUndirected()) { 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 Graph::writeDataSetToFile * Writes a known dataset to the given file * @param fileName */ void Graph::writeDataSetToFile (const QString dir, const QString fileName) { qDebug() << "Graph::writeDataSetToFile() to " << dir+fileName; QFile file( dir+fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); QString datasetDescription=QString::null; 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(); float 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; float 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) { QTime 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; float 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) { QTime 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(); float 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; float 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; long 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 (!graphModified()) { // 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(); graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @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 float 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 = (float ) ( rand()%100 ) / 100.0; new_radius=(maxRadius- (randomDecimal - offset)*maxRadius); qDebug () << "Vertice " << (*it)->name() << " at x=" << (*it)->x() << ", y= "<< (*it)->y() << ", maxradius " << maxRadius << " randomDecimal " << randomDecimal << " new radius " << new_radius; //Calculate new position rad= (2.0* Pi/ vert ); new_x=x0 + new_radius * cos(i * rad); new_y=y0 + new_radius * sin(i * rad); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug("Vertice will move to x=%f and y=%f ", new_x, new_y); //Move node to new position emit setNodePos((*it)->name(), new_x, new_y); i++; if (guides) { emit addGuideCircle ( x0, y0, new_radius ); } } emit signalProgressBoxKill(); graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @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* 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(); graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @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; float C=0, maxC=0; double x0=0, y0=0, maxRadius=0, new_radius=0, rad=0; double maxWidth=0, maxHeight=0; float 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 == 1) { if (graphModified() || !calculatedDC ) centralityDegree(true, dropIsolates); } else if ( prominenceIndex == 3 ){ if (graphModified() || !calculatedIRCC ) centralityClosenessIR(); } else if ( prominenceIndex == 8 ) { if (graphModified() || !calculatedIC ) centralityInformation(); } else if ( prominenceIndex == 9){ if (graphModified() || !calculatedEVC ) centralityEigenvector(true, dropIsolates); } else if ( prominenceIndex == 10){ if (graphModified() || !calculatedDP ) prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == 11 ) { if (graphModified() || !calculatedPRP ) prestigePageRank(); } else if ( prominenceIndex == 12 ){ if (graphModified() || !calculatedPP ) prestigeProximity(considerWeights, inverseWeights); } else{ if (graphModified() || !calculatedCentralities ) graphDistanceGeodesicCompute(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 1 : { C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxSDC; break; } case 2 : { C=(*it)->CC(); std= (*it)->SCC(); maxC=maxSCC; break; } case 3 : { C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { C=(*it)->BC(); std= (*it)->SBC(); maxC=maxSBC; break; } case 5 : { C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSSC; break; } case 6 : { C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { C=(*it)->PC(); std= (*it)->SPC(); maxC=maxSPC; break; } case 8 : { C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { C=(*it)->EVC(); std= (*it)->SEVC(); maxC=1; break; } case 10 : { C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxSDP; break; } case 11 : { C=(*it)->PRP(); std= (*it)->SPRP(); maxC=1; break; } case 12 : { 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* 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 + (float) 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(); graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @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 float K=1; // constant float L=0; // the desirable length of a single edge. float L0=0; // the length of a side of the display square area float 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 float b[2]; // holds the right hand vector of linear equations 11 and 12 float partDrvtEx = 0; // partial derivative of E by Xm float partDrvtEy = 0; // partial derivative of E by Ym float partDrvtExSec_m = 0; // partial second derivative of E by Xm float partDrvtEySec_m = 0; // partial second derivative of E by Ym float partDrvtExEySec_m = 0; // partial second derivative of E by Xm Ym float partDrvtEyExSec_m = 0; // partial second derivative of E by Ym Xm float partDrvtEx_m = 0; // cache for partial derivative of E by Xm, for particle with max D_i float partDrvtEy_m = 0; // cache for partial derivative of E by Ym, for particle with max D_i float partDrvDenom = 0; float xm=0,ym=0; float xi=0,yi=0; float xpm=0, ypm=0; // cache for pos of particle with max D_i float dx=0, dy=0; float epsilon=0.1; float Delta_m=0; float 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(); graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @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 (minumum)"; 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; } socnetv-2.4/src/mainwindow.cpp0000775000175000017500000214167013245520654016767 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt - mainwindow.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras blog : http://dimitris.apeiro.gr project site : http://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 "mainwindow.h" #include "texteditor.h" #include "graphicswidget.h" #include "graphicsnode.h" #include "graphicsedge.h" #include "graphicsnodenumber.h" #include "dialogwebcrawler.h" #include "dialogfilteredgesbyweight.h" #include "dialogpreviewfile.h" #include "dialogranderdosrenyi.h" #include "dialograndsmallworld.h" #include "dialograndscalefree.h" #include "dialograndregular.h" #include "dialogsettings.h" #include "dialognodeedit.h" #include "dialogsimilaritypearson.h" #include "dialogsimilaritymatches.h" #include "dialogclusteringhierarchical.h" #include "dialogdissimilarities.h" bool printDebug = false; void myMessageOutput ( QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); Q_UNUSED(context); if ( printDebug ) switch ( type ) { case QtDebugMsg: fprintf( stderr, "Debug: %s\n", localMsg.constData() ); break; #if QT_VERSION >= 0x050500 case QtInfoMsg: fprintf( stderr, "Info: %s\n", localMsg.constData() ); break; #endif 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) { qDebug() << "MW::MainWindow() - Constructor running on thread:"<< thread(); setWindowIcon (QIcon(":/images/socnetv.png")); appSettings = initSettings(); qInstallMessageHandler( myMessageOutput); initGraph(); setMinimumSize(1024,750); //set MW minimum size, before creating canvas initView(); //init our network "canvas" /** functions that invoke all other construction parts **/ initActions(); //register and construct menu Actions initMenuBar(); //construct the menu initToolBar(); //build the toolbar initPanels(); //build the toolbox initWindowLayout(); //init the application window, set layout etc initSignalSlots(); //connect signals and slots between app components initApp(); // load and initialise default app parameters // 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 ); } graphicsWidget->setFocus(); 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 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() - Checking if Graph is saved..."; statusMessage( tr("Closing SocNetV. Bye!") ); bool userCancelled=false; 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..."; delete printer; delete graphicsWidget; delete scene; 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(); } /** * @brief Resizes the scene when the window is resized. */ void MainWindow::resizeEvent( QResizeEvent * ){ qDebug() << "MW::resizeEvent(): Window resized to" << width() << "," << height() <<"Calling activeGraph->canvasSizeSet() to set canvas width and height"; activeGraph->canvasSizeSet(graphicsWidget->width(),graphicsWidget->height()); statusMessage( QString( tr("Window resized to (%1, %2)px. Canvas size: (%3, %4) px")) .arg(width()).arg(height()) .arg(graphicsWidget->width()).arg(graphicsWidget->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(); // 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" ; settingsDir = QDir::homePath() +QDir::separator() + "socnetv-data" + QDir::separator() ; settingsFilePath = settingsDir + "settings.conf"; // initially they are the same, but dataDir may be changed by the user 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["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"]="black"; 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"] = (printDebug) ? "true" : "false"; appSettings["viewReportsInSystemBrowser"] = "true"; appSettings["showProgressBar"] = "true"; 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"; // 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 conduct the developer." ), 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 conduct the developer." ), 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, this); connect( m_settingsDialog, &DialogSettings::saveSettings, this, &MainWindow::saveSettings); 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::setToolBar, this, &MainWindow::slotOptionsToolbarVisibility); connect( m_settingsDialog, &DialogSettings::setStatusBar, this, &MainWindow::slotOptionsStatusBarVisibility); connect( m_settingsDialog, &DialogSettings::setLeftPanel, this, &MainWindow::slotOptionsLeftPanelVisibility); connect( m_settingsDialog, &DialogSettings::setRightPanel, this, &MainWindow::slotOptionsRightPanelVisibility); 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 Initializes the Graph */ void MainWindow::initGraph() { qDebug() << "MW::initGraph()"; activeGraph = new Graph(); qDebug() << "MW::initGraph() - activeGraph created on thread:" << activeGraph->thread() << "moving it to new thread "; // activeGraph->moveToThread(&graphThread); // graphThread.start(); qDebug() << "MW::MainWindow() - activeGraph thread now:" << activeGraph->thread(); } /** * @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 a scene scene=new QGraphicsScene(); //create a view widget for this scene graphicsWidget=new GraphicsWidget(scene,this); 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 all QActions of the application * Take a breath, the listing below is HUGE. */ void MainWindow::initActions(){ qDebug()<< "MW::initActions()"; printer = new QPrinter; /** Network menu actions */ networkNew = new QAction(QIcon(":/images/new.png"), tr("&New"), this); networkNew->setShortcut(Qt::CTRL+Qt::Key_N); networkNew->setStatusTip(tr("Create a new network")); networkNew->setToolTip(tr("New network")); networkNew->setWhatsThis(tr("New\n\n" "Creates a new social network. " "Firtst, checks if current network needs to be saved.")); connect(networkNew, SIGNAL(triggered()), this, SLOT(slotNetworkNew())); networkOpen = new QAction(QIcon(":/images/open.png"), tr("&Open"), this); networkOpen->setShortcut(Qt::CTRL+Qt::Key_O); networkOpen->setToolTip(tr("Open network")); networkOpen->setStatusTip(tr("Open a GraphML formatted file of social network data.")); networkOpen->setWhatsThis(tr("Open\n\n" "Opens a file of a social network in GraphML format")); connect(networkOpen, 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())); } networkImportGML = new QAction( QIcon(":/images/open.png"), tr("&GML"), this); networkImportGML->setStatusTip(tr("Import GML-formatted file")); networkImportGML->setWhatsThis(tr("Import GML\n\n" "Imports a social network from a GML-formatted file")); connect(networkImportGML, SIGNAL(triggered()), this, SLOT(slotNetworkImportGML())); networkImportPajek = new QAction( QIcon(":/images/open.png"), tr("&Pajek"), this); networkImportPajek->setStatusTip(tr("Import Pajek-formatted file")); networkImportPajek->setWhatsThis(tr("Import Pajek \n\n" "Imports a social network from a Pajek-formatted file")); connect(networkImportPajek, SIGNAL(triggered()), this, SLOT(slotNetworkImportPajek())); networkImportSM = new QAction( QIcon(":/images/open.png"), tr("&Adjacency Matrix"), this); networkImportSM->setStatusTip(tr("Import Adjacency matrix")); networkImportSM->setWhatsThis(tr("Import Sociomatrix \n\n" "Imports a social network from an Adjacency matrix-formatted file")); connect(networkImportSM, SIGNAL(triggered()), this, SLOT(slotNetworkImportSM())); networkImportDot = new QAction( QIcon(":/images/open.png"), tr("GraphViz (.dot)"), this); networkImportDot->setStatusTip(tr("Import dot file")); networkImportDot->setWhatsThis(tr("Import GraphViz \n\n" "Imports a social network from an GraphViz formatted file")); connect(networkImportDot, SIGNAL(triggered()), this, SLOT(slotNetworkImportDot())); networkImportDL = new QAction( QIcon(":/images/open.png"), tr("UCINET (.dl)..."), this); networkImportDL->setStatusTip(tr("ImportDL-formatted file (UCINET)")); networkImportDL->setWhatsThis(tr("Import UCINET\n\n" "Imports social network data from a DL-formatted file")); connect(networkImportDL, SIGNAL(triggered()), this, SLOT(slotNetworkImportDL())); networkImportList = new QAction( QIcon(":/images/open.png"), tr("&Edge list"), this); networkImportList->setStatusTip(tr("Import an edge list file. ")); networkImportList->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(networkImportList, SIGNAL(triggered()), this, SLOT(slotNetworkImportEdgeList())); networkImportTwoModeSM = new QAction( QIcon(":/images/open.png"), 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())); networkSave = new QAction(QIcon(":/images/save.png"), tr("&Save"), this); networkSave->setShortcut(Qt::CTRL+Qt::Key_S); networkSave->setStatusTip(tr("Save social network to a file")); networkSave->setWhatsThis(tr("Save.\n\n" "Saves the social network to file")); connect(networkSave, SIGNAL(triggered()), this, SLOT(slotNetworkSave())); networkSaveAs = new QAction(QIcon(":/images/save.png"), tr("Save &As..."), this); networkSaveAs->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_S); networkSaveAs->setStatusTip(tr("Save network under a new filename")); networkSaveAs->setWhatsThis(tr("Save As\n\n" "Saves the social network under a new filename")); connect(networkSaveAs, SIGNAL(triggered()), this, SLOT(slotNetworkSaveAs())); networkExportBMP = new QAction(QIcon(":/images/image.png"), tr("&BMP..."), this); networkExportBMP->setStatusTip(tr("Export social network to BMP image")); networkExportBMP->setWhatsThis(tr("Export BMP\n\n" "Exports the social network to a BMP image")); connect(networkExportBMP, SIGNAL(triggered()), this, SLOT(slotNetworkExportBMP())); networkExportPNG = new QAction( QIcon(":/images/image.png"), tr("&PNG..."), this); networkExportPNG->setStatusTip(tr("Export social network to PNG image")); networkExportPNG->setWhatsThis(tr("Export PNG \n\n" "Exports the social network to a PNG image")); connect(networkExportPNG, SIGNAL(triggered()), this, SLOT(slotNetworkExportPNG())); networkExportPDF = new QAction( QIcon(":/images/pdf.png"), tr("&PDF..."), this); networkExportPDF->setStatusTip(tr("Export social network to PDF")); networkExportPDF->setWhatsThis(tr("Export PDF\n\n" "Exports the social network to a PDF document")); connect(networkExportPDF, SIGNAL(triggered()), this, SLOT(slotNetworkExportPDF())); networkExportSM = new QAction( QIcon(":/images/save.png"), tr("&Adjacency Matrix"), this); networkExportSM->setStatusTip(tr("Export social network to an adjacency/sociomatrix file")); networkExportSM->setWhatsThis(tr("Export network to Adjacency format\n\n" "Exports the social network to an " "adjacency matrix-formatted file")); connect(networkExportSM, SIGNAL(triggered()), this, SLOT(slotNetworkExportSM())); networkExportPajek = new QAction( QIcon(":/images/save.png"), 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())); networkExportList = new QAction( QIcon(":/images/save.png"), tr("&List"), this); networkExportList->setStatusTip(tr("Export to List-formatted file. ")); networkExportList->setWhatsThis(tr("Export List\n\n" "Exports the network to a List-formatted file")); connect(networkExportList, SIGNAL(triggered()), this, SLOT(slotNetworkExportList())); networkExportDL = new QAction( QIcon(":/images/save.png"), tr("&DL..."), this); networkExportDL->setStatusTip(tr("Export network to UCINET-formatted file")); networkExportDL->setWhatsThis(tr("Export UCINET\n\n" "Exports the active network to a DL-formatted")); connect(networkExportDL, SIGNAL(triggered()), this, SLOT(slotNetworkExportDL())); networkExportGW = new QAction( QIcon(":/images/save.png"), tr("&GW..."), this); networkExportGW->setStatusTip(tr("Export to GW-formatted file")); networkExportGW->setWhatsThis(tr("Export\n\n" "Exports the active network to a GW formatted file")); connect(networkExportGW, SIGNAL(triggered()), this, SLOT(slotNetworkExportGW())); networkClose = new QAction( tr("&Close"), this); networkClose->setStatusTip(tr("Close the actual network")); networkClose->setWhatsThis(tr("Close \n\nCloses the actual network")); connect(networkClose, SIGNAL(triggered()), this, SLOT(slotNetworkClose())); networkPrint = new QAction(QIcon(":/images/print.png"), tr("&Print"), this); networkPrint->setShortcut(Qt::CTRL+Qt::Key_P); networkPrint->setStatusTip(tr("Send the currrent social network to the printer")); networkPrint->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(networkPrint, SIGNAL(triggered()), this, SLOT(slotNetworkPrint())); networkQuit = new QAction(QIcon(":/images/exit.png"), tr("E&xit"), this); networkQuit->setShortcut(Qt::CTRL+Qt::Key_Q); networkQuit->setStatusTip(tr("Quit SocNetV. Are you sure?")); networkQuit->setWhatsThis(tr("Exit\n\n" "Quits the application")); connect(networkQuit, SIGNAL(triggered()), this, SLOT(close())); openTextEditorAct = new QAction(QIcon(":/images/texteditor.png"), 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\n\n" "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/networkfile.png"), 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\n\n" "Displays the adjacency matrix of the active network. \n\n" "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("Plot 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)\n\n" "Plots the adjacency matrix in a text file using " "unicode characters. \n\n" "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/petersengraph.png"), 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("Known Data Sets\n\n" "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())); createErdosRenyiRandomNetworkAct = new QAction(QIcon(":/images/erdos.png"), tr("ErdÅ‘s–Rényi"), this); createErdosRenyiRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_E) ); createErdosRenyiRandomNetworkAct->setStatusTip(tr("Create a random network " "according to the ErdÅ‘s–Rényi model")); createErdosRenyiRandomNetworkAct->setWhatsThis( tr("ErdÅ‘s–Rényi \n\n" "Creates a random network either of G(n, p) model or G(n,M) model.\n" "In the first, edges are created with Bernoulli trials (probability p).\n" "In the second, a graph of exactly M edges is created.")); connect(createErdosRenyiRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomErdosRenyiDialog())); createLatticeNetworkAct = new QAction( QIcon(":/images/net1.png"), tr("Ring Lattice"), this); createLatticeNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_L) ); createLatticeNetworkAct->setStatusTip(tr("Create a ring lattice random network.")); createLatticeNetworkAct->setWhatsThis( tr("Ring Lattice \n\n")+ tr("A ring lattice is a graph with N vertices each connected to d neighbors, d / 2 on each side.")); connect(createLatticeNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomRingLattice())); createRegularRandomNetworkAct = new QAction(QIcon(":/images/net.png"), tr("d-Regular"), this); createRegularRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_R) ); createRegularRandomNetworkAct->setStatusTip( tr("Create a d-regular random network, where every actor has the same degree d.")); createRegularRandomNetworkAct->setWhatsThis( tr("d-Regular \n\n") + tr("A random network where each actor has the same " "number d of neighbours, aka the same degree d ")); connect(createRegularRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomRegularDialog())); createGaussianRandomNetworkAct = new QAction(tr("Gaussian"), this); createGaussianRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_G) ); createGaussianRandomNetworkAct->setStatusTip(tr("Create a Gaussian distributed random network.")); createGaussianRandomNetworkAct->setWhatsThis(tr("Gaussian \n\nCreates a random network of Gaussian distribution")); connect(createGaussianRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomGaussian())); createSmallWorldRandomNetworkAct = new QAction(QIcon(":/images/sw.png"), tr("Small World"), this); createSmallWorldRandomNetworkAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_W) ); createSmallWorldRandomNetworkAct->setStatusTip(tr("Create a small-world random network.")); createSmallWorldRandomNetworkAct -> setWhatsThis( tr("Small World \n\n") + tr("A Small World, according to the Watts and Strogatz model, " "is a random network with short average path lengths and high clustering coefficient.")); connect(createSmallWorldRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomSmallWorldDialog())); createScaleFreeRandomNetworkAct = new QAction( QIcon(":/images/scalefree.png"), tr("Scale-free"), this); createScaleFreeRandomNetworkAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_S) ); createScaleFreeRandomNetworkAct->setStatusTip( tr("Create a random network with power-law degree distribution.")); createScaleFreeRandomNetworkAct-> setWhatsThis( tr("Scale-free (power-law)\n\n") + tr("A scale-free network is a network whose degree distribution follows a power law." " SocNetV generates random scale-free networks according to the " " Barabási–Albert (BA) model using a preferential attachment mechanism.")); connect(createScaleFreeRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomScaleFreeDialog())); webCrawlerAct = new QAction(QIcon(":/images/webcrawler2.png"), tr("Web Crawler"), this); webCrawlerAct->setShortcut(Qt::SHIFT+Qt::Key_C); webCrawlerAct->setEnabled(true); webCrawlerAct->setStatusTip(tr("Create a network from all links found in a given website" "Shift+C")); webCrawlerAct->setWhatsThis(tr("Web Crawler \n\n" "A Web crawler is a built-in bot, which " "starts with a given URL (website or webpage) " "to visit. As the algorithm crawls this webpage, " "it identifies all the links in the page and adds " "them to a list of URLs (called frontier). " "Then, all the URLs from the frontier are " "recursively visited. You must provide maximum " "recursion level (how many URLs from the frontier " "will be visited) and maximum running time, along " "with the initial web address...")); /** Edit menu actions */ editRelationNextAct = new QAction(QIcon(":/images/nextrelation.png"), 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/prevrelation.png"), 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/addrelation.png"), 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/edit-rename.png"), 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/zoomin.png"), 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/zoomout.png"), 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/rotateleft.png"), 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/rotateright.png"), 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/reset.png"), 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/selectall.png"), 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/find.png"), tr("Find Node"), this); editNodeFindAct->setShortcut(Qt::CTRL + Qt::Key_F); editNodeFindAct->setToolTip(tr("Find an actor by its number or label and highlight it. " "Press Ctrl+F again to undo.")); editNodeFindAct->setStatusTip(tr("Find an actor by its number or label and highlight it. " "Press Ctrl+F again to undo.")); editNodeFindAct->setWhatsThis(tr("Find Node\n\n" "Finds a node with a given number or label and " "highlights it by doubling its size. " "Ctrl+F again resizes back the node")); connect(editNodeFindAct, SIGNAL(triggered()), this, SLOT(slotEditNodeFind()) ); editNodeAddAct = new QAction(QIcon(":/images/add.png"), tr("Add Node"), this); editNodeAddAct->setShortcut(Qt::CTRL + Qt::Key_Period); 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/remove.png"),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/properties.png"),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/nodecolor.png"), 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/resize.png"), 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/nodeshape.png"), 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/nodenumbercolor.png"), 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/nodelabelsize.png"), 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/nodelabelcolor.png"), 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/connect.png"), 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/disconnect.png"), 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/letters.png"), 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.png"),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/edgeweight.png") ,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/edgecolor.png"), 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 Directed Edges"), this); editEdgeSymmetrizeAllAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_S)); editEdgeSymmetrizeAllAct->setStatusTip(tr("Make all arcs in this relation reciprocal (thus, a symmetric graph).")); editEdgeSymmetrizeAllAct->setWhatsThis( tr("Symmetrize Directed Edges\n\n" "Makes all directed arcs in this relation reciprocal. \n" "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.png"), tr("Symmetrize Edges 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 tranform all arcs to undirected edges and hereafter work with undirected edges .")); editEdgeUndirectedAllAct->setWhatsThis( tr("Undirected Edges\n\n" "Tranforms 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/symmetrize.png"), 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" "Create 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())); 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.png"), 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("Repelling forces between all nodes, and attracting forces between adjacent nodes.")); layoutFDP_KamadaKawai_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_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 weakly 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/color.png"), 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(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))); openSettingsAct = new QAction(QIcon(":/images/appsettings.png"), 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.png"), 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/help-hint.png"), 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/download.png"), 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())); helpAboutApp = new QAction(tr("About SocNetV"), this); helpAboutApp->setStatusTip(tr("About SocNetV")); helpAboutApp->setWhatsThis(tr("About\n\nBasic information about SocNetV")); connect(helpAboutApp, SIGNAL(triggered()), this, SLOT(slotHelpAbout())); helpAboutQt = new QAction(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(networkNew); networkMenu -> addAction(networkOpen); networkMenu -> addSeparator(); recentFilesSubMenu = new QMenu(tr("Recent files...")); 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/import.png")); importSubMenu -> addAction(networkImportGML); importSubMenu -> addAction(networkImportPajek); importSubMenu -> addAction(networkImportSM); importSubMenu -> addAction(networkImportTwoModeSM); importSubMenu -> addAction(networkImportList); importSubMenu -> addAction(networkImportDL); importSubMenu -> addAction(networkImportDot); 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.png")); networkMenu ->addMenu (randomNetworkMenu); randomNetworkMenu -> addAction (createScaleFreeRandomNetworkAct); randomNetworkMenu -> addAction (createSmallWorldRandomNetworkAct); randomNetworkMenu -> addAction (createErdosRenyiRandomNetworkAct ); randomNetworkMenu -> addAction (createRegularRandomNetworkAct); randomNetworkMenu -> addAction (createLatticeNetworkAct); // createGaussianRandomNetworkAct -> addTo(randomNetworkMenu); networkMenu->addSeparator(); networkMenu -> addAction(webCrawlerAct); networkMenu -> addSeparator(); networkMenu -> addAction(networkSave); networkMenu -> addAction(networkSaveAs); networkMenu -> addSeparator(); exportSubMenu = networkMenu -> addMenu(tr("Export...")); exportSubMenu -> addAction (networkExportBMP); exportSubMenu -> addAction (networkExportPNG); exportSubMenu -> addAction (networkExportPDF); exportSubMenu -> addSeparator(); exportSubMenu -> addAction (networkExportSM); exportSubMenu -> addAction (networkExportPajek); //exportSubMenu -> addAction (networkExportList); //exportSubMenu -> addAction (networkExportDL); //exportSubMenu -> addAction (networkExportGW); networkMenu -> addSeparator(); networkMenu -> addAction(networkPrint); networkMenu -> addSeparator(); networkMenu -> addAction(networkClose); networkMenu -> addAction(networkQuit); /** 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.png")); 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/line.png")); editMenu-> addMenu (editEdgeMenu); editEdgeMenu -> addAction(editEdgeAddAct); editEdgeMenu -> addAction(editEdgeRemoveAct); editEdgeMenu -> addSeparator(); editEdgeMenu -> addAction (editEdgeSymmetrizeAllAct); editEdgeMenu -> addAction (editEdgeSymmetrizeStrongTiesAct); editEdgeMenu -> addAction (editEdgesCocitationAct); editEdgeMenu -> addAction (editEdgeUndirectedAllAct); 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.png")); 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/distances.png")); 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.png")); analysisMenu->addMenu(centrlMenu); centrlMenu -> addSection(QIcon(":/images/centrality.png"), tr("Centrality")); centrlMenu -> addAction (cDegreeAct); centrlMenu -> addAction (cClosenessAct); centrlMenu -> addAction (cInfluenceRangeClosenessAct); centrlMenu -> addAction (cBetweennessAct); centrlMenu -> addAction (cStressAct); centrlMenu -> addAction (cEccentAct); centrlMenu -> addAction (cPowerAct); centrlMenu -> addAction (cInformationAct); centrlMenu -> addAction (cEigenvectorAct); centrlMenu -> addSection(QIcon(":/images/prestige.png"), tr("Prestige")); centrlMenu -> addAction (cInDegreeAct); centrlMenu -> addAction (cPageRankAct); centrlMenu -> addAction (cProximityPrestigeAct); analysisMenu -> addSeparator(); // COMMUNITIES & SUBGROUPS communitiesMenu = new QMenu(tr("Communities and Subgroups...")); communitiesMenu -> setIcon(QIcon(":/images/clustering.png")); 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...")); layoutMenu -> addMenu (randomLayoutMenu ); randomLayoutMenu -> addAction(layoutRandomAct); randomLayoutMenu -> addAction( layoutRandomRadialAct ); layoutMenu->addSeparator(); layoutRadialProminenceMenu = new QMenu(tr("Radial by prominence index...")); layoutRadialProminenceMenu -> setIcon(QIcon(":/images/circular.png")); 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/net3.png")); 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.png")); 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/nodecolor.png")); 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/nodes.png")); optionsMenu -> addMenu (nodeOptionsMenu); nodeOptionsMenu -> addAction (optionsNodeNumbersVisibilityAct); nodeOptionsMenu -> addAction (optionsNodeLabelsVisibilityAct); nodeOptionsMenu -> addAction (optionsNodeNumbersInsideAct); edgeOptionsMenu=new QMenu(tr("Edges...")); edgeOptionsMenu -> setIcon(QIcon(":/images/line.png")); 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 (openSettingsAct); /** menuBar entry helpMenu */ helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu -> addAction (helpApp); helpMenu -> addAction (tipsApp); helpMenu -> addSeparator(); helpMenu -> addAction (helpCheckUpdatesApp); helpMenu -> addSeparator(); 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 (networkNew); toolBar -> addAction (networkOpen); toolBar -> addAction (networkSave); toolBar -> addAction (networkPrint); toolBar -> addSeparator(); 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 *toolBoxEditNodeSubgraphSelectLabel = new QLabel; toolBoxEditNodeSubgraphSelectLabel->setText(tr("Subgraph:")); toolBoxEditNodeSubgraphSelectLabel->setMinimumWidth(90); 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(120); 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 *toolBoxSymmetrizeSelectLabel = new QLabel; toolBoxSymmetrizeSelectLabel->setText(tr("Symmetrize:")); toolBoxSymmetrizeSelectLabel->setMinimumWidth(90); toolBoxEditEdgeSymmetrizeSelect = new QComboBox; toolBoxEditEdgeSymmetrizeSelect->setStatusTip( tr("Select a method to symmetrize the network, i.e. tranform all directed edges to undirected.")); helpMessage = tr("

Symmetrize Network

" "

Select a method to symmetrize the network. 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.

" ); toolBoxEditEdgeSymmetrizeSelect->setToolTip( helpMessage ); toolBoxEditEdgeSymmetrizeSelect->setWhatsThis( helpMessage ); QStringList symmetrizeCommands; symmetrizeCommands << "Select" << "Directed ties" << "Strong ties" << "Cocitation"; toolBoxEditEdgeSymmetrizeSelect->addItems(symmetrizeCommands); toolBoxEditEdgeSymmetrizeSelect->setMinimumWidth(120); //create a grid layout for Edit buttons QGridLayout *editNodesGrid = new QGridLayout; editNodesGrid -> addWidget(toolBoxEditNodeSubgraphSelectLabel, 0,0); editNodesGrid -> addWidget(toolBoxEditNodeSubgraphSelect, 0,1); QGroupBox *editNodesGroupBox= new QGroupBox(tr("Nodes")); editNodesGroupBox->setLayout(editNodesGrid); QGridLayout *editEdgeGrid = new QGridLayout; editEdgeGrid -> addWidget(toolBoxEdgeModeSelectLabel,0,0); editEdgeGrid -> addWidget(toolBoxEditEdgeModeSelect,0,1); editEdgeGrid -> addWidget(toolBoxSymmetrizeSelectLabel,1,0); editEdgeGrid -> addWidget(toolBoxEditEdgeSymmetrizeSelect,1,1); QGroupBox *editEdgeGroupBox= new QGroupBox(tr("Edges")); editEdgeGroupBox->setLayout(editEdgeGrid); QGridLayout *EditGrid = new QGridLayout; EditGrid -> addWidget(editNodesGroupBox, 0,0,1,2); EditGrid -> addWidget(editEdgeGroupBox,1,0,1,2); EditGrid -> setSpacing(5); EditGrid -> setContentsMargins(5, 5, 5, 5); //create a groupbox "Edit" - Inside, display the grid layout of widgets QGroupBox *editGroupBox= new QGroupBox(tr("Edit")); editGroupBox->setLayout(EditGrid); editGroupBox->setMaximumWidth(255); editGroupBox->setMinimumHeight(100); //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 metric to compute, 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 metrics to measure how prominent (important) " "each actor (node) is inside the network.

" "

Centrality metrics 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" << "Degree Centr." << "Closeness Centr." << "IR Closeness Centr." << "Betweenness Centr." << "Stress Centr." << "Eccentricity Centr." << "Power Centr." << "Information Centr." << "Eigenvector Centr" << "Degree Prestige" << "PageRank Prestige" << "Proximity Prestige"; 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 metric / cohesive subgroup algorithm, i.e. cliques, triad census etc.")); helpMessage = tr("

Community Analysis

" "

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

" "

Available metrics

" "

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 differrent 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 metric 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(15, 5, 15, 5); //create a box and set the above layout inside QGroupBox *analysisBox= new QGroupBox(tr("Analyze")); analysisBox->setMinimumHeight(170); analysisBox->setMaximumWidth(255); analysisBox->setLayout (analysisGrid ); //create widgets for the "Visualization By Index" box QLabel *toolBoxLayoutByIndexSelectLabel = new QLabel; toolBoxLayoutByIndexSelectLabel->setText(tr("Index:")); toolBoxLayoutByIndexSelectLabel->setMinimumWidth(90); 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 indicesList; indicesList << "None" << "Random" << "Degree Centr." << "Closeness Centr." << "IR Closeness Centr." << "Betweenness Centr." << "Stress Centr." << "Eccentricity Centr." << "Power Centr." << "Information Centr." << "Eigenvector Centr." << "Degree Prestige" << "PageRank Prestige" << "Proximity Prestige"; toolBoxLayoutByIndexSelect->addItems(indicesList); toolBoxLayoutByIndexSelect->setMinimumHeight(20); toolBoxLayoutByIndexSelect->setMinimumWidth(120); QLabel *toolBoxLayoutByIndexTypeLabel = new QLabel; toolBoxLayoutByIndexTypeLabel->setText(tr("Layout Type:")); toolBoxLayoutByIndexTypeLabel->setMinimumWidth(90); 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.

"); toolBoxLayoutByIndexTypeSelect->setToolTip( helpMessage ); toolBoxLayoutByIndexTypeSelect->setWhatsThis( helpMessage ); QStringList layoutTypes; layoutTypes << "Radial" << "On Levels" << "Node Size"<< "Node Color"; toolBoxLayoutByIndexTypeSelect->addItems(layoutTypes); toolBoxLayoutByIndexTypeSelect->setMinimumHeight(20); toolBoxLayoutByIndexTypeSelect->setMinimumWidth(120); toolBoxLayoutByIndexApplyButton = new QPushButton(tr("Apply")); 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

"); layoutByIndexBox->setToolTip( helpMessage ); layoutByIndexBox->setLayout (layoutByIndexGrid ); // create widgets for the "Force-Directed Models" Box QLabel *toolBoxLayoutForceDirectedSelectLabel = new QLabel; toolBoxLayoutForceDirectedSelectLabel->setText(tr("Model:")); toolBoxLayoutForceDirectedSelectLabel->setMinimumWidth(90); 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(120); 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->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->setLayout (layoutForceDirectedGrid ); //Parent box with vertical layout for all layout/visualization boxes QVBoxLayout *visualizationBoxLayout = new QVBoxLayout; visualizationBoxLayout -> addWidget(layoutByIndexBox); visualizationBoxLayout -> addWidget(layoutDynamicBox); QGroupBox *visualizationBox= new QGroupBox(tr("Visualize")); visualizationBox->setMaximumWidth(255); visualizationBox->setLayout (visualizationBoxLayout ); //Parent box with vertical layout for all boxes of Controls QGridLayout *editGrid = new QGridLayout; editGrid -> addWidget(editGroupBox, 0,0); editGrid -> addWidget(analysisBox, 1, 0); editGrid -> addWidget(visualizationBox, 2, 0); editGrid -> setRowStretch(3,1); //fix stretch //create a box with title leftPanel = new QGroupBox(tr("Control Panel")); leftPanel -> setLayout (editGrid); //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("The total number of actors \n" "(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->setToolTip(tr("The density of a social network is the ratio of existing \n" "edges to all possible edges ( n*(n-1) ) between nodes.")); 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. \n" "The density of a network is the ratio of existing \n" "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.")); rightPanelClickedNodeClucofLabel -> setWhatsThis( tr("The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist " "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient ")); rightPanelClickedNodeClucofLabel -> setToolTip ( tr("The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n\n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist \n" "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient ")); 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( tr("The Clustering Coefficient of the active node. \n" "The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist " "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient ")); rightPanelClickedNodeClucofLCD -> setToolTip ( tr("The Clustering Coefficient of the active node. \n" "The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist " "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof \n" "from the menu Analysis > Clustering Coefficient ")); 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.")); 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 -> 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.")); 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); propertiesGrid -> setRowStretch(20,1); //make an invisible row stretch to rest of height //create a panel with title rightPanel = new QGroupBox(tr("Statistics Panel")); rightPanel->setMaximumWidth(190); 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/zoomin.png")); 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/zoomout.png")); 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/rotateleft.png")); 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/rotateright.png")); 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/reset.png")); 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") { slotOptionsRightPanelVisibility(false); } if ( appSettings["showLeftPanel"] == "false") { slotOptionsLeftPanelVisibility(false); } qDebug () << "MW::initWindowLayout - resize to 1280x900"; resize(1280,900); 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, SIGNAL( resized(int, int)), activeGraph, SLOT( canvasSizeSet(int,int)) ) ; connect( graphicsWidget, &GraphicsWidget::setCursor, this,&MainWindow::setCursor); connect( graphicsWidget, &GraphicsWidget::userClickOnEmptySpace, this, &MainWindow::slotEditClickOnEmptySpace ) ; connect( graphicsWidget, SIGNAL( userDoubleClickNewNode(const QPointF &) ), activeGraph, SLOT( vertexCreateAtPos(const QPointF &) ) ) ; 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 graphicsWidget // connect( graphicsWidget, &GraphicsWidget::userSelectedItems, activeGraph,&Graph::graphSelectionChanged); connect( activeGraph, SIGNAL( addGuideCircle(const double&, const double&, const double&) ), graphicsWidget, SLOT( addGuideCircle(const double&, const double&, const double&) ) ) ; connect( activeGraph, SIGNAL( addGuideHLine(const double&) ), graphicsWidget, SLOT( addGuideHLine(const double&) ) ) ; connect( activeGraph, SIGNAL( setNodePos(const int &, const qreal &, const qreal &) ), graphicsWidget, SLOT( moveNode(const int &, const qreal &, const qreal &) ) ) ; connect( activeGraph, SIGNAL( signalDrawNode( const int &, const int &, const QString &, const QString &, const QString &, const int &, const int &, const QString &, const QString &, const int &, const int &, const QPointF & ) ), graphicsWidget, SLOT( drawNode( const int &, const int &, const QString &, const QString &, const QString &, const int &, const int &, const QString &, const QString &, const int &, const int &, const QPointF & ) ) ) ; connect( activeGraph, &Graph::signalRemoveEdge, graphicsWidget,&GraphicsWidget::removeEdge); connect( activeGraph, SIGNAL( signalDrawEdge( const int&, const int&, const float &, const QString &, const QString &, const int&, const bool&, const bool&, const bool&)), graphicsWidget, SLOT( drawEdge( const int&, const int&, const float &, const QString &,const QString &, const int &, const bool&, const bool &, const bool&) ) ) ; connect( activeGraph, SIGNAL( setEdgeWeight(const long int &, const long int &, const float &)), graphicsWidget, SLOT( setEdgeWeight(const long int &, const long int &, const float &) ) ); connect( activeGraph, SIGNAL( signalEdgeType(const long int &, const long int &, const int &)), graphicsWidget, SLOT( setEdgeDirectionType(const long int &, const long int &, const int &) ) ); connect( activeGraph, SIGNAL( setEdgeColor(const long int &, const long int &, const QString &)), graphicsWidget, SLOT( setEdgeColor(const long int &, const long int &, const QString &) ) ); connect( activeGraph, SIGNAL( setEdgeLabel(const long int &, const long int &, const QString &)), graphicsWidget, SLOT( setEdgeLabel(const long int &, const long int &, const QString &) ) ); connect( activeGraph,&Graph::signalRemoveNode, graphicsWidget, &GraphicsWidget::removeNode ); connect( activeGraph, SIGNAL( setEdgeVisibility (int, int, int, bool) ), graphicsWidget, SLOT( setEdgeVisibility (int, int, int, bool) ) ); connect( activeGraph, SIGNAL( setVertexVisibility(long int, bool) ), graphicsWidget, SLOT( setNodeVisibility (long int , bool) ) ); connect( activeGraph, SIGNAL( setNodeSize(const long int &, const int &) ), graphicsWidget, SLOT( setNodeSize (const long int &, const int &) ) ); connect( activeGraph, SIGNAL( setNodeColor(long int,QString)) , graphicsWidget, SLOT( setNodeColor(long int, QString) ) ); connect( activeGraph, SIGNAL( setNodeShape(long int,QString)) , graphicsWidget, SLOT( setNodeShape(long int, QString) ) ); connect( activeGraph, SIGNAL( setNodeNumberSize(const long int &, const int &) ), graphicsWidget, SLOT( setNodeNumberSize (const long int &, const int &) ) ); connect( activeGraph, SIGNAL( setNodeNumberDistance(const long int &, const int &) ), graphicsWidget, SLOT( setNodeNumberDistance (const long int &, const int &) ) ); connect( activeGraph, &Graph::setNodeLabel , graphicsWidget, &GraphicsWidget::setNodeLabel ); connect( activeGraph,&Graph::setNodeLabelColor, graphicsWidget, &GraphicsWidget::setNodeLabelColor ); connect( activeGraph, SIGNAL( setNodeLabelSize(const long int &, const int &) ), graphicsWidget, SLOT( setNodeLabelSize (const long int &, const int &) ) ); connect( activeGraph, SIGNAL( setNodeLabelDistance(const long int &, const int &) ), graphicsWidget, SLOT( setNodeLabelDistance (const long int &, const int &) ) ); connect( graphicsWidget, &GraphicsWidget::userClickedNode, activeGraph, &Graph::vertexClickedSet ); connect( graphicsWidget, &GraphicsWidget::userClickedEdge, activeGraph, &Graph::edgeClickedSet ); connect( activeGraph, SIGNAL(signalRelationChangedToGW(int)), graphicsWidget, SLOT( relationSet(int)) ) ; // //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, SIGNAL( signalGraphModified(const int &, const bool &, const int &, const int &, const float &) ), this, SLOT( slotNetworkChanged(const int &, const bool &, const int &, const int &, const float &) ) ) ; connect( activeGraph, SIGNAL( signalGraphLoaded( const int &, const QString &, const QString &, const int &, const int &, const QString &) ), this, SLOT( slotNetworkFileLoaded( const int &, const QString &, const QString &, const int &, const int &, const QString &) ) ) ; connect( activeGraph, SIGNAL( signalGraphSaved( const int &) ), this, SLOT( slotNetworkSaved( const int &) ) ) ; 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); // //signals and slots inside MainWindow // connect( editRelationAddAct, SIGNAL(triggered()), this, SLOT(slotEditRelationAdd()) ); connect( editRelationRenameAct,SIGNAL(triggered()), this, SLOT(slotEditRelationRename()) ) ; connect( &m_DialogEdgeFilterByWeight, SIGNAL( userChoices( float, bool) ), activeGraph, SLOT( edgeFilterByWeight (float, bool) ) ); connect(webCrawlerAct, SIGNAL(triggered()), this, SLOT(slotNetworkWebCrawlerDialog())); connect( &m_datasetSelectDialog, SIGNAL( userChoices( QString) ), this, SLOT( slotNetworkDataSetRecreate(QString) ) ); 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(toolBoxEditNodeSubgraphSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxEditNodeSubgraphSelectChanged(int) ) ); connect(toolBoxEditEdgeModeSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(slotEditEdgeMode(int) ) ); connect(toolBoxEditEdgeSymmetrizeSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxEditEdgeSymmetrizeSelectChanged(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 INITIALIZATION 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"; networkSave->setIcon(QIcon(":/images/saved.png")); networkSave->setEnabled(true); markedNodesExist=false; //used by slotEditNodeFind() /** Clear previous network data */ activeGraph->clear(); activeGraph->setSocNetV_Version(VERSION); activeGraph->vertexShapeInit(appSettings["initNodeShape"]); 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 ); /** Clear graphicsWidget scene 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"])) ); } /** Clear LCDs **/ slotNetworkChanged(0, 0, 0, 0, 0); rightPanelClickedNodeInDegreeLCD->setText("-"); rightPanelClickedNodeOutDegreeLCD->setText("-"); rightPanelClickedNodeClucofLCD->setText("-"); rightPanelClickedNodeLCD->setText("-"); rightPanelClickedEdgeNameLCD->setText("-"); rightPanelClickedEdgeWeightLCD->setText("-"); rightPanelClickedEdgeReciprocalWeightLCD->setText(""); /** Clear toolbox and menu checkboxes **/ toolBoxEditEdgeSymmetrizeSelect->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(); setCursor(Qt::ArrowCursor); statusMessage( tr("Ready")); qDebug()<< "MW::initApp() - INITIALISATION END on thread" << thread(); } void MainWindow::initComboBoxes() { toolBoxAnalysisCommunitiesSelect->setCurrentIndex(0); toolBoxAnalysisStrEquivalenceSelect->setCurrentIndex(0); toolBoxAnalysisCohesionSelect->setCurrentIndex(0); toolBoxAnalysisProminenceSelect->setCurrentIndex(0); toolBoxAnalysisMatricesSelect->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 Subgraph from Selected * Nodes selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxEditNodeSubgraphSelectChanged(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 Symmetrize * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxEditEdgeSymmetrizeSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxEditEdgeSymmetrizeSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotEditEdgeSymmetrizeAll(); break; case 2: slotEditEdgeSymmetrizeStrongTies(); break; case 3: slotEditEdgeSymmetrizeCocitation(); break; }; } /** * @brief Called from MW, when user selects something in the Matrices * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxAnalysisMatricesSelectChanged(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(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(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(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(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 something * @param filePath */ void MainWindow::setLastPath(QString filePath) { qDebug()<< "MW::setLastPath() for " << filePath; appSettings["lastUsedDirPath"] = QFileInfo(filePath).dir().absolutePath(); 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 FILE_GRAPHML: fileType_filter = tr("GraphML (*.graphml *.xml);;All (*)"); break; case FILE_PAJEK: fileType_filter = tr("Pajek (*.net *.paj *.pajek);;All (*)"); break; case FILE_ADJACENCY: fileType_filter = tr("Adjacency (*.csv *.sm *.adj *.txt);;All (*)"); break; case FILE_GRAPHVIZ: fileType_filter = tr("GraphViz (*.dot);;All (*)"); break; case FILE_UCINET: fileType_filter = tr("UCINET (*.dl *.dat);;All (*)"); break; case FILE_GML: fileType_filter = tr("GML (*.gml);;All (*)"); break; case FILE_EDGELIST_WEIGHTED: fileType_filter = tr("Weighted Edge List (*.csv *.txt *.list *.edgelist *.lst *.wlst);;All (*)"); break; case FILE_EDGELIST_SIMPLE: fileType_filter = tr("Simple Edge List (*.csv *.txt *.list *.edgelist *.lst);;All (*)"); break; case FILE_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 (*.csv *.txt *.edgelist *.list *.lst *.wlst);;" "Simple Edge List (*.csv *.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==FILE_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(".gml",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".wlist",Qt::CaseInsensitive )&& ! m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".aff",Qt::CaseInsensitive )) { //ambigious 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=FILE_GRAPHML; } else if (userFileType == "GraphML") { m_fileFormat=FILE_PAJEK; } else if (userFileType == "GML") { m_fileFormat=FILE_GML; } else if (userFileType == "UCINET") { m_fileFormat=FILE_UCINET; } else if (userFileType == "Adjacency") { m_fileFormat=FILE_ADJACENCY; } else if (userFileType == "GraphViz") { m_fileFormat=FILE_GRAPHVIZ; } else if (userFileType == "Edge List (weighted)") { m_fileFormat=FILE_EDGELIST_WEIGHTED; } else if (userFileType == "Edge List (simple, non-weighted)") { m_fileFormat=FILE_EDGELIST_SIMPLE; } else if (userFileType == "Two-mode sociomatrix") { m_fileFormat=FILE_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=FILE_GRAPHML; } else if (m_fileName.endsWith(".net",Qt::CaseInsensitive ) || m_fileName.endsWith(".paj",Qt::CaseInsensitive ) || m_fileName.endsWith(".pajek",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_PAJEK; } else if (m_fileName.endsWith(".dl",Qt::CaseInsensitive ) || m_fileName.endsWith(".dat",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_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=FILE_ADJACENCY; } else if (m_fileName.endsWith(".dot",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_GRAPHVIZ; } else if (m_fileName.endsWith(".gml",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_GML; } else if (m_fileName.endsWith(".list",Qt::CaseInsensitive ) || m_fileName.endsWith(".lst",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_EDGELIST_SIMPLE; } else if (m_fileName.endsWith(".wlist",Qt::CaseInsensitive ) || m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_EDGELIST_WEIGHTED; } else if (m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".aff",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_TWOMODE; } else m_fileFormat=FILE_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=FILE_GRAPHML; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_GRAPHML"; } else if (filter.contains("PAJEK",Qt::CaseInsensitive ) ) { fileType=FILE_PAJEK; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_PAJEK"; } else if (filter.contains("DL",Qt::CaseInsensitive ) || filter.contains("UCINET",Qt::CaseInsensitive ) ) { fileType=FILE_UCINET; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_UCINET"; } else if (filter.contains("Adjancency",Qt::CaseInsensitive ) ) { fileType=FILE_ADJACENCY; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_ADJACENCY"; } else if (filter.contains("GraphViz",Qt::CaseInsensitive ) ) { fileType=FILE_GRAPHVIZ; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_GRAPHVIZ"; } else if (filter.contains("GML",Qt::CaseInsensitive ) ) { fileType=FILE_GML; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_GML"; } else if (filter.contains("Simple Edge List",Qt::CaseInsensitive ) ) { fileType=FILE_EDGELIST_SIMPLE; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_EDGELIST_SIMPLE"; } else if (filter.contains("Weighted Edge List",Qt::CaseInsensitive ) ) { fileType=FILE_EDGELIST_WEIGHTED; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_EDGELIST_WEIGHTED"; } else if (filter.contains("Two-Mode",Qt::CaseInsensitive ) ) { fileType=FILE_TWOMODE; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_TWOMODE"; } else { fileType=FILE_UNRECOGNIZED; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_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==FILE_UNRECOGNIZED) ? true : false ) ); } /** * @brief Saves the network to a file. * First check if a fileName is currently used * If not, calls slotNetworkSaveAs (which prompts for a fileName before returning here) * If a fileName is currently set, it checks if fileFormat is supported for export * If not supported, and the file is new, just tries to save in GraphML * For other exporing 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 ) ; } // if it is GraphML or new file not saved yet, just save it. else if (activeGraph->graphFileFormat()==FILE_GRAPHML || ( activeGraph->graphSaved() && !activeGraph->graphLoaded() ) ) { activeGraph->graphSave(fileName, FILE_GRAPHML); } // check whether Graph thinks this is supported. 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, FILE_GRAPHML); break; case QMessageBox::Cancel: case QMessageBox::No: statusMessage( tr("Save aborted...") ); break; } } } /** * @brief Saves the network in a new file. Always uses the graphml 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(FILE_GRAPHML); } else { statusMessage( tr("Saving aborted")); return; } } /** * @brief Called from Graph when we save file. * Updates Save icon and window title. * @param saved_ok * */ void MainWindow::slotNetworkSaved(const int &status) { if (status <= 0) { statusMessage( tr("Error! Could not save this file: %1").arg (fileNameNoPath)); } else { networkSave->setIcon(QIcon(":/images/saved.png")); networkSave->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() ) { 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::null, FILE_GRAPHML, m_checkSelectFileType); } /** * @brief Imports a network from a GML formatted file */ void MainWindow::slotNetworkImportGML(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_GML, m_checkSelectFileType); } /** * @brief Imports a network from a Pajek-like formatted file */ void MainWindow::slotNetworkImportPajek(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_PAJEK, m_checkSelectFileType); } /** * @brief Imports a network from a Adjacency matrix formatted file */ void MainWindow::slotNetworkImportSM(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_ADJACENCY, m_checkSelectFileType); } /** * @brief Imports a network from a Dot (GraphViz) formatted file */ void MainWindow::slotNetworkImportDot(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null ,FILE_GRAPHVIZ, m_checkSelectFileType); } /** * @brief Imports a network from a UCINET formatted file */ void MainWindow::slotNetworkImportDL(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_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::null, FILE_EDGELIST_WEIGHTED, m_checkSelectFileType); break; case 2: qDebug() << "*** MW: slotNetworkImportEdgeList - Simple list selected! " ; slotNetworkFileChoose( QString::null, FILE_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::null, FILE_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::null; int two_sm_mode = 0; if ( m_fileFormat == FILE_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 == FILE_EDGELIST_SIMPLE || m_fileFormat == FILE_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) { 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( QString(tr("Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges )); break; case 3: statusMessage( QString(tr("Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 4: statusMessage( QString(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( QString(tr("UCINET formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 6: statusMessage( QString(tr("GML formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 7: statusMessage( QString(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( QString(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( QString(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; } networkSave->setIcon(QIcon(":/images/saved.png")); networkSave->setEnabled(false); QApplication::restoreOverrideCursor(); } /** * @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::null, &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 Exports the network to a PNG image - Mediocre Quality but smaller file * @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 ("/"); qDebug("slotExportPNG: grabbing canvas"); QPixmap picture; picture=QPixmap::grabWidget(graphicsWidget, graphicsWidget->rect()); qDebug("slotExportPNG: adding logo"); QPainter p; p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); if (appSettings["printLogo"]=="true") { QImage logo(":/images/socnetv-logo.png"); p.drawImage(5,5, logo); p.drawText(7,47,tempFileNameNoPath.last()); } else p.drawText(5,15,tempFileNameNoPath.last()); p.end(); qDebug("slotExportPNG: checking filename"); if (fn.contains("png", Qt::CaseInsensitive) ) { picture.toImage().save(fn, "PNG"); QMessageBox::information(this, "Export to PNG...", tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); } else { picture.toImage().save(fn+".png", "PNG"); QMessageBox::information(this, "Export to PNG...", tr("Image Saved as: ")+tempFileNameNoPath.last()+".png" , "OK",0); } statusMessage( tr("Exporting completed") ); return true; } /** * @brief Exports the network to a BMP image - Better Quality but larger file * @return */ bool MainWindow::slotNetworkExportBMP(){ qDebug( "slotNetworkExportBMP()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } QString format="bmp"; QString fn = QFileDialog::getSaveFileName( this,tr("Save Image as"), getLastPath(),tr("Image Files (*.bmp)")); if (fn.isEmpty()) { statusMessage( tr("Saving aborted") ); return false; } setLastPath(fn); // store this path tempFileNameNoPath=fn.split ("/"); QPixmap picture; qDebug("slotNetworkExportBMP: grabbing canvas"); picture=QPixmap::grabWidget(graphicsWidget, graphicsWidget->viewport()->rect()); QPainter p; qDebug("slotNetworkExportBMP: adding 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,tempFileNameNoPath.last()); } else p.drawText(5,15,tempFileNameNoPath.last()); p.end(); qDebug("slotNetworkExportBMP: checking file"); if (fn.contains(format, Qt::CaseInsensitive) ) { picture.toImage().save(fn, format.toLatin1()); QMessageBox::information(this, tr("Export to BMP..."), tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); } else { picture.toImage().save(fn+"."+format, format.toLatin1()); QMessageBox::information(this, tr("Export to BMP..."), tr("Image Saved as: ")+tempFileNameNoPath.last()+"."+format , "OK",0); } qDebug()<< "Exporting BMP to "<< fn; statusMessage( tr("Exporting completed") ); qDebug("Export finished!"); return true; } /** * @brief Exports the network to a PDF Document - Best Quality * @return * */ bool MainWindow::slotNetworkExportPDF(){ qDebug()<< "MW::slotNetworkExportPDF()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } QString m_fileName = QFileDialog::getSaveFileName( this, tr("Export to PDF"), getLastPath(), tr("Portable Document Format files (*.pdf)")); if (m_fileName.isEmpty()) { statusMessage( tr("Saving aborted")); return false; } else { if (QFileInfo(m_fileName).suffix().isEmpty()) m_fileName.append(".pdf"); // dont set to HighResolution - it breaks pdf export QPrinter printer(QPrinter::ScreenResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(m_fileName); QPainter p; p.begin(&printer); graphicsWidget->render(&p); p.end(); } qDebug()<< "Exporting PDF to "<< m_fileName; tempFileNameNoPath=m_fileName.split ("/"); setLastPath(m_fileName); QMessageBox::information(this, tr("Export to PDF..."), tr("File saved as: ")+tempFileNameNoPath.last() , "OK",0); statusMessage( tr("Exporting completed") ); return true; } /** * @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, FILE_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->graphWeighted() ) { 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, FILE_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 ) { float 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.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=FILE_GRAPHML; } else if (m_fileName.endsWith(".pajek") || m_fileName.endsWith(".paj") || m_fileName.endsWith(".net")) { m_fileFormat=FILE_PAJEK; } else if (m_fileName.endsWith(".sm") || m_fileName.endsWith(".adj")) { m_fileFormat=FILE_ADJACENCY; } else if (m_fileName.endsWith(".dot")) { m_fileFormat=FILE_GRAPHVIZ; } else if (m_fileName.endsWith(".dl")) { m_fileFormat=FILE_UCINET; } else if (m_fileName.endsWith(".gml")) { m_fileFormat=FILE_GML; } else if (m_fileName.endsWith(".wlst")) { m_fileFormat=FILE_EDGELIST_WEIGHTED; } else if (m_fileName.endsWith(".lst")) { m_fileFormat=FILE_EDGELIST_SIMPLE; } else if (m_fileName.endsWith(".2sm")) { m_fileFormat=FILE_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 float 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; //float 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 float &zeroAppeal, const QString &mode) { qDebug() << "MW::slotNetworkRandomScaleFree()"; initApp(); activeGraph->randomNetScaleFreeCreate( newNodes, power, initialNodes, edgesPerStep, zeroAppeal, mode); setWindowTitle("Untitled scale-free network"); //float avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //float 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 float &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"); //float avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //float 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"); //float avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //float 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"); //float avGraphDistance=activeGraph->graphDistanceGeodesicAverage(); //float 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 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, SIGNAL( userChoices(const QString &, const QStringList &, const QStringList &, const QStringList &, const int&, const int&, const bool&, const bool&, const bool&, const bool &) ), this, SLOT( slotNetworkWebCrawler (const QString &, const QStringList &, const QStringList &, const QStringList &, const int&, const int&, const bool&, const bool&, const bool&, const bool &) ) ); 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 &extLinks, const bool &intLinks, const bool &selfLinks, const bool &delayedRequests ) { this->slotNetworkClose(); qDebug () << "MW::slotNetworkWebCrawler" << urlPatternsIncluded; qDebug () << "MW::slotNetworkWebCrawler" << linkClasses; activeGraph->webCrawl( urlSeed, urlPatternsIncluded, urlPatternsExcluded, linkClasses, maxNodes, maxLinksPerPage, extLinks, intLinks, selfLinks, delayedRequests) ; } /** * @brief MainWindow::slotNetworkChanged * Activated when something has been changed in the graph. * Makes the networkSave icon active and refreshes any LCD values. * Also called from activeGraph and graphicsWidget. */ void MainWindow::slotNetworkChanged(const int &graphStatus, const bool &undirected, const int &vertices, const int &edges, const float &density){ qDebug()<<"MW::slotNetworkChanged()" <<"graphStatus" << graphStatus <<"undirected" << undirected <<"vertices" << vertices <<"edges" <setIcon(QIcon(":/images/save.png")); networkSave->setEnabled(true); } rightPanelNodesLCD->setText (QString::number(vertices)); if ( undirected ) { 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()) { networkSave->setIcon(QIcon(":/images/save.png")); networkSave->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 MainWindow::slotEditNodeFind * Calls GW::setMarkedNode() to find a node by its number or label. * The node is then marked. */ void MainWindow::slotEditNodeFind(){ qDebug() << "MW::slotEditNodeFind()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if ( markedNodesExist ) { // if a node has been already marked graphicsWidget->setMarkedNode(""); // call setMarkedNode to just unmark it. markedNodesExist=false; statusMessage( tr("Node unmarked.") ); return; // and return to MW } bool ok=false; QString nodeText = QInputDialog::getText(this, tr("Find Node"), tr("Enter node label or node number:"), QLineEdit::Normal,QString::null, &ok ); if (!ok) { statusMessage( tr("Find node operation cancelled.") ); return; } else { if ( graphicsWidget->setMarkedNode(nodeText) ) { markedNodesExist=true; statusMessage( tr("Node found and marked. Press Ctrl+F again to unmark...") ); } else { QMessageBox::information(this, tr("Find Node"), tr("Sorry. There is no such node in this network. \n Try again."), "OK",0); } } } /** * @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 MainWindow::slotEditNodePropertiesDialog * Reads values from selected nodes * then open Node Properties dialog */ 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 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; } } else { foreach (nodeNumber, activeGraph->graphSelectedVertices() ) { qDebug() << "MW::slotEditNodePropertiesDialog() " "changing properties for selected node " << nodeNumber ; if ( selectedNodesCount > 1 ) { color = activeGraph->vertexColor( nodeNumber ); shape = activeGraph->vertexShape( nodeNumber); size = activeGraph->vertexSize ( nodeNumber); } else { label = activeGraph->vertexLabel( nodeNumber ); color = activeGraph->vertexColor( nodeNumber ); shape = activeGraph->vertexShape( nodeNumber); size = activeGraph->vertexSize ( nodeNumber); } } } //@todo add some grouping function here? m_nodeEditDialog = new DialogNodeEdit(this, label, size, color, shape) ; connect( m_nodeEditDialog, &DialogNodeEdit::userChoices, this, &MainWindow::slotEditNodeProperties ); m_nodeEditDialog->exec(); statusMessage( tr("Node properties dialog opened. Ready. ") ); } /** * @brief MainWindow::slotEditNodeProperties * Applies new (user-defined) values to all selected nodes * Called on exit from DialogNodeEdit * @param label * @param size * @param value * @param color * @param shape */ void MainWindow::slotEditNodeProperties( const QString label, const int size, const QString value, const QColor color, const QString shape) { int selectedNodesCount = activeGraph->graphSelectedVerticesCount(); qDebug()<< "MW::slotEditNodeProperties() - new properties: " << " label " << label << " size " << size << "value " << value << " color " << color << " shape " << shape << " vertexClicked " <vertexClicked() << " selectedNodesCount " << selectedNodesCount; if ( selectedNodesCount == 0 && activeGraph->vertexClicked() != 0) { // no node selected but user entered a node number in a dialog if ( label !="" && appSettings["initNodeLabelsVisibility"] != "true") slotOptionsNodeLabelsVisibility(true); qDebug()<< "MW::slotEditNodeProperties() - updating label "; activeGraph->vertexLabelSet( activeGraph->vertexClicked(), label ); qDebug()<< "MW::slotEditNodeProperties() - updating color "; activeGraph->vertexColorSet( activeGraph->vertexClicked(), color.name()); qDebug()<< "MW::slotEditNodeProperties() - updating size "; activeGraph->vertexSizeSet( activeGraph->vertexClicked(), size); qDebug()<< "MW::slotEditNodeProperties() - updating shape "; activeGraph->vertexShapeSet( activeGraph->vertexClicked(), shape); } else { //some nodes are selected int nodeNumber = 0; foreach (nodeNumber, activeGraph->graphSelectedVertices() ) { qDebug()<< "MW::slotEditNodeProperties() - node " << nodeNumber; qDebug()<< "MW::slotEditNodeProperties() - updating label "; if ( selectedNodesCount > 1 ) { activeGraph->vertexLabelSet( nodeNumber, label + QString::number(nodeNumber) ); } else activeGraph->vertexLabelSet( nodeNumber, label ); if ( label !="" && appSettings["initNodeLabelsVisibility"] != "true") slotOptionsNodeLabelsVisibility(true); qDebug()<< "MW::slotEditNodeProperties() - updating color "; activeGraph->vertexColorSet( nodeNumber, color.name()); qDebug()<< "MW::slotEditNodeProperties() - updating size "; activeGraph->vertexSizeSet(nodeNumber,size); qDebug()<< "MW::slotEditNodeProperties() - updating shape "; activeGraph->vertexShapeSet( nodeNumber, shape); } } statusMessage( tr("Ready. ")); } /** * @brief Creates a complete subgraph (clique) from selected nodes. * Calls Graph::verticesSelectedCreateClique() */ void MainWindow::slotEditNodeSelectedToClique () { qDebug() << "MW::slotEditNodeSelectedToClique()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph->graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create new clique. No nodes are selected."), tr("Select some nodes first.") ); return; } activeGraph->verticesCreateSubgraph(QList (), SUBGRAPH_CLIQUE); slotHelpMessageToUser(USER_MSG_INFO,tr("Clique created."), tr("A new clique has been created from ") + QString::number(selectedNodesCount) + tr(" nodes") ); } /** * @brief Creates a star subgraph from selected nodes. * User must choose a central actor. * Calls Graph::slotEditNodeSelectedToStar() */ void MainWindow::slotEditNodeSelectedToStar() { qDebug() << "MW::slotEditNodeSelectedToStar()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph->graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create star subgraph. No nodes are selected."), tr("Select some nodes first.") ); return; } int center; bool ok=false; int min = activeGraph->graphSelectedVerticesMin(); int max = activeGraph->graphSelectedVerticesMax(); center=QInputDialog::getInt( this, "Create star subgraph", tr("To create a star subgraph from selected nodes, \n" "enter the number of the central actor (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Create star subgraph cancelled." ); return; } activeGraph->verticesCreateSubgraph(QList (), SUBGRAPH_STAR,center); slotHelpMessageToUser(USER_MSG_INFO,tr("Star subgraph created."), tr("A new star subgraph has been created from ") + QString::number( selectedNodesCount ) + tr(" nodes") ); } /** * @brief Creates a cycle subgraph from selected nodes. * Calls Graph::verticesSelectedCreateCycle() */ void MainWindow::slotEditNodeSelectedToCycle() { qDebug() << "MW::slotEditNodeSelectedToCycle()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph->graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create cycle subgraph. No nodes are selected."), tr("Select some nodes first.") ); return; } activeGraph->verticesCreateSubgraph(QList (),SUBGRAPH_CYCLE); slotHelpMessageToUser(USER_MSG_INFO,tr("Cycle subgraph created."), tr("A new cycle subgraph has been created from ") + QString::number( selectedNodesCount ) + tr(" nodes") ); } /** * @brief Creates a line subgraph from selected nodes. * Calls Graph::verticesSelectedCreateLine() */ void MainWindow::slotEditNodeSelectedToLine() { qDebug() << "MW::slotEditNodeSelectedToLine()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph->graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create line subgraph. No nodes are selected."), tr("Select some nodes first.") ); return; } activeGraph->verticesCreateSubgraph(QList (),SUBGRAPH_LINE); slotHelpMessageToUser(USER_MSG_INFO,tr("Line subgraph created."), tr("A new line subgraph has been created from ") + QString::number( selectedNodesCount ) + tr(" nodes") ); } /** * @brief MainWindow::slotEditNodeColorAll * Changes the color of all nodes to parameter color * Calls activeGraph->vertexColorAllSet to do the work * If parameter color is invalid, opens a QColorDialog to * select a new node color for all nodes. * Called from Settings Dialog and Edit menu option * @param color */ void MainWindow::slotEditNodeColorAll(QColor color){ if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initNodeColor"] ), this, "Change the color of all nodes" ); } if (color.isValid()) { appSettings["initNodeColor"] = color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MW::slotEditNodeColorAll() : " << appSettings["initNodeColor"]; activeGraph->vertexColorAllSet(appSettings["initNodeColor"]); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotEditNodeSizeAll * Changes the size of nodes to newSize. * Calls activeGraph->vertexSizeAllSet to do the work. * Called from Edit menu item, DialogSettings * If newSize = 0 asks the user a new size for all nodes * If normalized = true, changes node sizes according to their plethos * @param newSize * @param normalized */ void MainWindow::slotEditNodeSizeAll(int newSize, const bool &normalized) { Q_UNUSED(normalized); qDebug () << "MW: slotEditNodeSizeAll() - " << " newSize " << newSize ; if ( newSize == 0 && !normalized ) { bool ok=true; newSize = QInputDialog::getInt( this, "Change node size", tr("Select new size for all nodes:"), appSettings["initNodeSize"].toInt(0, 10), 1, 100, 1, &ok ); if (!ok) { statusMessage( "Change node size operation cancelled." ); return; } } appSettings["initNodeSize"]= QString::number(newSize); activeGraph->vertexSizeAllSet(newSize); statusMessage(tr("Ready")); return; } /** * @brief MainWindow::slotEditNodeShape * If shape == null, prompts the user a list of available node shapes to select. * Then changes the shape of all nodes/vertices accordingly. * If vertex is non-zero, changes the shape of that node only. * Called when user clicks on Edit -> Node > Change all nodes shapes * Called from DialogSettings when the user has selected a new default node shape * Calls Graph::vertexShapeAllSet(QString) * @param shape * @param vertex */ void MainWindow::slotEditNodeShape(QString shape, const int vertex) { qDebug() << "MW::slotEditNodeShape() - vertex " << vertex << " (0 means all) - new shape " << shape; if (shape==QString::null) { bool ok=false; QStringList lst; lst << "box"<< "circle"<< "diamond" << "ellipse"<< "triangle" << "star"; int curShapeIndex = lst.indexOf(appSettings["initNodeShape"]); if ( curShapeIndex == -1 ) { curShapeIndex=1; } shape = QInputDialog::getItem(this, "Node shape", "Select a shape for all nodes: ", lst, curShapeIndex, true, &ok); if ( !ok ) { //user pressed Cancel statusMessage(tr("Change node shapes aborted.")); return; } } if (vertex == 0) { //change all nodes shapes activeGraph->vertexShapeAllSet(shape); appSettings["initNodeShape"] = shape; statusMessage(tr("All shapes have been changed. Ready.")); } else { //only one activeGraph->vertexShapeSet( vertex, shape); statusMessage(tr("Node shape has been changed. Ready.")); } } /** * @brief MainWindow::slotEditNodeNumberSize * Changes the size of one or all node numbers. * Called from Edit menu option and DialogSettings * if newSize=0, asks the user to enter a new node number font size * if v1=0, it changes all node numbers * @param v1 * @param newSize */ void MainWindow::slotEditNodeNumberSize(int v1, int newSize, const bool prompt) { bool ok=false; qDebug() << "MW::slotEditNodeNumberSize - newSize " << newSize; if (prompt) { newSize = QInputDialog::getInt(this, "Change text size", tr("Change all node numbers size to: (1-16)"), appSettings["initNodeNumberSize"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change font size: Aborted.") ); return; } } if (v1) { //change one node number only activeGraph->vertexNumberSizeSet(v1, newSize); } else { //change all appSettings["initNodeNumberSize"] = QString::number(newSize); activeGraph->vertexNumberSizeSetAll(newSize); } statusMessage( tr("Changed node numbers size. Ready.") ); } /** * @brief MainWindow::slotEditNodeNumbersColor * Changes the color of all nodes' numbers. * Called from Edit menu option and Settings dialog. * Asks the user to enter a new node number color */ void MainWindow::slotEditNodeNumbersColor(QColor color){ qDebug() << "MW:slotEditNodeNumbersColor() - new color " << color; if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initNodeNumberColor"] ), this, "Change the color of all node numbers" ); } if (color.isValid()) { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); QList list= graphicsWidget->scene()->items(); for (QList::iterator it=list.begin(); it!=list.end(); it++) { if ( (*it)->type() == TypeNumber) { GraphicsNodeNumber *jimNumber = (GraphicsNodeNumber *) (*it); jimNumber->update(); jimNumber->setDefaultTextColor(color); } } appSettings["initNodeNumberColor"] = color.name(); activeGraph->vertexNumberColorInit( color.name() ); QApplication::restoreOverrideCursor(); statusMessage( tr("Numbers' colors changed. Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotEditNodeNumberDistance * Changes the distance of one or all node numbers from their nodes. * Called from Edit menu option and DialogSettings * if newDistance=0, asks the user to enter a new node number distance * if v1=0, it changes all node number distances * @param v1 * @param newDistance */ void MainWindow::slotEditNodeNumberDistance(int v1, int newDistance) { bool ok=false; qDebug() << "MW::slotEditNodeNumberDistance - newSize " << newDistance; if (!newDistance) { newDistance = QInputDialog::getInt( this, "Change node number distance", tr("Change all node numbers distance from their nodes to: (1-16)"), appSettings["initNodeNumberDistance"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change node number distance aborted.") ); return; } } if (v1) { //change one node number distance only activeGraph->vertexNumberDistanceSet(v1, newDistance); } else { //change all appSettings["initNodeNumberDistance"] = QString::number(newDistance); activeGraph->vertexNumberDistanceSetAll(newDistance); } statusMessage( tr("Changed node number distance. Ready.") ); } /** * @brief MainWindow::slotEditNodeLabelSize * Changes the size of one or all node Labels. * Called from Edit menu option and DialogSettings * if newSize=0, asks the user to enter a new node Label font size * if v1=0, it changes all node Labels * @param v1 * @param newSize */ void MainWindow::slotEditNodeLabelSize(int v1, int newSize) { bool ok=false; qDebug() << "MW::slotEditNodeLabelSize - newSize " << newSize; if (!newSize) { newSize = QInputDialog::getInt(this, "Change text size", tr("Change all node labels text size to: (1-16)"), appSettings["initNodeLabelSize"].toInt(0,10), 1, 32, 1, &ok ); if (!ok) { statusMessage( tr("Change font size: Aborted.") ); return; } } if (v1) { //change one node Label only activeGraph->vertexLabelSizeSet(v1, newSize); } else { //change all appSettings["initNodeLabelSize"] = QString::number(newSize); activeGraph->vertexLabelSizeAllSet(newSize); } statusMessage( tr("Changed node label size. Ready.") ); } /** * @brief MainWindow::slotEditNodeLabelsColor * Changes the color of all nodes' labels. * Asks the user to enter a new node label color */ void MainWindow::slotEditNodeLabelsColor(QColor color){ qDebug() << "MW:slotEditNodeNumbersColor() - new color " << color; if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initNodeLabelColor"] ), this, "Change the color of all node labels" ); } if (color.isValid()) { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph->vertexLabelColorAllSet(color.name()); appSettings["initNodeLabelColor"] = color.name(); optionsNodeLabelsVisibilityAct->setChecked(true); QApplication::restoreOverrideCursor(); statusMessage( tr("Label colors changed. Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotEditNodeLabelDistance * Changes the distance of one or all node label from their nodes. * Called from Edit menu option and DialogSettings * if newDistance=0, asks the user to enter a new node label distance * if v1=0, it changes all node label distances * @param v1 * @param newDistance */ void MainWindow::slotEditNodeLabelDistance(int v1, int newDistance) { bool ok=false; qDebug() << "MW::slotEditNodeLabelDistance - newSize " << newDistance; if (!newDistance) { newDistance = QInputDialog::getInt( this, "Change node label distance", tr("Change all node labels distance from their nodes to: (1-16)"), appSettings["initNodeLabelDistance"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change node label distance aborted.") ); return; } } if (v1) { //change one node label distance only activeGraph->vertexLabelDistanceSet(v1, newDistance); } else { //change all appSettings["initNodeLabelDistance"] = QString::number(newDistance); activeGraph->vertexLabelDistanceAllSet(newDistance); } statusMessage( tr("Changed node label distance. Ready.") ); } /** * @brief MainWindow::slotEditNodeOpenContextMenu * Called from GW when the user has right-clicked on a node * Opens a node context menu with some options when the user right-clicks on a node */ void MainWindow::slotEditNodeOpenContextMenu() { qDebug("MW: slotEditNodeOpenContextMenu() for node %i at %i, %i", activeGraph->vertexClicked(), QCursor::pos().x(), QCursor::pos().y()); QMenu *nodeContextMenu = new QMenu(QString::number( activeGraph->vertexClicked() ), this); Q_CHECK_PTR( nodeContextMenu ); //displays "out of memory" if needed int nodesSelected = activeGraph->graphSelectedVerticesCount(); if ( nodesSelected == 1) { nodeContextMenu -> addAction( tr("## NODE ") + QString::number(activeGraph->vertexClicked()) + " ## " ); } else { nodeContextMenu -> addAction( tr("## NODE ") + QString::number(activeGraph->vertexClicked()) + " ## " + tr(" (selected nodes: ") + QString::number ( nodesSelected ) + ")"); } nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(editNodePropertiesAct ); nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(editEdgeAddAct); nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(editNodeRemoveAct ); nodeContextMenu -> addSeparator(); //QCursor::pos() is good only for menus not related with node coordinates nodeContextMenu -> exec(QCursor::pos() ); delete nodeContextMenu; } /** * @brief MainWindow::slotEditSelectionChanged * @param nodes * @param edges */ void MainWindow::slotEditSelectionChanged(const int &selNodes, const int &selEdges) { qDebug()<< "MW::slotEditSelectionChanged()"; rightPanelSelectedNodesLCD->setText(QString::number(selNodes)); rightPanelSelectedEdgesLCD->setText(QString::number(selEdges)); if (selNodes > 1 ){ editNodeRemoveAct->setText(tr("Remove ") + QString::number(selNodes) + tr(" nodes")); editNodeSelectedToCliqueAct->setEnabled(true); editNodeSelectedToCliqueAct->setText(tr("Create a clique from ") + QString::number(selNodes) + tr(" selected nodes")); editNodeSelectedToStarAct->setEnabled(true); editNodeSelectedToStarAct->setText(tr("Create a star from ") + QString::number(selNodes) + tr(" selected nodes")); editNodeSelectedToCycleAct->setEnabled(true); editNodeSelectedToCycleAct->setText(tr("Create a cycle from ") + QString::number(selNodes) + tr(" selected nodes")); editNodeSelectedToLineAct->setEnabled(true); editNodeSelectedToLineAct->setText(tr("Create a line from ") + QString::number(selNodes) + tr(" selected nodes")); } else { editNodeRemoveAct->setText(tr("Remove Node")); editNodeSelectedToCliqueAct->setText(tr("Create a clique from selected nodes")); editNodeSelectedToCliqueAct->setEnabled(false); editNodeSelectedToStarAct->setText(tr("Create a star from selected nodes")); editNodeSelectedToStarAct->setEnabled(false); editNodeSelectedToCycleAct->setText(tr("Create a cycle from selected nodes")); editNodeSelectedToCycleAct->setEnabled(false); editNodeSelectedToLineAct->setText(tr("Create a line from selected nodes")); editNodeSelectedToLineAct->setEnabled(false); } // DO NOT post a message on the status bar on high frequently called functions like this // statusMessage( tr("Selected %1 nodes and %2 edges") // .arg( selNodes ) // .arg( selEdges ) // ); } /** * @brief MainWindow::slotEditNodeInfoStatusBar * Called by Graph::userClickedNode() signal, when the user clicks on a node. * It displays information about the node on the statusbar. * @param jim */ void MainWindow::slotEditNodeInfoStatusBar (const int &number, const QPointF &p, const QString &label, const int &inDegree, const int &outDegree, const float &clc) { qDebug()<<"MW::slotEditNodeInfoStatusBar()"; rightPanelClickedNodeLCD->setText (QString::number(number)); rightPanelClickedNodeInDegreeLCD->setText ( QString::number (inDegree) ) ; rightPanelClickedNodeOutDegreeLCD->setText ( QString::number (outDegree) ) ; rightPanelClickedNodeClucofLCD->setText ( QString::number (clc) ) ; if (number!=0) { statusMessage( QString(tr("Position (%1, %2): Node %3, label %4 - " "In-Degree: %5, Out-Degree: %6")) .arg( ceil( p.x() ) ) .arg( ceil( p.y() )).arg( number ) .arg( ( label == "") ? "unset" : label ) .arg(inDegree).arg(outDegree) ); } } /** * @brief Called by Graph::signalEdgeClicked when the user clicks on an edge * Displays information about the clicked edge on the statusbar * @param edge */ void MainWindow::slotEditEdgeClicked (const int &v1, const int &v2, const float &weight, const int &type, const bool &openMenu) { qDebug()<<"MW::slotEditEdgeClicked()" << v1 << "->" << v2 << "=" << weight << "type" << type << "openMenu"<setText("-"); rightPanelClickedEdgeWeightLCD->setText("-"); rightPanelClickedEdgeReciprocalWeightLCD->setText(""); return; } QString edgeName; if ( type == EDGE_UNDIRECTED ) { statusMessage( QString (tr("Undirected edge %1 <--> %2 of weight %3 has been selected. " "Click anywhere else to unselect it.")) .arg( v1 ).arg( v2 ) .arg( weight ) ); rightPanelClickedEdgeNameLCD->setText(QString::number(v1)+QString(" -- ")+QString::number(v2)); rightPanelClickedEdgeWeightLabel->setText(tr("Weight:")); rightPanelClickedEdgeWeightLCD->setText(QString::number(weight)); rightPanelClickedEdgeReciprocalWeightLabel->setText(""); rightPanelClickedEdgeReciprocalWeightLCD->setText(""); if (openMenu) { edgeName=QString("EDGE: ") + QString::number(v1)+QString(" -- ")+QString::number(v2); } } else if (type == EDGE_RECIPROCATED){ statusMessage( QString (tr("Reciprocated edge %1 <--> %2 of weight %3 has been selected. " "Opposite exists. " "Click anywhere else to unselect it.")) .arg( v1 ).arg( v2 ) .arg( weight ) ); rightPanelClickedEdgeNameLCD->setText(QString::number(v1)+QString(" <--> ")+QString::number(v2)); rightPanelClickedEdgeWeightLabel->setText(tr("Weight:")); rightPanelClickedEdgeWeightLCD->setText(QString::number(weight)); rightPanelClickedEdgeReciprocalWeightLabel->setText("Recipr.:"); rightPanelClickedEdgeReciprocalWeightLCD->setText("-"); if (openMenu) { edgeName=QString("RECIPROCATED EDGE: ") + QString::number(v1)+QString(" <--> ")+QString::number(v2); } } else{ statusMessage( QString(tr("Directed edge %1 --> %2 of weight %3 has been selected. " "Click again to unselect it.")) .arg( v1 ).arg( v2 ) .arg( weight ) ); rightPanelClickedEdgeNameLCD->setText(QString::number(v1)+QString(" --> ")+QString::number(v2)); rightPanelClickedEdgeWeightLabel->setText(tr("Weight:")); rightPanelClickedEdgeWeightLCD->setText(QString::number(weight)); rightPanelClickedEdgeReciprocalWeightLabel->setText(""); rightPanelClickedEdgeReciprocalWeightLCD->setText(""); if (openMenu) { edgeName=QString("DIRECTED EDGE: ") + QString::number(v1)+QString(" --> ")+QString::number(v2); } } if (openMenu) { slotEditEdgeOpenContextMenu(edgeName); } } /** * @brief Popups a context menu with edge-related options * Called when the user right-clicks on an edge * @param str */ void MainWindow::slotEditEdgeOpenContextMenu(const QString &str) { qDebug()<< "MW: slotEditEdgeOpenContextMenu() for" << str << "at"<< QCursor::pos().x() << "," << QCursor::pos().y(); //make the menu QMenu *edgeContextMenu = new QMenu(str, this); edgeContextMenu -> addAction( str ); edgeContextMenu -> addSeparator(); edgeContextMenu -> addAction( editEdgeRemoveAct ); edgeContextMenu -> addAction( editEdgeWeightAct ); edgeContextMenu -> addAction( editEdgeLabelAct ); edgeContextMenu -> addAction( editEdgeColorAct ); edgeContextMenu -> exec(QCursor::pos() ); delete edgeContextMenu; } /** * @brief Adds a new edge between two nodes specified by the user. * Called when user clicks on the MW button/menu item "Add edge" */ void MainWindow::slotEditEdgeAdd(){ qDebug ()<<"MW::slotEditEdgeAdd()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int sourceNode=-1, targetNode=-1; float weight=1; //weight of this new edge should be one... bool ok=false; int min=activeGraph->vertexNumberMin(); int max=activeGraph->vertexNumberMax(); if (min==max) return; //if there is only one node -> no edge if ( ! activeGraph->vertexClicked() ) { sourceNode=QInputDialog::getInt( this, "Create new edge, Step 1", tr("This will draw a new edge between two nodes. \n" "Enter source node (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Add edge operation cancelled." ); return; } } else sourceNode=activeGraph->vertexClicked(); qDebug ()<<"MW::slotEditEdgeAdd() - sourceNode:" << sourceNode; if ( activeGraph->vertexExists(sourceNode) ==-1 ) { statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","No such node.", "OK",0); qDebug ()<<"MW::slotEditEdgeAdd() - cannot find sourceNode:" << sourceNode; return; } targetNode=QInputDialog::getInt (this, "Create new edge, Step 2", tr( "Source node:" ) + QString::number( sourceNode ) + tr(" \nNow enter a target node [") + QString::number(min).toLatin1() + "..." + QString::number(max).toLatin1()+"]:",min, min, max , 1, &ok) ; if (!ok) { statusMessage( "Add edge target operation cancelled." ); return; } if ( activeGraph->vertexExists(targetNode) ==-1 ) { statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","No such node.", "OK",0); qDebug ("MW: slotEditEdgeAdd: Cant find targetNode %i",targetNode); return; } weight=QInputDialog::getDouble( this, "Create new edge, Step 3", tr("Source and target nodes accepted. \n" "Please, enter the weight of new edge: "),1.0, -100.0, 100.0, 1, &ok); if (!ok) { statusMessage( "Add edge operation cancelled." ); return; } //Check if this edge already exists... if (activeGraph->edgeExists(sourceNode, targetNode)!=0 ) { qDebug("edge exists. Aborting"); statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","edge already exists.", "OK",0); return; } slotEditEdgeCreate(sourceNode, targetNode, weight); statusMessage( tr("Ready. ") ); } /** * @brief Helper to slotEditEdgeAdd() above * Also called from GW::userMiddleClicked() signal when user creates edges with middle-clicks * Calls Graph::edgeCreate method to add the new edge to the active Graph * @param source * @param target * @param weight */ void MainWindow::slotEditEdgeCreate (const int &source, const int &target, const float &weight) { qDebug()<< "MW::slotEditEdgeCreate() - edge" << source << "->" << target << "weight" << weight << "Setting user settings and calling Graph::edgeCreate(...)"; //int reciprocal=0; bool bezier = false; activeGraph->edgeCreate( source, target, weight, appSettings["initEdgeColor"] , ( editEdgeUndirectedAllAct->isChecked() ) ? 2:0, ( editEdgeUndirectedAllAct->isChecked() ) ? false : ( (appSettings["initEdgeArrows"] == "true") ? true: false) , bezier); if ( activeEdges() == 1 && editRelationChangeCombo->count() == 0 ) { slotEditRelationAdd(); } } /** * @brief Removes a clicked edge. Otherwise asks the user to specify one edge. * First deletes arc reference from object nodeVector then deletes arc item from scene */ void MainWindow::slotEditEdgeRemove(){ if ( !activeNodes() || activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } int min=0, max=0, sourceNode=-1, targetNode=-1; bool ok=false; bool removeOpposite = false; min=activeGraph->vertexNumberMin(); max=activeGraph->vertexNumberMax(); if (!activeGraph->edgeClicked().v1 || !activeGraph->edgeClicked().v2 ) { sourceNode=QInputDialog::getInt( this,tr("Remove edge"), tr("Source node: (")+QString::number(min)+ "..."+QString::number(max)+"):", min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Remove edge operation cancelled." ); return; } targetNode=QInputDialog::getInt( this, tr("Remove edge"), tr("Target node: (")+QString::number(min)+"..."+ QString::number(max)+"):",min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Remove edge operation cancelled." ); return; } if ( activeGraph->edgeExists(sourceNode, targetNode, false)!=0 ) { removeOpposite=false; if ( activeGraph->graphUndirected() ) { removeOpposite=true; } } else { QMessageBox::critical( this, "Remove edge",tr("There is no such edge."), "OK",0); statusMessage( tr("There are no nodes yet...") ); return; } } else { if (activeGraph->edgeClicked().type == EDGE_RECIPROCATED) { QStringList items; QString arcA = QString::number(activeGraph->edgeClicked().v1)+ " --> "+QString::number(activeGraph->edgeClicked().v2); QString arcB = QString::number(activeGraph->edgeClicked().v2)+ " --> "+QString::number(activeGraph->edgeClicked().v1); items << arcA << arcB << "Both"; ok = false; QString selectedArc = QInputDialog::getItem(this, tr("Select edge"), tr("This is a reciprocated edge. " "Select direction to remove:"), items, 0, false, &ok); if ( selectedArc == arcA ) { sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; } else if ( selectedArc == arcB ) { sourceNode = activeGraph->edgeClicked().v2; targetNode = activeGraph->edgeClicked().v1; } else { // both sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; removeOpposite=true; } } else { sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; } } activeGraph->edgeRemove(sourceNode, targetNode, removeOpposite); qDebug()<< "MW::slotEditEdgeRemove() -" << "View items now:"<< graphicsWidget->items().size() << "Scene items now:"<< graphicsWidget->scene()->items().size(); } /** * @brief Changes the label of an edge. */ void MainWindow::slotEditEdgeLabel(){ qDebug() << "MW::slotEditEdgeLabel()"; if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } int sourceNode=-1, targetNode=-1; bool ok=false; int min=activeGraph->vertexNumberMin(); int max=activeGraph->vertexNumberMax(); if (!activeGraph->edgeClicked().v1 || !activeGraph->edgeClicked().v2 ) { //no edge clicked. Ask user to define an edge. sourceNode=QInputDialog::getInt(this, "Change edge label", tr("Select edge source node: ("+ QString::number(min).toLatin1()+ "..."+QString::number(max).toLatin1()+ "):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge label operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, "Change edge label...", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..." + QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge label operation cancelled." ); return; } if ( ! activeGraph->edgeExists (sourceNode, targetNode ) ) { statusMessage( tr("There is no such edge. ") ); QMessageBox::critical(this, "Error", tr("No edge! \nNo such edge found in current network."), "OK",0); return; } } else { //edge has been clicked. sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; } QString label = QInputDialog::getText( this, tr("Change edge label"), tr("Enter label: ") ); if ( !label.isEmpty()) { qDebug() << "MW::slotEditEdgeLabel() - " << sourceNode << " -> " << targetNode << " new label " << label; activeGraph->edgeLabelSet( sourceNode, targetNode, label); slotOptionsEdgeLabelsVisibility(true); statusMessage( tr("Ready. ") ); } else { statusMessage( tr("Change edge label aborted. ") ); } } /** * @brief Changes the color of all edges weighted below threshold to parameter color * If color is not valid, it opens a QColorDialog * If threshold == RAND_MAX it changes the color of all edges. * Called from Edit -> Edges menu option and Settings Dialog. * @param color = QColor() * @param threshold = RAND_MAX */ void MainWindow::slotEditEdgeColorAll(QColor color, const int threshold){ if (!color.isValid()) { QString text; if (threshold < RAND_MAX) { text = "Change the color of edges weighted < " + QString::number(threshold) ; } else text = "Change the color of all edges" ; color = QColorDialog::getColor( appSettings["initEdgeColor"], this, text); } if (color.isValid()) { qDebug() << "MainWindow::slotEditEdgeColorAll() - new edge color: " << color.name() << " threshold " << threshold; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (threshold < 0 ) { appSettings["initEdgeColorNegative"]=color.name(); } else if (threshold == 0 ) { appSettings["initEdgeColorZero"]=color.name(); } else { appSettings["initEdgeColor"]=color.name(); } activeGraph->edgeColorAllSet(color.name(), threshold ); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("edges color change aborted. ") ); } } /** * @brief Changes the color of the clicked edge. * If no edge is clicked, then it asks the user to specify one. */ void MainWindow::slotEditEdgeColor(){ qDebug() << "MW::slotEditEdgeColor()"; if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } int sourceNode=-1, targetNode=-1; bool ok=false; int min=activeGraph->vertexNumberMin(); int max=activeGraph->vertexNumberMax(); if (!activeGraph->edgeClicked().v1 || !activeGraph->edgeClicked().v2) { //no edge clicked. Ask user to define an edge. sourceNode=QInputDialog::getInt(this, "Change edge color", tr("Select edge source node: ("+ QString::number(min).toLatin1()+ "..."+QString::number(max).toLatin1()+ "):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge color operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, "Change edge color...", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..." + QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge color operation cancelled." ); return; } if ( ! activeGraph->edgeExists(sourceNode, targetNode ) ) { statusMessage( tr("There is no such edge. ") ); QMessageBox::critical(this, "Error", tr("No edge! \nNo such edge found in current network."), "OK",0); return; } } else { //edge has been clicked. sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; } QString curColor = activeGraph->edgeColor(sourceNode, targetNode); if (!QColor(curColor).isValid()) { curColor=appSettings["initEdgeColor"]; } QColor color = QColorDialog::getColor( curColor, this, tr("Select new color....") ); if ( color.isValid()) { QString newColor=color.name(); qDebug() << "MW::slotEditEdgeColor() - " << sourceNode << " -> " << targetNode << " newColor " << newColor; activeGraph->edgeColorSet( sourceNode, targetNode, newColor); statusMessage( tr("Ready. ") ); } else { statusMessage( tr("Change edge color aborted. ") ); } } /** * @brief Changes the weight of the clicked edge. * If no edge is clicked, asks the user to specify an Edge. */ void MainWindow::slotEditEdgeWeight(){ if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug("MW::slotEditEdgeWeight()"); int sourceNode=-1, targetNode=-1; float newWeight=1.0; int min=activeGraph->vertexNumberMin(); int max=activeGraph->vertexNumberMax(); bool changeBothEdges=false; bool ok=false; if ( activeGraph->edgeClicked().v1==0 || activeGraph->edgeClicked().v2==0 ) { sourceNode=QInputDialog::getInt( this, "Edge weight", tr("Select edge source node: ("+ QString::number(min).toLatin1()+"..."+ QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge weight operation cancelled." ); return; } targetNode=QInputDialog::getInt( this, "Edge weight", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..."+ QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge weight operation cancelled." ); return; } qDebug("source %i target %i",sourceNode, targetNode); } else { qDebug() << "MW: slotEditEdgeWeight() - an Edge has already been clicked"; if (activeGraph->edgeClicked().type == EDGE_RECIPROCATED) { QStringList items; QString arcA = QString::number(activeGraph->edgeClicked().v1)+ " --> "+QString::number(activeGraph->edgeClicked().v2); QString arcB = QString::number(activeGraph->edgeClicked().v2)+ " --> "+QString::number(activeGraph->edgeClicked().v1); items << arcA << arcB << "Both"; ok = false; QString selectedArc = QInputDialog::getItem(this, tr("Select edge"), tr("This is a reciprocated edge. " "Select direction:"), items, 0, false, &ok); if ( selectedArc == arcA ) { sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; } else if ( selectedArc == arcB ) { sourceNode = activeGraph->edgeClicked().v2; targetNode = activeGraph->edgeClicked().v1; } else { // both sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; changeBothEdges=true; } } else { sourceNode = activeGraph->edgeClicked().v1; targetNode = activeGraph->edgeClicked().v2; } qDebug() << "MW: slotEditEdgeWeight() from " << sourceNode << " to " << targetNode; } float oldWeight= 0; QString dialogTitle="Edge " + QString::number(sourceNode) + "->" + QString::number(targetNode); bool undirected = activeGraph->graphUndirected(); if ( ( oldWeight= activeGraph->edgeWeight(sourceNode, targetNode)) != 0 ) { if (changeBothEdges || undirected ){ dialogTitle="Edge " + QString::number(sourceNode) + "<->" + QString::number(targetNode); } newWeight=(float) QInputDialog::getDouble( this, dialogTitle, tr("New edge weight: "), oldWeight, -100, 100 ,1, &ok ) ; if (ok) { activeGraph->edgeWeightSet(sourceNode, targetNode, newWeight, undirected|| changeBothEdges ); } else { statusMessage( QString(tr("Change edge weight cancelled.")) ); return; } } } /** * @brief Symmetrizes the ties between every two connected nodes. * If there is an arc from Node A to Node B, * then a new arc from Node B to Node is created of the same weight. * Thus, all arcs become reciprocal and the network becomes symmetric * with a symmetric adjacency matrix */ void MainWindow::slotEditEdgeSymmetrizeAll(){ if ( activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug("MW: slotEditEdgeSymmetrizeAll() calling graphSymmetrize()"); activeGraph->graphSymmetrize(); QMessageBox::information(this, "Symmetrize", tr("All arcs are reciprocal. \n" "The network is symmetric."), "OK",0); statusMessage(tr("All arcs are now reciprocal. Thus a symmetric network. Ready.")); } /** * @brief Adds a new symmetric relation with ties only between pairs of nodes * who are cocited by others. */ void MainWindow::slotEditEdgeSymmetrizeCocitation(){ if ( activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug("MW: slotEditEdgeSymmetrizeCocitation() calling graphCocitation()"); activeGraph->graphCocitation(); slotHelpMessageToUser(USER_MSG_INFO,tr("New symmetric cocitation relation created."), tr("New cocitation relation created from strong ties"), tr("A new relation \"%1\" has been added to the network. " "by counting cocitation ties only. " "This relation is symmetric. ").arg("Cocitation")); } /** * @brief MainWindow::slotEditEdgeSymmetrizeStrongTies */ void MainWindow::slotEditEdgeSymmetrizeStrongTies(){ if ( activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug()<< "MW::slotEditEdgeSymmetrizeStrongTies() - calling graphSymmetrizeStrongTies()"; int oldRelationsCounter=activeGraph->relations(); int answer=0; if (oldRelationsCounter>0) { switch ( answer=slotHelpMessageToUser(USER_MSG_QUESTION_CUSTOM, tr("Select"), tr("Symmetrize social network by examining strong ties"), tr("This network has multiple relations. " "Symmetrize by examining reciprocated ties across all relations or just the current relation?"), QMessageBox::NoButton, QMessageBox::NoButton, tr("all relations"), tr("current relation") ) ){ case 1: activeGraph->graphSymmetrizeStrongTies(true); break; case 2: activeGraph->graphSymmetrizeStrongTies(false); break; } } else { activeGraph->graphSymmetrizeStrongTies(false); } slotHelpMessageToUser(USER_MSG_INFO,tr("New symmetric relation created from strong ties"), tr("New relation created from strong ties"), tr("A new relation \"%1\" has been added to the network. " "by counting reciprocated ties only. " "This relation is binary and symmetric. ").arg("Strong Ties")); } /** * @brief Tranforms all directed arcs to undirected edges. * The result is a undirected and symmetric network */ void MainWindow::slotEditEdgeUndirectedAll(const bool &toggle){ qDebug()<<"MW: slotEditEdgeUndirectedAll() - calling Graph::graphUndirectedSet()"; if (toggle) { activeGraph->graphUndirectedSet(toggle); optionsEdgeArrowsAct->setChecked(false); if (activeEdges() !=0 ) { statusMessage(tr("Undirected data mode. " "All existing directed edges transformed to " "undirected. Ready") ) ; } else { statusMessage( tr("Undirected data mode. " "Any edge you add will be undirected. Ready")) ; } } else { activeGraph->graphUndirectedSet(toggle); optionsEdgeArrowsAct->trigger(); optionsEdgeArrowsAct->setChecked(true); if (activeEdges() !=0 ) { statusMessage ( tr("Directed data mode. " "All existing undirected edges transformed to " "directed. Ready")) ; } else { statusMessage ( tr("Directed data mode. " "Any new edge you add will be directed. Ready")) ; } } } /** * @brief Toggles between directed (mode=0) and undirected edges (mode=1) */ void MainWindow::slotEditEdgeMode(const int &mode){ if (mode==1) { qDebug()<<"MW: slotEditEdgeMode() - Calling Graph::graphUndirectedSet(true)"; activeGraph->graphUndirectedSet(true); qDebug()<<"MW: slotEditEdgeMode() - Disabling optionsEdgeArrowsAct checkbox"; optionsEdgeArrowsAct->setChecked(false); if (activeEdges() !=0 ) { statusMessage(tr("Undirected data mode. " "All existing directed edges transformed to " "undirected. Ready") ) ; } else { statusMessage( tr("Undirected data mode. " "Any edge you add will be undirected. Ready")) ; } } else { qDebug()<<"MW: slotEditEdgeMode() - calling Graph::graphUndirectedSet(false)"; activeGraph->graphUndirectedSet(false); qDebug()<<"MW: slotEditEdgeMode() - Triggering optionsEdgeArrowsAct checkbox"; optionsEdgeArrowsAct->trigger(); qDebug()<<"MW: slotEditEdgeMode() - disabling optionsEdgeArrowsAct checkbox"; optionsEdgeArrowsAct->setChecked(true); if (activeEdges() !=0 ) { statusMessage ( tr("Directed data mode. " "All existing undirected edges transformed to " "directed. Ready")) ; } else { statusMessage ( tr("Directed data mode. " "Any new edge you add will be directed. Ready")) ; } } } /** * Filters Nodes by their value TODO slotFilterNodes * */ void MainWindow::slotFilterNodes(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } } /** * @brief Calls Graph::vertexIsolatedAllToggle to toggle visibility of isolated vertices */ void MainWindow::slotEditFilterNodesIsolates(bool checked){ Q_UNUSED(checked); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } qDebug()<< "MW: slotEditFilterNodesIsolates"; activeGraph->vertexIsolatedAllToggle( ! editFilterNodesIsolatesAct->isChecked() ); statusMessage( tr("Isolate nodes visibility toggled!") ); } /** * @brief Shows a dialog from where the user may filter edges according to their weight * All edges weighted more (or less) than the specified weight will be disabled. */ void MainWindow::slotEditFilterEdgesByWeightDialog() { if ( !activeEdges() ) { statusMessage( QString(tr("Load a network file first. \nThen you may ask me to compute something!")) ); return; } m_DialogEdgeFilterByWeight.exec() ; } /** * @brief Calls Graph::edgeFilterUnilateral(bool). If bool==true, all unilateral * edges are filtered out. * @param checked * */ void MainWindow::slotEditFilterEdgesUnilateral(bool checked) { Q_UNUSED(checked); if ( !activeEdges() && editFilterEdgesUnilateralAct->isChecked() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } if (activeGraph->relations()>1) { } qDebug()<< "MW::slotEditFilterEdgesUnilateral"; activeGraph->edgeFilterUnilateral( ! editFilterEdgesUnilateralAct->isChecked() ); statusMessage( tr("Unilateral (weak) edges visibility toggled!") ); } /** * Transforms all nodes to edges TODO slotEditTransformNodes2Edges */ void MainWindow::slotEditTransformNodes2Edges(){ } /** TODO slotLayoutColorationStrongStructural */ void MainWindow::slotLayoutColorationStrongStructural() { } /** TODO slotLayoutColorationRegular */ void MainWindow::slotLayoutColorationRegular() { } /** * @brief Calls Graph::layoutRandom * to reposition all nodes on a random layout */ void MainWindow::slotLayoutRandom(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } graphicsWidget->clearGuides(); activeGraph->layoutRandom(); statusMessage( tr("Nodes in random positions.") ); } /** * @brief Calls Graph::layoutRadialRandom * to reposition all nodes on a radial layout randomly */ void MainWindow::slotLayoutRadialRandom(){ qDebug() << "MainWindow::slotLayoutRadialRandom()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } slotLayoutGuides(false); activeGraph->layoutRadialRandom(true); slotLayoutGuides(true); statusMessage( tr("Nodes in random concentric circles.") ); } /** * @brief Calls Graph::layoutForceDirectedSpringEmbedder to embed the Eades * spring-gravitational model to the network. * Called from menu or toolbox checkbox */ void MainWindow::slotLayoutSpringEmbedder(){ qDebug()<< "MW:slotLayoutSpringEmbedder"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } activeGraph->layoutForceDirectedSpringEmbedder(500); statusMessage( tr("Spring-Gravitational (Eades) model embedded.") ); } /** * @brief Calls Graph::layoutForceDirectedFruchtermanReingold to embed * the Fruchterman-Reingold model of repelling-attracting forces to the network. * Called from menu or toolbox */ void MainWindow::slotLayoutFruchterman(){ qDebug("MW: slotLayoutFruchterman ()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } activeGraph->layoutForceDirectedFruchtermanReingold(100); statusMessage( tr("Fruchterman & Reingold model embedded.") ); } /** * @brief Calls Graph::layoutForceDirectedKamadaKawai to embed * the Kamada-Kawai FDP model to the network. */ void MainWindow::slotLayoutKamadaKawai(){ qDebug()<< "MW::slotLayoutKamadaKawai ()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } activeGraph->layoutForceDirectedKamadaKawai(400); statusMessage( tr("Kamada & Kawai model embedded.") ); } /** * @brief Checks sender text() to find out who QMenu item was pressed * calls slotLayoutRadialByProminenceIndex(QString) */ void MainWindow::slotLayoutRadialByProminenceIndex(){ qDebug() << "MainWindow::slotLayoutRadialByProminenceIndex()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText=menuitem->text(); qDebug() << "MainWindow::slotLayoutRadialByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutRadialByProminenceIndex(menuItemText); } /** * @brief * Overloaded - called when user clicks Apply in the Layout toolbox * or from slotLayoutRadialByProminenceIndex() when the user click on menu * Places all nodes on concentric circles according to their index score. * More prominent nodes are closer to the centre of the screen. */ void MainWindow::slotLayoutRadialByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutRadialByProminenceIndex() "; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; slotLayoutGuides(true); if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName.contains("Closeness Centr") && !prominenceIndexName.contains("IR")) userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness") ) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutRadialByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(0); bool dropIsolates=false; //check if CC was selected and the graph is disconnected. if (userChoice == 2 ) { int connectedness=activeGraph->graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; case -3: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations" "otherwise Closeness Centrality " "index can not be defined, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can conside using the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); graphicsWidget->clearGuides(); activeGraph->layoutByProminenceIndex( userChoice, 0, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); statusMessage( tr("Nodes in inner circles have higher %1 score. ").arg(prominenceIndexName ) ); } /** * @brief * Checks sender text() to find out who QMenu item was pressed * and what prominence index was chosen * calls slotLayoutLevelByProminenceIndex(QString) */ void MainWindow::slotLayoutLevelByProminenceIndex(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText = menuitem->text(); qDebug() << "MainWindow::slotLayoutLevelByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutLevelByProminenceIndex(menuItemText); } /** * @brief * Overloaded - called when user clicks on toolbox options and when * the user selects a menu option (called by slotLayoutLevelByProminenceIndex()) * Repositions all nodes on different top-down levels according to the * chosen prominence index. * More prominent nodes are closer to the top of the canvas */ void MainWindow::slotLayoutLevelByProminenceIndex(QString choice=""){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; slotLayoutGuides(true); if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName.contains("Closeness Centr") && !prominenceIndexName.contains("IR")) userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutLevelByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(1); bool dropIsolates=false; //check if CC was selected and the graph is disconnected. if (userChoice == 2 ) { int connectedness=activeGraph->graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; case -3: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); graphicsWidget->clearGuides(); activeGraph->layoutByProminenceIndex( userChoice, 1, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); statusMessage( tr("Nodes in upper levels have higher %1 score. ").arg(prominenceIndexName ) ); } /** * @brief * Checks sender text() to find out who QMenu item was pressed * and what prominence index was chosen * calls slotLayoutNodeSizeByProminenceIndex(QString) */ void MainWindow::slotLayoutNodeSizeByProminenceIndex(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText = menuitem->text(); qDebug() << "MainWindow::slotLayoutNodeSizeByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutNodeSizeByProminenceIndex(menuItemText); } /** * @brief * Calls Graph::layoutByProminenceIndex(2) to apply a layout model * where the size of each node follows its prominence score * Called when selectbox changes in the toolbox */ void MainWindow::slotLayoutNodeSizeByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutNodeSizeByProminenceIndex() "; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName.contains("Closeness Centr") && !prominenceIndexName.contains("IR")) userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutNodeSizeByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(2); //check if CC was selected and the graph is disconnected. bool dropIsolates=false; if (userChoice == 2 ) { int connectedness=activeGraph->graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; case -3: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); graphicsWidget->clearGuides(); activeGraph->layoutByProminenceIndex( userChoice, 2, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); statusMessage( tr("Bigger nodes have greater %1 score.").arg(prominenceIndexName ) ); } /** * @brief * Checks sender text() to find out who QMenu item was pressed * and what prominence index was chosen * calls slotLayoutNodeColorByProminenceIndex(QString) */ void MainWindow::slotLayoutNodeColorByProminenceIndex(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText = menuitem->text(); qDebug() << "MainWindow::slotLayoutNodeColorByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutNodeColorByProminenceIndex(menuItemText); } /** * @brief Calls Graph::layoutVertexColorByProminenceIndex to apply a layout model * where the color of each node follows its prominence score * RED=rgb(255,0,0) most prominent * BLUE=rgb(0,0,255) least prominent * Called when selectbox changes in the toolbox */ void MainWindow::slotLayoutNodeColorByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutNodeColorByProminenceIndex() "; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName.contains("Closeness Centr") && !prominenceIndexName.contains("IR")) userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutNodeColorByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(3); //check if CC was selected and the graph is disconnected. bool dropIsolates=false; if (userChoice == 2 ) { int connectedness=activeGraph->graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; case -3: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); graphicsWidget->clearGuides(); activeGraph->layoutByProminenceIndex( userChoice, 3, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); statusMessage( tr("Nodes with red color have greater %1 score.").arg(prominenceIndexName)); } /** * @brief * Enables/disables layout guides * Called from * @param state */ void MainWindow::slotLayoutGuides(const bool &toggle){ qDebug()<< "MW:slotLayoutGuides()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (toggle){ layoutGuidesAct->setChecked(true); qDebug()<< "MW:slotLayoutGuides() - will be displayed"; statusMessage( tr("Layout Guides will be displayed") ); } else { layoutGuidesAct->setChecked(false); qDebug()<< "MW:slotLayoutGuides() - will NOT be displayed"; graphicsWidget->clearGuides(); statusMessage( tr("Layout Guides will not be displayed") ); } } /** * Returns the amount of enabled/active edges on the scene. */ int MainWindow::activeEdges(){ qDebug () << "MW::activeEdges()"; return activeGraph->edgesEnabled(); } /** * Returns the number of active nodes on the scene. */ int MainWindow::activeNodes(){ return activeGraph->vertices(); } /** * Displays the arc and dyad reciprocity of the network */ void MainWindow::slotAnalyzeReciprocity(){ 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-reciprocity-"+dateTime+".html"; askAboutWeights(); activeGraph->writeReciprocity(fn, optionsEdgeWeightConsiderAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Reciprocity report saved as: ") + QDir::toNativeSeparators(fn) ); } /** * Displays a box informing the user about the symmetry or not of the adjacency matrix */ void MainWindow::slotAnalyzeSymmetryCheck(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeGraph->graphSymmetric()) QMessageBox::information(this, "Symmetry", tr("The adjacency matrix is symmetric." ),"OK",0); else QMessageBox::information(this, "Symmetry", tr("The adjacency matrix is not symmetric." ),"OK",0); statusMessage (QString(tr("Ready")) ); } /** * @brief Writes the adjacency matrix inverse */ void MainWindow::slotAnalyzeMatrixAdjacencyInverse(){ 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-inverse-"+dateTime+".html"; statusMessage(tr ("Inverting adjacency matrix.") ); //activeGraph->writeMatrixAdjacencyInvert(fn, QString("lu")) ; activeGraph->writeMatrix(fn,MATRIX_ADJACENCY_INVERSE) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Inverse matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the transpose adjacency matrix */ void MainWindow::slotAnalyzeMatrixAdjacencyTranspose(){ 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-transpose-"+dateTime+".html"; statusMessage( tr ("Transposing adjacency matrix.") ); activeGraph->writeMatrix(fn,MATRIX_ADJACENCY_TRANSPOSE) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Transpose adjacency matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the cocitation matrix */ void MainWindow::slotAnalyzeMatrixAdjacencyCocitation(){ 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-cocitation-"+dateTime+".html"; statusMessage( tr ("Computing Cocitation matrix.") ); activeGraph->writeMatrix(fn,MATRIX_COCITATION) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Cocitation matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the degree matrix of the graph */ void MainWindow::slotAnalyzeMatrixDegree(){ 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-degree-"+dateTime+".html"; statusMessage(tr ("Computing Degree matrix.") ); //activeGraph->writeMatrixDegreeText(fn) ; activeGraph->writeMatrix(fn, MATRIX_DEGREE) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Degree matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the Laplacian matrix of the graph */ void MainWindow::slotAnalyzeMatrixLaplacian(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } qDebug() << "MW:slotAnalyzeMatrixLaplacian() - calling Graph::writeMatrix"; QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-laplacian-"+dateTime+".html"; statusMessage(tr ("Computing Laplacian matrix") ); activeGraph->writeMatrix(fn, MATRIX_LAPLACIAN) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Laplacian matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief If the network has weighted / valued edges, it asks the user * if the app should consider weights or not. */ void MainWindow::askAboutWeights(const bool userTriggered){ qDebug() << "MW::askAboutWeights() - checking if graph weighted."; if (userTriggered) { if (!activeGraph->graphWeighted() ){ slotHelpMessageToUser(USER_MSG_INFO, tr("Non-Weighted Network"), tr("You do not work on a weighted network at the moment. \n" "Therefore, I will not consider edge weights during " "computations. \n" "This option applies only when you load or create " "a weighted network ")); optionsEdgeWeightConsiderAct->setChecked(false); return; } } else { if (!activeGraph->graphWeighted() ){ optionsEdgeWeightConsiderAct->setChecked(false); return; } } qDebug() << "MW::askAboutWeights() - graph weighted - checking if we have asked user."; if (askedAboutWeights) { return; } qDebug() << "MW::askAboutWeights() - graph weighted - let's ask the user."; switch( slotHelpMessageToUser(USER_MSG_QUESTION, tr("Weighted Network"), tr("This is a weighted network. Consider edge weights?"), tr("The ties in this network have weights (non-unit values) assigned to them. " "Do you want me to take these edge weights into account (i.e. when computing distances) ?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes) ) { case QMessageBox::Yes: optionsEdgeWeightConsiderAct->setChecked(true); break; case QMessageBox::No: optionsEdgeWeightConsiderAct->setChecked(false); break; default: // just for sanity optionsEdgeWeightConsiderAct->setChecked(false); return; break; } if (optionsEdgeWeightConsiderAct->isChecked()){ switch( slotHelpMessageToUser( USER_MSG_QUESTION, tr("Inverse edge weights during calculations? "), tr("Inverse edge weights during calculations? "), tr("If the edge weights denote cost or real distances (i.e. miles between cities), " "press No, since the distance between two nodes should be the quickest " "or cheaper one. \n\n" "If the weights denote value or strength (i.e. votes or interaction), " "press Yes to inverse the weights, since the distance between two " "nodes should be the most valuable one."), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes) ) { case QMessageBox::Yes: inverseWeights=true; break; case QMessageBox::No: inverseWeights=false; break; default: // just for sanity inverseWeights=true; return; break; } } askedAboutWeights=true; return; } /** * @brief Displays the graph distance (geodesic distance) between two user-specified nodes This is the length of the shortest path between them. */ void MainWindow::slotAnalyzeDistance(){ if ( !activeNodes() || !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } bool ok=false; long int min=1, max=1, i=-1, j=-1; min=activeGraph->vertexNumberMin(); max=activeGraph->vertexNumberMax(); i=QInputDialog::getInt(this, tr("Distance between two nodes"), tr("Select source node: (" +QString::number(min).toLatin1() +"..."+QString::number(max).toLatin1() +"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Distance calculation operation cancelled." ); return; } j=QInputDialog::getInt(this, tr("Distance between two nodes"), tr("Select target node: (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1() +"):"),min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( tr("Distance calculation operation cancelled.") ); return; } qDebug() << "source " << i << " target" << j; if (activeGraph->graphSymmetric() && i>j) { qSwap(i,j); } askAboutWeights(); int distanceGeodesic = activeGraph->graphDistanceGeodesic(i,j, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights); if ( distanceGeodesic > 0 && distanceGeodesic < RAND_MAX) QMessageBox::information(this, tr("Geodesic Distance"), tr("The length of the shortest path between actors (") +QString::number(i)+", "+QString::number(j) +") = "+QString::number(distanceGeodesic) +tr("\nThe nodes are connected."),"OK",0); else QMessageBox::information(this, tr("Geodesic Distance"), tr("Network distance (") +QString::number(i)+", "+QString::number(j) +") = "+ QString("\xE2\x88\x9E") +tr("\nThe nodes are not connected."),"OK",0); } /** * @brief Invokes calculation of the matrix of geodesic distances for the loaded network, then displays it. */ void MainWindow::slotAnalyzeMatrixDistances(){ qDebug() << "MW::slotAnalyzeMatrixDistances()"; 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-geodesic-distances-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing geodesic distances. Please wait...") ); activeGraph->writeMatrix(fn,MATRIX_DISTANCES, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Geodesic Distances matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Invokes calculation of the geodedics matrix (the number of shortest paths * between each pair of nodes in the loaded network), then displays it. */ void MainWindow::slotAnalyzeMatrixGeodesics(){ qDebug("MW: slotViewNumberOfGeodesics()"); 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-geodesics-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing geodesics (number of shortest paths). Please wait...") ); activeGraph->writeMatrix(fn,MATRIX_GEODESICS, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Geodesics Matrix saved as: ") + QDir::toNativeSeparators(fn)); } /** Displays the network diameter (largest geodesic) */ void MainWindow::slotAnalyzeDiameter() { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } askAboutWeights(); statusMessage( QString(tr("Computing Graph Diameter. Please wait...")) ); int netDiameter=activeGraph->graphDiameter( optionsEdgeWeightConsiderAct->isChecked(), inverseWeights); if ( activeGraph->graphWeighted() ) { if (optionsEdgeWeightConsiderAct->isChecked()) { QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nSince this is a weighted network \n" "the diameter can be more than N"), "OK",0); } else { QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nThis is the diameter of the \n" "corresponding network without weights"), "OK",0); } } else QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nSince this is a non-weighted network, \n" "the diameter is always less than N-1."), "OK",0); statusMessage( tr("Graph Diameter computed. Ready.") ); } /** Displays the average shortest path length (average graph distance) */ void MainWindow::slotAnalyzeDistanceAverage() { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } askAboutWeights(); statusMessage( tr("Computing Average Graph Distance. Please wait...") ); float averGraphDistance=activeGraph->graphDistanceGeodesicAverage( optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked() ); QMessageBox::information(this, "Average Graph Distance", "The average shortest path length is = " + QString::number(averGraphDistance), "OK",0); statusMessage( tr("Average geodesic distance computed. Ready.") ); } /** * Writes Eccentricity indices into a file, then displays it. */ void MainWindow::slotAnalyzeEccentricity(){ 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-eccentricity-"+dateTime+".html"; askAboutWeights(); activeGraph->writeEccentricity( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Eccentricities saved as: ") + QDir::toNativeSeparators(fn) ); } /** * @brief Reports the network connectedness */ void MainWindow::slotAnalyzeConnectedness(){ qDebug () << "MW::slotAnalyzeConnectedness()" ; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int connectedness=activeGraph->graphConnectedness(true); qDebug () << "MW::slotAnalyzeConnectedness result " << connectedness; switch ( connectedness ) { case 1: QMessageBox::information(this, "Connectedness", "This undirected graph " "is connected.", "OK",0); break; case 0: QMessageBox::information(this, "Connectedness", tr("This undirected graph " " is not connected."), "OK",0); break; case 2: QMessageBox::information(this, "Connectedness", tr("This directed graph " "is strongly connected."), "OK",0); break; case -1: QMessageBox::information(this, "Connectedness", tr("This undirected graph " "is disconnected because isolate nodes exist. \n" "It can become connected by dropping isolates."), "OK",0); break; case -2: QMessageBox::information(this, "Connectedness", tr("This directed graph " "is unilaterally connected. \n" "For every pair of " "nodes (u,v) there is a path either from u to v or " "from v to u, but not always both."), "OK",0); break; case -3: QMessageBox::information(this, "Connectedness", "This directed graph " "is disconnected because isolate nodes exist. \n" "It can become strongly connected by dropping isolates.", "OK",0); break; case -4: QMessageBox::information(this, "Connectedness", "This directed graph " "is disconnected. \nThere are pairs of nodes that " "are disconnected.", "OK",0); break; default: QMessageBox::critical(this, "Connectedness", "Something went wrong!.", "OK",0); break; }; statusMessage( tr("Connectedness calculated. Ready.") ); } /** * Calls Graph:: writeWalksOfLengthMatrixPlainText() to calculate and print * the number of walks of a given length , between each pair of nodes. */ void MainWindow::slotAnalyzeWalksLength(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } bool ok=false; int length = QInputDialog::getInt( this, "Number of walks", tr("Select desired length of walk: (2 to %1)").arg(activeNodes()-1), 2, 2, activeNodes()-1, 1, &ok ); if (!ok) { statusMessage( "Cancelled." ); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-walks-length-"+QString::number(length)+"-"+dateTime+".html"; activeGraph->writeMatrixWalks(fn, length); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Walks of length %1 matrix saved as: ").arg(length) + QDir::toNativeSeparators(fn) ); } /** * @brief Calls Graph:: writeWalksTotalMatrixPlainText() to calculate and print * the total number of walks of any length , between each pair of nodes. */ void MainWindow::slotAnalyzeWalksTotal(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeNodes() > 50) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is VERY SLOW on large networks (n>50), " "since it will calculate all powers of the sociomatrix up to n-1 " "in order to find out all possible walks. \n\n" "If you need to make a simple reachability test, " "we advise to use the Reachability Matrix function instead. \n\n" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-walks-total-"+dateTime+".html"; statusMessage( tr("Computing total walks matrix. Please wait...") ); activeGraph->writeMatrixWalks(fn); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Total walks matrix saved as: " + QDir::toNativeSeparators(fn)); } /** * Calls Graph:: writeReachabilityMatrixPlainText() to calculate and print * the Reachability Matrix of the network. */ void MainWindow::slotAnalyzeReachabilityMatrix(){ 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-reachability-"+dateTime+".html"; statusMessage( tr("Computing reachability matrix. Please wait...") ); activeGraph->writeMatrix(fn, MATRIX_REACHABILITY ); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Reachability matrix saved as: ") + QDir::toNativeSeparators(fn) ); } /** * @brief Calls Graph::writeClusteringCoefficient() to write Clustering Coefficients * into a file, and displays it. */ void MainWindow::slotAnalyzeClusteringCoefficient (){ 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-clustering-coefficient-"+dateTime+".html"; bool considerWeights=true; activeGraph->writeClusteringCoefficient(fn, considerWeights); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Clustering Coefficients saved as: ") + QDir::toNativeSeparators(fn)); } /** * @brief Calls Graph:: writeCliqueCensus() to write the number of cliques (triangles) * of each vertex into a file, then displays it. */ void MainWindow::slotAnalyzeCommunitiesCliqueCensus(){ 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-clique-census-"+dateTime+".html"; bool considerWeights=true; if (! activeGraph->writeCliqueCensus(fn, considerWeights) ) { return; } if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Clique Census saved as: ") + QDir::toNativeSeparators(fn)); } /** * Calls Graph to conduct and write a triad census into a file, then displays it. */ void MainWindow::slotAnalyzeCommunitiesTriadCensus() { 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-triad-census-"+dateTime+".html"; bool considerWeights=true; activeGraph->writeTriadCensus(fn, considerWeights); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Triad Census saved as: ") + QDir::toNativeSeparators(fn)); } /** * @brief Displays the DialogSimilarityMatches dialog. */ void MainWindow::slotAnalyzeStrEquivalenceSimilarityMeasureDialog() { qDebug()<< "MW::slotAnalyzeStrEquivalenceSimilarityMeasureDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } m_dialogSimilarityMatches = new DialogSimilarityMatches(this); connect( m_dialogSimilarityMatches, &DialogSimilarityMatches::userChoices, this, &MainWindow::slotAnalyzeStrEquivalenceSimilarityByMeasure ); m_dialogSimilarityMatches->exec(); } /** * @brief Calls Graph::writeMatrixSimilarityMatching() to write a * similarity matrix according to given measure into a file, and displays it. * */ void MainWindow::slotAnalyzeStrEquivalenceSimilarityByMeasure(const QString &matrix, const QString &varLocation, const QString &measure, const bool &diagonal) { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString metric; if (measure.contains("Simple",Qt::CaseInsensitive)) metric = "simple-matching" ; else if (measure.contains("Jaccard",Qt::CaseInsensitive)) metric ="jaccard" ; else if (measure.contains("None",Qt::CaseInsensitive)) metric = "none"; else if (measure.contains("Hamming",Qt::CaseInsensitive)) metric ="hamming"; else if (measure.contains("Cosine",Qt::CaseInsensitive)) metric ="cosine"; else if (measure.contains("Euclidean",Qt::CaseInsensitive)) metric ="euclidean"; else if (measure.contains("Manhattan",Qt::CaseInsensitive)) metric ="manhattan"; else if (measure.contains("Pearson ",Qt::CaseInsensitive)) metric = "pearson"; else if (measure.contains("Chebyshev",Qt::CaseInsensitive)) metric = "chebyshev"; QString fn = appSettings["dataDir"] + "socnetv-report-equivalence-similarity-"+metric+"-"+dateTime+".html"; bool considerWeights=true; activeGraph->writeMatrixSimilarityMatching( fn, measure, matrix, varLocation, diagonal, considerWeights); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Similarity matrix saved as: ") + QDir::toNativeSeparators(fn)); } /** * @brief Displays the DialogDissimilarities dialog. */ void MainWindow::slotAnalyzeStrEquivalenceDissimilaritiesDialog() { qDebug()<< "MW::slotAnalyzeStrEquivalenceDissimilaritiesDialog()"; m_dialogdissimilarities = new DialogDissimilarities(this); connect( m_dialogdissimilarities, &DialogDissimilarities::userChoices, this, &MainWindow::slotAnalyzeStrEquivalenceDissimilaritiesTieProfile ); m_dialogdissimilarities->exec(); } /** * @brief Invokes calculation of pair-wise tie profile dissimilarities of the * network, then displays it. * @param metric * @param varLocation * @param diagonal */ void MainWindow::slotAnalyzeStrEquivalenceDissimilaritiesTieProfile(const QString &metric, const QString &varLocation, const bool &diagonal){ qDebug() << "MW::slotAnalyzeStrEquivalenceDissimilaritiesTieProfile()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString metricStr; if (metric.contains("Simple",Qt::CaseInsensitive)) metricStr = "simple-matching" ; else if (metric.contains("Jaccard",Qt::CaseInsensitive)) metricStr ="jaccard" ; else if (metric.contains("None",Qt::CaseInsensitive)) metricStr = "none"; else if (metric.contains("Hamming",Qt::CaseInsensitive)) metricStr ="hamming"; else if (metric.contains("Cosine",Qt::CaseInsensitive)) metricStr ="cosine"; else if (metric.contains("Euclidean",Qt::CaseInsensitive)) metricStr ="euclidean"; else if (metric.contains("Manhattan",Qt::CaseInsensitive)) metricStr ="manhattan"; else if (metric.contains("Pearson ",Qt::CaseInsensitive)) metricStr = "pearson"; else if (metric.contains("Chebyshev",Qt::CaseInsensitive)) metricStr = "chebyshev"; QString fn = appSettings["dataDir"] + "socnetv-report-equivalence-dissimilarities-"+metricStr+"-"+dateTime+".html"; askAboutWeights(); activeGraph->writeMatrixDissimilarities(fn, metric, varLocation,diagonal, optionsEdgeWeightConsiderAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Tie profile dissimilarities matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Calls the m_dialogSimilarityPearson to display the Pearson statistics dialog */ void MainWindow::slotAnalyzeStrEquivalencePearsonDialog(){ qDebug()<< "MW::slotAnalyzeStrEquivalencePearsonDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } m_dialogSimilarityPearson = new DialogSimilarityPearson(this); connect( m_dialogSimilarityPearson, &DialogSimilarityPearson::userChoices, this, &MainWindow::slotAnalyzeStrEquivalencePearson ); m_dialogSimilarityPearson->exec(); } /** * @brief Calls Graph::writeMatrixSimilarityPearson() to write Pearson * Correlation Coefficients into a file, and displays it. * */ void MainWindow::slotAnalyzeStrEquivalencePearson(const QString &matrix, const QString &varLocation, const bool &diagonal) { 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-equivalence-pearson-coefficients-"+dateTime+".html"; bool considerWeights=true; activeGraph->writeMatrixSimilarityPearson( fn, considerWeights, matrix, varLocation, diagonal); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Pearson correlation coefficients matrix saved as: ") + QDir::toNativeSeparators(fn)); } /** * @brief Displays the slotAnalyzeStrEquivalenceClusteringHierarchicalDialog dialog. */ void MainWindow::slotAnalyzeStrEquivalenceClusteringHierarchicalDialog() { qDebug()<< "MW::slotAnalyzeStrEquivalenceClusteringHierarchicalDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString preselectMatrix = "Adjacency"; if (!activeGraph->graphWeighted()) { preselectMatrix = "Distances"; } m_dialogClusteringHierarchical = new DialogClusteringHierarchical(this, preselectMatrix); connect( m_dialogClusteringHierarchical, &DialogClusteringHierarchical::userChoices, this, &MainWindow::slotAnalyzeStrEquivalenceClusteringHierarchical ); m_dialogClusteringHierarchical->exec(); } /** * @brief Called from DialogClusteringHierarchical with user choices. Calls * Graph::writeClusteringHierarchical() to compute and write HCA and displays the report. * @param matrix * @param similarityMeasure * @param linkageCriterion * @param diagonal */ void MainWindow::slotAnalyzeStrEquivalenceClusteringHierarchical(const QString &matrix, const QString &varLocation, const QString &metric, const QString &method, const bool &diagonal, const bool &diagram){ qDebug()<< "MW::slotAnalyzeStrEquivalenceClusteringHierarchical()"; QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-equivalence-hierarchical-clustering-"+dateTime+".html"; bool considerWeights=true; bool inverseWeights=false; bool dropIsolates=true; if (! activeGraph->writeClusteringHierarchical(fn, varLocation, matrix, metric, method, diagonal, diagram, considerWeights, inverseWeights, dropIsolates) ){ return; } if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Hierarchical Cluster Analysis saved as: ") + QDir::toNativeSeparators(fn)); } /** * Writes Out-Degree Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityDegree(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } askAboutWeights(false); QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-out-degree-"+dateTime+".html"; activeGraph->writeCentralityDegree( fn, optionsEdgeWeightConsiderAct->isChecked(), editFilterNodesIsolatesAct->isChecked() ); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Out-Degree Centralities saved as: ") + QDir::toNativeSeparators(fn)); } /** * Writes Closeness Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityCloseness(){ qDebug() << "MW::slotAnalyzeCentralityCloseness()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Please wait while computing Connectivity...") ); int connectedness=activeGraph->graphConnectedness(); QApplication::restoreOverrideCursor(); bool dropIsolates=false; switch ( connectedness ) { case 1: break; case 2: break; case -1: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; case -3: if (! editFilterNodesIsolatesAct->isChecked() ) { QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; } break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is undefined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; askAboutWeights(); QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-closeness-"+dateTime+".html"; activeGraph->writeCentralityCloseness( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Closeness Centralities saved as: ") + QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityClosenessIR * Writes Centrality Closeness (based on Influence Range) indices into a file, * then displays it. */ void MainWindow::slotAnalyzeCentralityClosenessIR(){ 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-centrality-closeness-influence-range-"+dateTime+".html"; askAboutWeights(); activeGraph->writeCentralityClosenessInfluenceRange( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Influence Range Closeness Centralities saved as: ")+QDir::toNativeSeparators(fn)); } /** * Writes Betweenness Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityBetweenness(){ 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-centrality-betweenness-"+dateTime+".html"; askAboutWeights(); activeGraph->writeCentralityBetweenness( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Betweenness Centralities saved as: ")+QDir::toNativeSeparators(fn)); } /** * Writes Degree Prestige indices (In-Degree Centralities) into a file, then displays it. */ void MainWindow::slotAnalyzePrestigeDegree(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeGraph->graphSymmetric()) { QMessageBox::warning( this, "Warning", tr("Undirected graph!\n" "Degree Prestige counts inbound edges, therefore is more " "meaningful on directed graphs.\n" "For undirected graphs, the DP scores are the same as " "Degree Centrality..."), "OK",0); } askAboutWeights(false); QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-prestige-degree-"+dateTime+".html"; activeGraph->writePrestigeDegree(fn, optionsEdgeWeightConsiderAct->isChecked(), editFilterNodesIsolatesAct->isChecked() ); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Degree Prestige (in-degree) indices saved as: ") + QDir::toNativeSeparators(fn)); } /** * Writes PageRank Prestige indices into a file, then displays it. */ void MainWindow::slotAnalyzePrestigePageRank(){ 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-prestige-pagerank-"+dateTime+".html"; askAboutWeights(); activeGraph->writePrestigePageRank(fn, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("PageRank Prestige indices saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzePrestigeProximity * Writes Proximity Prestige indices into a file, then displays them. */ void MainWindow::slotAnalyzePrestigeProximity(){ 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-prestige-proximity-"+dateTime+".html"; askAboutWeights(); activeGraph->writePrestigeProximity(fn, true, false , editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Proximity Prestige indices saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityInformation * Writes Informational Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityInformation(){ qDebug() << "MW::slotAnalyzeCentralityInformation()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-information-"+dateTime+".html"; askAboutWeights(); activeGraph->writeCentralityInformation( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Information Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief Writes Eigenvector Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityEigenvector(){ 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-centrality-eigenvector-"+dateTime+".html"; askAboutWeights(); bool dropIsolates = false; activeGraph->writeCentralityEigenvector( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, dropIsolates); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Eigenvector Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityStress * Writes Stress Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityStress(){ 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-centrality-stress-"+dateTime+".html"; askAboutWeights(); activeGraph->writeCentralityStress( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Stress Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityPower * Writes Gil-Schmidt Power Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityPower(){ 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-centrality-power-Gil-Schmidt-"+dateTime+".html"; askAboutWeights(); activeGraph->writeCentralityPower( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Gil-Schmidt Power Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityEccentricity * Writes Eccentricity Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityEccentricity(){ 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-centrality-eccentricity-"+dateTime+".html"; askAboutWeights(); activeGraph->writeCentralityEccentricity( fn, optionsEdgeWeightConsiderAct->isChecked(), inverseWeights, editFilterNodesIsolatesAct->isChecked()); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Eccentricity Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief Creates a Qt Progress Dialog * if max = 0, then max becomes equal to active vertices* * @param max * @param msg */ void MainWindow::slotProgressBoxCreate(const int &max, const QString &msg){ qDebug() << "MW::slotProgressBoxCreate" ; if ( appSettings["showProgressBar"] == "true" ){ int duration = (max==0) ? activeNodes(): max; QProgressDialog *progressBox = new QProgressDialog(msg, "Cancel", 0, duration, this); progressBox->setWindowModality(Qt::WindowModal); connect ( activeGraph, &Graph::signalProgressBoxUpdate, progressBox, &QProgressDialog::setValue ); progressBox->setMinimumDuration(0); progressBox->setAutoClose(true); progressBox->setAutoReset(true); progressDialogs.push(progressBox); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); } /** * @brief Destroys the first in queue Progress dialog */ void MainWindow::slotProgressBoxDestroy(const int &max){ qDebug () << "MainWindow::slotProgressBoxDestroy"; QApplication::restoreOverrideCursor(); if ( appSettings["showProgressBar"] == "true" && max > -1 ) { if (! progressDialogs.isEmpty()) { QProgressDialog *progressBox = progressDialogs.pop(); progressBox->reset(); progressBox->deleteLater(); delete progressBox; } } } /** * @brief MainWindow::slotOptionsNodeNumbersVisibility * Turns on/off displaying the numbers of nodes (outside ones) * @param toggle */ void MainWindow::slotOptionsNodeNumbersVisibility(bool toggle) { qDebug() << "MW::slotOptionsNodeNumbersVisibility()" << toggle; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Nodes Numbers. Please wait...") ); appSettings["initNodeNumbersVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setNodeNumberVisibility(toggle); optionsNodeNumbersVisibilityAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Node Numbers are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Node Numbers are visible again...") ); } QApplication::restoreOverrideCursor(); return; } /** * @brief MainWindow::slotOptionsNodeNumbersInside * Turns on/off displaying the nodenumbers inside the nodes. * @param toggle */ void MainWindow::slotOptionsNodeNumbersInside(bool toggle){ qDebug() << "MW::slotOptionsNodeNumbersInside()" << toggle; statusMessage( tr("Toggle Numbers inside nodes. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // if node numbers are hidden, show them first. if ( toggle && appSettings["initNodeNumbersVisibility"] != "true" ) slotOptionsNodeNumbersVisibility(true); appSettings["initNodeNumbersInside"] = (toggle) ? "true":"false"; graphicsWidget -> setNumbersInsideNodes(toggle); optionsNodeNumbersVisibilityAct->setChecked (toggle); if (toggle){ statusMessage( tr("Numbers inside nodes...") ); } else { statusMessage( tr("Numbers outside nodes...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsNodeLabelsVisibility * Turns on/off displaying labels * @param toggle */ void MainWindow::slotOptionsNodeLabelsVisibility(bool toggle){ qDebug() << "MW::slotOptionsNodeLabelsVisibility()" << toggle; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Nodes Labels. Please wait...") ); appSettings["initNodeLabelsVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setNodeLabelsVisibility(toggle); optionsNodeLabelsVisibilityAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Node Labels are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Node Labels are visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEdgesVisibility * @param toggle */ void MainWindow::slotOptionsEdgesVisibility(bool toggle){ if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Edges. Please wait...") ); appSettings["initEdgesVisibility"] = (toggle) ? "true": "false"; graphicsWidget->setAllItemsVisibility(TypeEdge, toggle); if (!toggle) { statusMessage( tr("Edges are invisible now. Click again the same menu to display them.") ); } else{ statusMessage( tr("Edges visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEdgeArrowsVisibility * Turns on/off the arrows of edges * @param toggle */ void MainWindow::slotOptionsEdgeArrowsVisibility(bool toggle){ qDebug()<<"MW::slotOptionsEdgeArrowsVisibility() - toggle" << toggle; statusMessage( tr("Toggle Edges Arrows. Please wait...") ); appSettings["initEdgeArrows"]= (toggle) ? "true":"false"; graphicsWidget->setEdgeArrowsVisibility(toggle); statusMessage( tr("Ready.")); } /** * @brief MainWindow::slotOptionsEdgeWeightsDuringComputation * @param toggle */ void MainWindow::slotOptionsEdgeWeightsDuringComputation(bool toggle) { askedAboutWeights=false; askAboutWeights(toggle); activeGraph->graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * FIXME edges Bezier */ void MainWindow::slotOptionsEdgesBezier(bool toggle){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Toggle edges bezier. Please wait...") ); // // graphicsWidget->setBezier(toggle); if (!toggle) { // QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // QList list = scene->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++) { // if ( (*item)->type() ==TypeEdge ){ // GraphicsEdge *edge = (GraphicsEdge*) (*item); // // edge->toggleBezier(false); // (*item)->hide();(*item)->show(); // } // // } // QApplication::restoreOverrideCursor(); // return; } else{ // QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // QList list = scene->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++){ // if ( (*item)->type() ==TypeEdge ){ // GraphicsEdge *edge = (GraphicsEdge*) (*item); // // edge->toggleBezier(true); // (*item)->hide();(*item)->show(); // } // } // QApplication::restoreOverrideCursor(); } } /** * @brief MainWindow::slotOptionsEdgeThicknessPerWeight * @param toggle */ void MainWindow::slotOptionsEdgeThicknessPerWeight(bool toogle) { if (toogle) { } else { } } /** * @brief Changes the distance of edge arrows from nodes * Called from Edit menu option and DialogSettings * if offset=0, asks the user to enter a new offset * if v1=0 and v2=0, it changes all edges * @param v1 * @param v2 * @param offset */ void MainWindow::slotOptionsEdgeOffsetFromNode(const int &offset, const int &v1, const int &v2) { bool ok=false; qDebug() << "MW::slotOptionsEdgeOffsetFromNode - new offset " << offset; int newOffset=offset; if (!newOffset) { newOffset = QInputDialog::getInt( this, "Change edge offset", tr("Change all edges offset from their nodes to: (1-16)"), appSettings["initNodeLabelDistance"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change edge offset aborted.") ); return; } } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (v1 && v2) { //change one edge offset only graphicsWidget->setEdgeOffsetFromNode(v1,v2,newOffset); } else { //change all appSettings["initEdgeOffsetFromNode"] = QString::number(newOffset); graphicsWidget->setEdgeOffsetFromNode(v1,v2,newOffset); } QApplication::restoreOverrideCursor(); statusMessage( tr("Changed edge offset from nodes. Ready.") ); } /** * @brief MainWindow::slotOptionsEdgeWeightNumbersVisibility * Turns on/off displaying edge weight numbers * @param toggle */ void MainWindow::slotOptionsEdgeWeightNumbersVisibility(bool toggle) { qDebug() << "MW::slotOptionsEdgeWeightNumbersVisibility - Toggling Edges Weights"; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Edges Weights. Please wait...") ); appSettings["initEdgeWeightNumbersVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setEdgeWeightNumbersVisibility(toggle); activeGraph->edgeWeightNumbersVisibilitySet(toggle); optionsEdgeWeightNumbersAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Edge weights are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Edge weights are visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEdgeLabelsVisibility * Turns on/off displaying edge labels * @param toggle */ void MainWindow::slotOptionsEdgeLabelsVisibility(bool toggle) { qDebug() << "MW::slotOptionsEdgeLabelsVisibility - Toggling Edges Weights"; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Edges Labels. Please wait...") ); appSettings["initEdgeLabelsVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setEdgeLabelsVisibility(toggle); activeGraph->edgeLabelsVisibilitySet(toggle); optionsEdgeLabelsAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Edge labels are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Edge labels are visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief Turns antialiasing on or off * @param toggle */ void MainWindow::slotOptionsCanvasAntialiasing(bool toggle) { statusMessage( tr("Toggle anti-aliasing. Please wait...") ); //Inform graphicsWidget about the change QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); graphicsWidget->setRenderHint(QPainter::Antialiasing, toggle); graphicsWidget->setRenderHint(QPainter::TextAntialiasing, toggle); if (!toggle) { appSettings["antialiasing"] = "false"; statusMessage( tr("Anti-aliasing off.") ); } else { appSettings["antialiasing"] = "true"; statusMessage( tr("Anti-aliasing on.") ); } QApplication::restoreOverrideCursor(); } /** * @brief Turns antialiasing auto-adjustment on or off * @param toggle */ void MainWindow::slotOptionsCanvasAntialiasingAutoAdjust(const bool &toggle) { qDebug()<< "MW::slotOptionsCanvasAntialiasingAutoAdjust() " << toggle; statusMessage( tr("Toggle anti-aliasing auto adjust. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (!toggle) { //When enabled, it minimizes the areas that require redrawing, which improves performance. graphicsWidget->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true); appSettings["canvasAntialiasingAutoAdjustment"] = "false"; statusMessage( tr("Antialiasing auto-adjustment off.") ); } else { graphicsWidget->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, false); appSettings["canvasAntialiasingAutoAdjustment"] = "true"; statusMessage( tr("Antialiasing auto-adjustment on.") ); } QApplication::restoreOverrideCursor(); } /** * @brief Turns smooth pixmap transformations on or off * @param toggle */ void MainWindow::slotOptionsCanvasSmoothPixmapTransform(const bool &toggle) { qDebug()<< "MW::slotOptionsCanvasSmoothPixmapTransform() " << toggle; statusMessage( tr("Toggle smooth pixmap transformations. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (!toggle) { graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, toggle); appSettings["canvasSmoothPixmapTransform"] = "false"; statusMessage( tr("Smooth pixmap transformations off.") ); } else { graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, toggle); appSettings["canvasSmoothPixmapTransform"] = "true"; statusMessage( tr("Smooth pixmap transformations on.") ); } QApplication::restoreOverrideCursor(); } /** * @brief Turns saving painter state on or off * @param toggle */ void MainWindow::slotOptionsCanvasSavePainterState(const bool &toggle) { qDebug()<< "MW::slotOptionsCanvasSavePainterState() " << toggle; statusMessage( tr("Toggle saving painter state. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (!toggle) { graphicsWidget->setOptimizationFlag(QGraphicsView::DontSavePainterState, true); appSettings["canvasPainterStateSave"] = "false"; statusMessage( tr("Saving painter state off.") ); } else { graphicsWidget->setOptimizationFlag(QGraphicsView::DontSavePainterState, false); appSettings["canvasPainterStateSave"] = "true"; statusMessage( tr("Saving painter state on.") ); } QApplication::restoreOverrideCursor(); } /** * @brief Turns caching of canvas background on or off * @param toggle */ void MainWindow::slotOptionsCanvasCacheBackground(const bool &toggle) { qDebug()<< "MW::slotOptionsCanvasCacheBackground() " << toggle; statusMessage( tr("Toggle canvas background caching state. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (!toggle) { graphicsWidget->setCacheMode(QGraphicsView::CacheNone); appSettings["canvasCacheBackground"] = "false"; statusMessage( tr("Canvas background caching off.") ); } else { graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); appSettings["canvasCacheBackground"] = "true"; statusMessage( tr("Canvas background caching on.") ); } QApplication::restoreOverrideCursor(); } /** * @brief Turns selected edge highlighting * @param toggle */ void MainWindow::slotOptionsCanvasEdgeHighlighting(const bool &toggle) { qDebug()<< "MW::slotOptionsCanvasEdgeHighlighting() " << toggle; statusMessage( tr("Toggle edge highlighting state. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if (!toggle) { graphicsWidget->setEdgeHighlighting(toggle); appSettings["canvasEdgeHighlighting"] = "false"; statusMessage( tr("Edge highlighting off.") ); } else { graphicsWidget->setEdgeHighlighting(toggle); appSettings["canvasEdgeHighlighting"] = "true"; statusMessage( tr("Edge highlighting on.") ); } QApplication::restoreOverrideCursor(); } /** * @brief Sets canvas update mode * @param toggle */ void MainWindow::slotOptionsCanvasUpdateMode(const QString &mode) { qDebug()<< "MW::slotOptionsCanvasUpdateMode() " << mode; statusMessage( tr("Setting canvas update mode. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if ( mode == "Full" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::FullViewportUpdate ); } else if ( mode == "Minimal" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::MinimalViewportUpdate ); } else if ( mode == "Smart" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::SmartViewportUpdate ); } else if ( mode == "Bounding Rectangle" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::BoundingRectViewportUpdate ); } else if ( mode == "None" ) { graphicsWidget->setViewportUpdateMode( QGraphicsView::NoViewportUpdate ); } else { // graphicsWidget->setViewportUpdateMode( QGraphicsView::MinimalViewportUpdate ); } appSettings["canvasUpdateMode"] = mode; statusMessage( tr("Canvas update mode: ") + mode ); QApplication::restoreOverrideCursor(); } /** * @brief Sets canvas index method. Called from Settings dialog. * @param toggle */ void MainWindow::slotOptionsCanvasIndexMethod(const QString &method) { qDebug()<< "MW::slotOptionsCanvasIndexMethod() " << method; statusMessage( tr("Setting canvas index method. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); if ( method == "BspTreeIndex" ) { // Qt default graphicsWidget->scene() -> setItemIndexMethod(QGraphicsScene::BspTreeIndex); } else if ( method == "NoIndex" ) { // for animated scenes graphicsWidget->scene() -> setItemIndexMethod(QGraphicsScene::NoIndex); } else { // default graphicsWidget->scene() -> setItemIndexMethod(QGraphicsScene::BspTreeIndex); } appSettings["canvasIndexMethod"] = method; statusMessage( tr("Canvas index method: ") + method ); QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEmbedLogoExporting * * @param toggle */ void MainWindow::slotOptionsEmbedLogoExporting(bool toggle){ if (!toggle) { statusMessage( tr("SocNetV logo print off.") ); appSettings["printLogo"] = "false"; } else { appSettings["printLogo"] = "true"; statusMessage( tr("SocNetV logo print on.") ); } } /** * @brief Turns progress dialogs on or off * @param toggle * */ void MainWindow::slotOptionsProgressDialogVisibility(bool toggle) { statusMessage( tr("Toggle progressbar...")); if (!toggle) { appSettings["showProgressBar"] = "false"; statusMessage( tr("Progress bars off.") ); } else { appSettings["showProgressBar"] = "true"; statusMessage( tr("Progress bars on.") ); } } /** * @brief MainWindow::slotOptionsDebugMessages * @param toggle * Turns debugging messages on or off */ void MainWindow::slotOptionsDebugMessages(bool toggle){ if (!toggle) { appSettings["printDebug"] = "false"; printDebug=false; statusMessage( tr("Debug messages off.") ); } else { appSettings["printDebug"] = "true"; printDebug=true; statusMessage( tr("Debug messages on.") ); } } /** * @brief MainWindow::slotOptionsBackgroundColor * Called from Options menu and Settings dialog * @param color QColor */ void MainWindow::slotOptionsBackgroundColor (QColor color){ if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initBackgroundColor"] ), this, "Change the background color" ); } if (color.isValid()) { appSettings["initBackgroundColor"] = color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); graphicsWidget ->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"])) ); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotOptionsBackgroundImageSelect * Toggles displaying a custom image in the background * If toggle = true, presents a dialog to select an image file * Called from app menu option * @param toggle */ void MainWindow::slotOptionsBackgroundImageSelect(bool toggle) { statusMessage( tr("Toggle BackgroundImage...")); QString m_fileName ; if (toggle == false) { statusMessage( tr("BackgroundImage off.") ); graphicsWidget->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"] ) ) ); } else { m_fileName = QFileDialog::getOpenFileName( this, tr("Select one image"), getLastPath(), tr("All (*);;PNG (*.png);;JPG (*.jpg)") ); if (m_fileName.isNull() ) appSettings["initBackgroundImage"] = ""; appSettings["initBackgroundImage"] = m_fileName; slotOptionsBackgroundImage(); } } /** * @brief MainWindow::slotOptionsBackgroundImage * Enables/disables displaying a user-defined custom image in the background * Called from Settings Dialog and */ void MainWindow::slotOptionsBackgroundImage() { statusMessage( tr("Toggle BackgroundImage...")); if (appSettings["initBackgroundImage"].isEmpty()) { statusMessage( tr("BackgroundImage off.") ); graphicsWidget->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"] ) ) ); } else { setLastPath(appSettings["initBackgroundImage"]); graphicsWidget->setBackgroundBrush(QImage(appSettings["initBackgroundImage"])); graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); statusMessage( tr("BackgroundImage on.") ); } } /** * @brief MainWindow::slotOptionsToolbarVisibility * @param toggle * Turns Toolbar on or off */ void MainWindow::slotOptionsToolbarVisibility(bool toggle) { statusMessage( tr("Toggle toolbar...")); if (toggle== false) { toolBar->hide(); appSettings["showToolBar"] = "false"; statusMessage( tr("Toolbar off.") ); } else { toolBar->show(); appSettings["showToolBar"] = "true"; statusMessage( tr("Toolbar on.") ); } } /** * @brief MainWindow::slotOptionsStatusBarVisibility * @param toggle * Turns Statusbar on or off */ void MainWindow::slotOptionsStatusBarVisibility(bool toggle) { statusMessage( tr("Toggle statusbar...")); if (toggle == false) { statusBar()->hide(); appSettings["showStatusBar"] = "false"; statusMessage( tr("Status bar off.") ); } else { statusBar()->show(); appSettings["showStatusBar"] = "true"; statusMessage( tr("Status bar on.") ); } } /** * @brief MainWindow::slotOptionsLeftPanelVisibility * @param toggle */ void MainWindow::slotOptionsLeftPanelVisibility(bool toggle) { statusMessage( tr("Toggle left panel...")); if (toggle == false) { leftPanel->hide(); appSettings["showLeftPanel"] = "false"; statusMessage( tr("Left Panel off.") ); } else { leftPanel->show(); appSettings["showLeftPanel"] = "true"; statusMessage( tr("Left Panel on.") ); } } /** * @brief MainWindow::slotOptionsRightPanelVisibility * @param toggle */ void MainWindow::slotOptionsRightPanelVisibility(bool toggle) { statusMessage( tr("Toggle left panel...")); if (toggle == false) { rightPanel->hide(); appSettings["showRightPanel"] = "false"; statusMessage( tr("Right Panel off.") ); } else { rightPanel->show(); appSettings["showRightPanel"] = "true"; statusMessage( tr("Right Panel on.") ); } } /** * Displays a random tip */ void MainWindow::slotHelpTips() { int randomTip=rand() % (tips.count()); //Pick a tip. QMessageBox::about( this, tr("Tip Of The Day"), tips[randomTip]); } /** Creates our tips. */ void MainWindow::slotHelpCreateTips(){ tips+=tr("To create a new node: \n" "- double-click somewhere on the canvas \n" "- or press the keyboard shortcut CTRL+. (dot)\n" "- or press the Add Node button on the left panel"); tips+=tr("SocNetV supports working with either undirected or directed data. " "When you start SocNetV for the first time, the application uses " "the 'directed data' mode; every edge you create is directed. " "To enter the 'undirected data' mode, press CTRL+E+U or enable the " "menu option Edit -> Edges -> Undirected Edges "); tips+=tr("If your screen is small, and the canvas appears even smaller " "hide the Control and/or Statistics panel. Then the canvas " "will expand to the whole application window. " "Open the Settings/Preferences dialog -> Window options and " "disable the two panels."); tips+=tr("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."); tips+=tr("To delete a node permanently: \n" "- right-click on it and select Remove Node \n" "- or press CTRL+ALT+. and enter its number\n" "- or press the Remove Node button on the Control Panel"); tips+=tr("To rotate the network: \n" " - drag the bottom slider to left or right \n" " - or click the buttons on the corners of the bottom slider\n" " - or press CTRL and the left or right arrow."); tips+=tr("To create a new edge between nodes A and B: \n" "- double-click on node A, then double-click on node B.\n" "- or middle-click on node A, and again on node B.\n" "- or right-click on the node, then select Add Edge from the popup.\n" "- or press the keyboard shortcut CTRL+/ \n" "- or press the Add Edge button on the Control Panel"); tips+=tr("Add a label to an edge by right-clicking on it " "and selecting Change Label."); tips+=tr("You can change the background color of the canvas. " "Do it from the menu Options > View or " "permanently save this setting in Settings/Preferences."); tips+=tr("Default node colors, shapes and sizes can be changed. " "Open the Settings/Preferences dialog and use the " "options on the Node tab."); tips+=tr("The Statistics Panel shows network-level information (i.e. density) " "as well as info about any node you clicked on (inDegrees, " "outDegrees, clustering)."); tips+=tr("You can move any node by left-clicking and dragging it with your mouse. " "If you want you can move multiple nodes at once. Left-click on empty space " "on the canvas and drag to create a rectangle selection around them. " "Then left-click on one of the selected nodes and drag it."); tips+=tr("To save the node positions in a network, you need to save your data " "in a format which supports node positions, suchs as GraphML or Pajek."); tips+=tr("Embed visualization models on the network from the options in " "the Layout menu or the select boxes on the left Control Panel. "); tips+=tr("To change the label of a node right-click on it, and click " "Selected Node Properties from the popup menu."); tips+=tr("All basic operations of SocNetV are available from the left Control panel " "or by right-clicking on a Node or an Edge or on canvas empty space."); tips+=tr("Node info (number, position, degree, etc) is displayed on the Status bar, " "when you left-click on it."); tips+=tr("Edge information is displayed on the Status bar, when you left-click on it."); tips+=tr("Save your work often, especially when working with large data sets. " "SocNetV alogorithms are faster when working with saved data. "); tips+=tr("The Closeness Centrality (CC) of a node v, is 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. " "This 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. "); tips+=tr("The Information Centrality (IC) index 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. " "This index should be calculated only for undirected graphs. " "Note: To compute this index, SocNetV drops all isolated nodes."); } /** * @brief MainWindow::slotHelp * Opens the system web browser to load the online Manual */ void MainWindow::slotHelp(){ statusMessage( tr("Opening the SocNetV Manual in your default web browser....") ); QDesktopServices::openUrl(QUrl("http://socnetv.org/docs/index.html")); } /** * @brief MainWindow::slotHelpCheckUpdateDialog * Opens a web browser to SocNetV website. */ void MainWindow::slotHelpCheckUpdateDialog() { qDebug() << "MW::slotHelpCheckUpdateDialog()"; http = new QNetworkAccessManager(this); qDebug() << "MW::slotHelpCheckUpdateDialog() - Connecting http finished signal"; connect ( http, &QNetworkAccessManager::finished, this, &MainWindow::slotHelpCheckUpdateParse ); request.setUrl(QUrl("http://socnetv.org/latestversion.txt")); request.setRawHeader( "User-Agent", "SocNetV harmless spider - see http://socnetv.org"); qDebug() << "MW::slotHelpCheckUpdateDialog() - making the call..."; QNetworkReply *reply = http->get(request) ; Q_UNUSED(reply); } void MainWindow::slotHelpCheckUpdateParse(QNetworkReply *reply) { qDebug() << "MW::slotHelpCheckUpdateParse(reply)"; QByteArray ba; ba=reply->readAll(); QString REMOTEVERSION(ba); REMOTEVERSION = REMOTEVERSION.simplified(); if (REMOTEVERSION.isEmpty()) { slotHelpMessageToUserError("Error connecting to http://socnetv.org. " "Please, check your internet connection and try again."); return; } QString remoteVersionStr = REMOTEVERSION; QString localVersionStr = VERSION; int localVersion=0; int remoteVersion=0; bool ok1=false; bool ok2=false; localVersionStr.remove("."); localVersion = localVersionStr.toInt(&ok1, 10); qDebug() << "MW::slotHelpCheckUpdateParse(reply) - localVersion:" << localVersion; if (!ok1) { slotHelpMessageToUserError("Error in current version string. " "Please, conduct our developer team."); return; } remoteVersionStr.remove("."); remoteVersion = remoteVersionStr.toInt(&ok2, 10); qDebug() << "MW::slotHelpCheckUpdateParse(reply) - remoteVersion:" << remoteVersion; if (!ok2) { slotHelpMessageToUserError("Error getting newest version details from http://socnetv.org. " "Please, try again."); return; } if( remoteVersion > localVersion ) { switch( slotHelpMessageToUser( USER_MSG_QUESTION, tr("Newer SocNetV version available!"), tr("

Your version: ")+ VERSION+ "

" + tr("

Remote version: ")+REMOTEVERSION + "

", tr("

There is a newer SocNetV version available!

" "

Do you want to download the latest version now?

" "

Press Yes, and I will open your default web browser for you " "to download the latest SocNetV package...

"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes ) ) { case QMessageBox::Yes: statusMessage( tr("Opening SocNetV website in your default web browser....") ); QDesktopServices::openUrl(QUrl ("http://socnetv.org/downloads" "?utm_source=application&utm_medium=banner&utm_campaign=socnetv"+ VERSION )); break; case QMessageBox::No: break; case QMessageBox::Cancel: //userCancelled = true; break; case QMessageBox::NoButton: default: // just for sanity break; } } else { slotHelpMessageToUserInfo( tr("

Your version: ")+ VERSION+ "

" + tr("

Remote version: ")+REMOTEVERSION + "

" + tr("

You are running the latest and greatest version of SocNetV.
" "Nothing to do!

") ); } } /** Displays the following message!! */ void MainWindow::slotHelpAbout(){ int randomCookie=rand()%fortuneCookie.count(); QString BUILD="Wed Feb 28 14:33:48 EET 2018"; QMessageBox::about( this, tr("About SocNetV"), tr("Social Network Visualizer (SocNetV)") + tr("

Version: ") + VERSION + "

" + tr("

Build: ") + BUILD + "

" + tr("

Website: http://socnetv.org

")+ tr("

(C) 2005-2018 by Dimitris V. Kalamaras

")+ tr("

Have questions? Contact us!

")+ tr("

Fortune cookie:
\"") + fortuneCookie[randomCookie] + "\"" + tr("

License:

") + tr("

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.

") + tr("

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.

") + tr("

You should have received a copy of the GNU " "General Public License along with this program; " "If not, see http://www.gnu.org/licenses/

")); } /** Creates the fortune cookies displayed on the above message. */ void MainWindow::createFortuneCookies(){ fortuneCookie+="sic itur ad astra / sic transit gloria mundi ?
" "--Unknown"; fortuneCookie+="The truth is not my business. I am a statistician... I don’t like words like \"correct\" and \"truth\". " "Statistics is about measuring against convention.
" "Walter Radermacher, Eurostat director, interview to NY Times, 2012."; fortuneCookie+="Losers of yesterday, the winners of tomorrow...
" "--B.Brecht"; fortuneCookie+="I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. " "I watched C-beams glitter in the dark near the Tannhauser gate. " "All those moments will be lost in time... like tears in rain... Time to die.
" "Replicant Roy Batty, Blade Runner (1982)"; fortuneCookie+="Patriotism is the virtue of the wicked...
" "--O. Wilde"; fortuneCookie+="No tengo nunca mas, no tengo siempre. En la arena
" "la victoria dejo sus piers perdidos.
" "Soy un pobre hombre dispuesto a amar a sus semejantes.
" "No se quien eres. Te amo. No doy, no vendo espinas.
" "--Pablo Neruda" ; fortuneCookie+="Man must not check reason by tradition, but contrawise, " "must check tradition by reason.
--Leo Tolstoy"; fortuneCookie+="Only after the last tree has been cut down,
" "only after the last river has been poisoned,
" "only after the last fish has been caught,
" "only then will you realize that money cannot be eaten.
" "--The Cree People"; fortuneCookie+="Stat rosa pristina nomine, nomina nuda tenemus
" " --Unknown"; fortuneCookie+="Jupiter and Saturn, Oberon, Miranda
" "And Titania, Neptune, Titan.
" "Stars can frighten.
Syd Barrett"; } /** Displays a short message about the Qt Toolbox. */ void MainWindow::slotAboutQt(){ QMessageBox::aboutQt(this, "About Qt - SocNetV"); } socnetv-2.4/src/graphicsedgelabel.cpp0000775000175000017500000000412613245520654020230 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsedgelabel.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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() { } socnetv-2.4/src/graphvertex.h0000775000175000017500000003164613245520654016616 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphvertex.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 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 QHash < 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* parent, const long int &name, const int &val, const int &relation, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape); GraphVertex(const long int &name); ~GraphVertex(); long int name() const { return m_name; } void setName (const long int &name) { m_name=name; } void setEnabled (const bool &flag ) { m_enabled=flag; } bool isEnabled () const { return m_enabled; } void relationSet(int newRel) ; void edgeAddTo (const long int &v2, const float &weight); void edgeAddFrom(const long int &v1, const float &weight); void changeOutEdgeWeight (long int target, float weight); void setOutEdgeEnabled (long int, bool); void edgeRemoveTo (long int target); void edgeRemoveFrom(long int source); QHash outEdgesEnabledHash(const bool &allRelations=false); QHash* outEdgesAllRelationsUniqueHash(); QHash* inEdgesEnabledHash(); QHash reciprocalEdgesHash(); QList neighborhoodList(); long int outEdges(); long int outEdgesConst() const ; long int inEdges(); long int inEdgesConst() const ; long int degreeOut(); long int outDegreeConst(); long int degreeIn(); long int inDegreeConst(); long int localDegree(); float distance(const long int &v1) ; void setDistance (const long int &v1, const float &d) ; void reserveDistance(const int &N); void clearDistance(); int shortestPaths(const long int &v1) ; void setShortestPaths(const long int &v1, const int &sp) ; void reserveShortestPaths(const int &N); void clearShortestPaths(); /* sets eccentricity */ void setEccentricity (float c){ m_Eccentricity=c;} float eccentricity() { return m_Eccentricity;} /* Returns true if there is an outLink from this vertex */ bool isOutLinked() { return (outEdges() > 0) ? true:false;} float hasEdgeTo(const long int &v, const bool &allRelations=false); /* Returns true if there is an outLink from this vertex */ bool isInLinked() { return (inEdges() > 0) ? true:false;} float hasEdgeFrom (const long int &v, const bool &allRelations=false); bool isIsolated() { return !(isOutLinked() | isInLinked()) ; } void setIsolated(bool isolated) {m_isolated = isolated; } void edgeFilterByWeight(float m_threshold, bool overThreshold); // void filterEdgesByColor(float 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) { m_shape=shape; } QString shape() const { return m_shape; } void setColor(const QString &color) { m_color=color; } QString color() const { return m_color; } QString colorToPajek(); void setNumberColor (const QString &color) { m_numberColor = color; } QString numberColor() const { return m_numberColor; } void setNumberSize (const int &size) { m_numberSize=size; } int numberSize() const { return m_numberSize; } void 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 float &x) { m_x=x; } float x() const { return m_x; } void setY(const float &y) { m_y=y; } float y() const { return m_y; } QPointF pos () const { return QPointF ( x(), y() ); } void setPos (QPointF &p) { m_x=p.x(); m_y=p.y(); } //returns displacement vector QPointF & disp() { return m_disp; } void set_dispX (float x) { m_disp.rx() = x ; } void set_dispY (float y) { m_disp.ry() = y ; } void setOutLinkColor(const long int &v2, const QString &color) { m_outLinkColors[v2]=color; } QString outLinkColor(const long int &v2) { return ( m_outLinkColors.contains(v2) ) ? m_outLinkColors.value(v2) : "black"; } void setOutEdgeLabel(const long int &v2, const QString &label) { m_outEdgeLabels[v2]=label; } QString outEdgeLabel(const long int &v2) const { return ( m_outEdgeLabels.contains(v2) ) ? m_outEdgeLabels.value(v2) : QString::null; } void setDelta (float c){ m_delta=c;} /* Sets vertex pair dependancy */ float delta() { return m_delta;} /* Returns vertex pair dependancy */ void clearPs() ; void appendToPs(long int vertex ) ; 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 (float c){ m_DC=c;} /* Sets vertex Degree Centrality*/ void setSDC (float c ) { m_SDC=c;} /* Sets standard vertex Degree Centrality*/ float DC() { return m_DC;} /* Returns vertex Degree Centrality*/ float SDC() { return m_SDC;} /* Returns standard vertex Degree Centrality*/ void setCC (float c){ m_CC=c;} /* sets vertex Closeness Centrality*/ void setSCC (float c ) { m_SCC=c;} /* sets standard vertex Closeness Centrality*/ float CC() { return m_CC;} /* Returns vertex Closeness Centrality*/ float SCC() { return m_SCC; } /* Returns standard vertex Closeness Centrality*/ void setIRCC (float c){ m_IRCC=c;} /* sets vertex IRCC */ void setSIRCC (float c ) { m_SIRCC=c;} /* sets standard vertex IRCC */ float IRCC() { return m_IRCC;} /* Returns vertex IRCC */ float SIRCC() { return m_SIRCC; } /* Returns standard vertex IRCC*/ void setBC(float c){ m_BC=c;} /* sets s vertex Betweenness Centrality*/ void setSBC (float c ) { m_SBC=c;} /* sets standard vertex Betweenness Centrality*/ float BC() { return m_BC;} /* Returns vertex Betweenness Centrality*/ float SBC() { return m_SBC; } /* Returns standard vertex Betweenness Centrality*/ void setSC (float c){ m_SC=c;} /* sets vertex Stress Centrality*/ void setSSC (float c ) { m_SSC=c;} /* sets standard vertex Stress Centrality*/ float SC() { return m_SC;} /* Returns vertex Stress Centrality*/ float SSC() { return m_SSC; } /* Returns standard vertex Stress Centrality*/ void setEC(float dist) { m_EC=dist;} /* Sets max Geodesic Distance to all other vertices*/ void setSEC(float c) {m_SEC=c;} float EC() { return m_EC;} /* Returns max Geodesic Distance to all other vertices*/ float SEC() { return m_SEC;} void setPC (float c){ m_PC=c;} /* sets vertex Power Centrality*/ void setSPC (float c ) { m_SPC=c;} /* sets standard vertex Power Centrality*/ float PC() { return m_PC;} /* Returns vertex Power Centrality*/ float SPC() { return m_SPC; } /* Returns standard vertex Power Centrality*/ void setIC (float c){ m_IC=c;} /* sets vertex Information Centrality*/ void setSIC (float c ) { m_SIC=c;} /* sets standard vertex Information Centrality*/ float IC() { return m_IC;} /* Returns vertex Information Centrality*/ float SIC() { return m_SIC; } /* Returns standard vertex Information Centrality*/ void setDP (float c){ m_DP=c;} /* Sets vertex Degree Prestige */ void setSDP (float c ) { m_SDP=c;} /* Sets standard vertex Degree Prestige */ float DP() { return m_DP;} /* Returns vertex Degree Prestige */ float SDP() { return m_SDP;} /* Returns standard vertex Degree Prestige */ void setPRP (float c){ m_PRC=c;} /* sets vertex PageRank*/ void setSPRP (float c ) { m_SPRC=c;} /* sets standard vertex PageRank*/ float PRP() { return m_PRC;} /* Returns vertex PageRank */ float SPRP() { return m_SPRC; } /* Returns standard vertex PageRank*/ void setPP (float c){ m_PP=c;} /* sets vertex Proximity Prestige */ void setSPP (float c ) { m_SPP=c;} /* sets standard vertex Proximity Prestige */ float PP() { return m_PP;} /* Returns vertex Proximity Prestige */ float SPP() { return m_SPP; } /* Returns standard vertex Proximity Prestige */ float CLC() { return m_CLC; } void setCLC(float clucof) { m_CLC=clucof; m_hasCLC=true; } bool hasCLC() { return m_hasCLC; } void setEVC (float c){ m_EVC=c;} /* Sets vertex Degree Centrality*/ void setSEVC (float c ) { m_SEVC=c;} /* Sets standard vertex Degree Centrality*/ float EVC() { return m_EVC;} /* Returns vertex Degree Centrality*/ float SEVC() { return m_SEVC;} /* Returns standard vertex Degree Centrality*/ int cliques (const int &ofSize); void cliqueAdd (const QList &clique); void clearCliques() { m_cliques.clear(); } //hold all outbound and inbound edges of this vertex. H_edges m_outEdges, m_inEdges; H_distance m_distance; H_shortestPaths m_shortestPaths; signals: void setEdgeVisibility (int, int, int, bool); protected: private: Graph *parentGraph; long int m_name, m_outEdgesCounter, m_inEdgesCounter, m_outDegree, m_inDegree, m_localDegree; long 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; float m_Eccentricity, m_CLC; float m_delta, m_EC, m_SEC; float m_DC, m_SDC, m_DP, m_SDP, m_CC, m_SCC, m_BC, m_SBC, m_IRCC, m_SIRCC, m_SC, m_SSC; float m_PC, m_SPC, m_SIC, m_IC, m_SPRC, m_PRC; float m_PP, m_SPP, m_EVC, m_SEVC; QString m_color, m_numberColor, m_label, m_labelColor, m_shape; QPointF m_disp; QHash m_reciprocalEdges; L_int myPs; QHash m_cliques; L_int m_neighborhoodList; H_IntToStr m_outLinkColors, m_outEdgeLabels; //FIXME vertex coords }; #endif socnetv-2.4/src/dialograndregular.cpp0000664000175000017500000001161713245520654020271 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialograndregular.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData ); 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 ) { 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::gatherData() { qDebug() << "DialogRandRegular::gatherData() " ; 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); } socnetv-2.4/src/graphicsguide.h0000775000175000017500000000506313245520654017067 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsguide.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 socnetv-2.4/src/dialogsimilaritymatches.cpp0000664000175000017500000000635313245520654021517 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogsimilaritymatches.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData(){ 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->gatherData(); this->accept(); } void DialogSimilarityMatches::on_buttonBox_rejected() { this->reject(); } DialogSimilarityMatches::~DialogSimilarityMatches(){ matrixList.clear(); variablesLocationList.clear(); } socnetv-2.4/src/mainwindow.h0000775000175000017500000006704513245520654016435 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt mainwindow.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras blog : http://dimitris.apeiro.gr project site : http://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 MAINWINDOW_H #define MAINWINDOW_H /** \file mainwindow.h \brief Documentation for the mainwindow file. */ #include #include #include #include #include #include #include #include "graph.h" #include "dialogfilteredgesbyweight.h" #include "dialogdatasetselect.h" static const QString VERSION="2.4"; static const int USER_MSG_INFO=0; static const int USER_MSG_CRITICAL=1; static const int USER_MSG_CRITICAL_NO_NETWORK=2; static const int USER_MSG_CRITICAL_NO_EDGES=3; static const int USER_MSG_QUESTION=4; static const int USER_MSG_QUESTION_CUSTOM=5; QT_BEGIN_NAMESPACE class QGraphicsScene; class QMenu; class QAction; class QCheckBox; class QProgressDialog; class QPushButton; class QToolButton; class QLCDNumber; class QSlider; class QComboBox; class QGroupBox; class QTabWidget; class QSpinBox; QT_END_NAMESPACE using namespace std; class GraphicsWidget; class GraphicsEdge; class GraphicsNode; class DialogWebCrawler; class DialogNodeEdit; class DialogPreviewFile; class DialogRandErdosRenyi; class DialogRandSmallWorld; class DialogRandScaleFree; class DialogSimilarityPearson; class DialogSimilarityMatches; class DialogDissimilarities; class DialogClusteringHierarchical; class DialogRandRegular; class DialogSettings; class TextEditor; /** \brief The base window of SocNetV contains all widgets and functionality. It sets up the main window and provides a menubar, toolbar and statusbar. For the main view, an instance of class GraphicsWidget is created which creates a graphics widget. */ class MainWindow : public QMainWindow { Q_OBJECT QThread graphThread; public: MainWindow(const QString &f); ~MainWindow(); void initGraph(); void terminateThreads(const QString &reason); void initView(); void initActions(); void initMenuBar(); void initToolBar(); void initPanels(); void initWindowLayout(); void initSignalSlots(); QMap initSettings(); void saveSettings(); void initApp(); void initComboBoxes(); void setLastPath(QString filePath); QString getLastPath(); void createFortuneCookies(); int activeEdges(); int activeNodes(); public slots: //NETWORK MENU void slotNetworkNew(); void slotNetworkFileChoose(QString m_fileName = QString::null, int m_fileFormat = -1, const bool &checkSelectFileType = true); void slotNetworkFileDialogFileSelected(const QString &fileName); void slotNetworkFileDialogFilterSelected(const QString &filter); void slotNetworkFileDialogRejected(); void slotNetworkFileRecentUpdateActions(); void slotNetworkAvailableTextCodecs(); bool slotNetworkFilePreview(const QString &, const int &); void slotNetworkFileLoad ( const QString, const QString, const int ); void slotNetworkFileLoaded(const int &type, const QString &fName=QString::null, const QString &netName=QString::null, const int &totalNodes=0, const int &totalEdges=0, const QString &message=QString::null); void slotNetworkFileLoadRecent(); void slotNetworkSaved(const int &status); void slotNetworkFileView(); void slotNetworkImportGraphML(); void slotNetworkImportPajek(); void slotNetworkImportSM(); void slotNetworkImportDot(); void slotNetworkImportGML(); void slotNetworkImportDL(); void slotNetworkImportEdgeList(); void slotNetworkImportTwoModeSM(); void slotNetworkChanged(const int &graphStatus, const bool &undirected, const int &vertices, const int &edges, const float &density); void slotNetworkSave(const int &fileFormat=-1); void slotNetworkSaveAs(); void slotNetworkClose(); void slotNetworkPrint(); void slotNetworkViewSociomatrix(); void slotNetworkViewSociomatrixPlotText(); bool slotNetworkExportBMP(); bool slotNetworkExportPNG(); bool slotNetworkExportPDF(); void slotNetworkExportPajek(); void slotNetworkExportSM(); bool slotNetworkExportDL(); bool slotNetworkExportGW(); bool slotNetworkExportList(); void slotNetworkTextEditor(); void slotNetworkDataSetSelect(); void slotNetworkDataSetRecreate(const QString); void slotNetworkRandomErdosRenyiDialog(); void slotNetworkRandomErdosRenyi( const int N, const QString model, const int edges, const float eprob, const QString mode, const bool diag) ; void slotNetworkRandomRegularDialog(); void slotNetworkRandomRegular(const int &newNodes, const int °ree, const QString &mode, const bool &diag); void slotNetworkRandomGaussian(); void slotNetworkRandomScaleFreeDialog(); void slotNetworkRandomScaleFree(const int &newNodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode); void slotNetworkRandomSmallWorldDialog(); void slotNetworkRandomSmallWorld (const int &newNodes, const int °ree, const float &beta, const QString &mode, const bool &diag); void slotNetworkRandomRingLattice(); void slotNetworkWebCrawlerDialog(); void slotNetworkWebCrawler(const QString &urlSeed, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &extLinks, const bool &intLinks, const bool &selfLinks, const bool &delayedRequests); //EDIT MENU void slotEditRelationsClear(); void slotEditRelationAdd(QString newRelationName=QString::null, const bool &changeRelation=true); void slotEditRelationChange(const int relIndex=RAND_MAX); void slotEditRelationRename(QString newName=QString::null); void slotEditOpenContextMenu(const QPointF & mPos); void slotEditSelectionChanged (const int &selNodes, const int &selEdges); void slotEditClickOnEmptySpace (const QPointF &p); void slotEditNodeSelectAll(); void slotEditNodeSelectNone(); void slotEditNodeInfoStatusBar(const int &number, const QPointF &p, const QString &label, const int &inDegree, const int &outDegree, const float &clc=0); void slotEditNodePosition(const int &nodeNumber, const int &x, const int &y); void slotEditNodeAdd(); void slotEditNodeFind(); void slotEditNodeRemove(); void slotEditNodeOpenContextMenu(); void slotEditNodePropertiesDialog(); void slotEditNodeProperties( const QString, const int, const QString, const QColor, const QString); void slotEditNodeSelectedToClique(); void slotEditNodeSelectedToStar(); void slotEditNodeSelectedToCycle(); void slotEditNodeSelectedToLine(); void slotEditNodeColorAll(QColor color=QColor()); void slotEditNodeSizeAll(int newSize=0, const bool &normalized=false); void slotEditNodeShape(QString shape=QString::null, const int vertex = 0); void slotEditNodeNumberSize(int v1=0, int newSize=0, const bool prompt=true); void slotEditNodeNumberDistance(int v1=0, int newSize=0); void slotEditNodeNumbersColor(QColor color=QColor()); void slotEditNodeLabelSize(int v1=0, int newSize=0); void slotEditNodeLabelsColor(QColor color=QColor()); void slotEditNodeLabelDistance(int v1=0, int newSize=0); void slotEditEdgeClicked (const int &v1, const int &v2, const float &weight, const int &type, const bool &openMenu=false); void slotEditEdgeOpenContextMenu(const QString &str="") ; void slotEditEdgeAdd(); void slotEditEdgeCreate (const int &source, const int &target, const float &weight=1); void slotEditEdgeRemove(); void slotEditEdgeLabel(); void slotEditEdgeColor(); void slotEditEdgeWeight(); void slotEditEdgeColorAll(QColor color=QColor(), const int threshold=RAND_MAX); void slotEditEdgeMode(const int &mode); void slotEditEdgeSymmetrizeAll(); void slotEditEdgeSymmetrizeStrongTies(); void slotEditEdgeSymmetrizeCocitation(); void slotEditEdgeUndirectedAll(const bool &toggle); void slotFilterNodes(); void slotEditFilterNodesIsolates(bool checked); void slotEditFilterEdgesByWeightDialog(); void slotEditFilterEdgesUnilateral(bool checked); void slotEditTransformNodes2Edges(); // LAYOUT MENU void slotLayoutRandom(); void slotLayoutRadialRandom(); void slotLayoutRadialByProminenceIndex(); void slotLayoutRadialByProminenceIndex(QString); void slotLayoutLevelByProminenceIndex(); void slotLayoutLevelByProminenceIndex(QString); void slotLayoutNodeSizeByProminenceIndex(); void slotLayoutNodeSizeByProminenceIndex(QString); void slotLayoutNodeColorByProminenceIndex(); void slotLayoutNodeColorByProminenceIndex(QString); void slotLayoutSpringEmbedder(); void slotLayoutFruchterman(); void slotLayoutKamadaKawai(); void slotLayoutColorationStrongStructural(); void slotLayoutColorationRegular(); void slotLayoutGuides(const bool &toggle); //ANALYSIS MENU void askAboutWeights(const bool userTriggered=false); void slotAnalyzeReciprocity(); void slotAnalyzeSymmetryCheck(); void slotAnalyzeMatrixAdjacencyInverse(); void slotAnalyzeMatrixAdjacencyTranspose(); void slotAnalyzeMatrixAdjacencyCocitation(); void slotAnalyzeMatrixDegree(); void slotAnalyzeMatrixLaplacian(); void slotAnalyzeClusteringCoefficient(); void slotAnalyzeMatrixDistances(); void slotAnalyzeMatrixGeodesics(); void slotAnalyzeDistance(); void slotAnalyzeDistanceAverage(); void slotAnalyzeDiameter(); void slotAnalyzeEccentricity(); void slotAnalyzeWalksLength(); void slotAnalyzeWalksTotal(); void slotAnalyzeReachabilityMatrix(); void slotAnalyzeConnectedness(); void slotAnalyzeCentralityDegree(); void slotAnalyzeCentralityCloseness(); void slotAnalyzeCentralityClosenessIR(); void slotAnalyzeCentralityBetweenness(); void slotAnalyzeCentralityInformation(); void slotAnalyzeCentralityEigenvector(); void slotAnalyzeCentralityStress(); void slotAnalyzeCentralityPower(); void slotAnalyzeCentralityEccentricity(); void slotAnalyzePrestigeDegree(); void slotAnalyzePrestigePageRank(); void slotAnalyzePrestigeProximity(); void slotAnalyzeCommunitiesCliqueCensus(); void slotAnalyzeCommunitiesTriadCensus(); void slotAnalyzeStrEquivalenceClusteringHierarchicalDialog(); void slotAnalyzeStrEquivalenceClusteringHierarchical(const QString &matrix, const QString &varLocation, const QString &metric, const QString &method, const bool &diagonal=false, const bool &diagram=false); void slotAnalyzeStrEquivalenceDissimilaritiesDialog(); void slotAnalyzeStrEquivalenceDissimilaritiesTieProfile(const QString &metric, const QString &varLocation, const bool &diagonal); void slotAnalyzeStrEquivalenceSimilarityMeasureDialog(); void slotAnalyzeStrEquivalenceSimilarityByMeasure(const QString &matrix, const QString &varLocation, const QString &measure, const bool &diagonal); void slotAnalyzeStrEquivalencePearsonDialog(); void slotAnalyzeStrEquivalencePearson(const QString &matrix, const QString &varLocation, const bool &diagonal=false); //OPTIONS MENU void slotOpenSettingsDialog(); void slotOptionsNodeNumbersVisibility(bool toggle); void slotOptionsNodeNumbersInside(bool toggle); void slotOptionsNodeLabelsVisibility(bool toggle); void slotOptionsEdgesVisibility(bool toggle); void slotOptionsEdgeOffsetFromNode(const int &offset, const int &v1=0, const int &v2=0); void slotOptionsEdgeLabelsVisibility(bool toggle); void slotOptionsEdgeWeightNumbersVisibility(bool toggle); void slotOptionsEdgeWeightsDuringComputation(bool); void slotOptionsEdgeThicknessPerWeight(bool toogle); void slotOptionsEdgesBezier(bool toggle); void slotOptionsEdgeArrowsVisibility(bool toggle); void slotOptionsEmbedLogoExporting(bool toggle); void slotOptionsProgressDialogVisibility(bool toggle); void slotOptionsToolbarVisibility(bool toggle); void slotOptionsStatusBarVisibility(bool toggle); void slotOptionsLeftPanelVisibility(bool toggle); void slotOptionsRightPanelVisibility(bool toggle); void slotOptionsDebugMessages(bool toggle); void slotOptionsBackgroundColor(QColor color=QColor()); void slotOptionsBackgroundImageSelect(bool toggle); void slotOptionsBackgroundImage(); void slotOptionsCanvasAntialiasing(bool toggle); void slotOptionsCanvasAntialiasingAutoAdjust(const bool &toggle=false); void slotOptionsCanvasSmoothPixmapTransform(const bool &toggle=false); void slotOptionsCanvasSavePainterState(const bool &toggle=false); void slotOptionsCanvasCacheBackground(const bool &toggle=false); void slotOptionsCanvasEdgeHighlighting(const bool &toggle=false); void slotOptionsCanvasUpdateMode(const QString &mode); void slotOptionsCanvasIndexMethod(const QString &method); //HELP MENU void slotHelpTips(); void slotHelp(); void slotHelpCheckUpdateDialog(); void slotHelpCheckUpdateParse(QNetworkReply *reply); void slotHelpCreateTips(); void slotHelpAbout(); void slotAboutQt(); void slotHelpMessageToUserInfo(const QString text=QString::null); void slotHelpMessageToUserError(const QString text=QString::null); int slotHelpMessageToUser(const int type=0, const QString statusMsg=QString::null, const QString text=QString::null, const QString info=QString::null, QMessageBox::StandardButtons buttons=QMessageBox::NoButton, QMessageBox::StandardButton defBtn=QMessageBox::Ok, const QString btn1=QString::null, const QString btn2=QString::null ); //Called by Graph to display some message to the user void statusMessage(const QString); //Called from MW, when user highlights something in the toolbox Comboboxes void toolBoxEditNodeSubgraphSelectChanged(int); void toolBoxEditEdgeSymmetrizeSelectChanged(int); void toolBoxAnalysisMatricesSelectChanged(int); void toolBoxAnalysisCohesionSelectChanged(int); void toolBoxAnalysisStrEquivalenceSelectChanged(int); void toolBoxAnalysisProminenceSelectChanged(int); void toolBoxAnalysisCommunitiesSelectChanged(int); void toolBoxLayoutByIndexApplyBtnPressed(); void toolBoxLayoutForceDirectedApplyBtnPressed(); void slotProgressBoxCreate(const int &max=0, const QString &msg="Please wait..."); void slotProgressBoxDestroy(const int &max=0); protected: void resizeEvent( QResizeEvent * ); void closeEvent( QCloseEvent* ce ); // void myMessageOutput(QtMsgType type, const char *msg); signals: void signalRelationAddAndChange(const QString &relName, const bool &changeRelation=true); private: QGraphicsScene *scene; GraphicsWidget *graphicsWidget; Graph *activeGraph; QMap appSettings; DialogFilterEdgesByWeight m_DialogEdgeFilterByWeight; DialogWebCrawler *m_WebCrawlerDialog; DialogDataSetSelect m_datasetSelectDialog; DialogNodeEdit *m_nodeEditDialog; DialogRandErdosRenyi *m_randErdosRenyiDialog; DialogRandSmallWorld *m_randSmallWorldDialog; DialogRandScaleFree *m_randScaleFreeDialog; DialogRandRegular *m_randRegularDialog; DialogSettings *m_settingsDialog; DialogSimilarityPearson *m_dialogSimilarityPearson; DialogSimilarityMatches *m_dialogSimilarityMatches; DialogDissimilarities *m_dialogdissimilarities; DialogClusteringHierarchical *m_dialogClusteringHierarchical; DialogPreviewFile *m_dialogPreviewFile; QList codecs; QString userSelectedCodecName; QList m_textEditors; QPrinter *printer; QToolBar *toolBar; QGroupBox *leftPanel, *rightPanel ; QComboBox *editRelationChangeCombo; QStack progressDialogs; QMenu *importSubMenu, *exportSubMenu, *editMenu, *analysisMenu, *helpMenu; QMenu *optionsMenu, *colorOptionsMenu, *edgeOptionsMenu, *nodeOptionsMenu; QMenu *editNodeMenu, *editEdgeMenu, *centrlMenu, *viewOptionsMenu, *layoutMenu; QMenu *cohesionMenu, *strEquivalenceMenu, *communitiesMenu, *connectivityMenu; QMenu *matrixMenu; QMenu *networkMenu, *randomNetworkMenu, *filterMenu, *recentFilesSubMenu; QMenu *randomLayoutMenu, *layoutRadialProminenceMenu, *layoutLevelProminenceMenu; QMenu *layoutForceDirectedMenu, *layoutNodeSizeProminenceMenu, *layoutNodeColorProminenceMenu; QMenu *colorationMenu; QComboBox *toolBoxEditNodeSubgraphSelect, *toolBoxEditEdgeModeSelect, *toolBoxEditEdgeSymmetrizeSelect, *toolBoxAnalysisCohesionSelect, *toolBoxAnalysisStrEquivalenceSelect, *toolBoxAnalysisProminenceSelect, *toolBoxAnalysisCommunitiesSelect, *toolBoxAnalysisMatricesSelect; QComboBox *toolBoxLayoutByIndexSelect, *toolBoxLayoutByIndexTypeSelect; QComboBox *toolBoxLayoutForceDirectedSelect; QPushButton *toolBoxLayoutByIndexApplyButton, *toolBoxLayoutForceDirectedApplyButton; QAction *zoomInAct,*zoomOutAct,*editRotateRightAct,*editRotateLeftAct, *editResetSlidersAct ; QToolButton *zoomInBtn,*zoomOutBtn,*rotateLeftBtn,*rotateRightBtn, *resetSlidersBtn ; QSlider *zoomSlider, *rotateSlider; QAction *networkNew, *networkOpen, *networkSave, *networkSaveAs, *networkClose, *networkPrint,*networkQuit; QAction *networkExportBMP, *networkExportPNG, *networkExportPajek, *networkExportPDF, *networkExportDL, *networkExportGW, *networkExportSM, *networkExportList; QAction *networkImportPajek, *networkImportGML, *networkImportSM, *networkImportList, *networkImportDot , *networkImportDL, *networkImportTwoModeSM; QAction *networkViewFileAct, *openTextEditorAct, *networkViewSociomatrixAct, *networkDataSetSelectAct, *networkViewSociomatrixPlotAct; QAction *createErdosRenyiRandomNetworkAct, *createGaussianRandomNetworkAct; QAction *createLatticeNetworkAct, *createScaleFreeRandomNetworkAct; QAction *createSmallWorldRandomNetworkAct, *createRegularRandomNetworkAct; QAction *optionsNodeNumbersVisibilityAct, *optionsNodeLabelsVisibilityAct, *optionsNodeNumbersInsideAct; QAction *editNodeSelectNoneAct, *editNodeSelectAllAct; QAction *editNodeSelectedToStarAct, *editNodeSelectedToCycleAct; QAction *editNodeSelectedToLineAct, *editNodeSelectedToCliqueAct; QAction *editNodeFindAct,*editNodeAddAct, *editNodeRemoveAct; QAction *editNodePropertiesAct; QAction *editEdgeAddAct, *editEdgeRemoveAct; QAction *editNodeNumbersSizeAct, *editNodeLabelsSizeAct; QAction *editNodeSizeAllAct, *editNodeShapeAll; QAction *editEdgeLabelAct, *editEdgeColorAct, *editEdgeWeightAct; QAction *filterNodesAct, *editFilterNodesIsolatesAct, *editFilterEdgesByWeightAct; QAction *editFilterEdgesUnilateralAct; QAction *transformNodes2EdgesAct, *editEdgeSymmetrizeAllAct; QAction *editEdgeSymmetrizeStrongTiesAct, *editEdgeUndirectedAllAct; QAction *changeBackColorAct, *editNodeColorAll, *editEdgeColorAllAct, *editNodeNumbersColorAct,*editNodeLabelsColorAct, *editEdgesCocitationAct; QAction *optionsEdgeThicknessPerWeightAct, *optionsEdgeWeightNumbersAct, *optionsEdgesVisibilityAct; QAction *optionsEdgeArrowsAct, *drawEdgesBezier,*optionsEdgeWeightConsiderAct; QAction *optionsEdgeLabelsAct; QAction *backgroundImageAct,*helpAboutApp, *helpAboutQt, *helpApp, *tipsApp; QAction *helpCheckUpdatesApp; QAction *openSettingsAct; QAction *webCrawlerAct; QAction *netDensity, *analyzeGraphReciprocityAct, *analyzeGraphSymmetryAct; QAction *analyzeGraphDistanceAct, *averGraphDistanceAct; QAction *analyzeMatrixDistancesGeodesicAct, *analyzeMatrixGeodesicsAct; QAction *analyzeGraphDiameterAct, *analyzeGraphEccentricityAct; QAction *analyzeStrEquivalenceTieProfileDissimilaritiesAct; QAction *analyzeGraphWalksAct,*analyzeGraphWalksTotalAct, *analyzeMatrixReachabilityAct, *analyzeGraphConnectednessAct; QAction *analyzeCommunitiesCliquesAct, *clusteringCoefAct, *analyzeCommunitiesTriadCensusAct; QAction *analyzeMatrixAdjTransposeAct, *analyzeMatrixAdjInvertAct; QAction *analyzeMatrixAdjCocitationAct; QAction *analyzeMatrixDegreeAct, *analyzeMatrixLaplacianAct; QAction *analyzeStrEquivalenceClusteringHierarchicalAct, *analyzeStrEquivalencePearsonAct; QAction *analyzeStrEquivalenceMatchesAct; QAction *cDegreeAct, *cInDegreeAct, *cClosenessAct, *cInfluenceRangeClosenessAct, *cBetweennessAct, *cInformationAct, *cEigenvectorAct, *cPageRankAct, *cStressAct, *cPowerAct, *cEccentAct, *cProximityPrestigeAct; QAction *layoutRandomAct, *layoutRandomRadialAct, *layoutGuidesAct; QAction *layoutRadialProminence_DC_Act, *layoutRadialProminence_DP_Act, *layoutRadialProminence_CC_Act, *layoutRadialProminence_SC_Act, *layoutRadialProminence_EC_Act, *layoutRadialProminence_PC_Act, *layoutRadialProminence_BC_Act, *layoutRadialProminence_IC_Act, *layoutRadialProminence_EVC_Act, *layoutRadialProminence_IRCC_Act,*layoutRadialProminence_PRP_Act, *layoutRadialProminence_PP_Act; QAction *layoutLevelProminence_DC_Act, *layoutLevelProminence_DP_Act, *layoutLevelProminence_CC_Act, *layoutLevelProminence_SC_Act, *layoutLevelProminence_EC_Act, *layoutLevelProminence_PC_Act, *layoutLevelProminence_BC_Act, *layoutLevelProminence_IC_Act, *layoutLevelProminence_EVC_Act, *layoutLevelProminence_IRCC_Act,*layoutLevelProminence_PRP_Act, *layoutLevelProminence_PP_Act; QAction *layoutNodeSizeProminence_DC_Act, *layoutNodeSizeProminence_DP_Act, *layoutNodeSizeProminence_CC_Act, *layoutNodeSizeProminence_SC_Act, *layoutNodeSizeProminence_EC_Act, *layoutNodeSizeProminence_PC_Act, *layoutNodeSizeProminence_BC_Act, *layoutNodeSizeProminence_IC_Act, *layoutNodeSizeProminence_EVC_Act, *layoutNodeSizeProminence_IRCC_Act,*layoutNodeSizeProminence_PRP_Act, *layoutNodeSizeProminence_PP_Act; QAction *layoutNodeColorProminence_DC_Act, *layoutNodeColorProminence_DP_Act, *layoutNodeColorProminence_CC_Act, *layoutNodeColorProminence_SC_Act, *layoutNodeColorProminence_EC_Act, *layoutNodeColorProminence_PC_Act, *layoutNodeColorProminence_BC_Act, *layoutNodeColorProminence_IC_Act, *layoutNodeColorProminence_EVC_Act, *layoutNodeColorProminence_IRCC_Act,*layoutNodeColorProminence_PRP_Act, *layoutNodeColorProminence_PP_Act; QAction *strongColorationAct, *regularColorationAct; QAction *layoutFDP_Eades_Act, *layoutFDP_FR_Act; QAction *layoutFDP_KamadaKawai_Act; QAction *editRelationNextAct, *editRelationPreviousAct, *editRelationAddAct; QAction *editRelationRenameAct; enum { MaxRecentFiles = 5 }; QAction *recentFileActs[MaxRecentFiles]; QString fileName, previous_fileName, fileNameNoPath, progressMsg; QString settingsFilePath, settingsDir ; QStringList fortuneCookie; QStringList tempFileNameNoPath, tips, recentFiles; int statusBarDuration, progressCounter; int fileType, maxNodes; int fortuneCookiesCounter; //QString VERSION; bool markedNodesExist; bool inverseWeights, askedAboutWeights; float randomErdosEdgeProb; QString initFileCodec; QLabel *rightPanelNetworkTypeLCD ; QLabel *rightPanelEdgesLabel; QLabel *rightPanelClickedNodeHeaderLabel; QLabel *rightPanelNodesLCD; QLabel *rightPanelEdgesLCD; QLabel *rightPanelDensityLCD; QLabel *rightPanelClickedNodeLCD; QLabel *rightPanelClickedNodeInDegreeLCD; QLabel *rightPanelClickedNodeOutDegreeLCD; QLabel *rightPanelClickedNodeClucofLCD; QLabel *rightPanelSelectedNodesLCD; QLabel *rightPanelSelectedEdgesLCD; QLabel *rightPanelSelectedEdgesLabel; QLabel *rightPanelClickedEdgeNameLabel; QLabel *rightPanelClickedEdgeNameLCD; QLabel *rightPanelClickedEdgeWeightLabel; QLabel *rightPanelClickedEdgeWeightLCD; QLabel *rightPanelClickedEdgeReciprocalWeightLabel; QLabel *rightPanelClickedEdgeReciprocalWeightLCD; QDateTime actualDateTime, actualDate, actualTime; QTime eTime; //used to time algorithms. QNetworkAccessManager *http; QNetworkRequest request; QNetworkReply *reply; }; #endif socnetv-2.4/src/icon.rc0000775000175000017500000000006113014570727015347 0ustar dimitrisdimitrisIDI_ICON1 ICON DISCARDABLE "images/socnetv.ico" socnetv-2.4/src/texteditor.h0000775000175000017500000000546013245520654016445 0ustar dimitrisdimitris/**************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt texteditor.h ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 TEXTEDITOR_H #define TEXTEDITOR_H #include //#include class QAction; class QMenu; class QTextEdit; class TextEditor : public QMainWindow { Q_OBJECT public: TextEditor(const QString &fileName , QWidget *parent=0 , const bool &format=false); protected: void closeEvent(QCloseEvent *event); private slots: void newFile(); void open(); bool save(); bool saveAs(); void about(); void documentWasModified(); //protected: // bool canInsertFromMimeData(const QMimeData *source) const; // void insertFromMimeData(const QMimeData *source) ; private: void createActions(); void createMenus(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool maybeSave(); void loadFile(const QString &fileName); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); QString strippedName(const QString &fullFileName); QTextEdit *textEdit; QString curFile; bool formatHTML; QMenu *fileMenu; QMenu *editMenu; QMenu *helpMenu; QToolBar *fileToolBar; QToolBar *editToolBar; QAction *newAct; QAction *openAct; QAction *saveAct; QAction *saveAsAct; QAction *exitAct; QAction *cutAct; QAction *copyAct; QAction *pasteAct; QAction *aboutAct; QAction *aboutQtAct; }; #endif socnetv-2.4/src/dialogclusteringhierarchical.h0000664000175000017500000000474313245520654022150 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogclusteringhierarchical.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0, QString preselectMatrix = ""); ~DialogClusteringHierarchical(); public slots: void gatherData(); 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 socnetv-2.4/src/dialogdatasetselect.h0000775000175000017500000000406213245520654020254 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogdatasetselect.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0); ~DialogDataSetSelect(); public slots: void gatherData(); signals: void userChoices(QString); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogDataSetSelect *ui; QStringList datasets_list, datasets_filenames; }; #endif socnetv-2.4/src/graphicsedge.h0000775000175000017500000001161013245520654016671 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsedge.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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; 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 float &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(); void setSourceNodeSize(const int & size); void setTargetNodeSize(const int & size); void setMinimumOffsetFromNode(const int & offset); void removeRefs(); void setWeight( const float &w) ; float 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(); float 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 ; QString colorToPajek(); void highlight (const bool &flag); void setHighlighting (const bool &toggle); QPainterPath shape() const; void setClicked(const bool &toggle=true); public slots: void adjust (); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void mousePressEvent(QGraphicsSceneMouseEvent *event); private: GraphicsWidget *graphicsWidget; GraphicsNode *source, *target; QPainterPath m_path, *m_path_up, *m_path_down, *m_path_shape; QPointF sourcePoint, targetPoint; qreal m_arrowSize; qreal m_minOffsetFromNode; qreal m_offsetFromTargetNode, m_offsetFromSourceNode; Qt::PenStyle m_style; int m_state; GraphicsEdgeWeight* weightNumber; GraphicsEdgeLabel* edgeLabel; QString m_color, m_colorNegative, m_label; float m_weight; int tox1, tox2, toy1, toy2, size; int sourceOrigSize; int targetOrigSize; int m_edgeDirType; double rad, theta, theta1, theta2; qreal angle, line_length, line_dx, line_dy; bool m_Bezier, m_drawArrows, m_drawWeightNumber; bool m_drawLabel, m_hoverHighlighting; bool m_isClicked; }; #endif socnetv-2.4/src/graphicsedgeweight.h0000775000175000017500000000400213245520654020076 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsedgeweight.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 socnetv-2.4/src/parser.h0000775000175000017500000001632213245520654015545 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt parser.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 PARSER_H #define PARSER_H #include #include #include #include #include #include #include class QXmlStreamReader; class QXmlStreamAttributes; /** * @brief The Actor struct * Used in loadEdgeListWeighed and loadEdgeListSimple */ struct Actor { QString key; int value; }; /** * @brief The CompareActors class * Implements a min-priority queue * Used in loadEdgeListWeighed */ class CompareActors { public: bool operator()(Actor& t1, Actor& t2) { if (t1.value== t2.value) return t1.key > t2.key ; // qDebug () << t1.value << " > " << t2.value << "?" // << ( t1.value > t2.value ) ; return t1.value > t2.value; //minimum priority // Returns true if t2.value smaller than t1.value } }; /** * @brief The Parser class * Main class for network file parsing and loading * Supports GraphML, Pajek, Adjacency, Graphviz, UCINET, EdgeLists etc */ class Parser : public QObject { Q_OBJECT public: Parser(); ~Parser(); void load(const QString fn, const QString codec, const int iNS, const QString iNC, const QString iNSh, const QString iNNC, const int iNNS, const QString iNLC, const int iNLS , const QString iEC, const int w, const int h, const int format, const int sm_mode, const QString delim=QString::null); bool loadPajek(); bool loadAdjacency(); bool loadDot(); bool loadGraphML(); bool loadGML(); bool loadGW(); bool loadDL(); bool readDLKeywords(QStringList &strList, int &N, int &NM, int &NR, int &NC, bool &fullmatrixFormat, bool &edgelist1Format); bool loadEdgeListSimple(const QString &delimiter); bool loadEdgeListWeighed(const QString &delimiter); bool loadTwoModeSociomatrix(); void readDotProperties(QString str, float &, QString &label, QString &shape, QString &color, QString &fontName, QString &fontColor ); bool readGraphML(QXmlStreamReader &); void readGraphMLElementGraph(QXmlStreamReader &); void readGraphMLElementNode (QXmlStreamReader &); void endGraphMLElementNode (QXmlStreamReader &); void readGraphMLElementEdge (QXmlStreamAttributes &); void endGraphMLElementEdge (QXmlStreamReader &); void readGraphMLElementData (QXmlStreamReader &); void readGraphMLElementUnknown (QXmlStreamReader &); void readGraphMLElementKey (QXmlStreamAttributes &); bool xmlStreamHasAttribute( QXmlStreamAttributes &, QString ) const ; void readGraphMLElementDefaultValue(QXmlStreamReader &); void readGraphMLElementNodeGraphics (QXmlStreamReader &); void readGraphMLElementEdgeGraphics (QXmlStreamReader &); void createMissingNodeEdges(); bool isComment(QString str); void createRandomNodes(const int &fixedNum=1,const QString &label=QString::null, const int &newNodes=1); void loadFileError(const QString &errorMessage); signals: void addRelation( const QString & relName, const bool &changeRelation=false); void relationSet( int ); void createNode( const int &num, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &lColor, const int &lSize, const QPointF &p, const QString &shape, const bool &signalMW=false); void createNodeAtPosRandom(const bool &signalMW=false); void createNodeAtPosRandomWithLabel (const int &num, const QString &label, const bool &signalMW=false ); void edgeCreate (const int &source, const int &target, const float &weight, const QString &color, const int &edgeDirType, const bool &arrows, const bool &bezier, const QString &edgeLabel=QString::null, const bool &signalMW=false); void networkFileLoaded(int fileType, QString fileName, QString netName, int totalNodes, int totalLinks, bool edgeDirType, const QString &message=QString::null); void removeDummyNode (int); void finished(QString); protected: private: QHash nodeHash; QHash keyFor, keyName, keyType, keyDefaultValue ; QHash edgesMissingNodesHash; QStringList edgeMissingNodesList,edgeMissingNodesListData, relationsList; QMultiMap firstModeMultiMap, secondModeMultiMap; QXmlStreamReader *xml; QString fileName, userSelectedCodecName, networkName, initNodeColor; QString initEdgeColor, initNodeShape, initNodeNumberColor, initNodeLabelColor; QString initEdgeLabel, delimiter, errorMessage; QString nodeColor, edgeColor, edgeType, nodeShape, nodeLabel, edgeLabel; QString nodeNumberColor, nodeLabelColor; QString key_id, key_value, key_name, key_what, key_type; QString node_id, edge_id, edge_source, edge_target, edge_weight, edge_directed; int gwWidth, gwHeight; int totalLinks, totalNodes, fileFormat, two_sm_mode, edgeDirType; int initNodeSize, initNodeNumberSize, nodeNumberSize, initNodeLabelSize; int nodeLabelSize, source, target, nodeSize; float initEdgeWeight, edgeWeight, arrowSize; float bez_p1_x,bez_p1_y, bez_p2_x, bez_p2_y; bool missingNode; bool arrows, bezier, conv_OK; bool bool_key, bool_node, bool_edge, fileContainsNodeColors; bool fileContainsNodeCoords, fileContainsLinkColors; bool fileContainsLinkLabels; double randX, randY; }; #endif socnetv-2.4/src/graphicsnodenumber.h0000775000175000017500000000416613245520654020133 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsnodenumber.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 socnetv-2.4/src/dialograndsmallworld.cpp0000664000175000017500000001200513245520654021000 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialograndsmallworld.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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::gatherData ); 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); // } //gatherData(); } void DialogRandSmallWorld::gatherData() { qDebug() << "DialogRandSmallWorld::gatherData() " ; nodes = ui->nodesSpinBox->value(); bprob = ui->probDoubleSpinBox->value(); degree= ui->degreeSpinBox->value(); mode = (ui->directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui->diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "bprob " << bprob; qDebug() << "degree" << degree; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, degree, bprob, mode, diag); } socnetv-2.4/src/dialogsettings.h0000664000175000017500000001143313245520654017264 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogsettings.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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, QWidget *parent = 0 ); ~DialogSettings(); public slots: void getDataDir(); void getCanvasBgColor(); void getCanvasBgImage(); void getCanvasUpdateMode(const QString &text); void getCanvasIndexMethod(const QString &text); void validateSettings(); void getNodeColor(); void getNodeShape(); 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 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 QString, const long int); 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 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 ; Ui::DialogSettings *ui; QPixmap m_pixmap; //QString m_nodeShape; QColor m_bgColor, m_nodeColor, m_nodeNumberColor, m_nodeLabelColor; QColor m_edgeColor, m_edgeColorNegative,m_edgeColorZero, m_edgeWeightNumberColor; }; #endif socnetv-2.4/src/images/0000775000175000017500000000000013245521146015333 5ustar dimitrisdimitrissocnetv-2.4/src/images/node.png0000664000175000017500000000233313014570727016772 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/ellipse.png0000664000175000017500000000074013014570727017502 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/remove-old.png0000664000175000017500000000406113014570727020116 0ustar dimitrisdimitris‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs íÀ,tIMEØ#`ŸÐv±IDAT8¦Yø 6Q 6Q  6Q 6QcdgþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtijpU@åûûÿÿÿCgÿ ÒºõìàCgÿ§˜ÿÿÿðñõîðõáç웡ÿ¾šùùû©ºÌö÷ùûúüìïóùúüÏÖâûûüBfÿ˱üóéÿú÷ýñéþþù VmÞÜýáÝ÷Æ®üýöþùòýÝØ*óñÿõôúáÓÿýþùòüØÑûâÕÿýüöë÷Ì·ÿý5P‹7n2×½IEND®B`‚socnetv-2.4/src/images/net3.png0000664000175000017500000000321313014570727016714 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/gridlines.png0000664000175000017500000000203413014570727020023 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/socnetv.xpm0000664000175000017500000000362513014570727017553 0ustar dimitrisdimitris/* XPM */ static char * socnetv_xpm[] = { "32 32 49 1", " c None", ". c #B40B00", "+ c #B50A00", "@ c #AB0E00", "# c #CC0100", "$ c #B00C00", "% c #CE0100", "& c #D10000", "* c #941700", "= c #A61000", "- c #9D1300", "; c #A21200", "> c #B10C00", ", c #CD0100", "' c #000000", ") c #950F00", "! c #CF0000", "~ c #CE0000", "{ c #A71000", "] c #B30B00", "^ c #D30000", "/ c #4A1100", "( c #850D00", "_ c #9D0C00", ": c #761200", "< c #7C0F00", "[ c #8D1000", "} c #B60A00", "| c #A40C00", "1 c #A20C00", "2 c #A11200", "3 c #D00000", "4 c #C20400", "5 c #5F1400", "6 c #4B1400", "7 c #6C1100", "8 c #671500", "9 c #D20000", "0 c #671300", "a c #8D0E00", "b c #9D1400", "c c #CD0000", "d c #930F00", "e c #A60C00", "f c #3A1500", "g c #CB0200", "h c #9D1500", "i c #B20B00", "j c #B20C00", " ", " .+ ", " @##$ ", " %&* ", " ", " = -; ", " >,& ' )!~{ ", " ]#^/ (!~= ", " _: ' <[ ", " ", " ", " ", " ' ", " ' ", " ", " }| 1} ", "}#! !#}", "234 432", " ", " ' ", " ", " ", " ", " ", " 56 78 ", " ;&90 a3&b ", " >#cd ' ' e,~{ ", " $@ @= ", " f ", " g9h ", " =,#i ", " j+ "}; socnetv-2.4/src/images/delete.png0000664000175000017500000000147713014570727017317 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/socnetv.icns0000664000175000017500000055236113014570727017711 0ustar dimitrisdimitrisicnsÔñ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`‚socnetv-2.4/src/images/home.png0000664000175000017500000000331013014570727016771 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/saveas.png0000664000175000017500000000216113014570727017326 0ustar dimitrisdimitris‰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’˜ 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òé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`‚socnetv-2.4/src/images/degreematrix.png0000664000175000017500000000120113041416261020506 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/webcrawler.png0000664000175000017500000001005213014570727020177 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/subgraphline.png0000664000175000017500000000021513041416261020515 0ustar dimitrisdimitris‰PNG  IHDR szzôTIDATX…í“I Äüÿ§ëµŠJ7ÔCsÒ¡jkð¢r¸¨%"ÂËÎ0Ü(Þu¼iá¹s:\Æ{ß"]öšLdzT¥;¥ì<¥À#¸H7@5ìIEND®B`‚socnetv-2.4/src/images/cocitation.png0000664000175000017500000000035413041416261020172 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/save.png0000664000175000017500000000224313014570727017003 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/net1.png0000664000175000017500000000261313014570727016715 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/back.png0000664000175000017500000000313113014570727016742 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/adjacencyplot.png0000664000175000017500000000025413041416261020655 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/net2.png0000664000175000017500000000102513014570727016712 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/socnetv-32px.png0000664000175000017500000000271213014570727020321 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/webcrawler2.png0000664000175000017500000000204313245330127020254 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/help-hint.png0000664000175000017500000000315113014570727017734 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/nodelabelcolor.png0000664000175000017500000000124213014570727021027 0ustar dimitrisdimitris‰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ËâœÁÃßò=òx¹*€íÒ O9òãJÅž¹žrT€ןPãЇ®˜ºWÀ– t#Y'ÀCm‡"ó¼l ´Ø@´ýEl­W¶¡Æ„=HF›qþ ±b–ìø-–y¯@bX*†¹&=+ö0æB@m„×èIEND®B`‚socnetv-2.4/src/images/prevrelation.png0000664000175000017500000000150213014570727020554 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/edgeweight.png0000664000175000017500000000116713014570727020165 0ustar dimitrisdimitris‰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Ô@{<Äø1_ãª.ÜÒ¦5ÞÂ#^°÷Ý·$Àgkó7Œp‚wŒ1,ñrREõÇ)eš¦àIEND®B`‚socnetv-2.4/src/images/similarity.png0000664000175000017500000000362213041416261020225 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/nodecolor.png0000664000175000017500000000403013014570727020025 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/subgraphstar.png0000664000175000017500000000027313041416261020543 0ustar dimitrisdimitris‰PNG  IHDR szzô‚IDATX…íÔÁ À0Pÿÿ§Ý­¸Ù™sQð´I^ÁÖ /Ý^î¡»·pb Ѿ& i € @–Š@® N3ÈÃßfª€cðsæëã¶«ÿÿAÓNs2SY*ê E»§i `RýJ•ÂÙ ®@ !ºÃÂáæî6Øv@í³IEND®B`‚socnetv-2.4/src/images/zoomout.png0000664000175000017500000000315213014570727017561 0ustar dimitrisdimitris‰PNG  IHDR szzô1IDATxÅ×YP[×ðóÎd¦õ´ÓiÇÓ&Åi_’8ñdRwúÏÄxœ´^@€±À, °d(¶Å5°0Á`×ì²b LjŒ ¶c0Â$bAhG,h_¤{%Ýûõj&žéCû€L•ßÌÎ9ßË÷Ý{^îEÛaX5 ÿE:;ƒ¶'„ÆN§óˆÕfés»F—Ã6«L[ƒ^תÖh ÊÄÄ8Úqf“ißææšN"ƒ‹åõ–òškEm›õ«[<ëå« Ö‚¶u|\ÓÓ’YÑÔÔ¯ÐNZ[_;©ÓkàByÓFSûÑ‰Õæ‹Å0[\„ÙŽÅæ »©wõ\qµI †‘Ñáýh'h´šƒr… rJ[tšMlM»…tf¯VoÆÕ«LeÆŒ©õ&\«7a†EµÕ˜^Pmxø°zz»ÿp›ËA!‹EH¦\zC!›‡ lžnɈiä÷’rͳ ÚĤ>­³ø$z*Z³O¢ÞÂ¥ËëÞŪ{iNc×ä•Ô®ßåqÕèuÌÌH*[x=ö>F.^vHçtî Å6ºbÆŸj­¾A½Ý?ŒÎæT›}O^nàÏçõžqÉŠSÊ\Xj»ÓàñºZŽB2%âIżÙQ¹]òâ¥cdZçýN±‰wªm>žÎå¿mðZW½T<ŽÆé¿óÒŒw,±ïfun¡hÅ={þJ³V¶0'D¡{1ºçÙ÷Cðinßol«_ tõN1Ž àš  Š©\ü!—¨”z*5u%“‹ïtÂ%¾P49(C?ãÞë Ä”½èÉo×ÔÞµ–ª"öþyuÿá$ùþ(º<2ê”,ò]ÜS5ÙÛ{ÿdÀš”&¼whÞÑ›|U0ÐÖÖ ŒÔ·Ðvõ=艾ÕÖî9Å–ð¿ì4²¯ ¬¬€Œ=}.9œÝôõ!Vãí(*‡˜ œÏ¨Dmæünß± ù±ÆSÄZþ•ò•¤;¿ðääfG íúæþ½O[8uŠ_ܳVU?iËP`D‰ ÐpȦöÌWÁ¨s OE’ùœ%­~Ø\ɨ—ödŸe#+mÚ®ªkoò;x@»"è/ï_¯½)¶gö˜ñƒßºüû¹ïz=Äo¨u÷kÄUäñÜ¥õœlÚjsj RSœ(Tcc#âzÞ3åˆÌñô›9Ç¥f<ê™7ðñ¨—x_ˆ{D8±[„“¿®Ô9"XÆŸ ê±3æ¶3ªµ´¸\Š—‹*ÔÔÒtT&—Aëcã÷7F,ezÏqj€È`£qŒx[ì#)ñ»¦|ÄÏ'pò·o`¯ |>¬ö²Ë;Vú’NÇC|í-Z\4 Y{û×¢bv«>·~ãæ¤9äòúÜø ‚jü‹Y?üdÆGþTê#~¯ôGi‹Àuûü…bWṯô:Jþþ%úàýwßÈÏ?kÎ(¨TtlÕqf\L¡—8<ë#">ò½µùÎ:Aþq €®ò@wÜÅ).g[i±Çæ%øô¯%;‡…2™é?KKKZ8‘‰±Ø]#¯ è€%uCЇ ©Î÷ɉê[Ý¢QV6“8zü‹¯šïˆ”´Ó((66ö¨;!12òJô¹¥u‹… ¹¯l0™ øâ¯QÚ®n>457Z%ž»‹F;Žv\ÜɘcbODÓŽUQ÷\{œý¢4·4s•ËJ+—ËFfú}ôÿB½Þÿºçóù2 Œ<!ÿYY'hG?ˆOˆEaSXPxK¥RÁÆætvw’¬¦…KJJ º^w=@R$Á›õ˜tŠŽÂ‰Åb¥ …B°ÙmððÛ~òoEù¢$''£°¹üË*jraq¨ïBHMK®Aá’••…?êêé·Û Ož>ÒË%pä/‡wÇÐN °)**êÕjµ¤ry:ø÷ÉÌ3™Ã(\èt::pàÀ®†Æpºœ Àµšjˆ;I;˜•šŽÂ&//¯dzftz-ù ¿ÎÏU£p«¨¬°m™¶°IÑ~½®P8uuu!F&#R®õ÷ ¼{wÿMá¸ðD©©«Aÿéßݧe¾ºÉõIEND®B`‚socnetv-2.4/src/images/colorize.png0000664000175000017500000000350313014570727017673 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/distance.png0000664000175000017500000000252013014570727017635 0ustar dimitrisdimitris‰PNG  IHDR  «hsBIT|dˆ pHYsýýâ{ƒ“tEXtSoftwarewww.inkscape.org›î<ÍIDATH‰­–ylÔUÇ?óÛ ¨–ÔÔƒ–ž4XJ*)¤Aj (Ð &àb"$I­¹Œ¡4þ¡ÄhäPbšJ©„(½„B)çö ]ÄB(Aè¶Û=~ãÝÕ²¥ÕIæ÷fÞûÎõÞŒ¨*±hæL™SXȺÔT2|>ú.]¢©¢‚µª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`‚socnetv-2.4/src/images/clustering.png0000664000175000017500000000036213041416261020214 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/subgraphcycle.png0000664000175000017500000000033413041416261020667 0ustar dimitrisdimitris‰PNG  IHDR szzô£IDATX…í–AÀ ùÿ§í­±AZÀeñPouv0VÉ1Múcšl‰Gx‡Ä/ $¤C@ÖÁ‰¯R /¼D" …Jda¡uÖiޭĵÞú§QÛ8óT‘¬‹Åä3^3ÚÔàp—€Hý*²BÂê‰4 !±ÍØÀv1‚Ÿ£°ìMà—¿ŠÞKÏËx! ?B ¨œ+ $Øá·„Â/`+  à­IEND®B`‚socnetv-2.4/src/images/pdf.png0000664000175000017500000001006613014570727016620 0ustar dimitrisdimitris‰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òÞç\ñ ø|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`‚socnetv-2.4/src/images/scalefree.png0000664000175000017500000000320213014570727017772 0ustar dimitrisdimitris‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEß‘_¡¢&iTXtCommentCreated with GIMP on a Mac•ä_[ÝIDATXý—{PTeÆßÙà "R,—IC“ÆÉÉ£³©q uºK”ãÔdl,º8ƒ:“c&#éd¡]°Q K sR“Œ”²›KІcÚ&•IgÏÛ|«Û†— 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`‚socnetv-2.4/src/images/resize.png0000664000175000017500000000231113014570727017342 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/clique.png0000664000175000017500000000265213014570727017333 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/centrality.png0000664000175000017500000000031213041416261020206 0ustar dimitrisdimitris‰PNG  IHDR szzô‘IDATX…í–A €0 ó~Ç×÷SõÔÒš¶&"» YŒÎ &T$‚Ké ?rj…–à ‰Ppxw¥Á) øÿàÞ¿ôÚoynÉðÎiÉ)µ²ÂW ï¦àF‰-cwsZe=€|Õpó2%aè71¨SðV¨K¨&$j>'A;–«E„– JC@I áMB&Sp«PG²›Hé7IEND®B`‚socnetv-2.4/src/images/nodelabelsize.png0000664000175000017500000000152513014570727020667 0ustar dimitrisdimitris‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà .[m¿tEXtCommentCreated with GIMPW½IDATXÃí—ËkSQÆic­6¾A V+Z|TA *QA.ü¬®Ü¨w‚¨*èÆhéB*>@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`‚socnetv-2.4/src/images/image.png0000664000175000017500000001300413014570727017124 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/svg.png0000664000175000017500000001030313014570727016640 0ustar dimitrisdimitris‰PNG  IHDR@@ªiqÞŠIDATxì•MjA…ƒ!q^å&7É"§ 8ëà/0ä&›€AKýX²eÙR,âÑÿèG£8•zE?ShÀy6<º[ÓÓ]ß«îÒ‹ççÑçùÙR½Tm«^o˜¶Cì[kÃïìì¼ÕvWõ^õaĘwÁ°® ¯TïdÃ0ËÏÕG,Òcéu»Òïõ¼dÐï{Ép0Èj8”Ñ4„²ëföF<cˆ1ãXÖ5àÁ¢C]Ô@GlƒÆPz<Y?´¡ñØ”@øÝ ï¼Â7XûaO¿Æ4& 6ÎEr4f<`x²ærpÞ2ÖA=ÀyØ$1MWfÖÁšFCjµš”ŠEÛߌI¼£ŒcÀßò5€àC‚{1£«ÐÚ'œ'“ÿæb]ì[*•¤ER­Ve@¼%%ÄÅ1Dcr3‹q“1µN Ú)¥ãÇÄÌcýèöVZ­–4¯¯¥R.KíìŒ5…§‘F0óŒ1 |åž&<ªì‚àÓ©LU3HÇh§Ôl&3 c—õv»-­fSzqì !‹0ÆŒƒfÑ #æåh€+Vž°û„ôЄkßäà'ü—fûêêʪ7ëI?Tw\íkÀiˆ‹hn˜³ßˆÀ8ÚÌ8 !fÙ›æsšo ´©Ù®×ë+8Ž6ÚnÈ6ö±µW®  i Æ&ôYr7ÀSÌ6E0 °Ì´Ÿ4./åüü\¢(’ߎ÷š°vuÜ÷4Ì 2`Ã<¾ó§±"æÜ Hü†Ì|6PÊÀÁ@¡‚_\\H|w'um‹Å¢eս谿ƒ` ëÀqÐrOo¬?™«®’gàý½Ћ‚¹€ÿ²·'ûûûR( Ù¶5ñ€ IÓ¢qx‡+뀃ý¼Áþšypg~°ªC¬â:ÀهР‚?>>–ƒƒ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`‚socnetv-2.4/src/images/symmetry-edge.png0000664000175000017500000000020513041416261020624 0ustar dimitrisdimitris‰PNG  IHDR szzôLIDATX…íÓA DQïéiŸ¨›ÔÿA›Ìž6{k @Ø_UÁÃn†²Ç¦Š.§âÝ4Hç¬þ‚» ;@YÔð—à‡yN¼“UIEND®B`‚socnetv-2.4/src/images/networkfile.png0000664000175000017500000000237213014570727020401 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/edgecolor.png0000664000175000017500000000056113014570727020011 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/selectall.png0000664000175000017500000000172113014570727020015 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/circle.png0000664000175000017500000000072513014570727017311 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/prestige.png0000664000175000017500000000171513014570727017672 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/spider.png0000664000175000017500000000375413014570727017343 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/qt.png0000664000175000017500000000471613014570727016500 0ustar dimitrisdimitris‰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¡ 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`‚socnetv-2.4/src/images/triangle.png0000664000175000017500000000047413014570727017656 0ustar dimitrisdimitris‰PNG  IHDRVÎŽWbKGDÿÿÿ ½§“ pHYsÄÄ•+tIMEß 8!œßÕtEXtCommentCreated with GIMPW¤IDAT8Ë­Ô±ƒ0 ÐO.}œ ·7€’1²‰Ï“IŒA™fø©h¸Û:~+Ý¿×H@!aèûž¥½ÇÕPUùÚ6¼÷"R,Ëf PŽÞÛŠT•£s$@œœ£IuhŽ"“ê¬1«Î“*§iVå4Mª’¦ZUÒT©j5EU­æRժɪZ5ÿTOUågž±X 7ù]Wˆ;ˆ1Ú_€”R‡»òAå*¦ÏãIEND®B`‚socnetv-2.4/src/images/hierarchical.png0000664000175000017500000000021613041416261020451 0ustar dimitrisdimitris‰PNG  IHDR szzôUIDATX…íÓ; DAîi<€…ëÊÆæ‘X@ã„OáG /¸É|D÷Âé Ì38ÕŒÔûœàÌû°ýåÊ':ôéMÏ1ueýIEND®B`‚socnetv-2.4/src/images/clucof.png0000664000175000017500000000065013041416261017310 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/random.png0000664000175000017500000001450613014570727017332 0ustar dimitrisdimitris‰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" BDFI_dÑF¯ƒQIDATxc Œ5um@] $¯ª§o„è멨˜˜šc¦&ê@ÚFfæ†ZšZ:*ÌŒ´Á @lc]lfŒŠ`PŒ,‚Ñ=t+;'7¯€°˜¸„¤¤¤„¸˜°/7';+Hš‘™•ƒ‹‡_HTJFNAIEEYQ^VZBD›“…‰ŸÅc:%×IEND®B`‚socnetv-2.4/src/images/remove.png0000664000175000017500000000406113014570727017342 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/nodeshape.png0000664000175000017500000000100413014570727020005 0ustar dimitrisdimitris‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEà :4‚dM‹tEXtCommentCreated with GIMPWlIDATXÃÕ—½JA…¿I¢„(VVBÄÂRAYAñ l-õ|; ­ÔÊÊÂF;-Å*±°µ|# 6ñçX¸Â²d×ÝÙÉ®.ÜfçÞ{ÎÜ;sfþó'Ø+¼*PÖ<¥ ±MŸÈBQ8HÐ*¼ìƒKÐ-‚Àl€€Sy8 ¸Î›€Bö’'xs ªymÈÿ'E•ÿÇ:6ù*‚`:¡\™ë‚  œ0WÏÀ‚’à0ffð} —v9'ðù¸þN†ü$¨']h»ŽÁwlV»çf÷²žù-Kà;AÅÕÞ?M ~éZ çRr^LêhÌ~è¥$ðn`ÄUûÅ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`‚socnetv-2.4/src/images/rotateleft.png0000664000175000017500000000272613014570727020224 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/eccentricity.png0000664000175000017500000000044013041416261020517 0ustar dimitrisdimitris‰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ï@ÊÁ­– 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`‚socnetv-2.4/src/images/rotateright.png0000664000175000017500000000272613014570727020407 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/symmetrize.png0000664000175000017500000000150413014570727020254 0ustar dimitrisdimitris‰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[« ÑhÐh4H$Äb±8uß÷I§ÓX­¹‹Åšñ‘Èç]÷bÛPJ½;???çû¾Ùìû8Àêò2¡ç}/"Qû“뺔ËeêõúYzbYV¼7x¸½²Rsáv§î8år™½½½C=›››C}Á€·Àù uê®ëR*•‚àPˆ|ÐÜ+\×Åó„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`‚socnetv-2.4/src/images/find.png0000664000175000017500000000237613014570727016774 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/disconnect.png0000664000175000017500000000161213014570727020175 0ustar dimitrisdimitris‰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 ËéóêjzU.ðßê”Ý^¡Ú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`‚socnetv-2.4/src/images/star.png0000664000175000017500000000127613014570727017023 0ustar dimitrisdimitris‰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Ä• ÁÎŽ&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`‚socnetv-2.4/src/images/box.png0000664000175000017500000000044113014570727016633 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/line.png0000664000175000017500000000134713014570727017000 0ustar dimitrisdimitris‰PNG  IHDR @ÈsBIT|dˆ pHYsþþO¢þtEXtSoftwarewww.inkscape.org›î<dIDATH‰µ–MKQ†Ÿ#¸.$µJ,~E²IJšE5Yˆ"]tÛm»Sè®®»*E …B AÿB°-Ö–‚()j)ÁDÐh£ƒH‹ xº0‰É$ׇa†÷òÌ{ï9gFT•Û¿_^ø|Œ¸\Ü;;ãt{›ýTŠ©­-]@U&ЋÇ9TåïÕŒDȃL¨ªyp_o679.‡–rzšlw7aÓnccü²‚–rx˜‡á£õö÷Ó\Iäñà2 –j…¦ÁkKKTíî’7 VÕóD‚ÅL†s+ÍÜ… ÞÞF;9C!æ“I åE’ ˜TUÄô‘`Ìïg¿³“ÇÍ͸ON8ÝÛã`}©LF?—¶Ç˜Sà¯<—õ† >`±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`‚socnetv-2.4/src/images/triadcensus.png0000664000175000017500000000220413041416261020356 0ustar dimitrisdimitris‰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º—²}Ov´{Ëð`~¯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`‚socnetv-2.4/src/images/saved.png0000664000175000017500000000111513014570727017144 0ustar dimitrisdimitris‰PNG  IHDR Ùs²bKGDÿ‡Ì¿ pHYs  šœtIME× +3§HÀžÞIDATHÇÍÕ?nÔPÇñÏ{¶“ÝM"$þH!¨¡¡àpƒpúPE! £¤ EHHá_ÂþK Í:YS¬ãl;8Ì“%?[óõofÞŒù×àþƒÞÊÙ]Gë’BoåÞí%¼¶eï/nó–]Ç/O8ðÙ|u«Ã—_Im¸ZîÒÙX޶§Ø‚ WíJ( óM%ÕŠ¢‰‰ƒjMd‚+ò〠"TÂN³ Q1Í~]AÔ;,ŒB(¯£ „j5*Ø*ù™9™Ì¾ÜX^…Eĺ‚éÛBxÖ¢B4_I5#ÿø]hVDÁK¡?gN*Märcc¹ÜA[‰ÂÀ!<-óÕƒ¬`É^[ûÞTlµN‹û[_Þرө[Ëx;H±íKuÆÛ,sÉeê½0îúi×>‰T*“JDLŒmJ›d ‹à½‚²âÑ9HÍ—Ã¥¯oàsëLJ¥õsÈ%-+_‡Öô¥B5z*@¬Z4ÌÎÛF@rJ‘t*`ÒœÄDp±à‚ĶÝz –Œ:õ¦-‡£õÚI çœw­æP”ù˜ÎÁ}Ÿlz7|»:Z› @(@| ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ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îÿÿÿÿÿÿÿÿÿƒÿûÛÿÃÿÿÿÿÿ÷ÿÿÿÿÿÿÿ¯ÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿøÿÿÿÿÿÿü?ÿÿÿsocnetv-2.4/src/images/socnetv-logo.png0000664000175000017500000000657413014570727020477 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/reset.png0000664000175000017500000001032013014570727017162 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/connect.png0000664000175000017500000000151313014570727017475 0ustar dimitrisdimitris‰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·³ˆ²þ“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`‚socnetv-2.4/src/images/open.png0000664000175000017500000000403113014570727017003 0ustar dimitrisdimitris‰PNG  IHDR szzôgAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<«IDATXíW[P“göÊÎìÌöb/¼ÙéÎìn½ÚÙ›µÎº;{°­Ìz±ÎÎ:³vTpuÛâÖ¶T»¥ mÁ‚QB€€€!’@Hs$!gr€B œ GµTxöû~0X‹}gžù/’üÏû>Ïû¾ß—]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`‚socnetv-2.4/src/images/erdos.png0000664000175000017500000000344013014570727017161 0ustar dimitrisdimitris‰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ÿ 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`‚socnetv-2.4/src/images/add.png0000664000175000017500000000406113014570727016575 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/circular.png0000664000175000017500000000410013014570727017643 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/texteditor.png0000664000175000017500000000367713014570727020254 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/zoomin.png0000664000175000017500000000316413014570727017363 0ustar dimitrisdimitris‰PNG  IHDR szzô;IDATxÚÅ×[LSwðóà4Û’e[f&nÓénêÌ– ·E§ó‚‚€P¡€-Šr \TÀ* ¢rÇFÊ´ÜÊpà -R(—J¹–z¥@Ëé¡¥—Óëo“=,Ù@Ö}’_þ¿üÏÃ÷÷?''9Y Õ¤ê_¯õöõ ‹±¤`ƒÁ°O‡¡uFƒ^=¯ŸN Í”J©•LH¥ß „ŽŽvdÙÍjµ[43SŠîÞ>8Ÿ™§Ë¼Q‚ÑrÊ0F~v)«‹K¥cüö6 _õu ï-kø¤Zí¯PH!)³p¦ð·§jØœt¨Ñ¢F'ª·€3Ž^Ä™<—BŸåµ¼îKîŽe —ʤ»ÅÃC}±X!ÓàSr-®R ¹µJ'Q|BI” Å¥ÊY«\1‹«DRLMŽ¿®zXÏšÚšOï2Ë–,t!¢Ñáâ¡HÊb)F&q™Xe‘L™'4x¯µ Mf¢”¨­[ªµöŽM[‡'M#ý2½ì\*cºœÅ”"oB(즳8úºV™X0fèíW˜:†§ð–qÔÚ4аaÇAÓúíÌ:'4HQÛ³ÑëË¥¹½{|¾—õxp¤ô^…£¼ü^ÀÕkK½ |kprE_‹XßÝ6jh*, ÃkÕfc”’ FBïŒèŠeóö{ÔÊ©ñ†>…‰ß9fê‹I/–ôñ—ÞÂ{éñ¼é 쎮kdµjëy#G¨ÆË0€ëV€tHŒ¦sšOgqZˆ>Áf Éôöj9jë©,€ •üÎ KñøÉ£Ÿ˜UÚX–Œ‘÷R—< íñÅw“;oûÑO”–WcN»UcÞöƒŸˆØZ»ñ[%1\©Dk­}2 ç®ñKJŠ ŒLZƒ,§®öèÒ s½»òB•:ë:£È(Ûöwï=]Äô ¿]D+kšZ(Oò­Â=ÔÂ2­ûzòS™9É×Ý&ÝxU÷k œ‰ŽZ‹,Öý; ŠËÀÿÚ«ÊäÚ©«¹sä1›3ÓpÉ ñZe3•(¢ÅÎÛ’å.WréˆÑç&¥…ß쩊 ‡ðÈÐÕÈbÑèWVV²Yà›ÞZŸQ?͸-ÐGÔ¢VχFû–“ã3-Àûž¡9w¾'Ê ðNƒÙññÂ~£Ù¾­Zfö/i›cDg7òNž<>,U+¯Yp“õBÂé›î÷Î×£Ö=/,ޝZ,Îωзzgæ|y$#Þæãε û\ܱó±ÒrªàÅl)%‘.÷9v„¹¤ðQÑRX\xH44%ÏÔÏóšuél¥Ù‹`ûBPÑö.лP)˜~E‡ÕõÏâØØ;ös¥–¬ öx݉`ðó÷Yãsì(²d¬ fWrV‰ò,k:ïV‡žòÄh÷l±86·ãNBp½…œ°ªßæüDbwî6ØÓŠyówc“G½ç#o"%õ²iãú±±Q(9Ž&cksËzŒ¾Ù¹·ÏæÜ>ê„ 3ë4_k'Ì®tfû|YrFæí{x!,œþœ>CE"N…­ =1LŽÄ©Yå´º.7*€ÚkÒ°È=ˆ©rÐókºZ¨g¨ÎC^¿´ý¾,H¡Á¯Wÿ@¿(_ßæãA@>—ªŒNËÅÓò£“.ÏP¨pààyUu%æcÁ/Ðwµ²ìŽùyoööõŠ#žñU¢DNœvB(*.bŽJF±»wK!<"ìò_!ÿ±g³ÙC@à6s]™´tðò9´Éï¸/â6ñqñwÆÇÇazfªjª\ÔÓT±ÛÂI$Òë•‘Ëp8vW—  ònÝ„ ˆ;Q)Ô0>Ÿsú9xøG½+>!_Ø AÜæâ¥‹Ä®AÑß…p24$Ûmá‘‘‘HPPÐÖêšj0Ðôü¤]L}ö~àísq›„„ŽL.sIÆ$À®|àŠ¤Dp݈ìÚµku~A>Œàµòàz6Žù{ïŽ<†¸ÍÙ³gS„=BP(å®ßëëà\L´q·+´+s­ïìj·æä2À­áÕÕÕHxDøvñ°ˆ¿g(¯¸WŽüì+ùíüu }vîßßÈ?ØBB‡“IEND®B`‚socnetv-2.4/src/images/transposematrix.png0000664000175000017500000000116113041416261021276 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/nodenumbersize.png0000664000175000017500000000112313014570727021072 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/print.png0000664000175000017500000000330413014570727017200 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/cliquenew.png0000664000175000017500000000272013041416261020031 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/walk.png0000664000175000017500000000163713014570727017011 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/selectnone.png0000664000175000017500000000076613014570727020214 0ustar dimitrisdimitris‰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ùž@#ðÆ`ç° 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`‚socnetv-2.4/src/images/nodenumber.png0000664000175000017500000000112313014570727020177 0ustar dimitrisdimitris‰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Œqz½že™ûÌ縃•ÈqƘ·zOÌ!4 Mâ$öúý¾ë†§(Á?§ qø¾ïÅÊAÂ0.Á#¸ ¤8! ‚©ÀI (ÓXæñªŒ‚Tg³ËIEND®B`‚socnetv-2.4/src/images/nodelabel.png0000664000175000017500000000115413014570727017772 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/help.png0000664000175000017500000000421213014570727016773 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/filter.png0000664000175000017500000000431613014570727017335 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/dm.png0000664000175000017500000001032013014570727016440 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/plines.png0000664000175000017500000000214513014570727017340 0ustar dimitrisdimitris‰PNG  IHDR szzôgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<÷IDATxÚÄW]hEŸ™½Íå’6_mÏØ+,Õ$m©¡¥¾øRª/FúЗBQ‚‚­ BÌ‹PQ Š>Ø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`‚socnetv-2.4/src/images/connectivity.png0000664000175000017500000000025413041416261020553 0ustar dimitrisdimitris‰PNG  IHDR szzôsIDATX…c`ƒ œ’–þŒéjºbJA²y4 küƒ çSÛ$ƒ¡çlšÈµœVæ‘ èná¨å£–˱ä볜î…˨ÜØAWËFL^¹–Óº”Û€iÅš?ê€w6EÔ²œù0åP_–P¸IEND®B`‚socnetv-2.4/src/images/nodes.png0000664000175000017500000000205213014570727017153 0ustar dimitrisdimitris‰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ª/ŸÑ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`‚socnetv-2.4/src/images/view.png0000664000175000017500000001032013014570727017012 0ustar dimitrisdimitris‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEØ+ »*Â(tEXtCommentCreated with GIMPW+IDATX  ßïÿÿÿwp5›-§_IEND®B`‚socnetv-2.4/src/images/forward.png0000664000175000017500000000315713014570727017516 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/nodein.png0000664000175000017500000000273213014570727017324 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/net.png0000664000175000017500000000243613014570727016637 0ustar dimitrisdimitris‰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`‚socnetv-2.4/src/images/bugs.png0000664000175000017500000000334513014570727017011 0ustar dimitrisdimitris‰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ï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`‚socnetv-2.4/src/graphvertex.cpp0000775000175000017500000010033613245520654017142 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphvertex.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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" GraphVertex::GraphVertex(Graph* parent, const long &name, const int &val, const int &relation, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape): parentGraph (parent) { qDebug() << "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_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(1000); // m_outEdges.reserve(1000); // m_inEdges.reserve(1000); // m_neighborhoodList.reserve(1000); m_outEdgesCounter=0; m_inEdgesCounter=0; m_outDegree=0; m_inDegree=0; m_localDegree=0; m_Eccentricity=0; m_DC=0; m_SDC=0; m_DP=0; m_SDP=0; m_CC=0; m_SCC=0; m_BC=0; m_SBC=0; m_SC=0; m_SSC=0; m_IRCC=0; m_SIRCC=0; m_CLC=0; m_hasCLC=false; m_curRelation=relation; m_enabled = true; connect (this, SIGNAL (setEdgeVisibility ( int, int, int, bool) ), parent, SLOT (edgeVisibilitySet (int, int, int, bool)) ); } /** * @brief constructor with default values * @param name */ GraphVertex::GraphVertex(const long 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 << "settting 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 long &v2, const float &weight) { 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.insertMulti( v2, pair_i_fb(m_curRelation, pair_f_b(weight, true) ) ); } /** * @brief GraphVertex::setOutEdgeEnabled * @param target * @param status */ void GraphVertex::setOutEdgeEnabled (long 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; float weight =0; int relation = 0; while ( it1.hasNext()) { it1.next(); relation = it1.value().first; if ( relation == m_curRelation ) { linkTarget=it1.key(); if ( linkTarget == target ) { weight = it1.value().second.first; qDebug() << " *** vertex " << m_name << " connected to " << linkTarget << " relation " << relation << " weight " << weight << " status " << it1.value().second.second; it1.setValue(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 long int &v1, const float &weight) { qDebug() <<"GraphVertex::edgeAddFrom() - new inbound edge" << name() << " <- "<< v1 << " weight "<< weight << " relation " << m_curRelation; m_inEdges.insertMulti( v1, pair_i_fb (m_curRelation, pair_f_b(weight, true) ) ); } void GraphVertex::changeOutEdgeWeight(long int target, float 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.insertMulti( 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 (long 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(long 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(float m_threshold, bool overThreshold){ qDebug() << "GraphVertex::edgeFilterByWeight of vertex " << this->m_name; int target=0; float 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; float 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; float 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 long int */ long 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 long int */ long 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; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if (!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; float 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(); float 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.insertMulti(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 * 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(); float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; if (this->hasEdgeFrom (it1.key()) == 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 long int */ long 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; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; 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 long int */ long int GraphVertex::inEdgesConst() const { return m_inEdgesCounter; } /** * @brief Returns the degreeOut (the sum of all enabled outEdges weights) of this vertex * @return long int */ long int GraphVertex::degreeOut() { qDebug() << "GraphVertex::degreeOut()"; m_outDegree=0; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; m_outDegree += m_weight; } } ++it1; } return m_outDegree; } long int GraphVertex::outDegreeConst() { return m_outDegree; } /** * @brief Returns the degreeIn (the sum of all enabled inEdges weights) of this vertex * @return long int */ long int GraphVertex::degreeIn() { qDebug() << "GraphVertex::degreeIn()"; m_inDegree=0; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; m_inDegree += m_weight; } } ++it1; } return m_inDegree; } long int GraphVertex::inDegreeConst() { return m_inDegree; } /** localDegree is the degreeOut + degreeIn minus the edges counted twice. */ long int GraphVertex::localDegree(){ long 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 */ float GraphVertex::hasEdgeTo(const long int &v2, const bool &allRelations){ float 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 long int &v1, const float &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 */ float GraphVertex::distance (const long int &v1) { float 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 long 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 long 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.insertMulti(clique.size(), clique); } /** * @brief GraphVertex::clearPs */ void GraphVertex::clearPs() { myPs.clear(); } void GraphVertex::appendToPs(long 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(); } socnetv-2.4/src/dialogdissimilarities.h0000664000175000017500000000425513245520654020626 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogdissimilarities.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0); ~DialogDissimilarities(); public slots: void gatherData(); 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 socnetv-2.4/src/graphicsedgelabel.h0000775000175000017500000000377213245520654017703 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsedgelabel.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 socnetv-2.4/src/graphicsedgeweight.cpp0000775000175000017500000000413713245520654020442 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsedgeweight.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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() { } socnetv-2.4/src/graphicsnodelabel.cpp0000775000175000017500000000440013245520654020244 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsnodelabel.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 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); } void GraphicsNodeLabel::setSize(const int &size) { prepareGeometryChange(); setFont( QFont ("Times", size, QFont::Black, false) ); //update(); } void GraphicsNodeLabel::removeRefs(){ source->deleteLabel(); } GraphicsNodeLabel::~GraphicsNodeLabel(){ } socnetv-2.4/src/graph.h0000775000175000017500000013134713245520654015357 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graph.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 //FYI: stack is a wrapper around in C++, see: www.cplusplus.com/reference/stl/stack #include #include #include "graphvertex.h" #include "matrix.h" #include "parser.h" #include "webcrawler.h" using namespace std; static const int EDGE_DIRECTED = 0; static const int EDGE_RECIPROCATED = 1; static const int EDGE_UNDIRECTED = 2; static const int FILE_GRAPHML = 1; // .GRAPHML .XML static const int FILE_PAJEK = 2; // .PAJ .NET static const int FILE_ADJACENCY = 3; // .ADJ .CSV .SM static const int FILE_GRAPHVIZ = 4; // .DOT static const int FILE_UCINET = 5; // .DL .DAT static const int FILE_GML = 6; // .GML static const int FILE_EDGELIST_WEIGHTED = 7; // .CSV, .TXT, .LIST, LST, WLST static const int FILE_EDGELIST_SIMPLE = 8; // .CSV, .TXT, .LIST, LST static const int FILE_TWOMODE = 9; // .2SM .AFF static const int FILE_UNRECOGNIZED =-1; // UNRECOGNIZED FILE FORMAT static const int GRAPH_CHANGED_NONE = 0; static const int GRAPH_CHANGED_MINOR_OPTIONS = 1; static const int GRAPH_CHANGED_VERTICES_METADATA = 2; static const int GRAPH_CHANGED_EDGES_METADATA = 3; static const int GRAPH_CHANGED_POSITIONS = 4; static const int GRAPH_CHANGED_VERTICES = 11; static const int GRAPH_CHANGED_EDGES = 12; static const int GRAPH_CHANGED_VERTICES_AND_EDGES = 13; static const int GRAPH_CHANGED_NEW = 14; static const int CLUSTERING_SINGLE_LINKAGE = 0; //"single-link" or minimum static const int CLUSTERING_COMPLETE_LINKAGE = 1; // "complete-link or maximum static const int CLUSTERING_AVERAGE_LINKAGE = 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; class QPointF; 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 QHash < int, pair_i_fb > H_edges; typedef QHash H_StrToBool; typedef QList L_int; typedef QVector V_int; typedef QVector V_str; struct ClickedEdge { int v1; int v2; int type; }; typedef QPair SelectedEdge; 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 } }; /** TODO & KNOWN BUGS: \todo Group edge editing: i.e. change weight or color. \todo Enrich Node properties dialog \todo Update app icons \todo - CHECK weighted networks results (IRCC and distance matrix with other combinations) \todo - CHECK graphWeighted corner case results, when !graphModified. \todo - CHECK graphConnectedness() algorithm implementation (m_vertexPairsUnilaterallyConnected) \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 Pajek files with no ic / label in nodes 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 \bug Fruchterman-Reingold model fixes some nodes to (1,1) breaking the layout 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 &num, const int &size, const QString &nodeColor, const QString &numColor, const int &numSize, const QString &label, const QString &lColor, const int &labelSize, const QPointF &p, const QString &nodeShape, const bool &signalMW );//Main vertex creation call void graphFileLoaded(const int &fileType, const QString &fName=QString::null, const QString &netName=QString::null, const int &totalNodes=0, const int &totalLinks=0, const bool &undirected=false, const QString &message=QString::null); 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 float &weight, const QString &color , const int &type=0, const bool &drawArrows=true, const bool &bezier=false, const QString &label=QString::null, 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; float 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 (float, bool); void edgeFilterByRelation(int relation, bool status); void edgeFilterUnilateral(const bool &toggle); void webCrawl(const QString &urlSeed, const QStringList &urlPatternsIncluded, const QStringList &urlPatternsExcluded, const QStringList &linkClasses, const int &maxNodes, const int &maxLinksPerPage, const bool &extLinks, const bool &intLinks, const bool &selfLinks, 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 signalGraphModified(const int &graphStatus, const bool &undirected, const int &vertices, const int &edges, const float &density); void signalGraphLoaded (const int &fileType, const QString &fileName=QString::null, const QString &netName=QString::null, const int &totalNodes=0, const int &totalLinks=0, const QString &message=QString::null ); void signalGraphSaved(const int &status); void statusMessage (const QString &message); void signalDatasetDescription(QString); void signalNodeClickedInfo(const int &number=0, const QPointF &p=QPointF(), const QString &label=QString::null, const int &inDegree=0, const int &outDegree=0, const float &clc=0); void signalEdgeClicked (const int &v1=0, const int &v2=0, const float &weight=0, const int &type=0, 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); /** Signals to GraphicsWidget */ void signalDrawNode( const int &num, const int &size, const QString &nodeShape, const QString &nodeColor, const QString &numberColor, const int &numSize, const int &numDistance, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance, const QPointF &p ); //signal to GW to erase a node void signalRemoveNode (long int ); //signal GW to draw an edge void signalDrawEdge ( const int &v1, const int &v2, const float &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 long int &v1, const long int &v2, const bool &removeOpposite); void setEdgeVisibility (int, int, int, bool); // emitted from each GraphVertex void setVertexVisibility(long int, bool); //notifies GW to disable a node void setNodePos(const int &, const qreal &, const qreal &); void setNodeSize(const long int &v, const int &size); void setNodeShape(const long int v, const QString &shape); void setNodeColor(const long int v, const QString &color); void setNodeLabel(long int, QString); void setNodeNumberSize(const long int &, const int &); void setNodeNumberDistance(const long int &, const int &); void setNodeLabelSize(const long int &, const int &); void setNodeLabelColor(const long int &, const QString &color); void setNodeLabelDistance(const long int &, const int &); void setEdgeWeight (const long int &v1, const long int &v2, const float &weight); void signalEdgeType(const long int &v1, const long int &v2, const int &type); void setEdgeColor(const long int &v1, const long int &v2, const QString &color); void setEdgeLabel (const long int &v1, const long 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: /* INIT AND CLEAR*/ Graph(); void clear(const QString &reason=""); ~Graph(); //destroy object void setSocNetV_Version (QString ver) { VERSION = ver; } /*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::null); 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); bool vertexIsolated(const long int &v1) const; int vertexExists(const long int &v1 ); int vertexExists(const QString &label); void vertexRemove (const long int &v1); void vertexSizeInit (const long int); void vertexSizeSet(const long int &v, const int &newsize ); void vertexSizeAllSet(const int newsize); int vertexSize(const long int &v); void vertexShapeInit (const QString); void vertexShapeSet(const int v, const QString shape); void vertexShapeAllSet(const QString shape); QString vertexShape(const int &v); void vertexColorInit (const QString &color); void vertexColorSet(const long &v, const QString &color); void vertexColorAllSet(const QString &color); QColor vertexColor(const long int &v); void vertexNumberColorInit ( QString color); void vertexNumberSizeInit (const int &size); void vertexNumberSizeSet(const long int &v, const int &newsize ); void vertexNumberSizeSetAll (const int &size); void vertexNumberDistanceInit (const int &distance); void vertexNumberDistanceSet(const long int &v, const int &newDistance ); void vertexNumberDistanceSetAll (const int &newDistance); void vertexLabelsVisibilitySet(bool toggle); void vertexLabelSizeInit(int newSize); void vertexLabelSizeSet(const long int &v, const int &newsize ); void vertexLabelSizeAllSet (const int &); void vertexLabelColorInit(QString color); void vertexLabelSet(int v, QString label); void vertexLabelColorSet(int v1, QString color); void vertexLabelColorAllSet(const QString &color); QString vertexLabel(const long int &v1); void vertexLabelDistanceInit (const int &distance); void vertexLabelDistanceSet(const long 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); 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); 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 ); /* EDGES */ int edgesEnabled(); ClickedEdge edgeClicked(); float edgeExists(const long &v1, const long &v2, const bool &checkReciprocal=false); void edgeRemove (const long int &v1, const long int &v2, const bool &removeOpposite=false); bool edgeSymmetric(const long &v1, const long &v2); void edgeTypeSet(const long int &v1, const long int &v2, const float &w, const int &dirType=EDGE_DIRECTED); void edgeWeightSet (const long int &v1, const long int &v2, const float &w, const bool &undirected=false); float edgeWeight(const long int &v1, const long int &v2) const; void edgeWeightNumbersVisibilitySet (const bool &toggle); void edgeLabelSet(const long int &v1, const long int &v2, const QString &label); QString edgeLabel (const long int &v1, const long int &v2) const; void edgeLabelsVisibilitySet (const bool &toggle); void edgeColorInit(const QString &); void edgeColorSet(const long int &v1, const long int &v2, const QString &color); QString edgeColor (const long int &v1, const long int &v2); bool edgeColorAllSet(const QString &color, const int &threshold=RAND_MAX); //GRAPH methods void graphModifiedSet(const int &graphNewStatus, const bool&signalMW=true); bool graphModified() 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(); float graphDensity(); bool graphWeighted(); float graphReciprocity(); bool graphSymmetric(); void graphSymmetrize(); void graphSymmetrizeStrongTies(const bool &allRelations=false); void graphCocitation(); void graphUndirectedSet(const bool &toggle, const bool &signalMW=true); bool graphUndirected(); 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 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 = "Adjancency", 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 graphConnectedness(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, const bool inverseWeights); float graphDistanceGeodesicAverage(const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void graphDistanceGeodesicCompute(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) ; 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); /* REACHABILTY 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); float 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); float clusteringCoefficientLocal(const long int &v1); float 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"); /* CRAWLER */ void webCrawlTerminateThreads (QString reason); /**RANDOM NETWORKS*/ void randomizeThings(); void randomNetErdosCreate ( const int &N, const QString &model, const int &m, const float &p, const QString &mode, const bool &diag); 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 randomNetScaleFreeCreate (const int &N, const int &power, const int &m0, const int &m, const float &alpha, const QString &mode); void randomNetSmallWorldCreate(const int &N, const int °ree, const double &beta, const QString &mode); 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; Parser *file_parser; //file loader threaded class. WebCrawler_Parser *wc_parser; WebCrawler_Spider *wc_spider; /** private member functions */ void vertexAdd ( const int &v1, const int &val, 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 ); void edgeAdd (const int &v1, const int &v2, const float &weight, const int &type, const QString &label, const QString &color ); /** methods used by graphDistanceGeodesicCompute() */ 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( float C, GraphVertex *v, float &max, float &min, int &maxNode, int &minNode ) ; void resolveClasses (float C, H_StrToInt &discreteClasses, int &classes); void resolveClasses ( float C, H_StrToInt &discreteClasses, int &classes, int name ); QList m_relationsList; QList m_graphFileFormatExportSupported; QList triadTypeFreqs; //stores triad type frequencies QList m_verticesList; QList m_verticesIsolatedList; QList m_verticesInfiniteEccentricity; QList m_verticesSelected; QSet m_verticesSet; QList m_selectedEdges; QHash influenceRanges, influenceDomains; QHash m_vertexPairsNotConnected; QHash m_vertexPairsUnilaterallyConnected; QMap 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 graphDistanceGeodesicCompute() */ H_StrToInt discreteDPs, discreteSDCs, discreteCCs, discreteBCs, discreteSCs; H_StrToInt discreteIRCCs, discreteECs, discreteEccentricities; H_StrToInt discretePCs, discreteICs, discretePRPs, discretePPs, discreteEVCs; int m_precision, m_fieldWidth, m_curRelation, m_fileFormat, m_vertexClicked; ClickedEdge m_clickedEdge; float edgeWeightTemp, edgeReverseWeightTemp; float meanSDC, varianceSDC; float meanSCC, varianceSCC; float meanIRCC, varianceIRCC; float meanSBC, varianceSBC; float meanSSC, varianceSSC; float meanEC, varianceEC; float meanSPC, varianceSPC; float meanIC, varianceIC; float meanEVC, varianceEVC; float meanSDP, varianceSDP; float meanPP, variancePP; float meanPRP, variancePRP; float minEccentricity, maxEccentricity; float minSDP, maxSDP, sumDP, sumSDP, groupDP; float minSDC, maxSDC, sumDC, sumSDC, groupDC; float minSCC, maxSCC, nomSCC, denomSCC, sumCC, sumSCC, groupCC, maxIndexCC; float minIRCC, maxIRCC, nomIRCC, denomIRCC, sumIRCC, groupIRCC; float minSBC, maxSBC, nomSBC, denomSBC, sumBC, sumSBC, groupSBC, maxIndexBC; float minSPC, maxSPC, nomSPC, denomSPC, t_sumIC, sumSPC, groupSPC, maxIndexPC; float minSSC, maxSSC, sumSC, sumSSC, groupSC, maxIndexSC; float minEC, maxEC, nomEC, denomEC, sumEC, groupEC, maxIndexEC; float minIC, maxIC, nomIC, denomIC, sumIC, maxIndexIC; float minEVC, maxEVC, nomEVC, denomEVC, sumEVC, sumSEVC, groupEVC; float minPRP, maxPRP, nomPRC, denomPRC, sumPC, t_sumPRP, sumPRP; float minPP, maxPP, nomPP, denomPP, sumPP, groupPP; float 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; float sizeOfComponent; /** General & initialisation variables */ int graphModifiedFlag; long int m_totalVertices, m_totalEdges, m_graphDiameter, initVertexSize; int initVertexLabelSize, initVertexNumberSize; int initVertexNumberDistance, initVertexLabelDistance; bool order; bool initEdgeWeightNumbers, initEdgeLabels; float m_graphAverageDistance, m_graphGeodesicsCount; float m_graphDensity; float m_graphReciprocityArc, m_graphReciprocityDyad; int m_graphReciprocityTiesReciprocated; int m_graphReciprocityTiesNonSymmetric; int m_graphReciprocityTiesTotal; int m_graphReciprocityPairsReciprocated; int m_graphReciprocityPairsTotal; int m_graphConnectedness; int outboundEdgesVert, inboundEdgesVert, reciprocalEdgesVert; //int timerId; int 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 calculatedGraphConnectedness; bool m_undirected, m_symmetric, m_isWeighted, m_graphDisconnected; int cliqueCensusRecursion; QString VERSION, fileName, m_graphName, initEdgeColor, initVertexColor, initVertexNumberColor, initVertexLabelColor, initVertexShape; QString htmlHead, htmlHeadLight, htmlEnd; QDateTime actualDateTime; }; #endif socnetv-2.4/src/dialograndregular.h0000664000175000017500000000451713245520654017737 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialograndregular.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://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 = 0); public slots: void checkErrors(const int &i); void gatherData(); 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 socnetv-2.4/src/dialogsimilaritypearson.h0000664000175000017500000000427513245520654021210 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogsimilaritypearson.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0); ~DialogSimilarityPearson(); public slots: void gatherData(); 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 socnetv-2.4/src/src.qrc0000775000175000017500000001107613245330127015371 0ustar dimitrisdimitris images/properties.png images/selectall.png images/selectnone.png images/new.png images/open.png images/save.png images/saved.png images/exit.png images/add.png images/remove.png images/print.png images/help.png images/color.png images/colorize.png images/find.png images/letters.png images/line.png images/node.png images/nodes.png images/sm.png images/dm.png images/socnetv.png images/socnetv-32px.png images/net.png images/net1.png images/net2.png images/net3.png images/box.png images/circle.png images/diamond.png images/triangle.png images/ellipse.png images/symmetry.png images/plines.png images/zoomin.png images/zoomout.png images/connect.png images/disconnect.png images/resize.png images/nodecolor.png images/view.png images/diameter.png images/distance.png images/back.png images/forward.png images/home.png images/sw.png images/erdos.png images/circular.png images/clique.png images/walk.png images/triad.png images/avdistance.png images/symmetrize.png images/nodeout.png images/nodein.png images/gridlines.png images/webcrawler.png images/prestige.png images/centrality.png images/eccentricity.png images/prevrelation.png images/addrelation.png images/nextrelation.png images/texteditor.png images/networkfile.png images/random.png images/filter.png images/import.png images/force.png images/scalefree.png images/rotateleft.png images/rotateright.png images/appsettings.png images/spider.png images/image.png images/pdf.png images/reset.png images/socnetv-logo.png images/star.png images/help-hint.png images/bugs.png images/edgeweight.png images/qt.png images/download.png images/nodelabel.png images/nodelabelsize.png images/nodenumber.png images/nodenumbercolor.png images/nodeshape.png images/nodelabelcolor.png images/nodenumbersize.png images/edgecolor.png images/petersengraph.png images/cliquenew.png images/edit-rename.png images/subgraphstar.png images/subgraphline.png images/subgraphcycle.png images/clustering.png images/adjacencyplot.png images/similarity.png images/distances.png images/connectivity.png images/hierarchical.png images/clucof.png images/symmetry-edge.png images/laplacian.png images/degreematrix.png images/invertmatrix.png images/transposematrix.png images/cocitation.png images/webcrawler2.png socnetv-2.4/src/dialogclusteringhierarchical.cpp0000664000175000017500000001054613245520654022501 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogclusteringhierarchical.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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; } void DialogClusteringHierarchical::gatherData(){ qDebug()<< "DialogClusteringHierarchical: gathering Data!..."; 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->gatherData(); this->accept(); } void DialogClusteringHierarchical::on_buttonBox_rejected() { this->reject(); } DialogClusteringHierarchical::~DialogClusteringHierarchical(){ matrixList.clear(); measureList.clear(); linkageList.clear(); } socnetv-2.4/src/graphicswidget.h0000775000175000017500000002027713245520654017261 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicswidget.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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); bool setMarkedNode(QString text); 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 int &num, const int &nodeSize, const QString &nodeShape, const QString &nodeColor, const QString &numberColor, const int &numberSize, const int &numberDistance, const QString &nodeLabel, const QString &labelColor, const int &labelSize, const int &labelDistance, const QPointF &p ); void removeNode(const long int &number); void setNodeVisibility(long int, bool ); //Called from Graph via MW void nodeClicked(GraphicsNode *); void moveNode(const int &num, const qreal &x, const qreal &y); //Called from Graph when creating random nets. bool setNodeSize(const long int &nodeNumber, const int &size=0); void setAllNodeSize(const int &size=0); bool setNodeShape(const long int &nodeNumber, const QString &shape); bool setNodeColor(const long int &, const QString &color); void setNodeNumberVisibility(const bool &toggle); bool setNodeNumberSize(const long int &, const int &size=0); bool setNodeNumberDistance(const long int &, const int &distance=0); void setNodeLabelsVisibility(const bool &toggle); bool setNodeLabelColor(const long int &number, const QString &color="green"); bool setNodeLabelSize(const long int &, const int &size=0); bool setNodeLabel(long int , QString ); bool setNodeLabelDistance(const long int &, const int &distance=0); void drawEdge(const int &source, const int &target, const float &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 long int &source, const long int &target, const bool &removeOpposite=false); void setEdgeVisibility (int relation, int, int, bool); bool setEdgeDirectionType(const long int &, const long int &, const int &dirType=false); bool setEdgeWeight(const long int &, const long int &, const float &); void setEdgeLabel(const long int &, const long int&, const QString &); void setEdgeColor(const long int &, const long int&, const QString &); void edgeClicked(GraphicsEdge *, const bool &openMenu=false); void setEdgeOffsetFromNode(const long int &source, const long 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, markedNodeOrigSize,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, markedNodeExist, clickedEdgeExists; bool m_nodeNumbersInside, m_nodeNumberVisibility, m_nodeLabelVisibility; bool m_edgeHighlighting; GraphicsNode *firstNode, *secondNode, *markedNode1; GraphicsNode *markedEdgeSource; GraphicsNode *markedEdgeTarget; GraphicsEdge *clickedEdge; }; #endif socnetv-2.4/src/dialograndsmallworld.h0000664000175000017500000000444513245520654020456 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialograndsmallworld.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 = 0); public slots: void checkErrors(); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); void modifyDegree(int value); signals: void userChoices( const int nodes, const int degree, const float prob, const QString mode, const bool diag); private: QString mode; int nodes, degree; float bprob; bool diag; Ui::DialogRandSmallWorld *ui; }; #endif socnetv-2.4/src/graphicswidget.cpp0000775000175000017500000014536413245520654017621 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicswidget.cpp description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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; markedNodeExist=false; //used in findNode() clickedEdge=0; // edgesHash.reserve(1000); // nodeHash.reserve(1000); 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) 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; markedNodeExist=false; 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 int &num, const int &nodeSize, const QString &nodeShape, const QString &nodeColor, const QString &numberColor, const int &numberSize, const int &numberDistance, const QString &nodeLabel, const QString &labelColor, const int &labelSize, const int &labelDistance, const QPointF &p ) { qDebug()<< "GW::drawNode() - Draw new node:" << num << " at:" << p.x() << ", "<< p.y() ; // Draw node GraphicsNode *jim= new GraphicsNode ( this, num, nodeSize, nodeColor, nodeShape, m_nodeNumberVisibility, m_nodeNumbersInside, numberColor, numberSize, numberDistance, m_nodeLabelVisibility, nodeLabel, labelColor, labelSize, labelDistance, m_edgeHighlighting, p ); // Add new node to a container to ease finding, edge creation etc nodeHash.insert(num, jim); } /** * @brief Draws an edge from source to target Node. * Used when we do not have references to nodes but only nodeNumbers: a) when we load a network file b) when the user clicks on the AddLink button on the MW. * @param source * @param target * @param weight * @param reciprocal * @param drawArrows * @param color * @param bezier */ void GraphicsWidget::drawEdge(const int &source, const int &target, const float &weight, const QString &label, const QString &color, const int &type, const bool &drawArrows, const bool &bezier, const bool &weightNumbers){ edgeName = createEdgeName(source, target); qDebug()<<"GW::drawEdge() - "<< edgeName << "weight:"<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::nodeClicked(GraphicsNode *node){ qDebug () << "GW::nodeClicked() - Emitting userClickedNode()"; if (clickedEdge) edgeClicked(0); emit userClickedNode(node->nodeNumber()); } /** * @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. Also, it highlights source and target nodes * @param edge */ void GraphicsWidget::edgeClicked(GraphicsEdge *edge, const bool &openMenu){ qDebug() <<"### GW::edgeClicked()"; if (edge) { if (clickedEdge) { if (clickedEdge != edge) { qDebug() <<"### GW::edgeClicked() - unmarking previously clicked edge"; clickedEdge->setClicked(false); qDebug() <<"### GW::edgeClicked() - marking newly clicked edge"; clickedEdge=edge; clickedEdge->setClicked(true); } else if (clickedEdge == edge && !openMenu){ clickedEdge->setClicked(false); } } else { qDebug() <<"### GW::edgeClicked() - no previously clicked edge"; qDebug() <<"### GW::edgeClicked() - setting new clicked edge"; clickedEdge=edge; edge->setClicked(true); } qDebug() <<"### GW::edgeClicked() - emitting userClickedEdge() to MW"; emit userClickedEdge(edge->sourceNode()->nodeNumber(), edge->targetNode()->nodeNumber(), openMenu); } else { if (clickedEdge) { qDebug() <<"### GW::edgeClicked() - with zero parameters. Clearing previously clicked edge"; clickedEdge->setClicked(false); } 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 * @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 long 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 :)" ; if ( nodeHash.value(number) == markedNode1 ) { qDebug() << "GW::removeNode() - node marked by the user. Unmarking then deleting."; setMarkedNode(""); } else { qDebug() << "GW::removeNode() - node not marked by the user. 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 long int &source, const long 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 == EDGE_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() == EDGE_RECIPROCATED ) { edgesHash.value(edgeName)->setDirectionType(EDGE_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){ long 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)" ; edgeClicked(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 long 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 long &nodeNumber, const QString &shape){ qDebug() << "GW::setNodeShape() : " << shape; nodeHash.value(nodeNumber) -> setShape(shape); return true; } /** * @brief GraphicsWidget::setNodeNumberVisibility * @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 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(long int nodeNumber, 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(long 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 long 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 GraphicsWidget::setAllNodeSize * @param size * @return */ void GraphicsWidget::setAllNodeSize(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 GraphicsWidget::setNodeNumberSize * @param number * @param size */ bool GraphicsWidget::setNodeNumberSize(const long int &number, const int &size){ qDebug () << " GraphicsWidget::setNodeNumberSize() node number: "<< number << " new number size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ qDebug() << "GW: setNodeNumberSize(): for "<< number << " to " << size ; nodeHash.value(number) ->setNumberSize(size) ; return true; } } qDebug() << "GW: setNodeSize(): cannot find node " << number; return false; } /** * @brief GraphicsWidget::setNodeNumberDistance * @param number * @param distance */ bool GraphicsWidget::setNodeNumberDistance( const long int &number, const int &distance ){ qDebug () << "GW::setNodeNumberDistance() node number: "<< number << " new number distance "<< distance; if ( nodeHash.contains (number) ) { if (distance>=0){ qDebug() << "GW::setNodeNumberDistance(): for "<< number << " to " << distance ; nodeHash.value(number) ->setNumberDistance(distance) ; return true; } } qDebug() << "GW::setNodeNumberSize(): cannot find node " << number; return false; } /** * @brief GraphicsWidget::setNodeLabelColor * @param number * @param color */ bool GraphicsWidget::setNodeLabelColor(const long 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 GraphicsWidget::setNodeLabelSize * @param number * @param size */ bool GraphicsWidget::setNodeLabelSize(const long 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 GraphicsWidget::setNodeLabelDistance * @param number * @param distance */ bool GraphicsWidget::setNodeLabelDistance( const long 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; } /* * Used by findNode. * Returns, if found, the node with label or number 'text' */ 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!"; markedNodeExist=true; return candidate; break; } } return markedNode1; //dummy return. We check markedNodeExist flag first... } /** Marks (by double-sizing and highlighting) or unmarks a node, given its number or label. Called by MW:slotEditNodeFind() */ bool GraphicsWidget::setMarkedNode(QString nodeText){ qDebug ("GW: setMarkedNode()"); if (markedNodeExist) { markedNode1->setSelected(false); //unselect it, so that it restores its color markedNode1->setSize(markedNodeOrigSize); //restore its size markedNodeExist=false; return true; } markedNode1 = hasNode (nodeText); if (!markedNodeExist) return false; markedNode1->setSelected(true); //select it, so that it changes color markedNodeOrigSize=markedNode1->size(); // save its original size markedNode1->setSize(2*markedNodeOrigSize-1); //now, make it larger return true; } /** * @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 long int &source, const long 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 GraphicsWidget::setEdgeColor * 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 long int &source, const long 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 long int &source, const long 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; } /** Changes/Sets the weight of an edge. Called from MW when the user changes the weight of an edge (right-clicking). */ bool GraphicsWidget::setEdgeWeight(const long int &source, const long int &target, const float &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; } 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); } } } /** * @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 long int &source, const long 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); } } } } void GraphicsWidget::setEdgeWeightNumbersVisibility (const bool &toggle){ qDebug()<< "GW::setEdgeWeightNumbersVisibility()" << toggle; foreach ( GraphicsEdge *m_edge, edgesHash) { m_edge->setWeightNumberVisibility(toggle); } } void GraphicsWidget::setEdgeLabelsVisibility (const bool &toggle){ qDebug()<< "GW::setEdgeLabelsVisibility()" << toggle << "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; } /** * Changes the visibility of an GraphicsView edge (number, label, edge, etc) */ 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"; } /** * Changes the visibility of all items of certain type (i.e. number, label, edge, etc) */ void GraphicsWidget::setAllItemsVisibility(int type, bool visible){ QList list = scene()->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { qDebug()<< "GW::setAllItemsVisibility. item type is " << (*item)->type(); if ( (*item)->type() == type){ if (visible) (*item)->show(); else (*item)->hide(); } } } void GraphicsWidget::addGuideCircle( 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 ( 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!"; node->setSelected(true); nodeClicked(node); 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); } void GraphicsWidget::mousePressEvent( QMouseEvent * e ) { QPointF p = mapToScene(e->pos()); // 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"; node->setSelected(true); nodeClicked(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(); emit userClickedNode(0); if ( e->button()==Qt::LeftButton ) { qDebug() << "GW::mousePressEvent() - Left click on an edge "; edgeClicked(edge); } else if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() - Right click on an edge." << "Emitting openEdgeContextMenu()"; edgeClicked(edge, true); } else { edgeClicked(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)"; edgeClicked(0); emit userClickOnEmptySpace(p); } } QGraphicsView::mousePressEvent(e); } /** * @brief GraphicsWidget::mouseReleaseEvent * @param e * 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 */ void GraphicsWidget::mouseReleaseEvent( QMouseEvent * e ) { 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(); } } /** 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); qDebug("GW: Mouse wheel event"); qDebug() << "GW: delta = " << e->delta(); if (ctrlKey) { float m_scale = e->delta() / qreal(600); qDebug("GW: m_scale = %f", m_scale); if ( m_scale > 0) zoomIn(1); else if ( m_scale < 0) zoomOut(1); else m_scale=1; } } /** * @brief GraphicsWidget::zoomOut * @param level * Called from MW magnifier button */ 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 Changes the zoom level of the scene. * Called from MW magnifier button * @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 GraphicsWidget::changeMatrixScale * @param value * Initiated from MW zoomSlider and rotateSlider widgets */ void GraphicsWidget::changeMatrixScale(int value) { transformationActive = true; 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; resetMatrix(); 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; resetMatrix(); 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; 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(); } socnetv-2.4/src/graphicsguide.cpp0000775000175000017500000000671513245520654017427 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsguide.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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(); } socnetv-2.4/src/graphicsnode.h0000775000175000017500000001220013245520654016706 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsnode.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 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); long 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); QString nodeShape() {return m_shape;} void setColor(QString str); void setColor(QColor color); QString color (); void addLabel(); GraphicsNodeLabel* label(); void deleteLabel(); void setLabelVisibility(const bool &toggle); void setLabelSize(const int &size); void setLabelText ( 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_numSize, m_labelSize, m_numberDistance, m_labelDistance; long int m_num; QString m_shape, 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 socnetv-2.4/src/dialogfilteredgesbyweight.cpp0000775000175000017500000000500113245520654022014 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogfilteredgesbyweight.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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(gatherData()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.overThresholdBt)-> setChecked(true); } void DialogFilterEdgesByWeight::gatherData(){ qDebug()<< "Dialog: gathering Data!..."; bool overThreshold=false; float my_threshold = static_cast ( (ui.weightThreshold)->value() ); if ( ui.overThresholdBt -> isChecked() ) { qDebug()<< "Dialog: We will filter edges weighted more than threshold: " << my_threshold; overThreshold = true; } else { qDebug()<< "Dialog: We will filter edges weighted less than threshold: " << my_threshold; overThreshold = false; } qDebug()<< "Dialog: emitting userChoices" ; emit userChoices( my_threshold, overThreshold ); } socnetv-2.4/src/dialogpreviewfile.cpp0000664000175000017500000001051213245520654020275 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogpreviewfile.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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(); } socnetv-2.4/src/graphicsnode.cpp0000775000175000017500000004473513245520654017263 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsnode.cpp - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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" #include GraphicsNode::GraphicsNode(GraphicsWidget* gw, const int &num, const int &size, const QString &color, const QString &shape, 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() - Node"<< num << "initializing..."; graphicsWidget->scene()->addItem(this); //Without this nodes don't appear on the screen... setFlags(ItemSendsGeometryChanges | ItemIsSelectable | ItemIsMovable ); //setCacheMode(QGraphicsItem::ItemCoordinateCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events setCacheMode(QGraphicsItem::NoCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events setAcceptHoverEvents(true); m_num=num; m_size=size; m_shape=shape; 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; setShape(m_shape); setPos(p); qDebug()<< "GraphicsNode::GraphicsNode() - Created at position:" << x()<<","<setTargetNodeSize(size); } foreach (GraphicsEdge *edge, outEdgeList) { qDebug("GraphicsNode: updating edges in outEdgeList"); edge->setSourceNodeSize(size); } setShape(m_shape); } /** Used by MainWindow::findNode() and GraphicsEdge::GraphicsEdge() */ int GraphicsNode::size() const{ return m_size; } /** Called every time the user needs to change the shape of an node. */ void GraphicsNode::setShape(const QString shape) { prepareGeometryChange(); m_shape=shape; qDebug()<< "GraphicsNode::setShape() - Node:" << nodeNumber() << "shape:" << m_shape << "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" ) { //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(); } m_path = path; update(); } /* * Returns the shape of the node as a path (an accurate outline of the item's shape) * Used by the collision algorithm in collidesWithItem() */ QPainterPath GraphicsNode::shape() const { //qDebug ("GraphicsNode: shape()"); return (m_path); } /* * Returns the bounding rectangle of the node * That is the rectangle where all painting will take place. */ QRectF GraphicsNode::boundingRect() const { qreal adjust = 5; return QRectF(-m_size -adjust , -m_size-adjust , 2*m_size+adjust , 2*m_size +adjust); } /** */ /** * @brief GraphicsNode::paint * 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 ); //if the node is being dragged around, darken it! if (option->state & QStyle::State_Selected) { //qDebug()<< " node : selected "; painter->setBrush(m_col.dark(150)); setZValue(ZValueNodeHighlighted); } else if (option->state & QStyle::State_MouseOver) { //qDebug()<< " node : mouse over"; painter->setBrush(m_col.dark(150)); setZValue(ZValueNodeHighlighted); } //else if (option->state & QStyle::State_Sunken) { //qDebug()<< " node : sunken "; //painter->setBrush(m_col_dark.dark(160)); //} else { //no, just paint it with the usual color. //qDebug()<< " node : nothing"; setZValue(ZValueNode); painter->setBrush(m_col); } painter->setPen(QPen(QColor("#222"), 0)); painter->drawPath (m_path); 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.66*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.66*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 GraphicsNode::itemChange * Called when the node moves or 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:{ if (ItemEnabledHasChanged) { return 1; } else{ return 0; } } case ItemVisibleHasChanged: { if (ItemVisibleHasChanged){ return 1; } else{ return 0; } } default: { break; } }; return QGraphicsItem::itemChange(change, value); } /** handles the events of a click on a node */ void 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) { if (m_edgeHighLighting) { foreach (GraphicsEdge *edge, inEdgeList) edge->highlight(true); foreach (GraphicsEdge *edge, outEdgeList) edge->highlight(true); } QGraphicsItem::hoverEnterEvent(event); } /** * @brief GraphicsNode::hoverLeaveEvent * Stops the connected edges highlighting * @param event */ void GraphicsNode::hoverLeaveEvent(QGraphicsSceneHoverEvent *event){ if (m_edgeHighLighting) { foreach (GraphicsEdge *edge, inEdgeList) edge->highlight(false); foreach (GraphicsEdge *edge, outEdgeList) edge->highlight(false); } 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 ( 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); } socnetv-2.4/src/dialogranderdosrenyi.cpp0000664000175000017500000001321313245520654021005 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialogranderdosrenyi.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 float 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::gatherData ); 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::gatherData() { qDebug() << "DialogRandErdosRenyi::gatherData() " ; nodes = ui.nodesSpinBox->value(); model = ( ui.gnpRadioButton->isChecked() ) ? "G(n,p)" : "G(n,M)"; if ( ui.gnpRadioButton->isChecked() ) { // eprob = ui.probDoubleSpinBox->value(); } else { edges = ui.edgesSpinBox->value(); } mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "model " << model; qDebug() << "eprob " << ui.probDoubleSpinBox->value(); qDebug() << "edges " << edges; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, model, edges, ui.probDoubleSpinBox->value(), mode, diag); } socnetv-2.4/src/dialognodeedit.h0000664000175000017500000000460313245520654017220 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt dialognodeedit.h - description ------------------- copyright : (C) 2005-2018 by Dimitris B. Kalamaras project site : http://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 #include "ui_dialognodeedit.h" class DialogNodeEdit : public QDialog { Q_OBJECT public: explicit DialogNodeEdit(QWidget *parent = 0, const QString &l = "", const int &s = 8, const QColor &c= QColor("red"), const QString &sh = "circle"); ~DialogNodeEdit(); public slots: void checkErrors (); void gatherData (); void selectColor(); signals: void userChoices( const QString, const int, const QString, const QColor, const QString); void nodeEditDialogError(QString); private: QColor nodeColor; QString nodeShape; QString nodeValue; QString nodeLabel; int nodeSize; QPixmap pixmap; Ui::DialogNodeEdit *ui; }; #endif socnetv-2.4/src/graphicsnodenumber.cpp0000775000175000017500000000444213245520654020463 0ustar dimitrisdimitris/*************************************************************************** SocNetV: Social Network Visualizer version: 2.4 Written in Qt graphicsnodenumber.cpp - description ------------------- copyright : (C) 2005-2018 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 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); } void GraphicsNodeNumber::setSize(const int size) { prepareGeometryChange(); setFont( QFont ("Times", size, QFont::Black, false) ); //update(); } void GraphicsNodeNumber::removeRefs(){ source->deleteNumber(); } GraphicsNodeNumber::~GraphicsNodeNumber(){ } socnetv-2.4/changelog.gz0000775000175000017500000005321413245331410015571 0ustar dimitrisdimitris‹³•Zchangelog•½[rÛh–.ú®Q ”§ËdI‰ºK]²$;Õ¶d¥d§«úDì „$¤I€€’é§=‡=‚=„3†ó~±GrÖúÖZÿ¤ÒYÑü×u¿þå/ôI’»rœ§Óä:kžËêKòK^/Òiþ-«’=¢Ÿé& ½ùÇÝ8{L‹‡ì}ùp²±ñKVÕyY$;ƒ½¤Ÿ¼ÉFÉÎQ/ÙÙ%gå$+ÒYv’l6U¹M³ÍþÊÿh‘×Ù3}š6‹ŠÞ}—ÎÒIÚ—>§y2M—å¢If4Òt°‘ЗEÒ<æµüÔ£gI¡{£_ÇeQ瓬Ê&IS&£,I“É’Ö“zY7ÙŒÆH’çGz#Éž²j™Ð§I:nʪNRúñÕ<­š|<ÍêWÉlÑÐYM—÷Å0g1¸O2¸pÌÎ8+šŠ6ËO²ºÉt®®\O‘Té„öÜ#°zʦ8¯‚WYó²‚zàæ|b”¬óyY5rf©¼ŽM…Ëî)Bð)á!QÆ`÷'Ù<7u’ÓÿÉÞi))F\ÔYÕ¯³© ÇßO^L²¯´–|6'ü™ó~ôú±|®úr¼º´hð§bPœ‘cü¦‹Ìªnð,:¯=/ÁÈe2)7á Ìh™€ò¼µPÙ–@œP11ø.’1h^Ý:#PÒ¢f´à§?Ýý¢oÕâåŒxÎlœ×Y/Q¨¼h’§tº©÷´´dÛ(C:ªË)­Q{ÍñÐü¤Îˆ¶33ã ¡éìÀøO“þôv“gø.-Júµ ÿ­/¤Ò†àºÆ¸´"b?Ì©ÆQ™etá`÷ÄÈæ  ,YØ,C‚DÝeÆ@|äA =<9"Å—„1ÓºLH ‰)Y‹y0˜¤ ѳq“?eÆp™¸º%×Yƈ¬«ÁY¸dì¥ÙÞ0é { u­\…ŒÝ‹ mÃè´/Y¥¾¤ª"Â=H>9œæã/¼ 4”IHÏwGR±÷|\ƒ.¬ÝjòâžIRÚˆxÔlu :¼’¦0)†G}Îò‡Ç—5¢Ë¦%ùœïâcIÅÏúXÚúáF„Y%›·tÕOr­›X/Á̦¶É¿ü3m cšÏiP¹¬´&|ú«2M ´y5Ï„ò8ˆ’–“m é>‰½AìI6 ˆŠ7±‚×øãts$„¬—3ÚÀº0' b’¼ fà¡ßqZ‘†¾@Sb‘„ó‹šA–%vœ¡A ØESê,5OK—·§WL‘F‰ðð|-€w¢“3a½™ÛémooËitÂUtbÉ#8Ȳ$»`jˆ¨ôºf»¹#¼L牒²OEv¼Ñ üLŠÈ¸JŸ§Y…Ýö',C•B1¦eE6ó‹ÊŠG¾—‰dA#mrýDÁGCÕYºúÕs‚¼{Z> œ)­…è ÁCwT@ðÏ‹ñtA%ܧ2ŸØ]¡«§xùóTåžÀÖ>fÖÅ$¥nÊ9ýJ+á/ªïû<H^!©NHÐ$3¢ é¬\¸ÒY>v¶ûCº¡nDTÏá"ø ù†™×ÅËzv^ËÕˆÊ!;1™LδÊT'Aºf‘vR7¼gC%% Šæ5)Áx=’ä pq!¼eú<»¿ÏÇ9‰K"|>õ\}bŒIj:×fu½ïÐ-~Ú¦AMÂÉÑÁN&,g«Q@sƒšõ.kl]w̰¼îu†)†äEÏçí!’ÎYZ<1ÛHGÝ— RŸ·C%a?7É[ámõ/9-å9'1˜„VR߉ÝLdÈÓ§4ŸB­õ›@&Ù¶!A|¶ ¡Y€‡o Ý#•„ßÖC$’6§ ö½è¯$]4e?ü¶¨›ÉÉôݬäÓžç_g)‰×5Ž‹—",‡TE&„ªüÖ$ãð¨£tüå¡"èŸöŒñÛXŽk1Ÿð›,§»ß ð„Ñ™¥pËtóS¾}zA僨 "Žê¹¨ì ŒO:+vAl¹²­ƒ`š¯ܨ`ùÚÀaœç5<4ïöp Ž +¯$:Ÿ²•å{0$ðN¾Ôáàļ Þã,ý"rÊ C …—.·ˆ® P1ê%¹0à"c^•~ ‡'„„àA²iV° hác?fã/£òk¬qäæÓÙåõÅÇD®×tXÒÓ)ýPå_™çöùÚÜÕL _»ŒJNáK À=Ñ‹‰ÝF&õ¤ÖñA«`pù]eF¶…àmH+4Á$OYcî_ßb×g¤ÇU9#GݦG)¾—Ïõ[–vÜ’êrœ—:3 OWƃ:”Ç&1?-TZ®ó™/°tÂ21¶ÀXç<±šiÖxYRI]VÍdŒÐ­fqZ–Á2³-‰°"eT>ÙÓFVðQEù"0Á9+,G¢ÕPdÞäU݈ø#šsÅ&áþø{f§Èaн'ú¼˜`Öž\ß ÷)d¤–µébV8Ÿÿ|Þóß_ßž\ŸñÝ.j·‡$Ú/vĺQc‘˜¾p3¥ëy›7dóeÑÿ–U%‰A½ßº 3Ÿ‘Q×ho…`0ô|QBs¾¨ðŠ£¥ãädmIÔ \L‡î#pÒÿÌ–æ_ù+z+¹}U'uAtˆ%äŽI,&,Åa Á¬7Yƒ"~NÐh ‚Ÿ²é<ùqÍGn·c÷\‰eFDÈDÄ*cËßîb6g„#‘ÆÑ"{‘~«*–±NQÍ<¸œÓ Éìÿ½lŒŒÏöro@±fL]…ýBC!Ò°2ÄÒü¢`ú U<<'BøQ‹ {•Ëd´xYø}þ5f¢BIu„‰µÜ ‘…rúdÆyŒ26KÜàD¾As¾æß߸áÆS’Œ'lýaoû$ù\±ÚôúÌ 'ˆZ†rb2¶pý°¿w’|Ó2ÊÊ#FQ¨{0ÔtÉO—hŠ>ÑMM$A 0<ÞîÑIÀ°rÚs7` X_Q«ƒ±A!%$+\b+CÛ h+¿cErGM+ãñǥɂÄ"r›B2Ää'D³‹uíž$WyÞ”ºõ–Ô÷bL'éô"ÈŠ§ x˜±ÚÕÅvZš~ÔÓ­aÜñºîI‰iÌ>ÁÚŠlZ0a8ËA ôL&‰Ú;é„ÍD½˜]±;ÔÓ²¬Ñ6ã½¼Æ,Ô¿å–ÂgÃꊲ`á¹$ºô«¬X8aŽn–©ËR,ßwÞ¦ÌçÒ6ílN *×lm- /Þ×Äo@PêrQ³­†5ÅF2ÀP,ÐjL­EßûaFêT쌨»1ÊUøˆœ´Á«Æ8;'¼R’>˜@a8S<¦-ŸñÈ<.iA%ìiÅ'‰Q±8(ëé”?ØÙc?Àwåx«W2O;ÛhÎï8üd¢ÌÞ1¢ÄMV€6á„Dn…’D`©à„z`Ÿ €æž•¹Z¸48Æ¥ùYæß¼crsJ$Ze×I™Õ¸túÌLÍϼHÛ€Qk6U®±èï7OÙ|‰©0P ÜŽËf`#1üþp@Èöq­ØeŸ¨\'4è`ßî‡0)ÁKrÚ1]Ÿ@r&(ŸœétYç*ë+éO2‚rHqsöNâ:¦@ÒOg§úa°qLoš“¯`°ÑÓcDlò @*΂õwš,]LyÃ5ý|%R¡bHØ?é|O'¿‘îKŠ*ñ„€Âyª fÅtQ€ÚÖÅ& u¯&;ƒÝ¤Ÿü×bšìùz9W™WTĵz¾L…a$§Lá¶.J§a€÷êÖÿÝ£ÍÒE‰ÞþªzÕ;q‘qI+¶8{Ä/×°½†öÆž!z,é,Ir>¸µÊ·ºlÛ• ¦|†n­]çÖRÛ›ùRäÒÔÆÂ~&tr†[5 3›:ÅA®k—f¸D ~¸] Õ™‚Œì «$ˆÿR 1½K›’gyÄÁŒ¾ p™/ΨD8G¡Z@Ìh:à)‰–ãÜ©ªaøoY`ñ zY_´‘´'¾uú‘ïG×Ú2Žªÿ†æ…•;ÓI:…)7´TãªêdØ/&vø¤!×!ë¦ZÐ)W€zIú&3¼Aòž@ð®·ß1ù¬œ¼ÛóbÅ›ò1r<@xœÑ¾äœÃã1›ùó%µpk1îá¦ä°Ò*¸Õ©è-+'F$æ@Uz?„( áa”™;Îq?}/ñÿ‚Q…D¹Vß%ø»D”SRÞ·H×Yœ]&¡áG¢¤J~ §·ž?Z±éBåW P¢É–Ö!ù¾~úxõÞ ô'É#ð޳2ÇvÒ;%ØÌ+Äîވݓ>Öw;€Ò¥dtŽÏÞєѻëÓ^òmå9~6§°À«Ô)#=#5{úR!à‚mó’M'0h/Œp±''%1lÞ{'ÖL²ÿC´µiÁø€Ì¥6KæÅ'?$§sÖŠfº´`â% è*)«JA¡ê° À??–S†ƒ‚W‚P8»(GÒạ\7øêrìá7›jÓáĦ|±×"ÂöŸÑŠsÀn•)o1EZ$}»‰Œ$㌰ãš¶éñ4ˆüðÛ¡ð‘ AXK2¬ÀzÞ#! ,N®_]»(üií¼ÌÅÙ¾­*:H}¨†ˆz5žˆõË+b}3[ Êæ€Ÿ“¹)@¹[¦&¥â¤ž²tíð„ŽW*©(!Ó@½·ZÀ3·1“çó²ÎÙ!ÙMZX{ í0®Ù¨C_ jM„…Ä—†ð`F–Z¬Òl-ÀÞ/ %” f<7«P2lì+%ŧ†L.þYvsËúÑèXd°EÞÀ°ÀPþÇøØ'pijO° HÛ†õÖ­ÏJ CÅá^G¼Ø;¡ÅŸyÝ}çHõ)H©Bëÿ¿ûÃÞv×ÉÍ4P.ÑS?ì;-„4•ì!…ÿ aß¿£•€E‡÷ÅÝjøZ½Fòí°¬É>¼áas”-IoúiÓàuÒ;Þà;$‘wAŒÛF[µ'áéÃÔ ¨’MªÀcAiAI±@'1Yw¦k˜ŒH¨8¼f)DGcäº=â_|aMø,ýŠgž[¬Ó#¼òt>ݼ½:׋HrwNI.þ¹Èé2³@‰msaÇR½p#€‡¹2Ö‚¸ÀÚÆY¨¿0ŽÃò*3$ Èã’\,ÆS"…©×¬{l¬"Th‚ßèþÇã´ ÆÊšñÀíNHŠÉ¬Xå&î×øYo§æ› 5>rM¥o‚« «êéÌFsŸÚå‡ï~ykvù6 ½`´é\ür&àõ¬q¶á€èêñäµ8È DâýûÁíÒÈ>‚ÊEåÐ¡Šªƒ‡ñ¸e &ú3 ¸z0“¾Ð–oEn²ÏÂôÌe$öøÓ è Îzº÷‚©‹]h^ÛY8ÿòHcÈ`bŸ² †”D†M¹H5µšöÝdiU³K«*'„êBå*‹ ¬xõZ’`/Á“bN舂SØÁöt\¶0¨mã’4š\± A¦yÕge!¹93û)Ý{{CPŸ9º1§Ó(9ÞÝBS§¹i¹[«Ü.¼FÆk•ƒ2åƒÃqZ3yT„ðɇ¤Þ†…¤: zªÌtî„]¥ºBÑ{ÉOél†_0çAK‘…Ó†WiG×ã„¥à՘ў…ó¤C@2‚1 DbÍRBƒÂ¦+-8«`ô1wÆ pk˳zåà}¬gxð/}îpWÐSº*‡_,WÏsÓö¿©ˆCüÄN@OƒSuç|ö˜–õcö4Xst‹Z—½¢åÂ5MÛR*tÙü+ÇÜÉ‹ðûê_1£# §­ü“äoZA½ˆ×­ðuUÿçþ¯wY5*k£m$R<#Ij)¶ŸU¥×ŸÆ™Ì}†¹Matñ9Uö”ÎæŸ3-ÅGG«º_Lƒ˜‘5H{§&o™‰±KÄYæÐ²ˆa³Uö£FÅõ“·9 ±™Àa›xçð˜}ÌJH°&~µ àê@“xµþ#`ø¨ÿcJ³›×:Àe K3Sæ ‡T?‚xéŒmÀ(ü:^T°‰¹µñ²VÎäŒUó®G¤T<÷>fÜþÎp$’ºp›í7 þãô/§ÿ㣚¯¿^Ûš—Á® ãšâPÍ3Œxrך°üómÖ¡µãä#­uòƒuêâ£ÀÔzáκäwä­g,’K£‹)¼©ÒY²м“Š€$V¹tòïßœ}&‡õ#Çã¸ä<Ï0C°¿+'W¨-)4&Ò[²JÄòIH1-4ïKÒp`™ƒA>Uà¤{a*ªÊh†JDðÙ ÷Ù3cSkŠ5˜û&Ÿ"”¦ÉØÕM4›ö«Æò.’œÅ–´;¬ÑzkYWðÜÉm¬èÉØÄÙ˜ˆtÕ*];º!«.Ç]Zðo¿˜êƒk,ÉÍyß®š^ƒA*°¸Uì­# ‰˜©«÷ RjOÌÄ_ñ¿Η#¾šOÈ,ß‘ˆ5üÖÙBÙÇ5ìö…dº,ø2<2Þ”{6"‡¦VNol,%Áù ?MGÙ´†¾²ÜK‰xÀÖ£\‰[%¼I˾ȑ¬H–8øà…—ÎŒ¯ä/§Õ¸^å*Ä‚2kïïaÐ(\ÞÂz‡§–Þ®šÿl²ay9âàk°*ô€F1ñÈLk.æSa¡ñBô75*<Ã-ˆÍ¿Û^M!E@Ì›gžÒš-O¹…B(Dhtš0(Ì'ãœÅ†/ËÞñ‹rI†›5±²8 Î ýÊÓ›ýûéRíöЋ¶­ªÑÙBV‘[‘WtŽ„^‘ÆÖ_*¿ºVxƒ%‚§¢Ycu„W« u&ü~”ÖPÞGb}7á"“X›õJ’|)’VšГSÆÓL úˆç÷#= ç”&¡2ðÒmD1ÀALßÊÜŠ0º_V\îSVâ{|Wf¤ ä\T5k _IGÍ8ÚÓX'òÌM‹yÉGW¤MKÝEà› )÷}JÐõ1ûÚ\LræbrÛ9òŠ Cª…ÉI” ìDó9³Õf‹ƒ#°Ë·Ÿ. z™LÕœ£ræYIÀZN%ðY µÌyŒ|¬¤ÁyCÒÊÎivߘ‹$éD£t%Àòž€Æz‰‡€×€—Àyo©Ì¢aaXv¦© ‘±2P"&“-I4`·DÃ+c𤄣0Ûm•=d5ãS´F5˜ÒÈ?¬<Ÿ…SÙ¨ì¾&iÈyج{âXÔaÌ{ž¨Y“ˆÕ˜YÓïÏüKAÆÊ¢-’ªïÏ-7y§çüR½ œ¥§Úîl¶(Dä•„&;Õ«çå|17YB¥6uèãL_-%!й¾r¤À)GL÷ÍJ.N Èj²¼÷)ÇÏç¬OFf8çhó+°˜lQ%!Àv&’œTáôUg.é N‘9 6ÍÀ0(6“õ±Vº 5Ì/FáÂí^2Šî ¶ô ]9a³bÏÛXr0“΋‘P7¶ܨ‚+ø¦ 9õO2…À™Sç7Y§t‰ç~ºôÃrˆÌ®*–¨5ù«–PNØ ‚hO¢k…F7k:bÓ›]%þ3ñ0^lÐ9Ê1“ÞHÝ9ÉfuÖØÖ™óLä'6Ô±å5SñSàÌí —ÞUéø ±¬IÃÖý‚©Œ›XAZ®þ;ež¶LÞIÒÁΈ‰]¹*¬é2\8£Å?b]? öö÷·÷4VÊ›öØQiËy_b{¿„Y ÷v·áZÒÅ¡]•Äy5,U>©I\çèŠ ê«p°s|||:dîIôc“×¥¸p&HPòíîÎÑþ¡OÏ,Ñ``’æ[+‹–ÛMRú5AÇ¢­Ø9€mp¶…K²gZj"›øÿ9‘01}ª s°¿³/©m*ZÇ ÷:ÇÚxÑÀ\£ão'g.jчáG²«w£kì;„L]AÇHXo›äy‰ˆÔÑŒ­¶þ0÷’ÛňÕþ‘OâC\+º ËÕÏv‡Ç{²aI\4‹ŠûF4ëv$¹CGuž)ùfÃ}3±_§, €!kÀ– ­vÑ’%å˜DÜ|m4ÛÍy¸»s¼×,»6²Ì ešÑ}Õ.˜šFÛt·¸)‘ìRAæmU5>— (¼¿™Ñݹ¦“Ž%Ó-<.Fz¶E˜E ð´Eól=]dõÖ‘´;L “·yó¡7 0<+)÷¼¿-–Àj–ÅH¬Ëª'Πûƒƒït7DâÔ„éë’åPÚ Â:£cUÃTBx”æÅ £iTŠÓ¡“µì„Œ-o$ G|Æòd‘‘Ot.1·ê&$–òbpògŽüX€"®xïƒKX5‹4S8ÿ©—åLóê©€Z7ަ¤ïïÈW*2X%Ò¤ÈgQñ”þד³t‰ö…:Fë¤fG_òG+”ÍJ"yÉ•ˆÇäÃŽ¦K ¦ëúÈbcö,cC"J$é­ÔeŠM, »*73kí#IbõˆTÜñig7Ä«nÓâ‹×:;7·7â"R§®BÚ¹9j’ÎéÛóîJ0¿Hìì«‡ÒØi©4é[©¶%ji}ºÜF¦QÄù»×«Î&.й&¹ììí ]Å¥À®äRÖ}Œá˜mjâ4&(#Ì.‰%ÇYŽŽƒKóÀณÁ¢¿Oûv÷h¸“œ"Ê]LØE°<]€ÏÐA…ˆ¬67¶½ûèuU·Ó£]‰‰Ø]¸»foîo'·¶.Ãy „«HúÑÏhüa‚d} Š·µàˆÞŠ5¦ušÙŒÇþ§cî’ä"Üq•™»,Ú·`·{x¸cÌÕ4&‘¼Úq’Ýâ£Ë ½tœ£Ý£(àǧևDNÎo´Ö´3Üuî>2qt‹ÑÓ°;MÓÐ|;_฻ܣáá¶F5±¼–›šÿØÌ¦’åÎ^=6aIhQDögµÇCh Ó(†,ÈU‘MWÉ·¾DmÄìîï$Z’ÁÅ*¹ô}wÛ\M(R¾ü»n ƒm’€DHFò”Ó9©ŠØ [’j “GpaÇûzñß$^áx-Kå°ªé‚ m$Vl«X1ÜY#VL²Œq—=É|gãϺثuÁXWéo(qÃ@°õHÛ—,2©®«L+³eféž|F¦y•¬@ÎsHŸHŽp#d_³1\„"s̳J æH+ ¸€FÙ=Ò×O4j’¤æéÎÊF20²j™Í^J©Ãj¯׃«VÓ–`”f“È–¨ôä«y¦Ü1(+ ü›-ƒæ²SK%µØE:ªÅÀ⇪UN´ÄÉÝh‡éÄ*×a‚ Nû½goxÔ¨Õ¸ÿ —’Ò8ðöE)OÐá ¡¶©RÒÌihÜ Û0zAÈh1iñÒ l”y[pltÖnäÿm$­ÙÛE"ºZèKŒPb¿P n1 ËExK† ºI ÈMÌEÉ–c!^&˹¼‘%¡o' ×°@6ÝžwHq†§pðÐ v¤pŠN„XÍEþ)IjkÍ^Hû]p˜kÅ÷ß nHµöÆÁ_&¼ÅR鵑 ÐP>’¨÷®‹Kêá±fóýq¶i£xxNüEŽFÞâ[ïËpjm-lÎ ¾ ˆ¾,ß' Œñ² îÛçð,ؤ0ž–ã/’aŠa¯>>Zé<¿TF–¼ÛÇ·²œ­îa1ï*°¶óG·€˜4s˜œÔÙûè‚Æ©S©Ô²IÜ›XÞ†j¢ç¸©ÃÛèz ²™f% ]ccÛô.ůž†ƒã°äÒÄÒ!AÊ–\O šBƤVõ‘†g¶Ÿe+R`Š‚:±L+厰,§y$¥TmÐe†w~^>¥_L§ZÔñ¢÷ô„ÖÒ.H’ü£\@2c>§X#;ù œ‘îâ³øW¶ÞçÅâ«…Ø…$Ï^¼JÇÝE,Yta^<•l:ñ…ó¾dËQÉ1§D6ª†¸Jröñöý_9> u ¨—¡z³8töu™Ÿ$o!O{’3+Z¤,Íöæ©ôþ¢§éUW9c|Wâh5*ih’Cz 0%Ö˜2¿ç™bÄuY¨#’Ä…"–u‡)&ˆeJزnû•Ä_Z'¨ym7åI¦³bÀ2k{"×tŽ®q3µiíøÍìpMùð0•Ê(òý=a°Ô•ÔÐl«ˆ¿–pg5Ð[!N‘gy½ÊÑÖ,RQOOÔ‰ºÎçŠìßU{„C9·O;óZ@ù8GpÑ”ˆNóO¼^g?ç›ËöáI½òkv¶¤Ž‰^ðȸ+ ‡d!=Ô†½eæo„Q$ö¨£8ŽTP ‰x”®]:B`æp!Bèœ3…ÁAª´µŽâ«¢cˆ³4ÕkÎWjíÌ_+^s°p‘ô¦ðø‰\1³bwj[Ìq…¹}/ VA0ÎÅW$…ÐSY™ÑPu|7~,ËÚ…:àæÉgŽª½A¦°ÊN=Wä-T0Þ¦ÐÒ«ÁøY(ªMR,¤–ƒj¨ 9Œ$ló ¢ÜÉÙA÷Tñ㨪€¸·×”€j[æì$´Ë›æWr¿ÚÇ}n"¬ ô–‹ªJWÖ°›9Æ;ã(ML²’†)ûR!Ç ©Øè’|Á|ZÓŒÏÓéRÎ½ó‚‘ Æa¥H­¨†YÖ¨º”,칪b‰¹&IN[€ Ñ…ÓFýh¨‰êþSücô“VÌ C¾BI«Æ¥¯Áõ™©ÌàUÏĪÐcéÐöYÔe`¹€V¦ Ö.†QD#þ±ç#!-ÒWøI^[íKÞ§øÛ4Cj{ ŽŽøja€Ï9.Ç™”…ÑT¯šhB„J'6<äÊ9ÀsN¥\(ýëuTµ3~ëµ`œ~¢Ež5QôIÔþ(ýÛ­]Ö„c ÐhuÑAbÌuæ8‹¢Zd¶d‰oóc­fiµÉg”tuѹqHsüUbuY0f¼÷ÎBY‡+ñ‰Ž&›ë ¤Ý‘úǧL…¼žºìa¥äc$Þ¹WR=i©R_tÙÌ· ŒBp¨MŽHixÂk«ƒº„-׬Ä+ƒv¹To0Õì«K_ÜH|™¬`EJϪ5ÙÒ-WeP€ (v]ªðP %T…+pº±6hÑH0"ÖœÔ=W·ÔíŒ(ò»¶X[@$Bÿ:!8„¬‰(îß2Ñì8½ !¦ä¬ã²6ä&Dü™ 1²¾R© ‘Ý|ì5c­‰x¾?çC]Q ÎaLôÁ”jSéŒEGaÓÌ|P·Ê³ÐsstKMœ~2.ÐÖä3•€Xã¥)®¸òŽã®B~KtSfÌîámxµÅ† ´Jkµó&†2€ð—·lš•Ú~‘o: %~Ô“ÇGV8s‚®â¢+Êåº0r¦jÖu~e7Ÿ RææÖ‰íI7 VvŠâ@If6]ÞØg£((5ƒ³kBÊí>ÑJMÄÞ}©ƒR¹™#oçøEey|L$ÅðVç.”k[uáÇÀþÜ©Pʯ¼`<«A-&Íca­,0ðˆ¨¼ì˜³BYc¼en3NtðÐÒëÄ, §¾ø4¤^Œ@æEÑæÙÌœÉ?’ iUÁIÈNŒ¡Ñ™Ì‚&Öâ­Zš¢|Ú³'v÷£€†8ReøpæÁK1½Lì]6'‘‘ON2­,˜VÉ`T«9çt‚†KFԮγŒ~öùsl32ö#ct>^ 6–äÔ§•$±²dÇÕ~¦‰_Õ²E\)Ê"0&$yµ^y7¶¡´k°r0ëBlÖ4á»À¦—ÎêT™IŒaì!õ,Åš8m©Ñ5‡É4 3ÅT Õ<£e¯‡·Ï@#Œˆä¶ ]«©Êi‘úÒRD æ‰ &=§[UN8Þe½"3G Å‹+´8¤ººË¯Ë¾q*MÊñ‚Ç|µ’­F²«´@…q+$¹Š NQzÎFµdDÀjŽš»(„>E9”Fjà+sLQÞß‹N€þ²4YUíî!(+ÕC¶¹À“V#ß*ùF—#BËGÇ–Á“4S\Íež3iÚyÀ‚UÀY¬6³d¸-©{ª™Q£m÷ž¸W ªÉ+Ÿš\#X‰º‰?ûÒ‰¢ù8—:“G7§sWkÁ@ØoèZ-:‡+ƒiJÐÿ8O'’3\”NൎQŒŠ¸æÎ.ÜVû‘ÛŠ›À0í²ìÛÚŽ?*“}9¡—V1£™ê²’?,J×™«Ê(µ/¼¡€&‚ÖûO,bÂ~˜;wâG./E¥×â~ sÂÆa œI ™«¾å EÖ"Âxà›Û@´pmÄ#D1—®7ü¦¤ŸÃsôjÑm·®yá]»¨Èƒcõt¶Ã}]ÜŸº/¬²žøcwö·w]QóZ2X\¼?œþ‰BK‚xUÖv4èY«©Ù@ûG;A^}pæ¤úÕœý¡ÁA¯noûÀÎãý0†<Ýb´ˆ™ "òí³½½Ý9—V¯øf‚Ò(òÊî¶ÛÉu-%¼1ŒÁƒß¹I-Ï€ñ/ưu¨ýÃÃ=•k´:¢F(j‡ŸYäÃUwBç´šèr™¬IáÇGu¿Úá‘[mP1Ñí.„J¸W£²w!BBoï¯Ch†èåw09b–Ã+ùÃ>´ àm”qìÊD¹`ö¿D³Ç H¤ŒÓYÀ£c¨2±×’8aPG‘P»Ñ¦ªL|ã²?Ë`!Ìç¡·r5%èŽã…ûˆÖ@ ÅQ>g”Ü*6Zè¯÷-ÐXÀ–UWˆp¥cÏë´JGÿïÿ®óÿó?ÿ×étÄöƒÎëÓ®´RZ(MáUh' gR¡)¯gƒV `×à8ФͶƒªHª,\BhˆÏY!ik6]Ú)Š’C§Ð9&¯½Æz°!Ê æ: 1ƒ $ÉDñ Ž„Ѩ×7ÿ5Oþ#I:ÿßÿ“ü5™Ðÿ#™w“-º‰Åì×ß’Îä×ß8šxM¡ò‹jRÖýÛ¬Xæ­«ŒW8h„ ©Sô®ºÖã©Õ•Ç‚œÞ8Ô!(hM‡ô`7¼R N@­¹lâfž{ðQÖ4Ìð˜¼Îª¢\L§yB„8®ÉHu Í»ðóöÏZl»^¾ÇHÄwáq£«¸áîØæÙÿ\VÓ‰•1Ó×À¹½ýÝý].tFXy§r%º4pT(ÒD¿:dZ ÅôÆe~!„D–síf‡SaÒ¡ÑB¤E”ñ(ãUº$¢¸†0²¿n^ÿ+„Q¢±Ín¿®vê0ðFDßì¢H¹80ƒëÀfê{â‘ä€dY±vëqá=¢¬‚‰÷Û)±ñ#i( d 1FlxT§ÜÉ´ZqÝùˆúÎYSMÿzÚUô JLHÝ=æ÷ ¿™±Ú&oƇ·Ö\1oC/Uâȵ}Fk/]s^jkœ­8yƒ§\,GàÉFÔ­!ïe5Ÿ,âÆz.> &vÓª.Ó¶Uþʽ¹/Øâ¼ù>ÕЧ#O—_¬-…&%㳨Â0,!ZIMúùàåaOþ»ƒì³îj†,wjE”Òøç ç»õ&zŽF›ÃI¾¼zª¡ô %Ðc¹Jǽ Ü,lêŒåµù(×inÅ9™ä='ÞÉ$Ú‘E©‘‹·¥ÙÒŽ²qÊjÛ†+õ {•ÊlD:ålWóôáû»u4Cl°å}FÆ(ÇFSöÍÔ€@­L»Ä5ÀpXŸ>¾é±9ï\ë䢠é¬fêµ»ÔÉKEzÄ—¢h(ÙC÷¨ˆx¿Bqrg¥Ò»œË#_€EŽÀ«-æŠ:îe&~hŸœfFê`µ_g^ˆÚçà6 Ù°(¡VT²BÏTx*Ĉ(¬PkîWd®²{^Oœ'ƒ7¯÷ûž[ͪ§ZH¶Î0pï‹ Ó|¼>†`WãJ4ÎÕ_¤Üá’8ÁíÆàø`kwí`”h.úÀÃ:8¡Ô±£©D°â(òÚ¡V¸³¿Ï) ¾t~¬u“Ò64¼‡ËʈÑÄᚢÚÔ¡šu»]Щ¦…ćè`©ï>\õoÝÅ„œ‹ÿD¡ß Î^’:Œ†Kjt]a¡>6+\ˆÍ2*#íØ:Î2‘_4\‡ŠTƒ¦nçgõjÌ¡ù߃%2U\ bjga£*ÍúE®ù’ÙTÁûû"@R³ƒÞ’!µ\*‘{¢2Ûü{OþûN^Ž=<ò†ê B… K¥Ët2q4t’iÝ4¼ðþ{ßHa©A$;¨ì4®‘P(ŸHÞïJOmVóY“6?g#ëˆfBºÕÈæ34÷Wë!"‚’e{¶úÂ1=t­Õ‚–/lëç–Â$™’X4GÔ¢˜laöpßê#áËRåɬ4Ÿnß+F¤fÈì*oãøG±Þó‡°޵+t#ÌÂúbaõ–&ÒVâÕ#éx¯WÛHÜAÂRéã2D ©ÑÇøß]s,ÕÞ” Iü:­R>Õ0^b¤@fêRÖòÜ'c)Ìás!šñ!`•0ý.Å%€}º’æ¼»B#°á–M©LÀ\§ òjM[-Ø%”!ï E(õâ_)b¾².¾XO†Ñr< í1±Ñê¸ÜM3§µWÁÖÔ‰õׂžY½z!p#FCÃLYyË\Ñvþ-¾ëRTp³u0ö’‡~6ЛeéG4˜8óp¯¢ëDDå³Iµ*ÑžëîÌVòkbðW?“ÁªuL:u–±£4ÒÏBõLLh/°ÜA•h~îì wv!O¦…é×b<Þ=Þ9ˆ¶Çâ’TŒó»Šï‘Îr/öŽÞ9MzTËËš.ßÛll{»ÇûAE¯ aæÒX/œRÂÿ\ûL]äÑÑÎÎ^ÔI2 ÍpÍ"¸O$ðÉ+CQ÷"³WF&r¯øÿ!Œû±H[sËŒ2áEϹ»»»é#ܧZ×ó2,¡Ü9ÏûB\/íŠß)—>3̺íä“õ|í:ÂÂÈþ?›l*ÙR>ÀÈ^_iqÅÄ’Û­:Ñ1¿gÓ VÔÛ…¶Šm{”Uíq죰}Ì–=00΂p8‹ÇÕ-$5޳8!ÕE.n„í½µ\|+ÖB$/þB:lкkô?Ñ‚w‚çA«._BÌàuu=­i,€ðO—ùeËÖ 0q0ÃFL2^CÇ—ºÈ6¾†öiçC–ÂÚ) èôâÓ)]:åîB}Å‚¾`+ÔHZ +,²Ç00ym¹G‡^œóBÅ)©ðºÖìâ:Á¯rµO×á,šIK(íâ>Øæ=Û]ÓM´G>ú©R ò¥ ^ÉC¨¿'D2=ãÉÄýLk–6*Ô¯o¤/í–ó§|b^&¢yV œyçœJÊ„ëËàamìÕj¿Ç&Nð‘OÁ§JÇxs{3 E%­ N,èËÍ;o5–Qª`;ÙÐVrŠ*I]œá/oÏκ½`¯B ¬âb-;)D=6¾îβyD1å)’\> «’ôe™ëS©:Îþe‚Uö¹â8 r¬í6ŸL‡®s†n/(wMbÈÆ·¬»’Ä즥Z *8=u^èf6‰VÇ©+2øk•cî9ç¹¾Þp! Lz¸Yg‡!T.¢‹’(D¼ÛVt‰=n'T˜á¼v(gg<R°”íÁ‘öÑôV}àÉVdK ²ùÜ©HXjXÎ9…«X ¿=“ëSÃ{¤´‘È4\ 4¼6B?ˆåE¸[ñ^{Xóe–`’ýí##X¨Ç2ı+Ä!£ÀŸ¬7Ÿ˜¦)wÚS®™ïpÿßÜtA–nBö¹VƒréýæáuñºÜ­©mÉ$Hôoª«ìEæ/ ñœÑLëû®ÚÂY ‡m·6’VuFPÙнŽÊº8,Q„Bï·9)Ã)Ô eSæ@œþ¹ÖRæ°È<:¦íYêÄŠÔ o¥ÓÂà&×}GÞAÌ­[Ëj¿³dR•óÚÏâMA^Ùþj^™¿+Pn,ݧéƒîE=à|®êA}IÏö¦©ƒ€Ò+öœ&ŒOGc#s²ìOÌLš¼7‘ÙVv0XÕ6v÷PHçíùY¤X䄿Ôïïï1!g}—ÔR±òó²¬Ñݰè†#®V‚byýaѼGû±äÏìòª7ß ŸÎ‘oãpÕª—‚Tòe×V–äavöÃ`-í2î’ÞÅ <“nôX?=ÕÑtÜ2Ϧ´íŽk•ȇ‡Ãí#‹(Ÿã^ NPâöèœ]š‡ß–¢+”`íî“Alq»»>BaÞ®ü)³+Xë²¶÷w‚8 ì-ò)8%[ ¿ô…u"z«ÉÆÍ°·»³­›ÜÝÞ%óɽ–ÿùÊÁ:Si«íʈ°†æ¾<zô³³g  éRÀœGƒqJ—~´»w`5²Ô#ä’ê\mwl$b×g»k2búE­iÓÿM_Ë>>:îž8Ó²·œ^žß±p4Qø½ 8<¦ö’}e°øôsZ×H]—oRR÷O¬ ¨°&WPT«‰Ò"m„PÞÓÛÃUXnŒÿŠUûNˆë–´ö¥pÛåŠZ ×)¥ö|Í™a«² K›01L¦øÏõìáòü?Üwò¡´&;Éžþk7ÙѸé¿bÌÒ¬P?Bÿcƒ zõACuôՌ(Ò½”½ÄùR„±H4ÔÁþn« ¯Õ]1£Å§Ù8´E¥ž?é÷»»ÇÇ0p%®¥Å%1g³Bëüd?ÍDôÞÊ‚ØzŽ÷ ê8Pßê?´Êò©ÇÀ/b×°NsW4JãL|#CšR=R¡ÍÄp躲Ƣm8»{›h0JA¢E½‘~6™évSÀÖµw‰ÛhKC0ø~8¤‘†‡„>V<‘ËÌeø±ÀãÖûýƒ=Ò]%ä.|C:hIÐB4Ói Í¥õë´ò)ÉFÀÜèÎ=…CSHö¢éŸ»úÒ,Î~©RˆC(âQ vYàß<ñ)lÃŽtñoè["¯êäâòâÎ¥:­âv½dwǵßDŸõØËÏñï}""{GžP}ïõ?öºÏ?±# ¾‹Mz܉õtñ`•à"rvoí_!i­O½ÝŒeb 5„Ðÿä—â3ð»Z»¼'¸‘×5Nõm}âÍU_ØÑÚjï¦ xVƒ 5¢™ò¨S†ó^GF7Îæ£cbWC©%Ë».¤Á÷Qˆ&ߊBÆ0 >2&¾FLêj—„rð(ððòïÕwo Ããk:vm¯ÐZîì`…”$8 {ʹ3ôúì+ËëÈs5,âÑÉOL–îDPÿB"¦«#egº;'-ˆÍ ®à¾:ž£ÐÁáöövØ8ˆÿÞŽŠ˜•hQiYŸV~œ%×½íí«×Éíé©A,±4CKþ“£#jQg_ö‡;;î”a¡‡ðѦ‚*QajÀƒýeÊC?´¿Áv­}–‹—}sEºÚÿë§WÞç_ipeÚÂÍÇAeñCŽÒbýI,~yÆ¥ïÏ´"÷&ÇÏ¥Õä×w¤ŸÓ©7¿¾áΊxÀrÓ1±5õm;Ìæ£þedݰ»mÙQÚ2\C[~JçsÉú^Ä™-^›{†E~îõ0Ò@içý^Ç7–éólÉͱAk"…}øy1ïúQÅ/Å¿mzcÚf¥.uîC»™x—áÚurçŸEêT黢ü’Ño¯Uƒ¾üéDÂA9±AöÙr! à…—q›ë­¿LV®ØðFúëÆ‘rÍçsQ¶óN‰“¹60ÝÞFT]ö×.Qp\Ü6èqüÑŒÿV ÅWÜ”r=n¢` 8îBBRôc™KWÀ uGŠä¼W¼ÄØ·½—,QújrÂÔåT ²å=;FMes'Ö-†FËr®I‹ × ¾(ß»,‰c†]JƒOƒd1¤ÆöòÂïNÎW–Ók¢ñùÂS]£–L•%‰µB%¨^%•=­´×²dö0µC¶ÌÞÆ<Á’Mh´ËMqšÔ‚QvÚ"ÿìáu ª$o€¥4šb÷ÀJ8Ù&àÆ Dm”kp„¶ðô¤åÐ=±+Õ›qÖ¾ö°'VŠØ ÿW‴'Ÿ(ƒëVúŒs³âÐØñÍ ’Ø Q)‹˜tIŠAúîCóØí%K.µ…ÇŒõbk‘–b)-y7‘T‚‡þ°+qL¦ÚBÎ"c¤[Ç ÷xc3™Y½y0¯½M±,ý2]F6ËŽmW;º'~÷¾÷\eÇ0Ôähð­þ6óÌݧgí¹Ö`ìº]¼ýt9@b¤¿G;2&­(®tf•½Ît[»d¶§=8KÄ\ÒTÐÕPWűÊ"@¢¸A‡Ä:a!•O¸F+/ºé‹b!®wUyx/Ò°½.¦Ê«Fj ‡ùŪ$ȉ-7:º:æc ´‡n5p›±ájPôèL cýmíÂÄ ;}ÔøxÂQˆ—·Ò÷ä1¼ÇíZ?Ñ¢Ëß¾É.üe}2ò¯à`â íÉ)Ïë¶…]­Žhߊ•øA­Äªÿ{íQÌ(N øBÀ¯gœöSÙüõcE\îWÖ~}Íõ ~åTRîkNÐ5ùõmYNpòq¢éIò Ea'­¬ÔŽ51Ív"<–ž÷,ÄÝâ¶4ø1s ¬N>þ{2˜Ì8·lÌ=ƒ^ç,¼“µW¼R!V¨ê–§×œò+f ªrîì“¢ŽÓ²›3ÛÆèI:ò“XÆdKm;HîÉ;J‡5¦xhäMôÐs´ôäÄš»Š%µ÷ Ò*ÒºxÕ¨ˆŸù^¹š±™A˜zk=]n> ÷OÆOŠ*1Ðâ É}CXÄAo#p] ÿŸÙIâ/_ÂúÕlÜ᯽¿ÐZX3 ƒq 6–Öê{>ýù¾y©®ç ÄËtºU£%¼ª °îkm¨Eî?}7ºk¢ Áê_Ò²—~9“(ÝšÎP5^<«“(_ÒéFªÇÐÌ-µc»dòHè÷¢Ú±Ñ×õ9X"‘œ/5ˆh¡·´÷’óÝ(çÅyT‰Sÿ2çƒõ*ð~ 7’*¨"W®úx€ma¿ç\ŒÙìô øý,†ÚÙæ‰Ãª!Z.Ä•·sÑ(lT(çHuˆGÄTæ­ínïí¢S•$Ñòz8 …±8jë!×òIRK¬@QQ#A!„mÁ„×>J€ ­2@¤SÕK¢ôœRóðûòJ¿-š$ItBàv½ðGµ>¬:³ËŠ0Á HRMšƒC¾Ž.HUëE”mrO5`‡XÉ’Ý`€ˆÝ!‘˜“#WAá¼´ müs(í fÞJøùàÞÃVó»¬n˜I’Xª¨/¤žÅ|ÿ6»‡>жÙ6¡”–ó~UÎ "ùg±k™Ž®›“ µàŠV1” Žö9é>²hÅÞ•`–íÀ‚Qv†;{'ÉÛ×gcÚ1Êñµ)ײ¶½áþÎááIœ£)äqI>×hb‡úbÞšøðxg‡–†œqÒ*»ì"0-ê%,¢¾rGÛ;„qUöœCžõÝ'›å¼túÙ‰ˆ0ÂŒiG(‘¡2âw¶·‰¦”>DÓT±K±Ô4¦@¡øªvèù;'œ¹tè‚|“?÷8cõÜXžÑÏÿž$+Úßßf;‡(ûŒÏ®ªœz£Ø_¸tIÑ-Úº·½{ÈakŸ-QŠ/èÓñ‡ûCúÐ÷5BÙH-DZÁJÿwžþBÊb ø4àX’ʺRB4¡IY¥f´s­h¢{£5ü9b}±râ,'aE­ÒíZU™ö†aYzw{Û)ùŸÙQ?h °˜‹Þ3Aö1¤É²2'´Ùc¢0_nqmóÚM]TD†õrZ,Én[ͧÝm¦ÞÛÇ!ñ¾C 7¦Fß%Û¢g>¹{.LKÂs¯HMQ„“Œƒs¢çÊ€Ñe ©YŠ®|×Âx¹Ê6f‘ËéDzãà‘ÕCäú'âù,W‹š$0OÈûÁWÉÃöžº;ѵ[Ç“¬é«ø~fTFóšx¤Ó­Â‘†ÛÇ«ãÁ'BPùÆµÊÆ  Ìkð²r{µ<æžJfÖíXƒ6XuÌœ‘[ Ì;W¶¥Àö‘œŠ$ËÔÉbÞÓZ ÜKºOžù,PnÇ<À XE³pl„ÆYtÐx¼UMÈÉÀZ¸OX³¼ó^ Ô5šeü‰Üa}AãvÞÊPÐEªÎñ†Ã\OK )æòfÈŠtV¯>Üâ|>aÈy½!YM¨¶±RÓ”¥õZ‹tZþ“ÅA>"†‡‰T#YMú%“ó‘³¼/‹|‚Âì,iм§ØÍ»·¯?]¾?¯Ãx_ùY:§!q§ŸnE†ÑFÎd"+ÛŠqàI=~¤S’Œ}PŸ-øûß76"âp€Æd¨{t¸J` ù.]0.%©¤ûqåj!û̈·ÐUEkÙö9ÐP[¢ük×è¤X-´6¹üZò`Ü›‡¹‹6A[ó”'cZn'4C¾óSžŠê.bó#ˆûâ’ß°Ùñb2¬²´´såÌ>`"©±Õz!y¡¢X-Z¸Ž˜`õí6X©eé'³)™C—ŽÂ’“£<õcÔu'ç˜ê ˜@ œH<ÌSE5ÎÊá\{Wl”¡ãkP§‹þ,Á¨ßPˆiB²FâC”§©´ï&ûÏiõï­¨èÖëô[Ê1q·$Æ•>7ÂøþVæZÿàçæ<«¿4åœ7&îhï_¤Ò?”%]Ú Ï·þÙLäÝ­nÔû¬»³Yux°ÓW,^¯±ž£°„ò\ï6²Óc™¶MqyuàE,ô ÂuE²3O‚„£ N:åŒZ#@ŽSÔ YÏ9¢þ æÒòTâéJÎß'öœÒq n‘çÛÚ‰UÔHµ:yóéýÕéÇÛË¿Ã>ˆj¤ÑÁnÛÁî®,—qF¥fÄí (-(¸·Y%bÖÛ*#éžwšÕ„ŽÝï_Æ+ÈÊ „åW/ò‘ÎgRÓ,^Ø@Ú|~—Ž¨Å¿ÖT1à;ŠÛ{‡$ô.–‚ð‘nûãëPv™J‘öÝ!IæÉuàjK£ì[ÎæÑéLNœœŒ bõ\;s¥ 2³3®Ô ¤oÏÅØ ðÍ0]:’]ù=Ë:G8./\íV'/èUqsþ¦ÝÖ*Ô®“:¼0µÙDé5÷¢ý²ï7¥‘ªîÕ›ë·Ò¶ãêF[¦”ƒ™‹œ®oiË­ð™ÿ‘ ùÃ;>'\ˆPC}¯iùPJø1»Çf” þ2CK‰¶€IÓó¤c Ík. /!ªè[mŠkª¶²ýOÊÙ·œoHâиg“pmc;h±æ½cµ@®A¶Ó)'ãœæ%é7å‚«¤s;¸Ü þB}añEþ…ÊæS ïê)iÔâbT„î†=.`^æÖ˜µ ¹fYæ'NÃ.ÙÒÊgWíËUU¸ ªÿ[|ôß%Q gÓŽ‡ê•P{Êi1©²?uÃy®Kg4èë7wf¬!¬Ò‡4üÔD׮ޖóÁ¹¯9ˆôCúDì5&©$¾Õ#M’"eŒnõ("¡¥änÓi±óŸß¿È~XÐôÂHË껾Ê-é¿'"rýEeZŸö㜹ü9‚›D&žwfdµkÙæœî|85¾Öæ>SŽ$ï%ÌÝ+²®ÇNgF3‹m\°.Óû}N¥æ×I—¯³D5@Á!ÚÆk QºžºôTåÏÚ]@‹•\¸J$R[ž;.ÃNÄD^]V®²µÎ›©Æé…3œÞ\ŠÑ;¿7aŒ__Wè…îýÙ¹•×­eqÇR-~4ËB-Çe:LJÐÞþeÑׯ9­jj$ØjLÊ&ÁZÕðh §<ÂÎ,·¨Q8©÷¥8qÌÕÑ (1‹¨l?v -£ŸcM¬óf¸Ý u1¯>-ÌI(g+Š ÙýÛ›+DÕ¹K(à²=wŸî.`™ÁЊO øYÍV‚²zØrß‘(¶Å=üN¶Ê¯Ë£ƒ-á×áp°M“@ß`¦@=ˆ U¦v–5ü„l œ¦Rh£Hó©¹ “åϽC×ìyÇ/8Tuñåùàv”›±’>¬pº%6þº¸îI¨”D»þeeá:‚0ÎÕ‘mùG@Pjâ†$§±ãÊŒ ®?qÒQE†©¦¥+Ⱥw›°ºº™’-¦w`G7\9ºÓE³˜ññqòÈwÏžƒKh(Ó.(€°ýf0Ù|´$T 툸x2ë|´…IžÎÊbR3¡‘dÞTÚg¹- ã¿w-m›Î_&§ôM@O#éD bEHš£ß}0y|Äû¶†é w._‘&)yPÃø÷…ŠÀ°*@ åTTCã?5[–2‹£`Mþà§f6ýE*‘JdPm¯¢¿‹¯‚Á¡ú¹ùœÞq\±á÷ 1œ‘ý܇Í4|%ÆPèð]#æÕW¸É°Hœ¨,zj‰¬ô̇ °3Y©{3C@ãÄ*—x˜ê% IÒ)J;O§ù¼¶Àƒ@È×.ÈÏ QŠE?7ÉÞ`7ùÙÂ*`Ö0_9ñ"Ü*]rÔv­pöÝ1T¶}·p»§Ýˆ9LvŒ”ƒ­ü€ÑU9R⡱¿˜KPe^˜TðË€%´WXÍ¥µø%Qß–ù"œ4úêNtº=AÞ›²~û™ÿ²âQ=í£˜E£Þt‚ .&Ä«å¾bDEö¬ %é|m!å2ÿòk9L&6’ÞD| ‚âÛ›÷»83í&3–p8›{¾ÖnHêËÚåŠÑ †s |Ò@ç{Ôð7B;)|ÆAu÷ÜD¢óöÓeW;#ÐÁzuG¢ |šÜÚH³¬x†ZÔØšùänî^DVp4è0‚Gñ áïü}P!XÑÊ-÷ÎTêV©}©ºIç[«L‹l*U”äƒhåUâÙöÙ—ÁF7BynÆÓ“Рrì".´ñ\+ü¬kÉ ¦~省ô€‡I‰d‹:/U ´ƒùb,£M8ú×j¦Y´¢6ýcz™§ÀÜŽËÕï›+Ìo}sԵÇñå+ "±hoWktã¤Ã탪“Í‹¯ßƒiøî/>'ïOÿñáÓGRÝâô·È¨xù÷“ä‹®UÍLá“1[h¨n.X;%½õ•1T:ˆÍP{œ=‰ìÖZ³—Ó™?&$d>þÁ}­[ïŠM÷÷ÖŽâóµ þÆ>w~~Í~èFõ>*öæÝœŸ~$Öõ³5ó™Ûw¬ÄÍÙé:Õï]q»Î™0 þ&(†³}}“I£~ž«Ÿük‹íȉûdMm¾IÈ&aîå½Dô²f±ÅéÛÌDprº„ø:Ù\Kä–½!ÛG+×¹yÆfîê‰&uÚÖIÿ{Ië2w†’olŒ7bÅŽQ'Y6³÷,-,PŒ[ Vsc5j쓉ܑ»R [|†ˆ¾á*ƒ*ƒàú9ºD§42Š‘/®n>Ês]GXçÓ9\@Ñ8&Ʋþ%¬•îu^>w`{ ¸]‘"âësa̽dˆø øúNÕâû:H^WÐþB®fµ ïq|4±*YßãÛ;ÕÖ}V7ó Þe™è~Øv¾ pý OÆ.!õeèOð«ÕÏ¢…±§¶Ž€¬UNú1á*ðµÐc÷ØŸïöþêùž§ß2qù¢p'«Évohí]¿þG{í td5×0k•æV3§¦üÞ=2y‰w|ÆÇ›eŸÓõ«¬a»'Ç¡÷ûQòÕ§SÒ1¾ akÏÅÄYW/WE³Bd¤z\¥ÍáÁÈ&g\]#é*5|ࣾªœÜA¯3-ˆb<½ejU&"›qÑÞƒt‡§ü[Wýh.4Mû7<¦Rrœ£KásxÎ&”±»ÀñÊgöwíDÆ%ŽÍé²Ú)±z š¯°}©©Ü”aéºöjmQ Ë— õ•ðIzçLíÞÁQ9´øm.wðs¼^ÔKöDÔe’…"TT!D+'9+‡¥©øŠ‰fævN bÚë:>Ñd Í×–O†]ˆ;ˆr1¬`9Ô'kÍ.îo»ê*´;Š›Ç¾DÒÄÓrñ€pêõÓ&ÿÉKo1±ª°ÊAA¡dŽý#|jŸ›:¯3×3šNõŠÀ„öü¨Sü$p@N¾ã]Pÿ”˜‹Ìˆê¢Ú‚ª1ìŸRq}ÅÚ†KÏ­iTÄ”  ‡Ðï^üyÁEø>rŸØHàÞ=Â:¸òã¨3ˆ/³„ÐT4ð¥ËZjº‹–ž”.aaïY'Àá:´Ž$[ éç+*›?h?26ý=7AA‘ûÍT¨ÎÙ#¨€„|¸ºHÞ\œ~üt{q—¼¹ýp•ÜÜ^ürùáÓ]òËÅíÝå‡ë;Í$<ûpuóáúâúã]rz}úþw—wÝäóÅíEr~ûáææâ\˜‘|Ĥù0錕'›OUY'—cV´ˆÿ£\¼òUiÓj¶„+n0ØìFäXBܹF'îzµuš&s¤öH‹ÃGçâ"÷œùLŒÚŒ´©Âõýt¹ᣬ^0Ñ!"ûËûUf¾´w°Ê8χ0FR›O£ ©¥Ç}eªô¹€%PÊk³PG°¿%Ý8: ª»l€pyвV<äVÈ ·Tly?eÓ°¥¾ £•­¼Ñ„wÌTbõþøX†@ž#)Sk¥÷.[ªDÀ‚UZ¬Â%9Òë0r: ü´8¤ònÎ] £ ~ †\ @òŠÑ%êA4wWUÊÇ¢¾·BËËeµXˆác–-^ñõ0-bç4òå¯ßáÒ-h‰Os?é ¾€ýµ'ézÒ¯²Ð4ÃÖ‚$íèÂðr;<” ²Ã"ËÏÓ§|BL#ùô@R>!ý]Öh«d“˓ПOe>MÿÔò@ïîÑÒO¯“áÞú¥#UÊG9²Mô’\ÿõ”Ó C¸­‘ü½ÒQ’8Þ»~RQ0Q$ÏÌUÖàãÚ·§Uçl$‚p¤„¡ÞÝÝÅ-éD›yò7NTÙL´Ï,l?ëÿèJgiWùÊ<¤Vò¬I¾dÙ†DÑ _Ð¥íhå'Ýâ² l‚¶´û‡é ¹76žü}ì&#éR¸½×}YÆ]9ƒx)¡°¯â§–«èµ]ÇâyyìWG¼õ«MaW®À£FOú~?öé#íåTN:(å:Âû&IÇݳ®ßÕÄÖ߯YPЬª‹ðaFE/íŽxÁ,ojãhäKÜçc|Œ1=±üùc’}My#\ÜÆRÛ`Ž +˹º2Q†Ôˆ~¸Sl”-Z+€ucÔSoDZø¡Yñ0kþ}ÛÜ•¨‰[lªíjÅÇÖêÀßà:CÝbŸµ½Œ¼AÚ&ê _@ôÀé‰ã[ÒèHf‘öW’5§ÔkßR/ɯ§ $¯\ÁìWÞ)§¿¶²øA¯þä²°/Ò¬•¸¢¡ÕÇà —¡U³õ0ÖqÚÿñµïŸÒZ¯T[Æî:übWð€äp_ßÛ­VfVÏ×ëþ§=­e®-5;œ M(ÔuÅ050ÿ,òoºÊ‹©d´&RaâÁ`fœ­Æ¾ÊY#G÷—KO@úƒù;˜Oÿëâ믕ú„> Ó@Hâz.™™uXÌ´É>Eb ó0ÛTZ^®t6ƒ¸Í®4ƒh&- ÿvýþ$¹:½>¿=}w‘†?®3÷}V“ ×L’#.œo‰ÔbmµPH½îq ÷¸4)0Ê¥H%°ŸùÖÑ[H¯mÛózÁA*í!„VåŠÞóV< ùºœ_4ËØœ±ýaª–qŒBá¸Ô,Ì ¬üXìí oÔloV¡¿úŽRYÉ)®O7W’•Eõ-ðB¥EõF™‹f®Ê-xˆP°8ö'ð€\ëÄú~ÉıO,©ç’$z·kx†Ž¡èâVäšIh%Äyñ¾´Ï ðUI|7±ˆö]Y¼cüIbŠ/œh™$‹zE`ˆ>V?9dr®?ˆ›À=¤Z:Ìã½ö¥jôΘª’FÕ9¥åM•lƒ[?ŠÙœp—c²:ÖÝ^£|ëň 6]'9%`ÙöÁ§Í&0WŸ­v5ÝàUJg×Õæ-;ßç&/mÛŠô‹„A`°’e;šÃý¾â~œgyÒïš„b%|*¦™×çZ’¸ZLr0¬ŠmN„ʃl!<ØX¸ÈŽÄZÂñ³î‰‚ ÆG1l…ÖLƒ¡ÓµýuÔ˜¼×3ÄÏ$“)6Ù×¹”xX&„ÅKTÔåÓë,½˜“§¸4jc´(+9˜Gc~áæ„ÏÄÖ¤®Äˆu˜jú2Ï4¡ãu ã*WœJ$xéjŸ?‰Z"-pÁ2:[ æ©Ý„ÿRqmÓP0‘•út³´xέ}üj—ÓÓ¬„!g©®·ƒg2q€ŠØ¸LâÅt\*C€ââÒæpš¿ÌA&ë¡•{¢„°z§ÉÙQ²$¿ÄÂ5jS% K;ÆTGüÎ}!>uˆ‡š‡ ¥è$‚;ѶÚbÄ$$Ýð4(«K¶$ÃU©ÐKÂí×ÿ_h«ÞÁïsocnetv-2.4/translations/0000775000175000017500000000000013245521146016020 5ustar dimitrisdimitrissocnetv-2.4/translations/socnetv_es.ts0000664000175000017500000060476513014570727020565 0ustar dimitrisdimitris HTMLViewer &File &Open Ctrl+O Opens another helpfile &Print Ctrl+P Prints out the actual network E&xit Ctrl+X Close Manual &Back Ctrl+B &Backward &Forward Ctrl+F &Home Ctrl+H &Go MainWindow Welcome to Social Networks Visualiser, Version &New Ctrl+N Creates a new network New network (Ctrl+N) New Creates a new network &Open Ctrl+O Open network (Ctrl+O) Opens a a file of an existing network Open Opens a file of an existing network &Save Ctrl+S Save network (Ctrl+S) Saves the actual network to the current file Save. Saves the actual network Save &As... Ctrl+Shift+S Saves the actual network under a new filename Save As Saves the actual network under a new filename &BMP... Export network to a BMP image Export BMP Export network to a BMP image &PNG... Export network to a PNG image Export PNG Export network to a PNG image &PDF... Export network to a PDF file Export PDF Export network to a PDF document &Adjacency Matrix Export network to an adjacency matrix file Export Sociomatrix Export network to a adjacency matrix-formatted file &Pajek Export network to a Pajek-formatted file Export Pajek Export network to a Pajek-formatted file &List Export network to a List-formatted file. Export List Export network to a List-formatted file &DL... Export network to a DL-formatted file Export DL Export network to a DL-formatted &GW... Export network to a GW-formatted file Export Export network to a GW formatted file &Close Closes the actual network Close Closes the actual network &Print Ctrl+P Prints whatever is viewable on the canvas. Printing This function prints whatever is viewable on the canvas. To print the whole network, you might want to zoom-out. E&xit Ctrl+Q Quits the application Exit Quits the application View Loaded File F5 Displays the loaded network file View Loaded File Displays the file of the loaded network View Adjacency Matrix F6 Displays the adjacency matrix of the active network View Network file Displays the adjacency matrix of the active network Erdos-Renyi G(n,p) Shift+U Creates a random network where each edge is included with a given probability Uniform Creates a random network of G(n, p) model by connecting nodes randomly. Each edge is included in the graph with equal probability p, independently of the other edges Connected Creates a connected random network Uniform Connected Creates a connected random network Ring Lattice Shift+L Creates a ring lattice random network Ring Lattice A ring lattice or a physicist's lattice is a graph with N nodes each connected to K neighbors, K / 2 on each side. Same Degree Creates a random network where all nodes have the same degree. Same Degree Creates a random network where all nodes have the same degree Gaussian Creates a Gaussian distributed random network Gaussian Creates a random network of Gaussian distribution Small World Shift+W Creates a random network with small world properties Small World A Small World, according to the Watts and Strogatz model, is a random network with short average path lengths and high clustering coefficient. Find Node Ctrl+F Finds and highlights a node by number or label. Press Ctrl+F again to undo. Find Node Finds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node Add Node Ctrl+A Adds a node Add Node Adds a node to the network Remove Node Ctrl+Shift+A Removes a node Remove Node Removes a node from the network Change Label Changes the Label of a node Change Label Changes the label of a node Change Color Changes the color of a node Change Color Changes the Color of a node Change Size Changes the actual size of a node Change Size Changes the actual size of a node Change Value Changes the value of a node Change Value Changes the value of a node Change all Nodes Size This option lets you change the size of all nodes Nodes Size This option lets you change the size of all nodes Change all Nodes Shape This option lets you change the shape of all nodes Nodes Shape This option lets you change the shape of all nodes Change Node Shape to Box This option lets you change the shape of a node to a box Node as a box This option lets you change the shape of a node to a box Change Node Shape to Triangle Change Node Shape to Circle Change Node Shape to Diamond Change Node Shape to Ellipse Change all Numbers Size It lets you change the font size of the numbers of all nodes Numbers Size Changes the size of the numbers of all nodes Change all Labels Size You can change the font size of the labels of all nodes Labels Size Change the fontsize of the labels of all nodes Add Link Ctrl+L Adds a Link to a Node Add Link Adds a Link to the network Remove Ctrl+Shift+L Removes a Link Remove Link Removes a Link from the network Changes the Label of a Link Change Label Changes the label of a Link Changes the Color of a Link Change Color Changes the Color of a Link Change Weight Changes the Weight of a Link Change Value Changes the Weight of a Link Filter Nodes Filters Nodes of some value out of the network Filter Nodes Filters Nodes of some value out of the network. Filter Links Filters Links of some weight out of the network Filter Links Filters Link of some specific weight out of the network. Change Background Color Click to change the background color Background Changes background color Change all Nodes Colors Click to choose a new color for all nodes. All Nodes Changes all nodes color at once. Change all Numbers Colors Click to change the color of all numbers. Numbers Changes the color of all numbers. Change all Labels Colors Click to change the color of all node labels. Numbers Changes the color of all node labels. Change all Links Colors Click to change the color of all links. Background Changes all links color Transform Nodes to Links Transforms the network so that nodes become links and vice versa Transform Nodes LinksAct Transforms network so that nodes become links and vice versa Symmetrize Links Shift+R Makes all edges reciprocal (thus, a symmetric graph). Symmetrize Edges Transforms all arcs to double links (edges). The result is a symmetric network Strong Structural Nodes are assigned the same color if they have identical in and out neighborhoods Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods Regular Nodes are assigned the same color if they have neighborhoods of the same set of colors Click this to colorize nodes; Nodes are assigned the same color if they have neighborhoods of the same set of colors Random Repositions the nodes in random places Random Layout Repositions the nodes in random places Random Circle Repositions the nodes randomly on a circle Random Circle Layout Repositions the nodes randomly on a circle In-Degree Ctrl+1 Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Circle In-Degree Centrality Layout Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Out-Degree Ctrl+2 Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Circle Out-Degree Centrality Layout Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Closeness Ctrl+3 Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Circle Closeness Centrality Layout Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Betweenness Ctrl+4 Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Circle Betweenness Centrality Layout Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Informational Ctrl+5 Repositions the nodes on circles of different radius. More Informational Central Nodes are situated towards the centre. Circle Informational Centrality Layout Repositions the nodes on circles of different radius. More Informational Central Nodes are positioned towards the centre. Stress Ctrl+6 Repositions the nodes on circles of different radius. More Stressed Central Nodes are positioned towards the centre. Circle Stress Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Stress Centrality are situated towards the centre. Graph Ctrl+7 Repositions the nodes on circles of different radius. More Graphed Central Nodes are positioned towards the centre. Circle Graph Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Graph Centrality are situated towards the centre. Eccentricity Ctrl+8 Repositions the nodes on circles of different radius. Nodes of large eccentricity are positioned towards the centre. Circle Eccentricity Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Eccentricity Centrality are situated towards the centre. Remove Layout GuideLines Removes Red GuideLines from the canvas. Remove GuideLines Removes any guidelines (circles or horizontal lines) created for the network layout. Ctrl+Shift+1 Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Level In-Degree Centrality Layout Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Ctrl+Shift+2 Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Level Out-Degree Centrality Layout Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Ctrl+Shift+3 Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. level Closeness Centrality Layout Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. Ctrl+Shift+4 Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. level Betweenness Centrality Layout Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. Ctrl+Shift+5 Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Level Informational Centrality Layout Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Spring Embedder Alt+1 All nodes repel each other while the connected ones are attracted as if connected by springs. Spring Embedder Layout In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. Fruchterman-Reingold Alt+2 Repelling forces between all nodes, and attracting forces between adjacent nodes. Fruchterman-Reingold Layout Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other. Zoom &in Ctrl++ Zoom in (Ctrl++) Zooms inside the actual network. Zoom In. Zooms in. What else did you expect? Zoom &out Ctrl+- Zoom out (Ctrl+-) Zooms out of the actual network. Zoom out. Zooms out. What else did you expect? NodeSize = F (OutDegree) Alt+3 Resizes all nodes according to their out edges. NodeSize = F (OutDegree) Adjusts the size of each node according to their out-edges (OutDegree). The more out-likned a node is, the bigger will appear... NodeSize = F (InDegree) Alt+4 Resizes all nodes according to their in edges. NodeSize = F (InDegree) This method adjusts the size of each node according to their in-edges (InDegree). The more in-linked a node is, the bigger will appear... Network Symmetry Shift+S Tests if the network is symmetric or not Network Symmetry A network is symmetric when all edges are reciprocal, or, in mathematical language, when the adjacency matrix is symmetric. Graph Distance Ctrl+G Calculates the length of the shortest path between two nodes... Graph Distance The graph distance (or geodesic distance) of two nodes is the length (number of edges) of the shortest path between them. Distance &Matrix Ctrl+M Displays the matrix of graph distances between all nodes Distance Matrix A distance matrix is a NxN matrix, where the (i,j) element is the graph distance from node i to node j. Diameter Ctrl+D Calculates and displays the diameter of the network. Diameter The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network. Average Graph Distance Ctrl+B Calculates and displays the average shortest path length. Average Graph Distance This the average length of all shortest paths between the connected pair of nodes of the network. Clustering Coefficient Ctrl+C Calculates and displays the average Clustering Coefficient of the network. Clustering Coefficient The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. OutDegree Calculates and displays OutDegree Centralities OutDegree Centrality For each node k, this is the number of arcs starting from it. This is oftenly a measure of activity. InDegree Calculates and displays InDegree Centralities InDegree Centrality For each node k, this the number of arcs ending at k. Most in-degree central node might be considered more prominent among others. Calculates and displays Closeness Centralities Closeness Centrality For each node k, this the invert sum of the shortest distances between k and every other node. It is interpreted as the ability to access information through the "grapevine" of network members. Calculates and displays Betweenness Centralities Betweenness Centrality For each node k, this is the ratio of all geodesics between pairs of nodes which run through k. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. Calculates and displays Graph Centralities Graph Centrality For each node k, this is the invert of the maximum of all geodesic distances from k to all other nodes in the network. Nodes with high GC have short distances to all other nodes in the graph. Calculate and display Stress Centrality Stress Centrality For each node k, this is the total number of geodesics between all other nodes which run through k. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality Calculate and display Eccentricity Centrality Stress Centrality For each node k, this is the largest geodesic distance (k,t) from every other vertex t. Therefore, EC(u) reflects how far, at most, is each node from every other node. Calculate and display Informational Centrality Informational Centrality Calculate and display Informational Centrality Display Num&bers Toggles displaying of node numbers Display Numbers Enables/disables node numbers Display Labels Toggles displaying of node labels Display Labels Enables/disables node labels Display Links Toggle to display or not links Display Links Click to enable or disable displaying of links Display Weight Numbers Toggles displaying of numbers of links weights Display Weight Numbers Click to enable or disable displaying numbers of links weight Display Arrows Toggles displaying of arrows in the end of links Display Arrows Click to enable or disable displaying of arrows in the end of links Thickness=Weight Draws links as thick as their weights (if specified) Draw As Thick As Weights Click to toggle having all links as thick as their weight (if specified) Bezier Curves Draws links as Bezier curves Links Bezier Enables/Disables drawing Links as Bezier curves. Anti-Aliasing F8 Enables/disables anti-aliasing Enable or disable Anti-Aliasing Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... Progress Bars F10 Enables/disables Progress Bars Enable or disable Progress Bars Progress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation. Debug Messages F9 Enables/disables printing debug messages to stdout Enables or disable Debug Messages Printing debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing. Toolbar Enables/disables the toolbar Enable or disable Toolbar The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like... Statusbar Enables/disables the statusbar Enable or disable Statusbar The statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it... Manual F1 Read the manual... Manual Displays the documentation of SocNetV Tip of the Day Read useful tips Quick Tips Displays some useful and quick tips About SocNetV About Basic information about SocNetV About Qt About About Qt &Network Create Random Network Export... &Edit Node... Link... Filter... Colors &Layout In circles by centrality... In levels by centrality... Physical... &Analysis Centralities &Options Nodes Links &View &Help 25% 50% 75% 100% 125% 150% 175% Rotation: &Add Node Add a new node to the network (Ctrl+A). Alternately, you can create a new node in a specific position by double-clicking on that spot of the canvas. &Remove Node Remove a node from the network (Ctrl+Shift+A). Alternately, you can remove a node by right-clicking on it. Add &Link Add a new link to the network (Ctrl+L). Alternately, you can create a new link between two nodes by middle-clicking on them consequetively. Remove Link Remove a link from the network Alternately, you can remove a link by right-clicking on it. Edit Network Edit Total Nodes Total Links Counts how many nodes (vertices) exist in the whole network. Counts how many links (in and out) exist in the whole network. Density The density of a network is the ratio of existing links to all possible links (n(n-1)) between nodes. OutLinked Nodes: This the number of nodes with outEdges to another node. They may also have inEdges or reciprocal links. Meaningful on directed graphs. InLinked Nodes: This the number of nodes with inEdges from another node. These may also have outEdges or reciprocal links. Meaningful on directed graphs. Reciprocal-Linked: This the number of nodes with reciprocal links, namely, both inEdges and outEdges to another node. Active Node Node Number: This is the number of the last selected node. Node In-Degree: This is the number of edges ending at the node you clicked on. Node Out-Degree: This is the number of edges starting from the node you clicked on. Clustering Coef. The Clustering Coefficient quantifies how close the vertex and its neighbors are to being a clique. The proportion of links between the vertices within the neighbourhood of the clicked vertex, divided by the number of links that could possibly exist between them. Network Analysis Embeds a spring-gravitational model on the network, where each node is regarded as physical object reppeling all other nodes, while springs between connected nodes attact them. The result is constant movement. This is a very SLOW process on networks with N > 100! In Fruchterman-Reingold model, the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces to each other. Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. Kamanda-Kwei ! If you enable this, all nodes will be resized so that their size reflect their out-degree (the amount of links from them). To put it simply, more out-linked nodes will be bigger... If you enable this, all nodes will be resized so that their size reflect their in-degree (the amount of links to them from other nodes). To put it simply, more in-linked nodes will be bigger... Layout Ready. Social Network Visualiser Ready Do you want to save the changes to the network file? Yes No Cancel Choose a network file... Select one file to open All (*);;GraphML (*.graphml *.gml);;GraphViz (*.dot);;Adjacency (*.txt *.csv *.net);;Pajek (*.net *.pajek);;DL (*.dl *.net) Loaded network: Error loading requested file. Aborted. Opening aborted Saving file... No network loaded. Do you want to save this network in Pajek-formatted or SocioMatrix - formatted file? Pajek Sociomatrix Network saved under filename: . Saving network under new filename... Saving aborted Closing file... Network has not been saved. Do you want to save before closing it? Erasing old network data.... Printing... Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Links. Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Links. Dot formatted network, named %1, loaded with %2 Nodes and %3 total Links. GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Links. DL-formatted network, named %1, loaded with %2 Nodes and %3 total Links. New node (numbered %1) added. The canvas is empty! Load a network file or create a new network first. Cannot export PNG. Save Image Files (*.png) Image Saved as: Exporting completed Nothing to export! Load a network file or create a new network first. Cannot export BMP. Save Image as Image Files (*.bmp) Export to BMP... Cannot export PDF. Export to PDF Portable Document Format files (*.pdf) Export to PDF... File saved as: Cannot export to Pajek. Could not write to %1 File %1 saved Nothing to export! Load a network file or create a new network first. Cannot export to Adjacency Matrix. Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1.) Adjacency matrix-formatted network saved into file %1 Cannot export to DL. Cannot export to GW. Viewing network file - Loaded network text file Network not saved yet. I will open a dialog for you to save it now. Network has been modified. Please save it now. Empty network! Load a network file first or create and save a new one... Nothing here. Not my fault, though! Empty network! Load a network file or create something by double-clicking on the canvas! Nothing to show! creating adjacency adjacency matrix of %1 nodes View Adjacency Matrix - This will create a new random symmetric network of G(n,p) model, where n is the nodes and p is the edge probability. Please enter the number n of nodes you want: Creating random network. Please wait... Random network created. Nodes: Edges: Average path length: Clustering coefficient: On the average, edges should be This graph is almost surely connected because: probability > ln(n)/n, that is: bigger than This graph is almost surely not connected because: probability < ln(n)/n, that is: Creating uniform random network. Please wait... This will create a same degree network. Please enter the number of nodes you want: Sorry. I cannot create such a network. Links must be even number This will create a small world network, that is an undirected graph with N nodes and N*d/2 edges, where d is the mean edge degree. Please enter the number N of nodes you want: Now, enter an even number d. This is the mean edge degree each new node will have: Now, enter a parameter beta. This is the edge rewiring probability: Erasing any existing network. Creating small world. Please wait... Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ). Small world random network created: Small world network created. This will create a ring lattice network, where each node has degree d: d/2 edges to the right and d/2 to the left. Please enter the number of nodes you want: Ring lattice network created. No nodes present! Load a network file first or create some nodes... OK Nothing to find! Enter node label or number: Node found! Sorry. There is no such node in this network. Try again. Options (%1, %2); Node %3, with label %4, has %5 in-Links and %6 out-Links. Link from Node %1 to Node %2, has weight %3 and color %4. Link between node %1 and node %2, weight %3 and color %4. Nothing to do! Load a network file or add some nodes first. Nothing to remove. Choose a node to remove between ( Node removed completely. Ready. Nothing to link to! Create some nodes first. There are no nodes yet... This will draw a new link between two nodes. Enter source node ( Aborting. Source node accepted. Now enter target node ( Source and target nodes accepted. Please, enter the weight of new link: Ready. No links present! Load a network file or create a new network first. No links to remove - sorry. Remove link Source node: ( Target node: ( There is no such link. This link is reciprocal. Select what Direction to delete or Both... Both There are no nodes! Load a network file or create a new network first. No nodes created. Please click on a node first... Enter new node label: Changed label to %1. Ready. No label text. Abort. No nodes... Select node: ( Error. There is no such link. Change node color aborted. Cannot change nothing. Change node size to: (1-16) No links here! Load a network file or create a new network first. No links present... Select link source node: ( Select link target node: ( Change link color cancelled. User abort. There are no links here! Load a network file or create a new network first. New link Weight: input error. Abort. Change link weight Select what Direction to change or Both... New link weight: Change link weight cancelled. Weight not changed. Sorry. Nothing to filter! Load a network file or create a new network. Then ask me to compute something! Nothing to filter! All links are reciprocal. Your network is symmetric... There are node nodes yet! Load a network file or create a new network first. Then we can talk about layouts! I am really sorry. You must really load a file first... Embedding a spring-gravitational model on the network.... Click on the checkbox "Spring-Embedder" to stop movement! Movement stopped! There are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Embedding a repelling-attracting forces model on the network.... Click on the checkbox "Fruchterman-Reingold" to stop movement! Wake up! Load a network file or create a new network first. Then we can talk about layouts! Embedding node size model on the network.... You must be dreaming! Load a network file or create a new network first. Then we can talk about layouts! Sorry, I can't follow! Load a network file or create a new network first. Then we can talk about layouts! Nothing to layout! Are you dreaming? Calculating new nodes positions. Please wait... Nodes in inner circles have greater In-Degree Centrality. Load a network file or create a new network first! Nodes in inner circles have greater Out-Degree Centrality. Sorry, there are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Closeness Centrality. No nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Betweenness Centrality. Nothing to do! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Stress Centrality. Nothing to do here! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Graph Centrality. Nodes in inner circles have greater Eccentricity Centrality. Nodes in upper levels have greater In-Degree Centrality. Load a network file or create a new network first. Then we can talk about layouts! Nodes in upper levels have greater Out-Degree Centrality. Nodes in upper levels have greater Closeness Centrality. Nodes in upper levels have greater Betweenness Centrality. There are no nodes! Load a network file or create a new network. Then ask me to compute something! There is no network! Adjacency matrix symmetry = YES Adjacency matrix symmetry = NO There are no nodes. Nothing to do... Distance between two nodes Select source node: ( Select target node: ( Distance calculation operation cancelled. Distance Network distance ( The nodes are connected. The nodes are not connected. There are no nodes nor links! Load a network file or create a new network. Then ask me to compute something! Nothing to do! Creating distance matrix. Please wait... Matrix of sigmas Matrix of distances Cannot find the diameter of nothing... Diameter calculated. Ready. Average distance calculated. Ready. Window resized to (%1, %2) pixels. Nothing to do! Load a network file or create a new network. Then ask me to compute something! No network here. Sorry. Nothing to do. CLUSTERING COEFFICIENT REPORT Created: CLUSTERING COEFFICIENT (CLC) OF EACH NODE CLC range: 0 < C < 1 Range: 0 < GCLC < 1 GCLC = 0, when there are no cliques (i.e. acyclic tree). GCLC = 1, when every node and its neighborhood are complete cliques. Take weights into account (Default: No)? OUT-DEGREE CENTRALITY REPORT OUT-DEGREE CENTRALITIES (ODC) OF EACH NODE ODC range: 0 < C < ODC' range: 0 < C'< 1 GODC range: 0 < GODC < 1 GODC = 0, when all in-degrees are equal (i.e. regular lattice). GODC = 1, when one node completely dominates or overshadows the other nodes. The degree of the node is a measure of the 'activity' of the node it represents Nothing to do! Load a network file or create a new network. Then ask me to compute something! Nothing to do... IN-DEGREE CENTRALITY REPORT IN-DEGREE CENTRALITIES (IDC) OF EACH NODE IDC range: 0 < C < IDC' range: 0 < C'< 1 GIDC range: 0 < GIDC < 1 GIDC = 0, when all in-degrees are equal (i.e. regular lattice). GIDC = 1, when one node is linked from every other node. The in-degree of the node is a measure of the 'activity' of the node it represents CLOSENESS - CENTRALITY REPORT CLOSENESS CENTRALITY (CC) OF EACH NODE CC(u) is the invert sum of the distances of node u from all other nodes. CC' is the standardized CC CC range: 0 < C < CC' range: 0 < C'< 1 All nodes have the same CC value. Node has the maximum ACC value (std): has the minimum ACC value (std): There are different Closeness Centrality classes. GROUP CLOSENESS CENTRALISATION (GCC) GCC = GCC range: 0 < GCC < 1 GCC = 0, when the lengths of the geodesics are all equal (i.e. a complete or a circle graph). GCC = 1, when one node has geodesics of length 1 to all the other nodes, and the other nodes have geodesics of length 2 to the remaining (N-2) nodes. This is exactly the situation realised by a star graph. This measure focuses on how close a node is to all the other nodes in the set of nodes. The idea is that a node is central if it can quickly interact with all others Nothing to do... Please wait... Finished with shortest-path distances... BETWEENESS - CENTRALITY REPORT BETWEENESS CENTRALITY (BC) OF EACH NODE BC of a node u is the sum of delta (s,t,u) for all s,t in V Delta(s,t,u) is the ratio of all geodesics between s and t which run through u. Therefore, BC(u) reflects how often the node u lies on the geodesics between the other nodes of the network BC' is the standardized BC BC range: 0 < BC < (Number of pairs of nodes excluding i) BC' range: 0 < BC'< 1 (C' is 1 when the node falls on all geodesics) All nodes have the same BC value. Node has the maximum BC value: has the minimum BC value: different Betweenness Centrality classes. GROUP BETWEENESS CENTRALISATION (GBC) GBC = GBC range: 0 < GBC < 1 GBC = 0, when all the nodes have exactly the same betweenness index. GBC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Nothing to do! Why dont you try creating something first? STRESS CENTRALITY REPORT STRESS CENTRALITY (SC) OF EACH NODE SC(u) is the sum of sigma(s,t,u): the number of geodesics from s to t through u. SC(u) reflects the total number of geodesics between all other nodes which run through u SC range: 0 < SC < SC' range: 0 < SC'< 1 (SC'=1 when the node falls on all geodesics) All nodes have the same SC value. has the maximum SC value: has the minimum SC value: different Stress Centrality classes. GROUP STRESS CENTRALISATION (GSC) GSC = GSC range: 0 < GSC < 1 GSC = 0, when all the nodes have exactly the same stress index. GSC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Try creating a network first. Then I compute whatever you want... GRAPH - CENTRALITY REPORT GRAPH CENTRALITY (GC) OF EACH NODE GC range: 0 < GC < GC' range: 0 < GC'< 1 (GC'=1 => directly linked with all nodes) All nodes have the same GC value. has the maximum GC value: has the minimum GC value: different Graph Centrality classes. GROUP GRAPH CENTRALISATION (GGC) GGC = GGC range: 0 < GGC < 1 GGC = 0, when all the nodes have exactly the same graph index. GGC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. ECCENTRICITY- CENTRALITY REPORT ECCENTRICITY CENTRALITY (EC) OF EACH NODE EC of a node u is the largest geodesic distance (u,t) for t in V Therefore, EC(u) reflects how far, at most, is each node from every other node. EC' is the standardized EC EC range: 0 < EC < (max geodesic distance) EC' range: 0 < EC'< 1 All nodes have the same EC value. has the maximum EC value: has the minimum EC value: different eccentricity Centrality classes. GROUP ECCENTRICITY CENTRALISATION (GEC) GEC = GEC range: 0 < GEC < 1 GEC = 0, when all the nodes have exactly the same betweenness index. GEC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Eccentricity Centralities saved as: There are no nodes! Load a network file or create a new network. Errr...no nodes here. Sorry! Toggle Nodes Numbers. Please wait... Node Numbers are invisible now. Click the same option again to display them. Node Labels are visible again... There are no nodes! Load a network file or create a new network first. No nodes found. Sorry... Toggle Nodes Labels. Please wait... Node Labels are invisible now. Click the same option again to display them. Select new size for all nodes: (1-16) All shapes have been changed. Ready Change node shapes aborted... Change all nodenumbers size to: (1-16) Change font size: Aborted. Changed numbers size. Ready. There are no links! Load a network file or create a new network first. No nodes or edges found. Sorry... Toggle Edges Weights. Please wait... Edge weights are invisible now. Click the same option again to display them. Edge weights are visible again... There are no nodes nor links! Load a network file or create a new network first! No links found... Toggle Edges Arrows. Please wait... Links are invisible now. Click again the same menu to display them. Links visible again... There are no links! Load a network file or create a new network first! There are no links! Load a network file or create a new network! There are NO links here! Toggle links bezier. Please wait... Change link color aborted. Numbers' colors changed. Ready. Label colors changed. Ready. Toggle anti-aliasing. This will take some time if the network is large (>500)... Anti-aliasing off. Anti-aliasing on. Toggle progressbar... Progress bars off. Progress bars on. Debug messages off. Debug messages on. Toggle toolbar... Toolbar off. Toolbar on. Toggle statusbar... Status bar off. Status bar on. Tip Of The Day You can add a new node by double-clicking on the scene. You can add a new node by clicking on Add button. You can remove a node by clicking on Remove button. You can rotate the network by selecting a new angle on the dock. You can add a new link between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node. You can remove a node by right-clicking on it and selecting Remove. You can change background color (from the menu Edit > Colors). Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette. The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering). You can move a node easily by dragging it with your mouse. SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format. You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes You can change the label of node by right-clicking on it, and selecting Options > Change Label. All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or a link. Node information is displayed on the Status bar, when you left-click on it. Link information is displayed on the Status bar, when you left-click on it. Manual TextEditor &New Ctrl+N Create a new file &Open... Ctrl+O Open an existing file &Save Ctrl+S Save the document to disk Save &As... Save the document under a new name E&xit Ctrl+Q Exit the application Cu&t Ctrl+X Cut the current selection's contents to the clipboard &Copy Ctrl+C Copy the current selection's contents to the clipboard &Paste Ctrl+V Paste the clipboard's contents into the current selection &About Show the application's About box About &Qt Show the Qt library's About box &File &Edit &Help File Edit Ready TextEditor The document has been modified. Do you want to save your changes? Application Cannot read file %1: %2. File loaded Cannot write file %1: %2. File saved %1[*] - %2 socnetv-2.4/translations/socnetv_de.ts0000664000175000017500000062323013014570727020532 0ustar dimitrisdimitris HTMLViewer &File Datei &Open Öffnen Ctrl+O Strg+O Opens another helpfile Öffnet andere Hilfequelle &Print Drucken Ctrl+P Strg+P Prints out the actual network Druckt das aktuelle Netzwerk E&xit Beenden Ctrl+X Strg+X Close Manual Schließe Handbuch &Back Zurück Ctrl+B Strg+B &Backward Rückwärts &Forward Vorwärts Ctrl+F Strg+F &Home Start Ctrl+H Strg+H &Go Los MainWindow Welcome to Social Networks Visualiser, Version Willkommen zu Social Networks Visualiser, Version &New Neu Ctrl+N Strg+N Creates a new network Erstellt neues Netzwerk New network (Ctrl+N) Neues Netzwerk (Strg+N) New Creates a new network Neu Erstellt neues Netzwerk &Open Öffnen Ctrl+O Strg+O Open network (Ctrl+O) Öffne Netzwerk (Strg+O) Opens a a file of an existing network Öffnet Datei mit existierendem Netzwerk Open Opens a file of an existing network Öffnen Öffnet Datei mit existierendem Netzwerk &Save Speichern Ctrl+S Strg+S Save network (Ctrl+S) Speichere Netzwerk (Srtg+S) Saves the actual network to the current file Speichert aktuelles Netzwerk aktueller Datei Save. Saves the actual network Speichern Speichert aktuelles Netzwerk Save &As... Speichern unter Ctrl+Shift+S Strg+Shift+S Saves the actual network under a new filename Speichere aktuelles Netzwerk unter neuer Datei Save As Saves the actual network under a new filename Speichern unter Speichert aktuelles Netzwerk unter neuer Datei &BMP... BMP Export network to a BMP image Exportiere Netzwerk als BMP Bild Export BMP Export network to a BMP image Export BMP Exportiere Netzwerk als BMP Bild &PNG... PNG Export network to a PNG image Exportiere Netzwerk als PNG Bild Export PNG Export network to a PNG image Export PNG Exportiert Netzwerk als PNG Bild &PDF... PDF Export network to a PDF file Exportiere Netzwerk als PDF Datei Export PDF Export network to a PDF document Export PDF Exportiere Netzwerk als PDF Dokument &Adjacency Matrix Adjacency Matrix Export network to an adjacency matrix file Exportiere Netzwerk als Adjacency Matrix Datei Export Sociomatrix Export network to a adjacency matrix-formatted file Export Sociomatrix Exportiere Netzwerk als Adjacency Matrix-formatierte Datei &Pajek Pajek Export network to a Pajek-formatted file Exportiere Netzwerk als Pajek-formatierte Datei Export Pajek Export network to a Pajek-formatted file Export Pajek Exportiere Netzwerk als Pajek-formatierte Datei &List List Export network to a List-formatted file. Exportiere Netzwerk als List-formatierte Datei. Export List Export network to a List-formatted file Export List Exportiere Netzwerk als List-formatierte Datei &DL... DL... Export network to a DL-formatted file Exportiere Netzwerk als DL-formatierte Datei Export DL Export network to a DL-formatted Export DL Exportiere Netzwerk als DL-formatierte Datei &GW... GW... Export network to a GW-formatted file Exportiere Netzwerk als GW-formatierte Datei Export Export network to a GW formatted file Exportiere Exportiere Netzwerk als GW-formatierte Datei &Close Schließen Closes the actual network Schließt das aktuelle Netzwerk Close Closes the actual network Schließen Schließt das aktuelle Netzwerk &Print Drucken Ctrl+P Strg+P Prints whatever is viewable on the canvas. Druckt sichtbaren Bereich Printing This function prints whatever is viewable on the canvas. To print the whole network, you might want to zoom-out. Drucken Diese Funktion druckt den sichtbaren Bereich. Um das gesamte Netzwerk zu drucken ggf. herauszoomen E&xit Beenden Ctrl+Q Strg+Q Quits the application Beendet die Anwendung Exit Quits the application Beenden Beendet die Anwendung View Loaded File Betrachte geladene Datei F5 F5 Displays the loaded network file Zeigt geladene Netzwerk-Datei an View Loaded File Displays the file of the loaded network Betrachte geladene Datei Zeigt die geladene Netzwerk-Datei an View Adjacency Matrix Zeige Adjacency Matrix F6 F6 Displays the adjacency matrix of the active network Zeigt Adjacency Matrix des aktiven Netzwerkes View Network file Displays the adjacency matrix of the active network Zeige Netzwerk-Datei an Zeigt die Adjacency Matrix des aktiven Netzwerkes Erdos-Renyi G(n,p) Erdos-Renyi G(n,p) Shift+U Shift+U Creates a random network where each edge is included with a given probability Erschafft ein zufälliges Netzwerk in dem jede Kante mit gegebener Wahrscheinlichkeit eingeschlossen ist Uniform Creates a random network of G(n, p) model by connecting nodes randomly. Each edge is included in the graph with equal probability p, independently of the other edges Uniform Erschafft ein zufälliges Netzwerk im G(n, p) Model durch zufälliges Verbinden der Knoten. Jede Kante ist mit gleicher Wahrscheinlichkeit p im Graphen eingschlossen, unabhängig von den anderen Kanten Connected Verbunden Creates a connected random network Erschafft ein verbundenes zufälliges Netzwerk Uniform Connected Creates a connected random network Uniform Connected Erschafft ein verbundenes zufälliges Netzwerk Ring Lattice Ring Lattice Shift+L Shift+L Creates a ring lattice random network Erschafft ein zufälliges Ring Lattice Netzwerk Ring Lattice A ring lattice or a physicist's lattice is a graph with N nodes each connected to K neighbors, K / 2 on each side. Ring Lattice Ein Ring Lattice oder Physicist' Lattice ist ein Graph mit N Knoten, jeder verbunden mit K Nachbarn, K / 2 auf jeder Seite. Same Degree Same Degree Creates a random network where all nodes have the same degree. Erschafft ein Netzwerk in dem alle Knoten den gleichen Grad besitzen. Same Degree Creates a random network where all nodes have the same degree Same Degree Erschafft ein zufälliges Netzwerk in dem alle Knoten den selben Grad besitzen Gaussian Gaussian Creates a Gaussian distributed random network Erschafft ein zufälliges normalverteiltes Netzwerk Gaussian Creates a random network of Gaussian distribution Gaussian Erschafft ein zufälliges Netzwerk mit Gauß'scher Verteilung Small World Small World Shift+W Shift+W Creates a random network with small world properties Erschafft ein zufälliges Netzwerk mit Small World Eigenschaften Small World A Small World, according to the Watts and Strogatz model, is a random network with short average path lengths and high clustering coefficient. Small World Eine Small World, nach Watts' und Strogatz' Model, ist ein zufälliges Netzwerk mit kurzen mittleren Pfadlängen und hohem Clustering Koeffizienten Find Node Finde Knoten Ctrl+F Strg+F Finds and highlights a node by number or label. Press Ctrl+F again to undo. Findet einen Knoten nach Nummer oder Label und hebt ihn hervor. Umschalten mit Strg+F Find Node Finds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node Finde Knoten Findet einen Knoten mit gegebener Nummer oder Label und verdoppelt dessen Größe. Strg+F setzt die Skalierung wieder zurück Add Node Füge Knoten hinzu Ctrl+A Strg+A Adds a node Fügt einen Knoten hinzu Add Node Adds a node to the network Füge Knoten hinzu Fügt dem Netzwerk einen Knoten hinzu Remove Node Entferne Knoten Ctrl+Shift+A Strg+Shift+A Removes a node Entfernt einen Knoten Remove Node Removes a node from the network Entferne Knoten Entfernt einen Knoten des Netzwerkes Change Label Ändere Label Changes the Label of a node Ändert das Label eines Knotens Change Label Changes the label of a node Ändere Label Ändert das Label eines Knotens Change Color Ändere Farbe Changes the color of a node Ändert die Farbe eines Knotens Change Color Changes the Color of a node Ändere Farbe Ändert die Farbe eines Knotens Change Size Ändere Größe Changes the actual size of a node Ändert die Größe eines Knotens Change Size Changes the actual size of a node Ändere Größe Ändert die Größe eines Knotens Change Value Ändere Wert Changes the value of a node Ändert den Wert eines Knotens Change Value Changes the value of a node Ändere Wert Ändert den Wert eines Knotens Change all Nodes Size Ändere Größe aller Knoten This option lets you change the size of all nodes Diese Option erlaubt die Größe aller Knoten zu ändern Nodes Size This option lets you change the size of all nodes Knoten Größe Dies Option erlaubt die Größe aller Knoten zu ändern Change all Nodes Shape Ändere Form aller Knoten This option lets you change the shape of all nodes Diese Option erlaubt die Form aller Knoten zu ändern Nodes Shape This option lets you change the shape of all nodes Knoten Form Diese Option erlaubt die Form aller Knoten zu ändern Change Node Shape to Box Ändere Knotenform zu Box This option lets you change the shape of a node to a box Diese Option erlaubt die Form eines Knotens zu Box zu ändern Node as a box This option lets you change the shape of a node to a box Knoten als Box Diese Option erlaubt die Form eines Knotens zu Box zu ändern Change Node Shape to Triangle Ändere Knotenform zu Dreieck Change Node Shape to Circle Ändere Knotenform zu Kreis Change Node Shape to Diamond Ändere Knotenform zu Diamant Change Node Shape to Ellipse Ändere Knotenform zu Ellipse Change all Numbers Size Ändere Größe aller Nummern It lets you change the font size of the numbers of all nodes Erlaubt die Schrifftgröße der Nummern aller Knoten zu ändern Numbers Size Changes the size of the numbers of all nodes Nummern Größe Ändert die Größe der Nummern aller Knoten Change all Labels Size Ändere Größe aller Labels You can change the font size of the labels of all nodes Erlaubt die Schrifftgröße der Labels aller Knoten zu ändern Labels Size Change the fontsize of the labels of all nodes Label Größe Ändere die Schrifftgröße der Label aller Knoten Add Link Füge Link hinzu Ctrl+L Strg+L Adds a Link to a Node Fügt einem Knoten einen Link hinzu Add Link Adds a Link to the network Fügt dem Netzwerk einen Link hinzu Remove Entfernen Ctrl+Shift+L Strg+Shift+L Removes a Link Entfernt einen Link Remove Link Removes a Link from the network Entferne Link Entfernt einen Link vom Netzwerk Changes the Label of a Link Ändere Label eines Links Change Label Changes the label of a Link Ändere Label Ändert das Label eines Links Changes the Color of a Link Ändert die Farbe eines Links Change Color Changes the Color of a Link Ändere Farbe Ändert die Farbe eines Links Change Weight Ändere Gewichtung Changes the Weight of a Link Ändert die Gewichtung eines Links Change Value Changes the Weight of a Link Ändere Wert Ändert die Gewichtung eines Links Filter Nodes Filtere Knoten Filters Nodes of some value out of the network Filtert Knoten eines bestimmten Wertes aus dem Netzwerk Filter Nodes Filters Nodes of some value out of the network. Filter Knoten Filtert Knoten eines bestimmten Wertes aus dem Netzwerk Filter Links Filtere Links Filters Links of some weight out of the network Filtert Links einer bestimmten Gewichtung aus dem Netzwerk Filter Links Filters Link of some specific weight out of the network. Filter Links Filtert Links einer bestimmten Gewichtung aus dem Netzwerk Change Background Color Ändere Hintergrundfarbe Click to change the background color Klicken um Hintergrundfarbe zu ändern Background Changes background color Hintergrund Ändert Hintergrundfarbe Change all Nodes Colors Ändere Farbe aller Knoten Click to choose a new color for all nodes. Klicken um neue Farbe für alle Knoten zu wählen All Nodes Changes all nodes color at once. Alle Knoten Ändert die Farbe aller Knoten. Change all Numbers Colors Ändere Farbe aller Nummern Click to change the color of all numbers. Klicken um Farbe aller Nummern zu ändern Numbers Changes the color of all numbers. Nummern Ändert die Farbe aller Nummern Change all Labels Colors Ändere Farbe aller Label Click to change the color of all node labels. Klicken um Farbe aller Labels zu ändern Numbers Changes the color of all node labels. Nummern Ändert die Farbe aller Label Change all Links Colors Ändere Farbe aller Links Click to change the color of all links. Klicken um Farbe aller Links zu ändern Background Changes all links color Hintergrund Ändert die Farbe aller Links Transform Nodes to Links Transformiere Knoten zu Link Transforms the network so that nodes become links and vice versa Transformiert das Netzwerk so dass Knoten zu Links werden und umgekehrt Transform Nodes LinksAct Transforms network so that nodes become links and vice versa Transformiere Knoten LinksAct Transformiert das Netzwerk so dass Knoten zu Links werden und umgekehrt Symmetrize Links Mache Links symmetrisch Shift+R Shift+R Makes all edges reciprocal (thus, a symmetric graph). Macht alle Kanten reziprok (d.h. einen symmetrischen Graphen) Symmetrize Edges Transforms all arcs to double links (edges). The result is a symmetric network Mache Kanten symmetrisch Transformiert alle Bögen zu doppelten Links (Kanten). Das Ergebnis ist ein symmetrisches Netzwerk Strong Structural Strong Structural Nodes are assigned the same color if they have identical in and out neighborhoods Knoten erhalten die selbe Farbe wenn sie identische In- und Out-Nachbarn besitzen Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods Klicken um Knoten zu färben; Knoten erhalten die selbe Farbe wenn sie identische In- und Out-Nachbarn besitzen Regular Regular Nodes are assigned the same color if they have neighborhoods of the same set of colors Knoten erhalten die selbe Farbe wenn sie Nachbarschaften gleicher Farbsets besitzen Click this to colorize nodes; Nodes are assigned the same color if they have neighborhoods of the same set of colors Klicken um Knoten zu färben; Knoten erhalten die selbe Farbe wenn sie Nachbarschaften gleicher Farbsets besitzen Random Random Repositions the nodes in random places Setzt die Knoten in zufälliger Verteilung zurück Random Layout Repositions the nodes in random places Zufälliges Layout Setzt die Knoten in zufälliger Verteilung zurück Random Circle Zufälliger Kreis Repositions the nodes randomly on a circle Verteilt die Knoten zufällig auf einen Kreis Random Circle Layout Repositions the nodes randomly on a circle Kreis Layout Verteilt die Knoten zufällig auf einen Kreis In-Degree In-Degree Ctrl+1 Strg+1 Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Circle In-Degree Centrality Layout Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Out-Degree Ctrl+2 Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Circle Out-Degree Centrality Layout Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Closeness Ctrl+3 Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Circle Closeness Centrality Layout Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Betweenness Ctrl+4 Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Circle Betweenness Centrality Layout Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Informational Ctrl+5 Repositions the nodes on circles of different radius. More Informational Central Nodes are situated towards the centre. Circle Informational Centrality Layout Repositions the nodes on circles of different radius. More Informational Central Nodes are positioned towards the centre. Stress Ctrl+6 Repositions the nodes on circles of different radius. More Stressed Central Nodes are positioned towards the centre. Circle Stress Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Stress Centrality are situated towards the centre. Graph Ctrl+7 Repositions the nodes on circles of different radius. More Graphed Central Nodes are positioned towards the centre. Circle Graph Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Graph Centrality are situated towards the centre. Eccentricity Ctrl+8 Repositions the nodes on circles of different radius. Nodes of large eccentricity are positioned towards the centre. Circle Eccentricity Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Eccentricity Centrality are situated towards the centre. Remove Layout GuideLines Removes Red GuideLines from the canvas. Remove GuideLines Removes any guidelines (circles or horizontal lines) created for the network layout. Ctrl+Shift+1 Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Level In-Degree Centrality Layout Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Ctrl+Shift+2 Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Level Out-Degree Centrality Layout Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Ctrl+Shift+3 Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. level Closeness Centrality Layout Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. Ctrl+Shift+4 Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. level Betweenness Centrality Layout Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. Ctrl+Shift+5 Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Level Informational Centrality Layout Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Spring Embedder Alt+1 All nodes repel each other while the connected ones are attracted as if connected by springs. Spring Embedder Layout In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. Fruchterman-Reingold Alt+2 Repelling forces between all nodes, and attracting forces between adjacent nodes. Fruchterman-Reingold Layout Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other. Zoom &in Ctrl++ Zoom in (Ctrl++) Zooms inside the actual network. Zoom In. Zooms in. What else did you expect? Zoom &out Ctrl+- Zoom out (Ctrl+-) Zooms out of the actual network. Zoom out. Zooms out. What else did you expect? NodeSize = F (OutDegree) Alt+3 Resizes all nodes according to their out edges. NodeSize = F (OutDegree) Adjusts the size of each node according to their out-edges (OutDegree). The more out-likned a node is, the bigger will appear... NodeSize = F (InDegree) Alt+4 Resizes all nodes according to their in edges. NodeSize = F (InDegree) This method adjusts the size of each node according to their in-edges (InDegree). The more in-linked a node is, the bigger will appear... Network Symmetry Shift+S Tests if the network is symmetric or not Network Symmetry A network is symmetric when all edges are reciprocal, or, in mathematical language, when the adjacency matrix is symmetric. Graph Distance Ctrl+G Calculates the length of the shortest path between two nodes... Graph Distance The graph distance (or geodesic distance) of two nodes is the length (number of edges) of the shortest path between them. Distance &Matrix Ctrl+M Displays the matrix of graph distances between all nodes Distance Matrix A distance matrix is a NxN matrix, where the (i,j) element is the graph distance from node i to node j. Diameter Ctrl+D Calculates and displays the diameter of the network. Diameter The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network. Average Graph Distance Ctrl+B Calculates and displays the average shortest path length. Average Graph Distance This the average length of all shortest paths between the connected pair of nodes of the network. Clustering Coefficient Ctrl+C Calculates and displays the average Clustering Coefficient of the network. Clustering Coefficient The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. OutDegree Calculates and displays OutDegree Centralities OutDegree Centrality For each node k, this is the number of arcs starting from it. This is oftenly a measure of activity. InDegree Calculates and displays InDegree Centralities InDegree Centrality For each node k, this the number of arcs ending at k. Most in-degree central node might be considered more prominent among others. Calculates and displays Closeness Centralities Closeness Centrality For each node k, this the invert sum of the shortest distances between k and every other node. It is interpreted as the ability to access information through the "grapevine" of network members. Calculates and displays Betweenness Centralities Betweenness Centrality For each node k, this is the ratio of all geodesics between pairs of nodes which run through k. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. Calculates and displays Graph Centralities Graph Centrality For each node k, this is the invert of the maximum of all geodesic distances from k to all other nodes in the network. Nodes with high GC have short distances to all other nodes in the graph. Calculate and display Stress Centrality Stress Centrality For each node k, this is the total number of geodesics between all other nodes which run through k. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality Calculate and display Eccentricity Centrality Stress Centrality For each node k, this is the largest geodesic distance (k,t) from every other vertex t. Therefore, EC(u) reflects how far, at most, is each node from every other node. Calculate and display Informational Centrality Informational Centrality Calculate and display Informational Centrality Display Num&bers Toggles displaying of node numbers Display Numbers Enables/disables node numbers Display Labels Toggles displaying of node labels Display Labels Enables/disables node labels Display Links Toggle to display or not links Display Links Click to enable or disable displaying of links Display Weight Numbers Toggles displaying of numbers of links weights Display Weight Numbers Click to enable or disable displaying numbers of links weight Display Arrows Toggles displaying of arrows in the end of links Display Arrows Click to enable or disable displaying of arrows in the end of links Thickness=Weight Draws links as thick as their weights (if specified) Draw As Thick As Weights Click to toggle having all links as thick as their weight (if specified) Bezier Curves Draws links as Bezier curves Links Bezier Enables/Disables drawing Links as Bezier curves. Anti-Aliasing F8 Enables/disables anti-aliasing Enable or disable Anti-Aliasing Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... Progress Bars F10 Enables/disables Progress Bars Enable or disable Progress Bars Progress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation. Debug Messages F9 Enables/disables printing debug messages to stdout Enables or disable Debug Messages Printing debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing. Toolbar Enables/disables the toolbar Enable or disable Toolbar The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like... Statusbar Enables/disables the statusbar Enable or disable Statusbar The statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it... Manual F1 Read the manual... Manual Displays the documentation of SocNetV Tip of the Day Read useful tips Quick Tips Displays some useful and quick tips About SocNetV About Basic information about SocNetV About Qt About About Qt &Network Create Random Network Export... &Edit Node... Link... Filter... Colors &Layout In circles by centrality... In levels by centrality... Physical... &Analysis Centralities &Options Nodes Links &View &Help 25% 50% 75% 100% 125% 150% 175% Rotation: &Add Node Add a new node to the network (Ctrl+A). Alternately, you can create a new node in a specific position by double-clicking on that spot of the canvas. &Remove Node Remove a node from the network (Ctrl+Shift+A). Alternately, you can remove a node by right-clicking on it. Add &Link Add a new link to the network (Ctrl+L). Alternately, you can create a new link between two nodes by middle-clicking on them consequetively. Remove Link Remove a link from the network Alternately, you can remove a link by right-clicking on it. Edit Network Edit Total Nodes Total Links Counts how many nodes (vertices) exist in the whole network. Counts how many links (in and out) exist in the whole network. Density The density of a network is the ratio of existing links to all possible links (n(n-1)) between nodes. OutLinked Nodes: This the number of nodes with outEdges to another node. They may also have inEdges or reciprocal links. Meaningful on directed graphs. InLinked Nodes: This the number of nodes with inEdges from another node. These may also have outEdges or reciprocal links. Meaningful on directed graphs. Reciprocal-Linked: This the number of nodes with reciprocal links, namely, both inEdges and outEdges to another node. Active Node Node Number: This is the number of the last selected node. Node In-Degree: This is the number of edges ending at the node you clicked on. Node Out-Degree: This is the number of edges starting from the node you clicked on. Clustering Coef. The Clustering Coefficient quantifies how close the vertex and its neighbors are to being a clique. The proportion of links between the vertices within the neighbourhood of the clicked vertex, divided by the number of links that could possibly exist between them. Network Analysis Embeds a spring-gravitational model on the network, where each node is regarded as physical object reppeling all other nodes, while springs between connected nodes attact them. The result is constant movement. This is a very SLOW process on networks with N > 100! In Fruchterman-Reingold model, the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces to each other. Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. Kamanda-Kwei ! If you enable this, all nodes will be resized so that their size reflect their out-degree (the amount of links from them). To put it simply, more out-linked nodes will be bigger... If you enable this, all nodes will be resized so that their size reflect their in-degree (the amount of links to them from other nodes). To put it simply, more in-linked nodes will be bigger... Layout Ready. Social Network Visualiser Ready Do you want to save the changes to the network file? Yes No Cancel Choose a network file... Select one file to open All (*);;GraphML (*.graphml *.gml);;GraphViz (*.dot);;Adjacency (*.txt *.csv *.net);;Pajek (*.net *.pajek);;DL (*.dl *.net) Loaded network: Error loading requested file. Aborted. Opening aborted Saving file... No network loaded. Do you want to save this network in Pajek-formatted or SocioMatrix - formatted file? Pajek Sociomatrix Network saved under filename: . Saving network under new filename... Saving aborted Closing file... Network has not been saved. Do you want to save before closing it? Erasing old network data.... Printing... Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Links. Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Links. Dot formatted network, named %1, loaded with %2 Nodes and %3 total Links. GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Links. DL-formatted network, named %1, loaded with %2 Nodes and %3 total Links. New node (numbered %1) added. The canvas is empty! Load a network file or create a new network first. Cannot export PNG. Save Image Files (*.png) Image Saved as: Exporting completed Nothing to export! Load a network file or create a new network first. Cannot export BMP. Save Image as Image Files (*.bmp) Export to BMP... Cannot export PDF. Export to PDF Portable Document Format files (*.pdf) Export to PDF... File saved as: Cannot export to Pajek. Could not write to %1 File %1 saved Nothing to export! Load a network file or create a new network first. Cannot export to Adjacency Matrix. Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1.) Adjacency matrix-formatted network saved into file %1 Cannot export to DL. Cannot export to GW. Viewing network file - Loaded network text file Network not saved yet. I will open a dialog for you to save it now. Network has been modified. Please save it now. Empty network! Load a network file first or create and save a new one... Nothing here. Not my fault, though! Empty network! Load a network file or create something by double-clicking on the canvas! Nothing to show! creating adjacency adjacency matrix of %1 nodes View Adjacency Matrix - This will create a new random symmetric network of G(n,p) model, where n is the nodes and p is the edge probability. Please enter the number n of nodes you want: Creating random network. Please wait... Random network created. Nodes: Edges: Average path length: Clustering coefficient: On the average, edges should be This graph is almost surely connected because: probability > ln(n)/n, that is: bigger than This graph is almost surely not connected because: probability < ln(n)/n, that is: Creating uniform random network. Please wait... This will create a same degree network. Please enter the number of nodes you want: Sorry. I cannot create such a network. Links must be even number This will create a small world network, that is an undirected graph with N nodes and N*d/2 edges, where d is the mean edge degree. Please enter the number N of nodes you want: Now, enter an even number d. This is the mean edge degree each new node will have: Now, enter a parameter beta. This is the edge rewiring probability: Erasing any existing network. Creating small world. Please wait... Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ). Small world random network created: Small world network created. This will create a ring lattice network, where each node has degree d: d/2 edges to the right and d/2 to the left. Please enter the number of nodes you want: Ring lattice network created. No nodes present! Load a network file first or create some nodes... OK Nothing to find! Enter node label or number: Node found! Sorry. There is no such node in this network. Try again. Options (%1, %2); Node %3, with label %4, has %5 in-Links and %6 out-Links. Link from Node %1 to Node %2, has weight %3 and color %4. Link between node %1 and node %2, weight %3 and color %4. Nothing to do! Load a network file or add some nodes first. Nothing to remove. Choose a node to remove between ( Node removed completely. Ready. Nothing to link to! Create some nodes first. There are no nodes yet... This will draw a new link between two nodes. Enter source node ( Aborting. Source node accepted. Now enter target node ( Source and target nodes accepted. Please, enter the weight of new link: Ready. No links present! Load a network file or create a new network first. No links to remove - sorry. Remove link Source node: ( Target node: ( There is no such link. This link is reciprocal. Select what Direction to delete or Both... Both There are no nodes! Load a network file or create a new network first. No nodes created. Please click on a node first... Enter new node label: Changed label to %1. Ready. No label text. Abort. No nodes... Select node: ( Error. There is no such link. Change node color aborted. Cannot change nothing. Change node size to: (1-16) No links here! Load a network file or create a new network first. No links present... Select link source node: ( Select link target node: ( Change link color cancelled. User abort. There are no links here! Load a network file or create a new network first. New link Weight: input error. Abort. Change link weight Select what Direction to change or Both... New link weight: Change link weight cancelled. Weight not changed. Sorry. Nothing to filter! Load a network file or create a new network. Then ask me to compute something! Nothing to filter! All links are reciprocal. Your network is symmetric... There are node nodes yet! Load a network file or create a new network first. Then we can talk about layouts! I am really sorry. You must really load a file first... Embedding a spring-gravitational model on the network.... Click on the checkbox "Spring-Embedder" to stop movement! Movement stopped! There are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Embedding a repelling-attracting forces model on the network.... Click on the checkbox "Fruchterman-Reingold" to stop movement! Wake up! Load a network file or create a new network first. Then we can talk about layouts! Embedding node size model on the network.... You must be dreaming! Load a network file or create a new network first. Then we can talk about layouts! Sorry, I can't follow! Load a network file or create a new network first. Then we can talk about layouts! Nothing to layout! Are you dreaming? Calculating new nodes positions. Please wait... Nodes in inner circles have greater In-Degree Centrality. Load a network file or create a new network first! Nodes in inner circles have greater Out-Degree Centrality. Sorry, there are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Closeness Centrality. No nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Betweenness Centrality. Nothing to do! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Stress Centrality. Nothing to do here! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Graph Centrality. Nodes in inner circles have greater Eccentricity Centrality. Nodes in upper levels have greater In-Degree Centrality. Load a network file or create a new network first. Then we can talk about layouts! Nodes in upper levels have greater Out-Degree Centrality. Nodes in upper levels have greater Closeness Centrality. Nodes in upper levels have greater Betweenness Centrality. There are no nodes! Load a network file or create a new network. Then ask me to compute something! There is no network! Adjacency matrix symmetry = YES Adjacency matrix symmetry = NO There are no nodes. Nothing to do... Distance between two nodes Select source node: ( Select target node: ( Distance calculation operation cancelled. Distance Network distance ( The nodes are connected. The nodes are not connected. There are no nodes nor links! Load a network file or create a new network. Then ask me to compute something! Nothing to do! Creating distance matrix. Please wait... Matrix of sigmas Matrix of distances Cannot find the diameter of nothing... Diameter calculated. Ready. Average distance calculated. Ready. Window resized to (%1, %2) pixels. Nothing to do! Load a network file or create a new network. Then ask me to compute something! No network here. Sorry. Nothing to do. CLUSTERING COEFFICIENT REPORT Created: CLUSTERING COEFFICIENT (CLC) OF EACH NODE CLC range: 0 < C < 1 Range: 0 < GCLC < 1 GCLC = 0, when there are no cliques (i.e. acyclic tree). GCLC = 1, when every node and its neighborhood are complete cliques. Take weights into account (Default: No)? OUT-DEGREE CENTRALITY REPORT OUT-DEGREE CENTRALITIES (ODC) OF EACH NODE ODC range: 0 < C < ODC' range: 0 < C'< 1 GODC range: 0 < GODC < 1 GODC = 0, when all in-degrees are equal (i.e. regular lattice). GODC = 1, when one node completely dominates or overshadows the other nodes. The degree of the node is a measure of the 'activity' of the node it represents Nothing to do! Load a network file or create a new network. Then ask me to compute something! Nothing to do... IN-DEGREE CENTRALITY REPORT IN-DEGREE CENTRALITIES (IDC) OF EACH NODE IDC range: 0 < C < IDC' range: 0 < C'< 1 GIDC range: 0 < GIDC < 1 GIDC = 0, when all in-degrees are equal (i.e. regular lattice). GIDC = 1, when one node is linked from every other node. The in-degree of the node is a measure of the 'activity' of the node it represents CLOSENESS - CENTRALITY REPORT CLOSENESS CENTRALITY (CC) OF EACH NODE CC(u) is the invert sum of the distances of node u from all other nodes. CC' is the standardized CC CC range: 0 < C < CC' range: 0 < C'< 1 All nodes have the same CC value. Node has the maximum ACC value (std): has the minimum ACC value (std): There are different Closeness Centrality classes. GROUP CLOSENESS CENTRALISATION (GCC) GCC = GCC range: 0 < GCC < 1 GCC = 0, when the lengths of the geodesics are all equal (i.e. a complete or a circle graph). GCC = 1, when one node has geodesics of length 1 to all the other nodes, and the other nodes have geodesics of length 2 to the remaining (N-2) nodes. This is exactly the situation realised by a star graph. This measure focuses on how close a node is to all the other nodes in the set of nodes. The idea is that a node is central if it can quickly interact with all others Nothing to do... Please wait... Finished with shortest-path distances... BETWEENESS - CENTRALITY REPORT BETWEENESS CENTRALITY (BC) OF EACH NODE BC of a node u is the sum of delta (s,t,u) for all s,t in V Delta(s,t,u) is the ratio of all geodesics between s and t which run through u. Therefore, BC(u) reflects how often the node u lies on the geodesics between the other nodes of the network BC' is the standardized BC BC range: 0 < BC < (Number of pairs of nodes excluding i) BC' range: 0 < BC'< 1 (C' is 1 when the node falls on all geodesics) All nodes have the same BC value. Node has the maximum BC value: has the minimum BC value: different Betweenness Centrality classes. GROUP BETWEENESS CENTRALISATION (GBC) GBC = GBC range: 0 < GBC < 1 GBC = 0, when all the nodes have exactly the same betweenness index. GBC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Nothing to do! Why dont you try creating something first? STRESS CENTRALITY REPORT STRESS CENTRALITY (SC) OF EACH NODE SC(u) is the sum of sigma(s,t,u): the number of geodesics from s to t through u. SC(u) reflects the total number of geodesics between all other nodes which run through u SC range: 0 < SC < SC' range: 0 < SC'< 1 (SC'=1 when the node falls on all geodesics) All nodes have the same SC value. has the maximum SC value: has the minimum SC value: different Stress Centrality classes. GROUP STRESS CENTRALISATION (GSC) GSC = GSC range: 0 < GSC < 1 GSC = 0, when all the nodes have exactly the same stress index. GSC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Try creating a network first. Then I compute whatever you want... GRAPH - CENTRALITY REPORT GRAPH CENTRALITY (GC) OF EACH NODE GC range: 0 < GC < GC' range: 0 < GC'< 1 (GC'=1 => directly linked with all nodes) All nodes have the same GC value. has the maximum GC value: has the minimum GC value: different Graph Centrality classes. GROUP GRAPH CENTRALISATION (GGC) GGC = GGC range: 0 < GGC < 1 GGC = 0, when all the nodes have exactly the same graph index. GGC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. ECCENTRICITY- CENTRALITY REPORT ECCENTRICITY CENTRALITY (EC) OF EACH NODE EC of a node u is the largest geodesic distance (u,t) for t in V Therefore, EC(u) reflects how far, at most, is each node from every other node. EC' is the standardized EC EC range: 0 < EC < (max geodesic distance) EC' range: 0 < EC'< 1 All nodes have the same EC value. has the maximum EC value: has the minimum EC value: different eccentricity Centrality classes. GROUP ECCENTRICITY CENTRALISATION (GEC) GEC = GEC range: 0 < GEC < 1 GEC = 0, when all the nodes have exactly the same betweenness index. GEC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Eccentricity Centralities saved as: There are no nodes! Load a network file or create a new network. Errr...no nodes here. Sorry! Toggle Nodes Numbers. Please wait... Node Numbers are invisible now. Click the same option again to display them. Node Labels are visible again... There are no nodes! Load a network file or create a new network first. No nodes found. Sorry... Toggle Nodes Labels. Please wait... Node Labels are invisible now. Click the same option again to display them. Select new size for all nodes: (1-16) All shapes have been changed. Ready Change node shapes aborted... Change all nodenumbers size to: (1-16) Change font size: Aborted. Changed numbers size. Ready. There are no links! Load a network file or create a new network first. No nodes or edges found. Sorry... Toggle Edges Weights. Please wait... Edge weights are invisible now. Click the same option again to display them. Edge weights are visible again... There are no nodes nor links! Load a network file or create a new network first! No links found... Toggle Edges Arrows. Please wait... Links are invisible now. Click again the same menu to display them. Links visible again... There are no links! Load a network file or create a new network first! There are no links! Load a network file or create a new network! There are NO links here! Toggle links bezier. Please wait... Change link color aborted. Numbers' colors changed. Ready. Label colors changed. Ready. Toggle anti-aliasing. This will take some time if the network is large (>500)... Anti-aliasing off. Anti-aliasing on. Toggle progressbar... Progress bars off. Progress bars on. Debug messages off. Debug messages on. Toggle toolbar... Toolbar off. Toolbar on. Toggle statusbar... Status bar off. Status bar on. Tip Of The Day You can add a new node by double-clicking on the scene. You can add a new node by clicking on Add button. You can remove a node by clicking on Remove button. You can rotate the network by selecting a new angle on the dock. You can add a new link between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node. You can remove a node by right-clicking on it and selecting Remove. You can change background color (from the menu Edit > Colors). Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette. The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering). You can move a node easily by dragging it with your mouse. SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format. You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes You can change the label of node by right-clicking on it, and selecting Options > Change Label. All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or a link. Node information is displayed on the Status bar, when you left-click on it. Link information is displayed on the Status bar, when you left-click on it. Manual TextEditor &New Ctrl+N Create a new file &Open... Ctrl+O Open an existing file &Save Ctrl+S Save the document to disk Save &As... Save the document under a new name E&xit Ctrl+Q Exit the application Cu&t Ctrl+X Cut the current selection's contents to the clipboard &Copy Ctrl+C Copy the current selection's contents to the clipboard &Paste Ctrl+V Paste the clipboard's contents into the current selection &About Show the application's About box About &Qt Show the Qt library's About box &File &Edit &Help File Edit Ready TextEditor The document has been modified. Do you want to save your changes? Application Cannot read file %1: %2. File loaded Cannot write file %1: %2. File saved %1[*] - %2 socnetv-2.4/NEWS0000664000175000017500000002043613245330127014000 0ustar dimitrisdimitrisSocial Networks Visualizer (SocNetV) SocNetV News -==========- 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 socnetv-2.4/COPYING0000664000175000017500000010577013014570727014347 0ustar dimitrisdimitris You may use, distribute and copy SocNetV under the terms of GNU General Public License version 3, which is displayed below. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .