diff --git a/.gitignore b/.gitignore index 46f35855b6ae89aa7957b3766d25cd0e86a3d3c9..6f0c20c4e415d2e188525aa559489864e972cde7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ *.exe *.out *.app +Sparkle.framework # Qt-es *.user diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..1db2f94407518617045a7a6f66509408e9792f70 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sparkle/Sparkle"] + path = sparkle/Sparkle + url = https://github.com/sparkle-project/Sparkle.git diff --git a/.packer.yml b/.packer.yml index 8ef58dac29a5ac3db2857bfc2839af4a60350123..137453af58ca4f02b43c175c16bfa8cc6d4038be 100644 --- a/.packer.yml +++ b/.packer.yml @@ -39,6 +39,13 @@ osx: - echo "Client" - git clone https://gerrit-ring.savoirfairelinux.com/ring-client-macosx ring-client-macosx - cd ring-client-macosx + - git submodule init + - git submodule update + - cd sparkle/Sparkle + - export BUILDDIR=$(pwd) + - make release + - mv Build/Products/Release/Sparkle.framework ../ + - cd ../../ - mkdir build - cd build - export CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index a7d44719931e70e8f533e4df1b10c7924d191f1f..ba0e551e2885538474cf363b994502f8f6d7907b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,51 +5,129 @@ IF(POLICY CMP0022) ENDIF(POLICY CMP0022) SET(PROJ_NAME Ring) -SET(RING_VERSION "0.1.0") + +# To build nighlies we need to update RING_VERSION with an optional command line arg +IF("${RING_VERSION}" STREQUAL "") + SET(RING_VERSION "0.4.0") +ENDIF("${RING_VERSION}" STREQUAL "") + +MESSAGE("Building Ring version - " ${RING_VERSION}) + SET(RING_VERSION_NAME "Samuel de Champlain") -SET(BUNDLE_VERSION "Samuel de Champlain (0.1.0") +SET(BUNDLE_VERSION "Samuel de Champlain - beta") SET(PROJ_COPYRIGHT " © 2015 Savoir-faire Linux \n GPLv3 https://www.gnu.org/copyleft/gpl.html") -ADD_DEFINITIONS("-std=c++11") +ADD_DEFINITIONS("-std=c++11 -fobjc-arc") PROJECT(${PROJ_NAME}) FIND_PACKAGE(Qt5Core REQUIRED) +FIND_PACKAGE(Qt5MacExtras REQUIRED) FIND_PACKAGE(Qt5Widgets REQUIRED) FIND_PACKAGE(LibRingClient REQUIRED) +IF(NOT (${ENABLE_SPARKLE} MATCHES false)) + MESSAGE("Sparkle auto-update enabled") + + # find_library searches in /Library/Frameworks by default + # We add an hint to our custom location + + FIND_LIBRARY(SPARKLE_FRAMEWORK + NAMES Sparkle + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/sparkle) + IF(EXISTS ${SPARKLE_FRAMEWORK}) + SET(ENABLE_SPARKLE 1 CACHE BOOLEAN "Enable Sparkle") + ADD_DEFINITIONS(-DENABLE_SPARKLE=1) + MESSAGE("Sparkle is here:" ${SPARKLE_FRAMEWORK}) + FIND_PATH(SPARKLE_INCLUDE_DIR Sparkle.h HINTS ${SPARKLE_FRAMEWORK}/Headers) + MESSAGE("INCLUDE " ${SPARKLE_INCLUDE_DIR}) + # we need to copy the public key to check the updates + SET(PUBLIC_KEY_PATH "${CMAKE_CURRENT_SOURCE_DIR}/sparkle/dsa_pub.pem") + IF(EXISTS ${PUBLIC_KEY_PATH}) + MESSAGE(STATUS "Looking for Public Key - found") + SET_SOURCE_FILES_PROPERTIES(${PUBLIC_KEY_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + SET(PUBLIC_KEY ${PUBLIC_KEY_PATH}) + ELSE(EXISTS ${PUBLIC_KEY_PATH}) + MESSAGE(WARNING "Looking for Public Key - not found") + MESSAGE(WARNING "${PUBLIC_KEY_PATH} not found Sparkle Framework will NOT work and may even prevent application from launching. Please consider disabling Sparkle Framework, creating a keypair for testing purposes") + ENDIF(EXISTS ${PUBLIC_KEY_PATH}) + ELSE() + MESSAGE(FATAL_ERROR "Sparkle framework not found, build it (see README) or disable Sparkle (-DENABLE_SPARKLE=false)") + ENDIF(EXISTS ${SPARKLE_FRAMEWORK}) +ENDIF(NOT (${ENABLE_SPARKLE} MATCHES false)) + INCLUDE_DIRECTORIES(SYSTEM ${Qt5Core_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(SYSTEM ${Qt5MacExtras_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}) INCLUDE_DIRECTORIES(${LIB_RING_CLIENT_INCLUDE_DIR}) -MESSAGE("LibRingClient is here:" ${LIB_RING_CLIENT_INCLUDE_DIR}) +MESSAGE("LRC is here:" ${LIB_RING_CLIENT_INCLUDE_DIR}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w") #Files to compile -SET(ringclient_SRCS - src/main.mm - src/AppDelegate.mm - src/RingWindowController.mm - src/ConversationsViewController.mm - src/PreferencesViewController.mm +SET(ringclient_CONTROLLERS src/QNSTreeController.mm + src/QNSTreeController.h + src/CurrentCallVC.mm + src/CurrentCallVC.h + src/GeneralPrefsVC.mm + src/RingWizardWC.mm + src/RingWizardWC.h + src/RingWindowController.mm + src/RingWindowController.h + src/ConversationsVC.mm + src/ConversationsVC.h + src/PreferencesVC.mm + src/PreferencesVC.h src/AccGeneralVC.mm - src/AccAudioVC.mm + src/AccGeneralVC.h src/AccVideoVC.mm + src/AccVideoVC.h + src/AccAudioVC.mm + src/AccAudioVC.h src/AccRingVC.mm + src/AccRingVC.h src/AccAdvancedVC.mm + src/AccAdvancedVC.h src/AccSecurityVC.mm - src/AccountsVC.mm - src/CurrentCallVC.mm + src/AccSecurityVC.h + src/CertificateWC.mm + src/CertificateWC.h src/AudioPrefsVC.mm + src/AudioPrefsVC.h + src/AccountsVC.mm + src/AccountsVC.h src/VideoPrefsVC.mm - src/GeneralPrefsVC.mm - src/RingWizardWC.mm - src/HistoryViewController.mm - - src/backends/MinimalHistoryBackend.mm) + src/VideoPrefsVC.h + src/GeneralPrefsVC.h + src/HistoryVC.mm + src/HistoryVC.h + src/PersonsVC.mm + src/PersonsVC.h + src/ChatVC.mm + src/ChatVC.h) + +SET(ringclient_BACKENDS + src/backends/AddressBookBackend.mm + src/backends/AddressBookBackend.h) + +SET(ringclient_VIEWS + src/views/CallView.mm + src/views/CallView.h + src/views/ITProgressIndicator.mm + src/views/ITProgressIndicator.h + src/views/PersonCell.mm + src/views/PersonCell.h) + +SET(ringclient_OTHERS + src/main.mm + src/AppDelegate.mm + src/AppDelegate.h + src/delegates/ImageManipulationDelegate.mm + src/delegates/ImageManipulationDelegate.h) + SET(ringclient_XIBS MainMenu @@ -66,32 +144,10 @@ SET(ringclient_XIBS AudioPrefs VideoPrefs PreferencesScreen - RingWizard) - -SET(ringclient_HDRS - src/AppDelegate.h - src/RingWindowController.h - src/CurrentCallVC.h - src/ConversationsViewController.h - src/PreferencesViewController.h - src/AccGeneralVC.h - src/AccVideoVC.h - src/AccAudioVC.h - src/AccRingVC.h - src/AccAdvancedVC.h - src/AccSecurityVC.h - src/AudioPrefsVC.h - src/AccountsVC.h - src/VideoPrefsVC.h - src/GeneralPrefsVC.h - src/HistoryViewController.h - src/RingWizardWC.h - src/QNSTreeController.h - - src/backends/MinimalHistoryBackend.h) + RingWizard + CertificateWindow) # Icons - # This part tells CMake where to find and install the file itself SET(myApp_ICON ${CMAKE_CURRENT_SOURCE_DIR}/data/appicon.icns) SET_SOURCE_FILES_PROPERTIES(${myApp_ICON} PROPERTIES @@ -100,8 +156,12 @@ SET_SOURCE_FILES_PROPERTIES(${myApp_ICON} PROPERTIES SET(ring_ICONS ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_accept.png ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_call.png ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_cancel.png -${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_email.png -${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_new_email.png +${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_hangup.png +${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_hold.png +${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_holdoff.png +${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_mute_video.png +${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_mute_audio.png +${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_chat.png ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_search.png ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ancrage.png ${CMAKE_CURRENT_SOURCE_DIR}/data/dark/audio.png @@ -119,20 +179,44 @@ FOREACH(xib ${ringclient_XIBS}) SET(ringclient_XIBS_FOR_EXECUTABLE ${ringclient_XIBS_FOR_EXECUTABLE} ui/${xib}.xib) ENDFOREACH() -ADD_EXECUTABLE(${PROJ_NAME} MACOSX_BUNDLE - ${ringclient_SRCS} - ${ringclient_HDRS} +SET(TO_ADD + ${ringclient_CONTROLLERS} + ${ringclient_BACKENDS} + ${ringclient_VIEWS} + ${ringclient_OTHERS} ${ringclient_XIBS_FOR_EXECUTABLE} ${myApp_ICON} Credits.rtf ${ring_ICONS}) +IF(ENABLE_SPARKLE) + SET( TO_ADD ${TO_ADD} ${PUBLIC_KEY} ${SPARKLE_FRAMEWORK}) +ENDIF(ENABLE_SPARKLE) + +ADD_EXECUTABLE(${PROJ_NAME} MACOSX_BUNDLE ${TO_ADD}) + +# Follow Xcode hierarchy principles +SOURCE_GROUP("Controllers" FILES ${ringclient_CONTROLLERS}) +SOURCE_GROUP("Backends" FILES ${ringclient_BACKENDS}) +SOURCE_GROUP("CustomViews" FILES ${ringclient_VIEWS}) +SOURCE_GROUP("Classes" FILES ${ringclient_OTHERS}) +SOURCE_GROUP("Resources\\Interface Builder" FILES ${ringclient_XIBS_FOR_EXECUTABLE}) + +IF(ENABLE_SPARKLE) + SOURCE_GROUP("Frameworks" FILES ${SPARKLE_FRAMEWORK}) +ENDIF(ENABLE_SPARKLE) + TARGET_LINK_LIBRARIES( ${PROJ_NAME} ${LIB_RING_CLIENT_LIBRARY} ${Qt5Core_LIBRARIES} + ${Qt5MacExtras_LIBRARIES} ${Qt5Widgets_LIBRARIES} ) +IF(ENABLE_SPARKLE) + TARGET_LINK_LIBRARIES(${PROJ_NAME} ${SPARKLE_FRAMEWORK}) +ENDIF(ENABLE_SPARKLE) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Cocoa") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Quartz") @@ -140,15 +224,15 @@ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AVFoundation") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AddressBook") # These variables are specific to our plist and are NOT standard CMake variables -set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "MainMenu") -set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "NSApplication") +SET(MACOSX_BUNDLE_NSMAIN_NIB_FILE "MainMenu") +SET(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "NSApplication") SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in MACOSX_BUNDLE_GUI_IDENTIFIER "cx.ring" - MACOSX_BUNDLE_SHORT_VERSION_STRING ${RING_VERSION_NAME} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${RING_VERSION} MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJ_NAME} ${RING_VERSION} Nightly" - MACOSX_BUNDLE_BUNDLE_VERSION ${RING_VERSION} + MACOSX_BUNDLE_BUNDLE_VERSION ${RING_VERSION_NAME} MACOSX_BUNDLE_COPYRIGHT "${PROJ_COPYRIGHT}" MACOSX_BUNDLE_INFO_STRING "Nightly build of ${PROJ_NAME} ${RING_VERSION} for testing and development" MACOSX_BUNDLE_BUNDLE_NAME ${PROJ_NAME} @@ -163,10 +247,6 @@ IF(${IBTOOL} STREQUAL "IBTOOL-NOTFOUND") the Apple developer tools. The default system paths were searched in addition to ${OSX_DEVELOPER_ROOT}/usr/bin") endif() -# Make sure the 'Resources' Directory is correctly created before we build -ADD_CUSTOM_COMMAND(TARGET ${PROJ_NAME} PRE_BUILD - COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/\${CONFIGURATION}/${PROJ_NAME}.app/Contents/Resources) - # Compile the .xib files using the 'ibtool' program with the destination being the app package FOREACH(xib ${ringclient_XIBS}) ADD_CUSTOM_COMMAND(TARGET ${PROJ_NAME} POST_BUILD @@ -177,26 +257,45 @@ FOREACH(xib ${ringclient_XIBS}) ENDFOREACH() -set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}) +SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}) SET(APPS "\${CMAKE_INSTALL_PREFIX}/${PROJ_NAME}.app") -#-------------------------------------------------------------------------------- -# Install the QtTest application, on Apple, the bundle is at the root of the -# install tree INSTALL(TARGETS ${PROJ_NAME} BUNDLE DESTINATION . COMPONENT Runtime) -SET(QT_PLUGINS_DESTDIR ${PROJ_NAME}.app/Contents/Plugins/platforms) +SET(QT_PLUGINS_DESTDIR ${PROJ_NAME}.app/Contents/Plugins) #-------------------------------------------------------------------------------- # Install needed Qt plugins by copying directories from the qt installation -LIST(APPEND QT_PLUGINS Qt5::QTgaPlugin Qt5::QTiffPlugin Qt5::QCocoaIntegrationPlugin) -FOREACH(plugin ${QT_PLUGINS}) + + +#MESSAGE("GUI====") +#foreach(plugin ${Qt5Gui_PLUGINS}) +# message("Plugin ${plugin} is at location ${_loc}") +#endforeach() + +LIST(APPEND QT_PLUGINS_IMAGEFORMAT Qt5::QTgaPlugin + Qt5::QGifPlugin + Qt5::QICNSPlugin + Qt5::QICOPlugin + Qt5::QJpegPlugin + Qt5::QJp2Plugin + Qt5::QMngPlugin + Qt5::QTiffPlugin + Qt5::QDDSPlugin) + +# we need two plugin directories platform and imageformats +GET_TARGET_PROPERTY(_loc Qt5::QCocoaIntegrationPlugin LOCATION) +INSTALL(FILES ${_loc} DESTINATION ${QT_PLUGINS_DESTDIR}/platforms COMPONENT Runtime) +LIST(APPEND QT_PLUGINS Qt5::QCocoaIntegrationPlugin) + +FOREACH(plugin ${QT_PLUGINS_IMAGEFORMAT}) GET_TARGET_PROPERTY(_loc ${plugin} LOCATION) - INSTALL(FILES ${_loc} DESTINATION ${QT_PLUGINS_DESTDIR} COMPONENT Runtime) + INSTALL(FILES ${_loc} DESTINATION ${QT_PLUGINS_DESTDIR}/imageformats COMPONENT Runtime) + LIST(APPEND QT_PLUGINS ${plugin}) ENDFOREACH() # directories to look for dependencies -SET(DIRS ${CMAKE_INSTALL_PREFIX}/lib ${QT_LIB_DIR}) +SET(DIRS ${CMAKE_INSTALL_PREFIX}/lib ${QT_LIB_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/sparkle) INSTALL(CODE " file(GLOB_RECURSE QTPLUGINS diff --git a/README.md b/README.md index b2eaadd93c6ac6f27a7a68f4f3399b54147f811f..e5567c74a969393feabdb02c7b5f97b50927a293 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,47 @@ Requirements Build instructions ================== -2. mkdir build && cd build +Build Sparkle framework (optional) +---------------------------------- +Ring can ship with the Sparkle framework to allow automatic app updates. +This can be disabled for your custom build by specifying -DENABLE_SPARKLE=false +in the cmake phase. -3. export CMAKE_PREFIX_PATH=<dir_to_qt5> +1. cd sparkle/ +2. git submodule update +3. cd Sparkle/ +4. make release +5. A Finder window will popup in the directory where Sparkle has been built. +Copy-paste the Sparkle.framework in sparkle/ in our project, or in +/Library/Frameworks on your system. + +Build Client +------------ + +1. mkdir build && cd build + +2. export CMAKE_PREFIX_PATH=<dir_to_qt5> Now generate an Xcode project with CMake: -4. cmake ../ -DCMAKE_INSTALL_PREFIX=<libringclient_install_path> -G Xcode -5. open Ring.xcodeproj/ -6. Build and run it from Xcode. You can also generate the final Ring.app bundle. +3. cmake ../ -DCMAKE_INSTALL_PREFIX=<libringclient_install_path> -G Xcode +4. open Ring.xcodeproj/ +5. Build and run it from Xcode. You can also generate the final Ring.app bundle. You can also build it from the command line: -4. cmake ../ -DCMAKE_INSTALL_PREFIX=<libringclient_install_path> -5. make -6. open Ring.app/ +3. cmake ../ -DCMAKE_INSTALL_PREFIX=<libringclient_install_path> +4. make +5. open Ring.app/ + +If you want to create the final app (self-containing .dmg): + +4. make install +5. cpack -G DragNDrop Ring + +Notes: +By default the client version is specified in CMakeLists.txt but it can be +overriden by specifying -DRING_VERSION=<num> in the cmake command line. Debugging ================== diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in index 857c6f65cea51a321d7ccbd12a8b31b345e93210..379e70967bbd8be90ecdda4f7d3cca03d81dd4be 100644 --- a/cmake/MacOSXBundleInfo.plist.in +++ b/cmake/MacOSXBundleInfo.plist.in @@ -2,36 +2,40 @@ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>CFBundleExecutable</key> - <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> - <key>CFBundleGetInfoString</key> - <string>${MACOSX_BUNDLE_INFO_STRING}</string> - <key>CFBundleIconFile</key> - <string>${MACOSX_BUNDLE_ICON_FILE}</string> - <key>CFBundleIdentifier</key> - <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleLongVersionString</key> - <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> - <key>CFBundleName</key> - <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> - <key>CSResourcesFileMapped</key> - <true/> - <key>LSRequiresCarbon</key> - <true/> - <key>NSHumanReadableCopyright</key> - <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleGetInfoString</key> + <string>${MACOSX_BUNDLE_INFO_STRING}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>SUPublicDSAKeyFile</key> + <string>dsa_pub.pem</string> + <key>SUFeedURL</key> + <string>http://gpl.savoirfairelinux.net/ring-download/mac_osx/sparkle-ring.xml</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleLongVersionString</key> + <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CSResourcesFileMapped</key> + <true/> + <key>LSRequiresCarbon</key> + <true/> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> <key>NSHighResolutionCapable</key> <string>True</string> <key>NSMainNibFile</key> diff --git a/data/dark/ic_action_call.png b/data/dark/ic_action_call.png index 4960e9699fa68b2751c694503cf89b652123ff59..7c9d1b09c55f77d44877d4a354280ae5c2e1f645 100644 Binary files a/data/dark/ic_action_call.png and b/data/dark/ic_action_call.png differ diff --git a/data/dark/ic_action_chat.png b/data/dark/ic_action_chat.png new file mode 100644 index 0000000000000000000000000000000000000000..9102f25dfd0e35c97a5c19c8988c02b364007fd1 Binary files /dev/null and b/data/dark/ic_action_chat.png differ diff --git a/data/dark/ic_action_email.png b/data/dark/ic_action_email.png deleted file mode 100644 index 2e5855137e34c10301a793648cadc6feeabfa779..0000000000000000000000000000000000000000 Binary files a/data/dark/ic_action_email.png and /dev/null differ diff --git a/data/dark/ic_action_hangup.png b/data/dark/ic_action_hangup.png new file mode 100644 index 0000000000000000000000000000000000000000..44794a9b9d2dbd58656bf4a0d107de3e830dafb4 Binary files /dev/null and b/data/dark/ic_action_hangup.png differ diff --git a/data/dark/ic_action_hold.png b/data/dark/ic_action_hold.png new file mode 100644 index 0000000000000000000000000000000000000000..bb707eab97b3d48167a29bb8c21fc02cf582ffac Binary files /dev/null and b/data/dark/ic_action_hold.png differ diff --git a/data/dark/ic_action_holdoff.png b/data/dark/ic_action_holdoff.png new file mode 100644 index 0000000000000000000000000000000000000000..5345ee3c4a7a3576b7493cfe3b4243d183c5d996 Binary files /dev/null and b/data/dark/ic_action_holdoff.png differ diff --git a/data/dark/ic_action_mute_audio.png b/data/dark/ic_action_mute_audio.png new file mode 100644 index 0000000000000000000000000000000000000000..084bf3c9f4d780e5e79d6d64e9c352f02f82b207 Binary files /dev/null and b/data/dark/ic_action_mute_audio.png differ diff --git a/data/dark/ic_action_mute_video.png b/data/dark/ic_action_mute_video.png new file mode 100644 index 0000000000000000000000000000000000000000..32d61d8dff7c7cb6eb0be698441b12190a4e3579 Binary files /dev/null and b/data/dark/ic_action_mute_video.png differ diff --git a/data/dark/ic_action_new_email.png b/data/dark/ic_action_new_email.png deleted file mode 100644 index 2199ba2a989a994004f437891115a6e37c5445df..0000000000000000000000000000000000000000 Binary files a/data/dark/ic_action_new_email.png and /dev/null differ diff --git a/sparkle/LICENSE b/sparkle/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f4020f8d31d5a4deaaf783a8a843cab1da9ba21d --- /dev/null +++ b/sparkle/LICENSE @@ -0,0 +1,60 @@ +Copyright (c) 2006-2013 Andy Matuschak. +Copyright (c) 2009-2013 Elgato Systems GmbH. +Copyright (c) 2011-2014 Kornel Lesiński. +Copyright (c) 2014 C.W. Betts. +Copyright (c) 2014 Petroules Corporation. +Copyright (c) 2014 Big Nerd Ranch. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================= +EXTERNAL LICENSES +================= + +bspatch.c and bsdiff.c, from bsdiff 4.3 <http://www.daemonology.net/bsdiff/>: + Copyright (c) 2003-2005 Colin Percival. + +sais.c and sais.c, from sais-lite (2010/08/07) <https://sites.google.com/site/yuta256/sais>: + Copyright (c) 2008-2010 Yuta Mori. + +SUDSAVerifier.m: + Copyright (c) 2011 Mark Hamlin. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted providing that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/sparkle/Sparkle b/sparkle/Sparkle new file mode 160000 index 0000000000000000000000000000000000000000..4e332850686b2ac9f581a4779af4d4b442fdd5be --- /dev/null +++ b/sparkle/Sparkle @@ -0,0 +1 @@ +Subproject commit 4e332850686b2ac9f581a4779af4d4b442fdd5be diff --git a/sparkle/dsa_pub.pem b/sparkle/dsa_pub.pem new file mode 100644 index 0000000000000000000000000000000000000000..97455e2f22881744b33fbb98e112f0a40681bfcd --- /dev/null +++ b/sparkle/dsa_pub.pem @@ -0,0 +1,36 @@ +-----BEGIN PUBLIC KEY----- +MIIGRzCCBDoGByqGSM44BAEwggQtAoICAQCp4+JqCDyIMIMGtvpMvEPsQJ2SLJrt +y16KsLNmcUXLMMSmHdiC2EEZMhfp4OyuXwLGewA1NXBrBS6+6GidA0hh/IhclMUs +9kjzplVK4mOdKdSvFwuoJ9fdth+ySAXnhpcyLVFKQeoZ/jP20IhW9p+qZE4EMUlx +Pmls+MbNcZLu/HKiGI4XMN2K4yCxLSFjlpEPcT4yBYAZb+YRdY0v2HK3e9Jnja1b +Jfm23NaTRxkWzAu2Cm2S8G7JRo3Uuaw7RUmaAkmVWXFC0ZloGKBSeey6y1EuUtVy +dju3DRVI3RuvmB4yFJvdfgctTR2U6N26H733aOLFsvsSr6/hNp7q0ryDEfjqyW+R +SJwKZIRwl0WTsxwUzw+OejQH9CNcgkRaPgWBntnZ4OWSr2gFPkolt+VpLhSvKiSb +0ef3vZBuTp3KNCDGE20OVfQSeCstUyLZpLeG7tRyJEP/aCni9YTpIhZ5B9XNFe2J +jfzZE2VefKJWpxI1THfPgb0hto6zBuc8kpcKRPqwTRUHQuNwjAuAUKFV3GM9aoUC +KISWXPg2p1z8LgkuM8sgGEhn0BYEfpJFP3wc1OtIlv0t8Bqm1QR1y6hD/uxCYqq+ +KR9/0eOsNH7dO/+7ydZjvVcBZ3TeGhvLQB/0Iic4Y895WMvN8bSB7NOZ8ODesO0J +zg2UkMdxdntiKQIhAKISld6gn3g1WSPXvWqT9mZzBly0hXr4DnGI1UtCeQm3AoIC +AQCMiu6knB8mbhcb7bOGhm3JEfi42+j3zavBYOga7LxP18Fobbf+5bHP3kMdNx8y +Paf0q0BkGtRC0WyH0ja05vR0bS9dSUT7qshQXm+/BsA/fnWPC54NcGSfRlj1UqHc +NN39r68EseO7w+w5x1gYFY7Jx/wJqR7gbYgS2GhgIrUo4+vBurl2bVtx6cAwsNXa +h0GUPAGQUu6qJaM5cpZL2Fkx+ac73q9i3WAlCECrkLpvOkLBSbYNvRR1rlhGawGr +Z96zEBEcW5FPJvPsjY2WaOvaRfGF9Y0MK8WXptdxY41jdts7n7kRKuwheUrm0bHm +aCRkGwhtc6hsMdrSzNFLDDScaSjYMx5erqnAKMyieyoiD8gyYN5mhZUokTBdpT1m +n7lrpQ0KfJtNKFtNUfNmU406vMEiTPKG4wxX/RxdzUqLSKNV1j0JHN6kx4Sq/vLN +EzO85ZaA79nBd2/8+ktWRiOuCiLu913Obgw3muNKYNVmH6iJibAYP+n7uUZHCzO4 +MxccO5gy1umgTx/16Sya5ov+xt7CmS7kE4M4GzQ+AwXqzx3Mo8O72OWJP7RoRPxt +KTNiNZcjFrPkP4MkAogKNDt3McUXmKzfWEa+EvKHtXav7yiKoZ/kmQCawYQyvKFP +oBloHZ5N2iPnRGfABmFk/exF1Nb2dlhtD1hNYqtD3IWmVAOCAgUAAoICAFSPpbKF +wWcMAwTP7nEWZUr/8efPftwR2Q3F00dbh3ND+Yv7VRam6br+sPnrrPElWL+pPoFy +Vg7qJ6qmsOBgB+dDSiJ5w5L+aIj+vtmQHyCbbLTkCqzC5AO4pMaaXhg5hRQJw6JN +VkLByDsqHmjGG5ZLILzzKLi88X5Tz/Zz5FHWisnwRSGQaoZ5xJOCLfPLTOnASB/Q +uR5nBpYjImZslsPnDwTXVLqqOFo2TiQ3BXGV3BGpP83jaoDSVMjgc2NJNLw7X++b +mEFkALkG9uhhO57dTShwI+S3IzJfIBhSFW59bkY/N0f8peKAiUXmi3M/QWCvfh4k ++WRBaRiq+Ap+wV+IM+PH/INm0uEJ97mP5+7dPMZDNq1iPnJOKhqyXskq6i/Z9eg5 +ZzgBw6Pxj6cNhZeg8OQuTfCGIV0m0FtfOZZVUs6l1JlMGb9bGbx2cDJBoI1DQxpG +X01TCtyNF4ShHbFmMG4JLuxBm99YuUJud2wPXToD9pxGWbh7naJwHzL7ywQQ/A0+ +gSPE436MLSYPVeGr1RdIxFudZcoGZ2gG6V1aqZfNNlVO++UQ0wNTecFMPhdaC4O/ +mnufQC8fSX9qBdnuWfkQQk8bE0kvqz4WSZ+B9Q7bEr7XeOcWibscCslIM2Rs68DK +ZnO5P9x/rPIJLCXY4xQYBryQCMu6JC5ibWzP +-----END PUBLIC KEY----- diff --git a/sparkle/sign_update.sh b/sparkle/sign_update.sh new file mode 100644 index 0000000000000000000000000000000000000000..d15c3cd9806fabc7e97ba2e2bbf2e3612c72e0c0 --- /dev/null +++ b/sparkle/sign_update.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e +set -o pipefail +if [ "$#" -ne 2 ]; then + echo "Usage: $0 update_archive private_key" + exit 1 +fi +openssl=/usr/bin/openssl +$openssl dgst -sha1 -binary < "$1" | $openssl dgst -dss1 -sign "$2" | base64 --wrap=0 + diff --git a/sparkle/sparkle-xml-updater.sh b/sparkle/sparkle-xml-updater.sh new file mode 100644 index 0000000000000000000000000000000000000000..d0dc91bc32b605ec43ece54a894f6df8d6ef9a0c --- /dev/null +++ b/sparkle/sparkle-xml-updater.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Take the package to add as argument ./sparkle-xml-updater.sh ring.dmg + +REPO_FOLDER=<dir> +SPARKLE_FILE=<xml_filename> +REPO_URL=<url> +PACKAGE=$1 +DSA_KEY=<path_to_key_file> + +if [ ! -f ${PACKAGE} -o ! -f ${DSA_KEY} ]; then + echo "Can't find package or dsa key, aborting..." + exit 1 +fi + +if [ -f ${REPO_FOLDER}/${SPARKLE_FILE} ]; then + ITEMS=$(sed -n "/<item>/,/<\/item>/p" ${REPO_FOLDER}/${SPARKLE_FILE}) +fi + +cat << EOFILE > ${REPO_FOLDER}/${SPARKLE_FILE} +<?xml version="1.0" encoding="utf-8"?> +<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <channel> + <title>Ring - nightly</title> + <link>${REPO_URL}/${SPARKLE_FILE}</link> + <description>Most recent changes with links to updates.</description> + <language>en</language> + <item> + <title>Ring nightly $(date "+%Y/%m/%d %H:%M")</title> + <pubDate>$(date -R)</pubDate> + <enclosure url="${REPO_URL}/$(basename ${PACKAGE})" sparkle:version="$(date +%Y%m%d%H%M)" sparkle:shortVersionString="nightly-$(date "+%Y%m%d")" length="$(stat -c %s ${PACKAGE})" type="application/octet-stream" sparkle:dsaSignature="$(/opt/joulupukki/mac_keys/sign_update.sh ${PACKAGE} ${DSA_KEY})" /> + <sparkle:minimumSystemVersion>10.7</sparkle:minimumSystemVersion> + </item> +$(echo -e "${ITEMS}") + </channel> +</rss> +EOFILE + diff --git a/src/AccAdvancedVC.h b/src/AccAdvancedVC.h index 52216be7ce60e7bca1dc2e5380b47eb441cdcceb..1803d395f5e7070052701d5e910f6bd8f8e5ab05 100644 --- a/src/AccAdvancedVC.h +++ b/src/AccAdvancedVC.h @@ -31,10 +31,6 @@ #import <account.h> -@interface AccAdvancedVC : NSViewController <NSTextFieldDelegate> { - -} - -- (void)loadAccount:(Account *)account; +@interface AccAdvancedVC : NSViewController <NSTextFieldDelegate> @end diff --git a/src/AccAdvancedVC.mm b/src/AccAdvancedVC.mm index d4bfeca790c10ce8f00f2c1465c7c4bc1f4791f4..4c6be7e48b1add26760316c2d50068e194024999 100644 --- a/src/AccAdvancedVC.mm +++ b/src/AccAdvancedVC.mm @@ -39,9 +39,11 @@ #import "AccAdvancedVC.h" +#import <accountmodel.h> +#import <qitemselectionmodel.h> + @interface AccAdvancedVC () -@property Account* privateAccount; @property (assign) IBOutlet NSTextField *registrationField; @property (assign) IBOutlet NSTextField *localPortField; @property (assign) IBOutlet NSButton *isUsingSTUN; @@ -67,7 +69,6 @@ @end @implementation AccAdvancedVC -@synthesize privateAccount; @synthesize registrationField; @synthesize localPortField; @synthesize isUsingSTUN; @@ -106,12 +107,27 @@ [STUNserverURLField setTag:STUNURL_TAG]; [publishedPortField setTag:PUBLICPORT_TAG]; [publishedAddrField setTag:PUBLICADDR_TAG]; - + + QObject::connect(AccountModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + if(!current.isValid()) + return; + [self loadAccount]; + }); + } -- (void)loadAccount:(Account *)account +- (Account*) currentAccount { - privateAccount = account; + auto accIdx = AccountModel::instance()->selectionModel()->currentIndex(); + return AccountModel::instance()->getAccountByModelIndex(accIdx); +} + +- (void)loadAccount +{ + auto account = [self currentAccount]; + [self updateControlsWithTag:REGISTRATION_TAG]; [self updateControlsWithTag:LOCALPORT_TAG]; [self updateControlsWithTag:MINAUDIO_TAG]; @@ -119,40 +135,24 @@ [self updateControlsWithTag:MINVIDEO_TAG]; [self updateControlsWithTag:MAXVIDEO_TAG]; - [STUNserverURLField setStringValue:privateAccount->sipStunServer().toNSString()]; - [isUsingSTUN setState:privateAccount->isSipStunEnabled()?NSOnState:NSOffState]; - [STUNserverURLField setEnabled:privateAccount->isSipStunEnabled()]; + [STUNserverURLField setStringValue:account->sipStunServer().toNSString()]; + [isUsingSTUN setState:account->isSipStunEnabled()?NSOnState:NSOffState]; + [STUNserverURLField setEnabled:account->isSipStunEnabled()]; - if(privateAccount->isPublishedSameAsLocal()) + if(account->isPublishedSameAsLocal()) [publishAddrAndPortRadioGroup selectCellAtRow:0 column:0]; else { [publishAddrAndPortRadioGroup selectCellAtRow:1 column:0]; } - [publishedAddrField setStringValue:privateAccount->publishedAddress().toNSString()]; - [publishedPortField setIntValue:privateAccount->publishedPort()]; - [publishedAddrField setEnabled:!privateAccount->isPublishedSameAsLocal()]; - [publishedPortField setEnabled:!privateAccount->isPublishedSameAsLocal()]; + [publishedAddrField setStringValue:account->publishedAddress().toNSString()]; + [publishedPortField setIntValue:account->publishedPort()]; + [publishedAddrField setEnabled:!account->isPublishedSameAsLocal()]; + [publishedPortField setEnabled:!account->isPublishedSameAsLocal()]; } #pragma mark - NSTextFieldDelegate methods -- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor -{ - NSLog(@"textShouldBeginEditing"); - return YES; -} - -- (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error -{ - NSLog(@"didFailToValidatePartialString"); -} - --(void)controlTextDidBeginEditing:(NSNotification *)obj -{ - -} - -(void)controlTextDidChange:(NSNotification *)notif { NSTextField *textField = [notif object]; @@ -168,31 +168,31 @@ { switch ([sender tag]) { case REGISTRATION_TAG: - privateAccount->setRegistrationExpire([sender integerValue]); + [self currentAccount]->setRegistrationExpire([sender integerValue]); break; case LOCALPORT_TAG: - privateAccount->setLocalPort([sender integerValue]); + [self currentAccount]->setLocalPort([sender integerValue]); break; case STUNURL_TAG: - privateAccount->setSipStunServer([[sender stringValue] UTF8String]); + [self currentAccount]->setSipStunServer([[sender stringValue] UTF8String]); break; case PUBLICADDR_TAG: - privateAccount->setPublishedAddress([[sender stringValue] UTF8String]); + [self currentAccount]->setPublishedAddress([[sender stringValue] UTF8String]); break; case PUBLICPORT_TAG: - privateAccount->setPublishedPort([sender integerValue]); + [self currentAccount]->setPublishedPort([sender integerValue]); break; case MINAUDIO_TAG: - privateAccount->setAudioPortMin([sender integerValue]); + [self currentAccount]->setAudioPortMin([sender integerValue]); break; case MAXAUDIO_TAG: - privateAccount->setAudioPortMax([sender integerValue]); + [self currentAccount]->setAudioPortMax([sender integerValue]); break; case MINVIDEO_TAG: - privateAccount->setVideoPortMin([sender integerValue]); + [self currentAccount]->setVideoPortMin([sender integerValue]); break; case MAXVIDEO_TAG: - privateAccount->setVideoPortMax([sender integerValue]); + [self currentAccount]->setVideoPortMax([sender integerValue]); break; default: break; @@ -202,20 +202,20 @@ - (IBAction)toggleSTUN:(NSButton *)sender { - privateAccount->setSipStunEnabled([sender state]==NSOnState); - [STUNserverURLField setEnabled:privateAccount->isSipStunEnabled()]; + [self currentAccount]->setSipStunEnabled([sender state]==NSOnState); + [STUNserverURLField setEnabled:[self currentAccount]->isSipStunEnabled()]; } - (IBAction)didSwitchPublishedAddress:(NSMatrix *)matrix { NSInteger row = [matrix selectedRow]; if(row == 0) { - privateAccount->setPublishedSameAsLocal(YES); + [self currentAccount]->setPublishedSameAsLocal(YES); } else { - privateAccount->setPublishedSameAsLocal(NO); + [self currentAccount]->setPublishedSameAsLocal(NO); } - [publishedAddrField setEnabled:!privateAccount->isPublishedSameAsLocal()]; - [publishedPortField setEnabled:!privateAccount->isPublishedSameAsLocal()]; + [publishedAddrField setEnabled:![self currentAccount]->isPublishedSameAsLocal()]; + [publishedPortField setEnabled:![self currentAccount]->isPublishedSameAsLocal()]; } @@ -223,28 +223,28 @@ { switch (tag) { case REGISTRATION_TAG: - [registrationStepper setIntegerValue:privateAccount->registrationExpire()]; - [registrationField setIntegerValue:privateAccount->registrationExpire()]; + [registrationStepper setIntegerValue:[self currentAccount]->registrationExpire()]; + [registrationField setIntegerValue:[self currentAccount]->registrationExpire()]; break; case LOCALPORT_TAG: - [localPortStepper setIntegerValue:privateAccount->localPort()]; - [localPortField setIntegerValue:privateAccount->localPort()]; + [localPortStepper setIntegerValue:[self currentAccount]->localPort()]; + [localPortField setIntegerValue:[self currentAccount]->localPort()]; break; case MINAUDIO_TAG: - [minAudioPortStepper setIntegerValue:privateAccount->audioPortMin()]; - [minAudioRTPRange setIntegerValue:privateAccount->audioPortMin()]; + [minAudioPortStepper setIntegerValue:[self currentAccount]->audioPortMin()]; + [minAudioRTPRange setIntegerValue:[self currentAccount]->audioPortMin()]; break; case MAXAUDIO_TAG: - [maxAudioPortStepper setIntegerValue:privateAccount->audioPortMax()]; - [maxAudioRTPRange setIntegerValue:privateAccount->audioPortMax()]; + [maxAudioPortStepper setIntegerValue:[self currentAccount]->audioPortMax()]; + [maxAudioRTPRange setIntegerValue:[self currentAccount]->audioPortMax()]; break; case MINVIDEO_TAG: - [minVideoPortStepper setIntegerValue:privateAccount->videoPortMin()]; - [minVideoRTPRange setIntegerValue:privateAccount->videoPortMin()]; + [minVideoPortStepper setIntegerValue:[self currentAccount]->videoPortMin()]; + [minVideoRTPRange setIntegerValue:[self currentAccount]->videoPortMin()]; break; case MAXVIDEO_TAG: - [maxVideoPortStepper setIntegerValue:privateAccount->videoPortMax()]; - [maxVideoRTPRange setIntegerValue:privateAccount->videoPortMax()]; + [maxVideoPortStepper setIntegerValue:[self currentAccount]->videoPortMax()]; + [maxVideoRTPRange setIntegerValue:[self currentAccount]->videoPortMax()]; break; default: break; diff --git a/src/AccAudioVC.h b/src/AccAudioVC.h index 38f8283cf4ad275dd86b0b37186c9a0d0ff36583..1434fc85cde7315a86b921b907f4342007ba0576 100644 --- a/src/AccAudioVC.h +++ b/src/AccAudioVC.h @@ -36,11 +36,7 @@ #import "QNSTreeController.h" -@interface AccAudioVC : NSViewController <NSOutlineViewDelegate> { - -} - -- (void)loadAccount:(Account *)account; +@interface AccAudioVC : NSViewController <NSOutlineViewDelegate> @end diff --git a/src/AccAudioVC.mm b/src/AccAudioVC.mm index 704c8cc03fbf2dced06a6ec97a68b8051416635b..1370a38adc85c1f4b52b636c8f8f54f2521dcafa 100644 --- a/src/AccAudioVC.mm +++ b/src/AccAudioVC.mm @@ -37,10 +37,10 @@ #import <QSortFilterProxyModel> #import <audio/codecmodel.h> #import <accountmodel.h> +#import <qitemselectionmodel.h> @interface AccAudioVC () -@property Account* privateAccount; @property QNSTreeController *treeController; @property (assign) IBOutlet NSOutlineView *codecsView; @@ -49,17 +49,29 @@ @implementation AccAudioVC @synthesize treeController; @synthesize codecsView; -@synthesize privateAccount; - (void)awakeFromNib { NSLog(@"INIT Audio VC"); + QObject::connect(AccountModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + if(!current.isValid()) + return; + [self loadAccount]; + }); } -- (void)loadAccount:(Account *)account +- (Account*) currentAccount { - privateAccount = account; - treeController = [[QNSTreeController alloc] initWithQModel:privateAccount->codecModel()->audioCodecs()]; + auto accIdx = AccountModel::instance()->selectionModel()->currentIndex(); + return AccountModel::instance()->getAccountByModelIndex(accIdx); +} + +- (void)loadAccount +{ + auto account = [self currentAccount]; + treeController = [[QNSTreeController alloc] initWithQModel:account->codecModel()->audioCodecs()]; [treeController setAvoidsEmptySelection:NO]; [treeController setChildrenKeyPath:@"children"]; @@ -75,8 +87,8 @@ if(!qIdx.isValid()) return; - QMimeData* mime = privateAccount->codecModel()->audioCodecs()->mimeData(QModelIndexList() << qIdx); - privateAccount->codecModel()->audioCodecs()->dropMimeData(mime, Qt::MoveAction, qIdx.row() - 1, 0, QModelIndex()); + QMimeData* mime = [self currentAccount]->codecModel()->audioCodecs()->mimeData(QModelIndexList() << qIdx); + [self currentAccount]->codecModel()->audioCodecs()->dropMimeData(mime, Qt::MoveAction, qIdx.row() - 1, 0, QModelIndex()); } } @@ -86,8 +98,8 @@ if(!qIdx.isValid()) return; - QMimeData* mime = privateAccount->codecModel()->audioCodecs()->mimeData(QModelIndexList() << qIdx); - privateAccount->codecModel()->audioCodecs()->dropMimeData(mime, Qt::MoveAction, qIdx.row() + 1, 0, QModelIndex()); + QMimeData* mime = [self currentAccount]->codecModel()->audioCodecs()->mimeData(QModelIndexList() << qIdx); + [self currentAccount]->codecModel()->audioCodecs()->dropMimeData(mime, Qt::MoveAction, qIdx.row() + 1, 0, QModelIndex()); } } @@ -95,8 +107,8 @@ NSInteger row = [sender clickedRow]; NSTableColumn *col = [sender tableColumnWithIdentifier:COLUMNID_STATE]; NSButtonCell *cell = [col dataCellForRow:row]; - QModelIndex qIdx = privateAccount->codecModel()->audioCodecs()->index(row, 0, QModelIndex()); - privateAccount->codecModel()->audioCodecs()->setData(qIdx, cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); + QModelIndex qIdx = [self currentAccount]->codecModel()->audioCodecs()->index(row, 0, QModelIndex()); + [self currentAccount]->codecModel()->audioCodecs()->setData(qIdx, cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); } #pragma mark - NSOutlineViewDelegate methods @@ -157,16 +169,16 @@ if(!qIdx.isValid()) return; if([[tableColumn identifier] isEqualToString:COLUMNID_STATE]) { - [cell setState:privateAccount->codecModel()->audioCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState]; + [cell setState:[self currentAccount]->codecModel()->audioCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState]; } else if ([[tableColumn identifier] isEqualToString:COLUMNID_CODECS]) { - cell.title = privateAccount->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::NAME).toString().toNSString(); + cell.title = [self currentAccount]->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::NAME).toString().toNSString(); } else if ([[tableColumn identifier] isEqualToString:COLUMNID_FREQ]) { - cell.title = privateAccount->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::SAMPLERATE).toString().toNSString(); + cell.title = [self currentAccount]->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::SAMPLERATE).toString().toNSString(); } else if ([[tableColumn identifier] isEqualToString:COLUMNID_BITRATE]) { - cell.title = privateAccount->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::BITRATE).toString().toNSString(); + cell.title = [self currentAccount]->codecModel()->audioCodecs()->data(qIdx, CodecModel::Role::BITRATE).toString().toNSString(); } } diff --git a/src/AccGeneralVC.h b/src/AccGeneralVC.h index 139fd1b9bcf54dfc02c1804d2b012883fa9c763c..ccde3e4e422bdab95e0862e04c8e40916bbf1110 100644 --- a/src/AccGeneralVC.h +++ b/src/AccGeneralVC.h @@ -38,12 +38,6 @@ } -- (IBAction)toggleUpnp:(NSButton *)sender; -- (IBAction)toggleAutoAnswer:(NSButton *)sender; -- (IBAction)toggleCustomAgent:(NSButton *)sender; - -- (void)loadAccount:(Account *)account; - @end #endif // ACCGENERALVC_H \ No newline at end of file diff --git a/src/AccGeneralVC.mm b/src/AccGeneralVC.mm index cfb92ead7bb3b32c3df1cea02e7da4210a9b95e5..a2d33b1ba92f41f79297fc9e8895e5d6a1216e0a 100644 --- a/src/AccGeneralVC.mm +++ b/src/AccGeneralVC.mm @@ -42,8 +42,6 @@ @interface AccGeneralVC () -@property Account* privateAccount; - @property (assign) IBOutlet NSView *boxingAccount; @property (assign) IBOutlet NSView *boxingParameters; @property (assign) IBOutlet NSView *boxingCommon; @@ -54,6 +52,8 @@ @property (assign) IBOutlet NSTextField *serverHostTextField; @property (assign) IBOutlet NSTextField *usernameTextField; @property (assign) IBOutlet NSSecureTextField *passwordTextField; +@property (assign) IBOutlet NSTextField *clearTextField; +@property (assign) IBOutlet NSButton *tryRegisterButton; @property (assign) IBOutlet NSButton *upnpButton; @property (assign) IBOutlet NSButton *autoAnswerButton; @@ -72,11 +72,11 @@ @synthesize serverHostTextField; @synthesize usernameTextField; @synthesize passwordTextField; +@synthesize clearTextField; @synthesize upnpButton; @synthesize autoAnswerButton; @synthesize userAgentButton; @synthesize userAgentTextField; -@synthesize privateAccount; - (void)awakeFromNib { @@ -86,25 +86,38 @@ [usernameTextField setTag:USERNAME_TAG]; [passwordTextField setTag:PASSWORD_TAG]; [userAgentTextField setTag:USERAGENT_TAG]; + + QObject::connect(AccountModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + if(!current.isValid()) + return; + [self loadAccount]; + }); +} + +- (Account*) currentAccount +{ + auto accIdx = AccountModel::instance()->selectionModel()->currentIndex(); + return AccountModel::instance()->getAccountByModelIndex(accIdx); } - (IBAction)toggleUpnp:(NSButton *)sender { - privateAccount->setUpnpEnabled([sender state] == NSOnState); + [self currentAccount]->setUpnpEnabled([sender state] == NSOnState); } - (IBAction)toggleAutoAnswer:(NSButton *)sender { - privateAccount->setAutoAnswer([sender state] == NSOnState); + [self currentAccount]->setAutoAnswer([sender state] == NSOnState); } - (IBAction)toggleCustomAgent:(NSButton *)sender { [self.userAgentTextField setEnabled:[sender state] == NSOnState]; - privateAccount->setHasCustomUserAgent([sender state] == NSOnState); + [self currentAccount]->setHasCustomUserAgent([sender state] == NSOnState); } -- (void)loadAccount:(Account *)account +- (void)loadAccount { - - privateAccount = account; + auto account = [self currentAccount]; if([account->alias().toNSString() isEqualToString:@"IP2IP"]) { [boxingAccount.subviews setValue:@YES forKeyPath:@"hidden"]; @@ -124,6 +137,7 @@ [self.serverHostTextField setStringValue:account->hostname().toNSString()]; [self.usernameTextField setStringValue:account->username().toNSString()]; [self.passwordTextField setStringValue:account->password().toNSString()]; + [self.clearTextField setStringValue:account->password().toNSString()]; } switch (account->protocol()) { @@ -141,13 +155,43 @@ break; } - [upnpButton setState:privateAccount->isUpnpEnabled()]; - [userAgentButton setState:privateAccount->hasCustomUserAgent()]; - [userAgentTextField setEnabled:privateAccount->hasCustomUserAgent()]; - [self.autoAnswerButton setState:privateAccount->isAutoAnswer()]; + [upnpButton setState:[self currentAccount]->isUpnpEnabled()]; + [userAgentButton setState:[self currentAccount]->hasCustomUserAgent()]; + [userAgentTextField setEnabled:[self currentAccount]->hasCustomUserAgent()]; + [self.autoAnswerButton setState:[self currentAccount]->isAutoAnswer()]; [self.userAgentTextField setStringValue:account->userAgent().toNSString()]; } +- (IBAction)tryRegistration:(id)sender { + [self currentAccount] << Account::EditAction::SAVE; +} + +- (IBAction)showPassword:(NSButton *)sender { + if (sender.state == NSOnState) { + clearTextField = [[NSTextField alloc] initWithFrame:passwordTextField.frame]; + [clearTextField setTag:passwordTextField.tag]; + [clearTextField setDelegate:self]; + [clearTextField setBounds:passwordTextField.bounds]; + [clearTextField setStringValue:passwordTextField.stringValue]; + [clearTextField becomeFirstResponder]; + [boxingParameters addSubview:clearTextField]; + [passwordTextField setHidden:YES]; + } else { + [passwordTextField setStringValue:clearTextField.stringValue]; + [passwordTextField setHidden:NO]; + [clearTextField removeFromSuperview]; + clearTextField = nil; + } +} + +/** + * Debug purpose + */ +-(void) dumpFrame:(CGRect) frame WithName:(NSString*) name +{ + NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); +} + #pragma mark - NSTextFieldDelegate methods - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor @@ -161,19 +205,19 @@ switch ([textField tag]) { case ALIAS_TAG: - privateAccount->setAlias([[textField stringValue] UTF8String]); + [self currentAccount]->setAlias([[textField stringValue] UTF8String]); break; case HOSTNAME_TAG: - privateAccount->setHostname([[textField stringValue] UTF8String]); + [self currentAccount]->setHostname([[textField stringValue] UTF8String]); break; case USERNAME_TAG: - privateAccount->setUsername([[textField stringValue] UTF8String]); + [self currentAccount]->setUsername([[textField stringValue] UTF8String]); break; case PASSWORD_TAG: - privateAccount->setPassword([[textField stringValue] UTF8String]); + [self currentAccount]->setPassword([[textField stringValue] UTF8String]); break; case USERAGENT_TAG: - privateAccount->setUserAgent([[textField stringValue] UTF8String]); + [self currentAccount]->setUserAgent([[textField stringValue] UTF8String]); break; default: break; diff --git a/src/AccRingVC.h b/src/AccRingVC.h index df21709abc506ee7bcb02d61b9dfa2c016bc7501..67f03e0d07939349d17c1366e1170fd70bbc8ddb 100644 --- a/src/AccRingVC.h +++ b/src/AccRingVC.h @@ -34,12 +34,7 @@ #import <account.h> -@interface AccRingVC : NSViewController <NSTextFieldDelegate> { - - NSTextField *hashField; -} - -- (void)loadAccount:(Account *)account; +@interface AccRingVC : NSViewController <NSTextFieldDelegate> @end diff --git a/src/AccRingVC.mm b/src/AccRingVC.mm index 88a5f6a71dcd3bfc0015981b0aa9c0c151584981..f262f4ee2657d45b83eb2686f85bd13b043eb0da 100644 --- a/src/AccRingVC.mm +++ b/src/AccRingVC.mm @@ -35,9 +35,10 @@ #import "AccRingVC.h" -@interface AccRingVC () +#import <accountmodel.h> +#import <qitemselectionmodel.h> -@property Account* privateAccount; +@interface AccRingVC () @property (assign) IBOutlet NSTextField *aliasTextField; @property (assign) IBOutlet NSTextField *typeLabel; @@ -52,7 +53,6 @@ @end @implementation AccRingVC -@synthesize privateAccount; @synthesize typeLabel; @synthesize bootstrapField; @synthesize hashField; @@ -68,14 +68,25 @@ [aliasTextField setTag:ALIAS_TAG]; [userAgentTextField setTag:USERAGENT_TAG]; [bootstrapField setTag:HOSTNAME_TAG]; + + QObject::connect(AccountModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + if(!current.isValid()) + return; + [self loadAccount]; + }); } -- (void)loadAccount:(Account *)account +- (Account*) currentAccount { - if(privateAccount == account) - return; + auto accIdx = AccountModel::instance()->selectionModel()->currentIndex(); + return AccountModel::instance()->getAccountByModelIndex(accIdx); +} - privateAccount = account; +- (void)loadAccount +{ + auto account = [self currentAccount]; [self.aliasTextField setStringValue:account->alias().toNSString()]; @@ -94,33 +105,33 @@ break; } - [upnpButton setState:privateAccount->isUpnpEnabled()]; - [userAgentButton setState:privateAccount->hasCustomUserAgent()]; - [userAgentTextField setEnabled:privateAccount->hasCustomUserAgent()]; + [upnpButton setState:[self currentAccount]->isUpnpEnabled()]; + [userAgentButton setState:[self currentAccount]->hasCustomUserAgent()]; + [userAgentTextField setEnabled:[self currentAccount]->hasCustomUserAgent()]; - [autoAnswerButton setState:privateAccount->isAutoAnswer()]; + [autoAnswerButton setState:[self currentAccount]->isAutoAnswer()]; [userAgentTextField setStringValue:account->userAgent().toNSString()]; [bootstrapField setStringValue:account->hostname().toNSString()]; - if([privateAccount->username().toNSString() isEqualToString:@""]) + if([[self currentAccount]->username().toNSString() isEqualToString:@""]) [hashField setStringValue:@"Reopen account to see your hash"]; else - [hashField setStringValue:privateAccount->username().toNSString()]; + [hashField setStringValue:[self currentAccount]->username().toNSString()]; } - (IBAction)toggleUpnp:(NSButton *)sender { - privateAccount->setUpnpEnabled([sender state] == NSOnState); + [self currentAccount]->setUpnpEnabled([sender state] == NSOnState); } - (IBAction)toggleAutoAnswer:(NSButton *)sender { - privateAccount->setAutoAnswer([sender state] == NSOnState); + [self currentAccount]->setAutoAnswer([sender state] == NSOnState); } - (IBAction)toggleCustomAgent:(NSButton *)sender { [self.userAgentTextField setEnabled:[sender state] == NSOnState]; - privateAccount->setHasCustomUserAgent([sender state] == NSOnState); + [self currentAccount]->setHasCustomUserAgent([sender state] == NSOnState); } #pragma mark - NSTextFieldDelegate methods @@ -136,16 +147,16 @@ switch ([textField tag]) { case ALIAS_TAG: - privateAccount->setAlias([[textField stringValue] UTF8String]); + [self currentAccount]->setAlias([[textField stringValue] UTF8String]); break; case HOSTNAME_TAG: - privateAccount->setHostname([[textField stringValue] UTF8String]); + [self currentAccount]->setHostname([[textField stringValue] UTF8String]); break; case PASSWORD_TAG: - privateAccount->setPassword([[textField stringValue] UTF8String]); + [self currentAccount]->setPassword([[textField stringValue] UTF8String]); break; case USERAGENT_TAG: - privateAccount->setUserAgent([[textField stringValue] UTF8String]); + [self currentAccount]->setUserAgent([[textField stringValue] UTF8String]); break; default: break; diff --git a/src/AccSecurityVC.h b/src/AccSecurityVC.h index 952a8de945be990f69ed79bbc139158c21404d72..9aaf995a4c7f64cd9f20ab537bfe63b579862765 100644 --- a/src/AccSecurityVC.h +++ b/src/AccSecurityVC.h @@ -32,14 +32,8 @@ #import <Cocoa/Cocoa.h> -#import <account.h> - -@interface AccSecurityVC : NSViewController<NSPathControlDelegate, NSOpenSavePanelDelegate> { - -} - -- (void)loadAccount:(Account *)account; +@interface AccSecurityVC : NSViewController<NSMenuDelegate, NSPathControlDelegate, NSOpenSavePanelDelegate> @end -#endif // ACCSECURITYVC_H \ No newline at end of file +#endif // ACCSECURITYVC_H diff --git a/src/AccSecurityVC.mm b/src/AccSecurityVC.mm index 6302cbead0dd1bb647b95a47b12f20b13f8521de..532bfd0def76f803228bb92c1f9a440167263095 100644 --- a/src/AccSecurityVC.mm +++ b/src/AccSecurityVC.mm @@ -29,26 +29,268 @@ */ #import "AccSecurityVC.h" +#import <QUrl> +#import <certificate.h> +#import <tlsmethodmodel.h> +#import <qitemselectionmodel.h> +#import <ciphermodel.h> +#import <accountmodel.h> + +#import "QNSTreeController.h" +#import "CertificateWC.h" + +// Tags for views +#define PVK_PASSWORD_TAG 0 +#define OUTGOING_TLS_SRV_NAME 1 +#define TLS_NEGOTIATION_TAG 2 + +#define COLUMNID_NAME @"CipherNameColumn" +#define COLUMNID_STATE @"CipherStateColumn" + @interface AccSecurityVC () -@property Account* privateAccount; +@property QNSTreeController *treeController; +@property (unsafe_unretained) IBOutlet NSOutlineView *cipherListView; +@property (unsafe_unretained) IBOutlet NSButton *useTLS; +@property (unsafe_unretained) IBOutlet NSView *tlsContainer; +@property (unsafe_unretained) IBOutlet NSSecureTextField *pvkPasswordField; +@property (unsafe_unretained) IBOutlet NSTextField *outgoingTlsServerName; +@property (unsafe_unretained) IBOutlet NSTextField *tlsNegotiationTimeout; +@property (unsafe_unretained) IBOutlet NSStepper *tlsNegotiationTimeoutStepper; + +@property CertificateWC* certificateWC; + +@property (unsafe_unretained) IBOutlet NSPathControl *caListPathControl; +@property (unsafe_unretained) IBOutlet NSPathControl *certificatePathControl; +@property (unsafe_unretained) IBOutlet NSPathControl *pvkPathControl; +@property (unsafe_unretained) IBOutlet NSPopUpButton *tlsMethodList; +@property (unsafe_unretained) IBOutlet NSButton *srtpRTPFallback; +@property (unsafe_unretained) IBOutlet NSButton *useSRTP; +@property (unsafe_unretained) IBOutlet NSButton *verifyCertAsClientButton; +@property (unsafe_unretained) IBOutlet NSButton *verifyCertAsServerButton; +@property (unsafe_unretained) IBOutlet NSButton *requireCertButton; @end @implementation AccSecurityVC -@synthesize privateAccount; +@synthesize treeController; +@synthesize cipherListView; +@synthesize certificateWC; +@synthesize tlsContainer; +@synthesize useTLS; +@synthesize useSRTP; +@synthesize srtpRTPFallback; +@synthesize pvkPasswordField; +@synthesize tlsNegotiationTimeout; +@synthesize tlsNegotiationTimeoutStepper; +@synthesize outgoingTlsServerName; +@synthesize caListPathControl; +@synthesize certificatePathControl; +@synthesize pvkPathControl; +@synthesize verifyCertAsClientButton; +@synthesize verifyCertAsServerButton; +@synthesize requireCertButton; - (void)awakeFromNib { NSLog(@"INIT Security VC"); + [pvkPasswordField setTag:PVK_PASSWORD_TAG]; + [outgoingTlsServerName setTag:OUTGOING_TLS_SRV_NAME]; + [tlsNegotiationTimeoutStepper setTag:TLS_NEGOTIATION_TAG]; + [tlsNegotiationTimeout setTag:TLS_NEGOTIATION_TAG]; + + QObject::connect(AccountModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + if(!current.isValid()) + return; + [self loadAccount]; + }); } -- (void)loadAccount:(Account *)account +- (Account*) currentAccount { - privateAccount = account; + auto accIdx = AccountModel::instance()->selectionModel()->currentIndex(); + return AccountModel::instance()->getAccountByModelIndex(accIdx); +} + +- (void)loadAccount +{ + auto account = [self currentAccount]; + + [self updateControlsWithTag:PVK_PASSWORD_TAG]; + [self updateControlsWithTag:OUTGOING_TLS_SRV_NAME]; + [self updateControlsWithTag:TLS_NEGOTIATION_TAG]; + + QModelIndex qTlsMethodIdx = account->tlsMethodModel()->selectionModel()->currentIndex(); + [self.tlsMethodList removeAllItems]; + [self.tlsMethodList addItemWithTitle:qTlsMethodIdx.data(Qt::DisplayRole).toString().toNSString()]; + + treeController = [[QNSTreeController alloc] initWithQModel:account->cipherModel()]; + [treeController setAvoidsEmptySelection:NO]; + [treeController setAlwaysUsesMultipleValuesMarker:YES]; + [treeController setChildrenKeyPath:@"children"]; + + [cipherListView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil]; + [cipherListView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil]; + [cipherListView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil]; + + [useTLS setState:[self currentAccount]->isTlsEnabled()]; + [tlsContainer setHidden:![self currentAccount]->isTlsEnabled()]; + + [useSRTP setState:[self currentAccount]->isSrtpEnabled()]; + [srtpRTPFallback setState:[self currentAccount]->isSrtpRtpFallback()]; + [srtpRTPFallback setEnabled:useSRTP.state]; + + if([self currentAccount]->tlsCaListCertificate() != nil) { + NSLog(@"CA ==> %@", account->tlsCaListCertificate()->path().toNSURL()); + [caListPathControl setURL:account->tlsCaListCertificate()->path().toNSURL()]; + } else { + [caListPathControl setURL:nil]; + } + + if([self currentAccount]->tlsCertificate() != nil) { + NSLog(@" CERT ==> %@", account->tlsCertificate()->path().toNSURL()); + [certificatePathControl setURL:account->tlsCertificate()->path().toNSURL()]; + } else { + [certificatePathControl setURL:nil]; + } + + if([self currentAccount]->tlsPrivateKeyCertificate() != nil) { + NSLog(@" PVK ==> %@", account->tlsPrivateKeyCertificate()->path().toNSURL()); + [pvkPathControl setURL:account->tlsPrivateKeyCertificate()->path().toNSURL()]; + } else { + [pvkPathControl setURL:nil]; + } + + [verifyCertAsServerButton setState:[self currentAccount]->isTlsVerifyServer()]; + [verifyCertAsClientButton setState:[self currentAccount]->isTlsVerifyClient()]; + [requireCertButton setState:[self currentAccount]->isTlsRequireClientCertificate()]; +} + +- (IBAction)chooseTlsMethod:(id)sender { + int index = [sender indexOfSelectedItem]; + QModelIndex qIdx = [self currentAccount]->tlsMethodModel()->index(index, 0); + [self currentAccount]->tlsMethodModel()->selectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect); +} + +- (IBAction)toggleUseTLS:(id)sender { + [self currentAccount]->setTlsEnabled([sender state]); + [tlsContainer setHidden:![sender state]]; +} + +- (IBAction)toggleUseSRTP:(id)sender { + [self currentAccount]->setSrtpEnabled([sender state]); + [srtpRTPFallback setEnabled:[sender state]]; +} +- (IBAction)toggleRTPFallback:(id)sender { + [self currentAccount]->setSrtpRtpFallback([sender state]); +} + +- (IBAction)toggleVerifyCertAsClient:(id)sender { + [self currentAccount]->setTlsVerifyClient([sender state]); +} + +- (IBAction)toggleVerifyCertServer:(id)sender { + [self currentAccount]->setTlsVerifyServer([sender state]); +} + +- (IBAction)toggleRequireCert:(id)sender { + [self currentAccount]->setTlsRequireClientCertificate([sender state]); +} + +- (IBAction)toggleCipher:(id)sender { + NSInteger row = [sender clickedRow]; + NSTableColumn *col = [sender tableColumnWithIdentifier:COLUMNID_STATE]; + NSButtonCell *cell = [col dataCellForRow:row]; + [self currentAccount]->cipherModel()->setData([self currentAccount]->cipherModel()->index(row, 0, QModelIndex()), + cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); +} + +- (void) updateControlsWithTag:(NSInteger) tag +{ + switch (tag) { + case PVK_PASSWORD_TAG: + [pvkPasswordField setStringValue:[self currentAccount]->tlsPassword().toNSString()]; + break; + case OUTGOING_TLS_SRV_NAME: + [outgoingTlsServerName setStringValue:[self currentAccount]->tlsServerName().toNSString()]; + break; + case TLS_NEGOTIATION_TAG: + [tlsNegotiationTimeout setIntegerValue:[self currentAccount]->tlsNegotiationTimeoutSec()]; + [tlsNegotiationTimeoutStepper setIntegerValue:[self currentAccount]->tlsNegotiationTimeoutSec()]; + break; + default: + break; + } +} + +#pragma mark - NSTextFieldDelegate methods + +-(void)controlTextDidChange:(NSNotification *)notif +{ + NSTextField *textField = [notif object]; + NSRange test = [[textField currentEditor] selectedRange]; + + [self valueDidChange:textField]; + //FIXME: saving account lose focus because in NSTreeController we remove and reinsert row so View selction change + [textField.window makeFirstResponder:textField]; + [[textField currentEditor] setSelectedRange:test]; +} + +- (IBAction) valueDidChange: (id) sender +{ + switch ([sender tag]) { + case PVK_PASSWORD_TAG: + [self currentAccount]->setTlsPassword([[sender stringValue] UTF8String]); + break; + case OUTGOING_TLS_SRV_NAME: + [self currentAccount]->setTlsServerName([[sender stringValue] UTF8String]); + break; + case TLS_NEGOTIATION_TAG: + [self currentAccount]->setTlsNegotiationTimeoutSec([sender integerValue]); + break; + default: + break; + } + [self updateControlsWithTag:[sender tag]]; } #pragma mark - NSPathControl delegate methods +- (IBAction)caListPathControlSingleClick:(id)sender { + NSURL* fileURL = [[sender clickedPathComponentCell] URL]; + NSLog(@"==> %@", fileURL); + [self.caListPathControl setURL:fileURL]; + [self currentAccount]->setTlsCaListCertificate(QUrl::fromNSURL(fileURL).toString()); +} + +- (IBAction)certificatePathControlSingleClick:(id)sender { + // Select that chosen component of the path. + NSURL* fileURL = [[sender clickedPathComponentCell] URL]; + NSLog(@"==> %@", fileURL); + [self.certificatePathControl setURL:fileURL]; + [self currentAccount]->setTlsCertificate(QUrl::fromNSURL(fileURL).toString()); +} + +- (IBAction)pvkFilePathControlSingleClick:(id)sender { + NSURL* fileURL = [[sender clickedPathComponentCell] URL]; + NSLog(@"==> %@", fileURL); + [self.pvkPathControl setURL:fileURL]; + [self currentAccount]->setTlsPrivateKeyCertificate(QUrl::fromNSURL(fileURL).toString()); +} + +- (IBAction)showCA:(id)sender +{ + certificateWC = [[CertificateWC alloc] initWithWindowNibName:@"CertificateWindow"]; + [certificateWC setCertificate:[self currentAccount]->tlsCaListCertificate()]; + [self.view.window beginSheet:certificateWC.window completionHandler:nil]; +} + +- (IBAction)showEndpointCertificate:(id)sender +{ + certificateWC = [[CertificateWC alloc] initWithWindowNibName:@"CertificateWindow"]; + [certificateWC setCertificate:[self currentAccount]->tlsCertificate()]; + [self.view.window beginSheet:certificateWC.window completionHandler:nil];} /* Delegate method of NSPathControl to determine how the NSOpenPanel will look/behave. @@ -60,7 +302,15 @@ [openPanel setCanChooseDirectories:NO]; [openPanel setCanChooseFiles:YES]; [openPanel setResolvesAliases:YES]; - [openPanel setTitle:NSLocalizedString(@"Choose a file", @"Open panel title")]; + + if(pathControl == self.caListPathControl) { + [openPanel setTitle:NSLocalizedString(@"Choose a CA list", @"Open panel title")]; + } else if (pathControl == self.certificatePathControl) { + [openPanel setTitle:NSLocalizedString(@"Choose a certificate", @"Open panel title")]; + } else { + [openPanel setTitle:NSLocalizedString(@"Choose a private key file", @"Open panel title")]; + } + [openPanel setPrompt:NSLocalizedString(@"Choose", @"Open panel prompt for 'Choose a file'")]; [openPanel setDelegate:self]; } @@ -91,12 +341,97 @@ { NSLog(@"validateURL"); return YES; +} + +#pragma mark - NSMenuDelegate methods + +- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel +{ + QModelIndex qIdx; + + if([menu.title isEqualToString:@"tlsmethodlist"]) + { + qIdx = [self currentAccount]->tlsMethodModel()->index(index); + [item setTitle:qIdx.data(Qt::DisplayRole).toString().toNSString()]; + } + return YES; +} +- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu +{ + if([menu.title isEqualToString:@"tlsmethodlist"]) + return [self currentAccount]->tlsMethodModel()->rowCount(); } -- (void)panel:(id)sender didChangeToDirectoryURL:(NSURL *)url +#pragma mark - NSOutlineViewDelegate methods + +// ------------------------------------------------------------------------------- +// shouldSelectItem:item +// ------------------------------------------------------------------------------- +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item; { - //NSLog(@"didChangeToDirectoryURL"); + return YES; +} + +// ------------------------------------------------------------------------------- +// dataCellForTableColumn:tableColumn:item +// ------------------------------------------------------------------------------- +- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + NSCell *returnCell = [tableColumn dataCell]; + return returnCell; +} + +// ------------------------------------------------------------------------------- +// textShouldEndEditing:fieldEditor +// ------------------------------------------------------------------------------- +- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor +{ + if ([[fieldEditor string] length] == 0) + { + // don't allow empty node names + return NO; + } + else + { + return YES; + } +} + +// ------------------------------------------------------------------------------- +// shouldEditTableColumn:tableColumn:item +// +// Decide to allow the edit of the given outline view "item". +// ------------------------------------------------------------------------------- +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + return NO; +} + +// ------------------------------------------------------------------------------- +// outlineView:willDisplayCell:forTableColumn:item +// ------------------------------------------------------------------------------- +- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)]; + if(!qIdx.isValid()) + return; + + if ([[tableColumn identifier] isEqualToString:COLUMNID_NAME]) + { + cell.title = qIdx.data(Qt::DisplayRole).toString().toNSString(); + } +} + +// ------------------------------------------------------------------------------- +// outlineViewSelectionDidChange:notification +// ------------------------------------------------------------------------------- +- (void)outlineViewSelectionDidChange:(NSNotification *)notification +{ + // ask the tree controller for the current selection + if([[treeController selectedNodes] count] > 0) { + + } } @end diff --git a/src/AccVideoVC.h b/src/AccVideoVC.h index b7fa537841f49350e1b4a6cfcbc1ff1121bb90d0..aade71ce76dcf07e64409334162e6c096f4de243 100644 --- a/src/AccVideoVC.h +++ b/src/AccVideoVC.h @@ -34,11 +34,7 @@ #import <account.h> -@interface AccVideoVC : NSViewController <NSOutlineViewDelegate> { - -} - -- (void)loadAccount:(Account *)account; +@interface AccVideoVC : NSViewController <NSOutlineViewDelegate> @end diff --git a/src/AccVideoVC.mm b/src/AccVideoVC.mm index 7d3ebe590301911b60d495785b0f288ea0f23eb6..7e5ed1e9f6b9c1e1b5dd65c2694ea0a50bb0a4b6 100644 --- a/src/AccVideoVC.mm +++ b/src/AccVideoVC.mm @@ -37,12 +37,12 @@ #include <QtCore/QSortFilterProxyModel> #import <audio/codecmodel.h> #import <accountmodel.h> +#import <qitemselectionmodel.h> #import "QNSTreeController.h" @interface AccVideoVC () -@property Account* privateAccount; @property QNSTreeController *treeController; @property (assign) IBOutlet NSOutlineView *codecsView; @property (assign) IBOutlet NSView *videoPanelContainer; @@ -53,40 +53,52 @@ @implementation AccVideoVC @synthesize treeController; @synthesize codecsView; -@synthesize privateAccount; @synthesize videoPanelContainer; @synthesize toggleVideoButton; - (void)awakeFromNib { NSLog(@"INIT Video VC"); + QObject::connect(AccountModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + if(!current.isValid()) + return; + [self loadAccount]; + }); } -- (void)loadAccount:(Account *)account +- (Account*) currentAccount { - privateAccount = account; + auto accIdx = AccountModel::instance()->selectionModel()->currentIndex(); + return AccountModel::instance()->getAccountByModelIndex(accIdx); +} + +- (void)loadAccount +{ + auto account = [self currentAccount]; - treeController = [[QNSTreeController alloc] initWithQModel:privateAccount->codecModel()->videoCodecs()]; + treeController = [[QNSTreeController alloc] initWithQModel:account->codecModel()->videoCodecs()]; [treeController setAvoidsEmptySelection:NO]; [treeController setChildrenKeyPath:@"children"]; [codecsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil]; [codecsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil]; [codecsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil]; - [videoPanelContainer setHidden:!privateAccount->isVideoEnabled()]; - [toggleVideoButton setState:privateAccount->isVideoEnabled()?NSOnState:NSOffState]; + [videoPanelContainer setHidden:!account->isVideoEnabled()]; + [toggleVideoButton setState:account->isVideoEnabled()?NSOnState:NSOffState]; } - (IBAction)toggleVideoEnabled:(id)sender { - privateAccount->setVideoEnabled([sender state] == NSOnState); - [videoPanelContainer setHidden:!privateAccount->isVideoEnabled()]; + [self currentAccount]->setVideoEnabled([sender state] == NSOnState); + [videoPanelContainer setHidden:![self currentAccount]->isVideoEnabled()]; } - (IBAction)toggleCodec:(NSOutlineView*)sender { NSInteger row = [sender clickedRow]; NSTableColumn *col = [sender tableColumnWithIdentifier:COLUMNID_STATE]; NSButtonCell *cell = [col dataCellForRow:row]; - privateAccount->codecModel()->videoCodecs()->setData(privateAccount->codecModel()->videoCodecs()->index(row, 0, QModelIndex()), + [self currentAccount]->codecModel()->videoCodecs()->setData([self currentAccount]->codecModel()->videoCodecs()->index(row, 0, QModelIndex()), cell.state == NSOnState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); } @@ -97,8 +109,8 @@ if(!qIdx.isValid()) return; - QMimeData* mime = privateAccount->codecModel()->mimeData(QModelIndexList() << qIdx); - privateAccount->codecModel()->dropMimeData(mime, Qt::MoveAction, qIdx.row() - 1, 0, QModelIndex()); + QMimeData* mime = [self currentAccount]->codecModel()->mimeData(QModelIndexList() << qIdx); + [self currentAccount]->codecModel()->dropMimeData(mime, Qt::MoveAction, qIdx.row() - 1, 0, QModelIndex()); } } @@ -108,8 +120,8 @@ if(!qIdx.isValid()) return; - QMimeData* mime = privateAccount->codecModel()->mimeData(QModelIndexList() << qIdx); - privateAccount->codecModel()->dropMimeData(mime, Qt::MoveAction, qIdx.row() + 1, 0, QModelIndex()); + QMimeData* mime = [self currentAccount]->codecModel()->mimeData(QModelIndexList() << qIdx); + [self currentAccount]->codecModel()->dropMimeData(mime, Qt::MoveAction, qIdx.row() + 1, 0, QModelIndex()); } } @@ -171,17 +183,17 @@ return; if([[tableColumn identifier] isEqualToString:COLUMNID_STATE]) { - [cell setState:privateAccount->codecModel()->videoCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState]; + [cell setState:[self currentAccount]->codecModel()->videoCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState]; } else if ([[tableColumn identifier] isEqualToString:COLUMNID_CODECS]) { - cell.title = privateAccount->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::NAME).toString().toNSString(); - [cell setState:privateAccount->codecModel()->videoCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState]; + cell.title = [self currentAccount]->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::NAME).toString().toNSString(); + [cell setState:[self currentAccount]->codecModel()->videoCodecs()->data(qIdx, Qt::CheckStateRole).value<BOOL>()?NSOnState:NSOffState]; } else if ([[tableColumn identifier] isEqualToString:COLUMNID_FREQ]) { - cell.title = privateAccount->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::SAMPLERATE).toString().toNSString(); + cell.title = [self currentAccount]->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::SAMPLERATE).toString().toNSString(); } else if ([[tableColumn identifier] isEqualToString:COLUMNID_BITRATE]) { - cell.title = privateAccount->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::BITRATE).toString().toNSString(); + cell.title = [self currentAccount]->codecModel()->videoCodecs()->data(qIdx, CodecModel::Role::BITRATE).toString().toNSString(); } } diff --git a/src/AccountsVC.mm b/src/AccountsVC.mm index 3bbd086d933c1e1070861bfc4e526213f9f180d6..168e59b79c7bfe7bfc849e9b97c1b184f1c6791c 100644 --- a/src/AccountsVC.mm +++ b/src/AccountsVC.mm @@ -113,18 +113,16 @@ public: [accountsListView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil]; [accountsListView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil]; - QObject::connect(AccountModel::instance(), &QAbstractItemModel::dataChanged, [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) { - NSLog(@"data changed %d, %d", topLeft.row(), bottomRight.row()); - [accountsListView reloadDataForRowIndexes: [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(topLeft.row(), bottomRight.row() + 1)] columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, accountsListView.tableColumns.count)]]; - }); + AccountModel::instance()->selectionModel()->clearCurrentIndex(); + self.proxyProtocolModel = new ActiveProtocolModel(AccountModel::instance()->protocolModel()); QModelIndex qProtocolIdx = AccountModel::instance()->protocolModel()->selectionModel()->currentIndex(); [self.protocolList addItemWithTitle: @@ -180,10 +178,9 @@ public: - (IBAction)addAccount:(id)sender { QModelIndex qIdx = AccountModel::instance()->protocolModel()->selectionModel()->currentIndex(); - NSString* newAccName = [[NSString alloc] initWithFormat:@"%@ account", + auto newAccName = [[NSString alloc] initWithFormat:@"%@ account", AccountModel::instance()->protocolModel()->data(qIdx, Qt::DisplayRole).toString().toNSString(), nil]; - - Account* newAcc =AccountModel::instance()->add([newAccName UTF8String], qIdx); + AccountModel::instance()->add([newAccName UTF8String], qIdx); AccountModel::instance()->save(); } @@ -196,10 +193,8 @@ public: } -- (void) setupSIPPanelsForAccount:(Account*) acc +- (void) setupSIPPanels { - NSTabViewItem* selected = [configPanels selectedTabViewItem]; - // Start by removing all tabs for(NSTabViewItem* item in configPanels.tabViewItems) { [configPanels removeTabViewItem:item]; @@ -208,20 +203,12 @@ public: [configPanels insertTabViewItem:generalTabItem atIndex:0]; [configPanels insertTabViewItem:audioTabItem atIndex:1]; [configPanels insertTabViewItem:videoTabItem atIndex:2]; - //[configPanels insertTabViewItem:advancedTabItem atIndex:3]; - //[configPanels insertTabViewItem:securityTabItem atIndex:4]; - - [self.generalVC loadAccount:acc]; - [self.audioVC loadAccount:acc]; - [self.videoVC loadAccount:acc]; - [self.advancedVC loadAccount:acc]; - [self.securityVC loadAccount:acc]; + [configPanels insertTabViewItem:advancedTabItem atIndex:3]; + [configPanels insertTabViewItem:securityTabItem atIndex:4]; } -- (void) setupIAXPanelsForAccount:(Account*) acc +- (void) setupIAXPanels { - NSTabViewItem* selected = [configPanels selectedTabViewItem]; - // Start by removing all tabs for(NSTabViewItem* item in configPanels.tabViewItems) { [configPanels removeTabViewItem:item]; @@ -230,16 +217,10 @@ public: [configPanels insertTabViewItem:generalTabItem atIndex:0]; [configPanels insertTabViewItem:audioTabItem atIndex:1]; [configPanels insertTabViewItem:videoTabItem atIndex:2]; - - [self.generalVC loadAccount:acc]; - [self.audioVC loadAccount:acc]; - [self.videoVC loadAccount:acc]; } -- (void) setupRINGPanelsForAccount:(Account*) acc +- (void) setupRINGPanels { - NSTabViewItem* selected = [configPanels selectedTabViewItem]; - // Start by removing all tabs for(NSTabViewItem* item in configPanels.tabViewItems) { [configPanels removeTabViewItem:item]; @@ -248,14 +229,6 @@ public: [configPanels insertTabViewItem:ringTabItem atIndex:0]; [configPanels insertTabViewItem:audioTabItem atIndex:1]; [configPanels insertTabViewItem:videoTabItem atIndex:2]; - //[configPanels insertTabViewItem:advancedTabItem atIndex:3]; - //[configPanels insertTabViewItem:securityTabItem atIndex:4]; - - [self.ringVC loadAccount:acc]; - [self.audioVC loadAccount:acc]; - [self.videoVC loadAccount:acc]; - [self.advancedVC loadAccount:acc]; - [self.securityVC loadAccount:acc]; } - (IBAction)toggleAccount:(NSOutlineView*)sender { @@ -270,6 +243,7 @@ public: Account* toToggle = AccountModel::instance()->getAccountByModelIndex(accIdx); NSButtonCell *cell = [col dataCellForRow:row]; toToggle->setEnabled(cell.state == NSOnState ? NO : YES); + toToggle << Account::EditAction::SAVE; } } @@ -288,8 +262,17 @@ public: // ------------------------------------------------------------------------------- - (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item { - NSCell *returnCell = [tableColumn dataCell]; - return returnCell; + NSCell *returnCell; + + QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)]; + // Prevent user from enabling/disabling IP2IP account + if ([[tableColumn identifier] isEqualToString:COLUMNID_ENABLE] && + AccountModel::instance()->ip2ip()->index() == qIdx) { + + return [[NSCell alloc] init]; + } else { + return [tableColumn dataCell]; + } } // ------------------------------------------------------------------------------- @@ -331,25 +314,30 @@ public: { cell.title = AccountModel::instance()->data(qIdx, Qt::DisplayRole).toString().toNSString(); } else if([[tableColumn identifier] isEqualToString:COLUMNID_STATE]) { - Account::RegistrationState state = qvariant_cast<Account::RegistrationState>(AccountModel::instance()->data(qIdx, (int)Account::Role::RegistrationState)); + NSTextFieldCell* stateCell = cell; + Account::RegistrationState state = qvariant_cast<Account::RegistrationState>(qIdx.data((int)Account::Role::RegistrationState)); switch (state) { case Account::RegistrationState::READY: - [cell setTitle:@"Ready"]; + [stateCell setTextColor:[NSColor colorWithCalibratedRed:116/255.0 green:179/255.0 blue:93/255.0 alpha:1.0]]; + [stateCell setTitle:@"Ready"]; break; case Account::RegistrationState::TRYING: - [cell setTitle:@"Trying..."]; + [stateCell setTextColor:[NSColor redColor]]; + [stateCell setTitle:@"Trying..."]; break; case Account::RegistrationState::UNREGISTERED: - [cell setTitle:@"Unregistered"]; + [stateCell setTextColor:[NSColor blackColor]]; + [stateCell setTitle:@"Unregistered"]; break; case Account::RegistrationState::ERROR: - [cell setTitle:@"Error"]; + [stateCell setTextColor:[NSColor redColor]]; + [stateCell setTitle:@"Error"]; break; default: break; } } else if([[tableColumn identifier] isEqualToString:COLUMNID_ENABLE]) { - [cell setState:AccountModel::instance()->data(qIdx, Qt::CheckStateRole).value<BOOL>()]; + [cell setState:qIdx.data(Qt::CheckStateRole).value<BOOL>()]; } } @@ -360,29 +348,28 @@ public: { // ask the tree controller for the current selection if([[treeController selectedNodes] count] > 0) { - QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]]; + auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]]; //Update details view + auto acc = AccountModel::instance()->getAccountByModelIndex(qIdx); AccountModel::instance()->selectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect); - Account* acc = AccountModel::instance()->getAccountByModelIndex(qIdx); switch (acc->protocol()) { case Account::Protocol::SIP: NSLog(@"SIP"); - [self setupSIPPanelsForAccount:acc]; + [self setupSIPPanels]; break; case Account::Protocol::IAX: NSLog(@"IAX"); - [self setupIAXPanelsForAccount:acc]; + [self setupIAXPanels]; break; case Account::Protocol::RING: - [self setupRINGPanelsForAccount:acc]; + [self setupRINGPanels]; NSLog(@"DRING"); break; default: break; } - [self.accountDetailsView setHidden:NO]; } else { [self.accountDetailsView setHidden:YES]; @@ -396,7 +383,7 @@ public: { QModelIndex proxyIdx = proxyProtocolModel->index(index, 0); QModelIndex qIdx = AccountModel::instance()->protocolModel()->index(proxyProtocolModel->mapToSource(proxyIdx).row()); - [item setTitle:AccountModel::instance()->protocolModel()->data(qIdx, Qt::DisplayRole).toString().toNSString()]; + [item setTitle:qIdx.data(Qt::DisplayRole).toString().toNSString()]; return YES; } diff --git a/src/AppDelegate.mm b/src/AppDelegate.mm index b73e0e551d80fe78d3a9e3e6d96742035d017972..63254387147287dc160196b5bac5cd30d86ef64c 100644 --- a/src/AppDelegate.mm +++ b/src/AppDelegate.mm @@ -30,15 +30,24 @@ #import "AppDelegate.h" #import <callmodel.h> - +#import <qapplication.h> #import <accountmodel.h> #import <protocolmodel.h> #import <QItemSelectionModel> #import <account.h> +#if ENABLE_SPARKLE +#import <Sparkle/Sparkle.h> +#endif + +#import "Constants.h" #import "RingWizardWC.h" +#if ENABLE_SPARKLE +@interface AppDelegate() <SUUpdaterDelegate> +#else @interface AppDelegate() +#endif @property RingWindowController* ringWindowController; @property RingWizardWC* wizard; @@ -50,9 +59,11 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"]; - [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; + NSAppleEventManager* appleEventManager = [NSAppleEventManager sharedAppleEventManager]; + [appleEventManager setEventHandler:self andSelector:@selector(handleQuitEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; + if([self checkForRingAccount]) { [self showMainWindow]; } else { @@ -66,8 +77,8 @@ QObject::connect(CallModel::instance(), &CallModel::incomingCall, [=](Call* call) { - BOOL shouldComeToForeground = [[NSUserDefaults standardUserDefaults] boolForKey:@"window_behaviour"]; - BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:@"enable_notifications"]; + BOOL shouldComeToForeground = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::WindowBehaviour]; + BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::Notifications]; if(shouldComeToForeground) [NSApp activateIgnoringOtherApps:YES]; @@ -132,4 +143,42 @@ return YES; } +- (void)handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent +{ + delete CallModel::instance()->QObject::parent(); + [[NSApplication sharedApplication] terminate:self]; +} + +-(void)applicationWillTerminate:(NSNotification *)notification +{ + delete CallModel::instance()->QObject::parent(); + [[NSApplication sharedApplication] terminate:self]; +} + +#if ENABLE_SPARKLE + +#pragma mark - +#pragma mark Sparkle delegate + +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update +{ + [NSApp activateIgnoringOtherApps:YES]; +} + +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle +{ + return YES; +} + +- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater +{ + return YES; +} + +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error +{ + NSLog(@"Error:%@", error.localizedDescription); +} + +#endif @end diff --git a/src/AudioPrefsVC.mm b/src/AudioPrefsVC.mm index 16700a6b93c4f26487e2cbee6c56d9e26acfe1cc..124893229ab311bfd18b437d3a323ae9b68a537a 100644 --- a/src/AudioPrefsVC.mm +++ b/src/AudioPrefsVC.mm @@ -30,6 +30,7 @@ #import "AudioPrefsVC.h" #import <audio/settings.h> +#import <media/recordingmodel.h> #import <QUrl> #import <audio/inputdevicemodel.h> #import <audio/outputdevicemodel.h> @@ -65,14 +66,16 @@ [self.inputDeviceList addItemWithTitle: Audio::Settings::instance()->inputDeviceModel()->data(qInputIdx, Qt::DisplayRole).toString().toNSString()]; [self.alwaysRecordingButton setState: - Audio::Settings::instance()->isAlwaysRecording()?NSOnState:NSOffState]; + Media::RecordingModel::instance()->isAlwaysRecording() ? NSOnState:NSOffState]; [self.muteDTMFButton setState: Audio::Settings::instance()->areDTMFMuted()?NSOnState:NSOffState]; - if([[Audio::Settings::instance()->recordPath().toNSURL() absoluteString] isEqualToString:@""]) { + if([[Media::RecordingModel::instance()->recordPath().toNSURL() absoluteString] isEqualToString:@""]) { NSArray * pathComponentArray = [self pathComponentArray]; [recordingsPathControl setPathComponentCells:pathComponentArray]; + } else { + [recordingsPathControl setURL:Media::RecordingModel::instance()->recordPath().toNSURL()]; } } @@ -83,13 +86,13 @@ - (IBAction)toggleAlwaysRecording:(NSButton *)sender { - Audio::Settings::instance()->setAlwaysRecording([sender state] == NSOnState); + Media::RecordingModel::instance()->setAlwaysRecording([sender state] == NSOnState); } - (IBAction)pathControlSingleClick:(id)sender { // Select that chosen component of the path. [self.recordingsPathControl setURL:[[self.recordingsPathControl clickedPathComponentCell] URL]]; - Audio::Settings::instance()->setRecordPath(QUrl::fromNSURL(self.recordingsPathControl.URL)); + Media::RecordingModel::instance()->setRecordPath(QUrl::fromNSURL(self.recordingsPathControl.URL)); } - (IBAction)chooseOutput:(id)sender { diff --git a/src/CertificateWC.h b/src/CertificateWC.h new file mode 100644 index 0000000000000000000000000000000000000000..ea45bfbdfde7e108f8c4e68d26ba023786e6bee7 --- /dev/null +++ b/src/CertificateWC.h @@ -0,0 +1,17 @@ +// +// CertificateWC.h +// Ring +// +// Created by Alexandre Lision on 2015-04-28. +// +// + +#import <Cocoa/Cocoa.h> + +#import <certificate.h> + +@interface CertificateWC : NSWindowController + +- (void) setCertificate:(Certificate*) cert; + +@end diff --git a/src/CertificateWC.mm b/src/CertificateWC.mm new file mode 100644 index 0000000000000000000000000000000000000000..8d76a603929ed9131460a976d67e8caf60db101d --- /dev/null +++ b/src/CertificateWC.mm @@ -0,0 +1,29 @@ +// +// CertificateWC.m +// Ring +// +// Created by Alexandre Lision on 2015-04-28. +// +// + +#import "CertificateWC.h" + +@implementation CertificateWC + +- (void)windowDidLoad { + [super windowDidLoad]; +} + +- (void) setCertificate:(Certificate*) cert +{ + NSLog(@"CertificateWC loaded"); +} + +- (IBAction)closePanel:(id)sender +{ + [NSApp endSheet:self.window]; + [self.window orderOut:self]; +} + + +@end diff --git a/src/ChatVC.h b/src/ChatVC.h new file mode 100644 index 0000000000000000000000000000000000000000..f07f5afeace829c64852eafe4e95712385761838 --- /dev/null +++ b/src/ChatVC.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#import <Cocoa/Cocoa.h> + +@interface ChatVC : NSViewController <NSTextFieldDelegate> + +/** + * Message contained in messageField TextField. + * This is a KVO method to bind the text with the send Button + * if message.length is > 0, button is enabled, otherwise disabled + */ +@property (retain) NSString* message; + +@end diff --git a/src/ChatVC.mm b/src/ChatVC.mm new file mode 100644 index 0000000000000000000000000000000000000000..b23cfb8cdc30d8981149e80db962217d97ddcc00 --- /dev/null +++ b/src/ChatVC.mm @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import "ChatVC.h" + +#import <QItemSelectionModel> +#import <qstring.h> + +#import <media/media.h> +#import <media/text.h> +#import <media/textrecording.h> +#import <callmodel.h> + +@interface MediaConnectionsHolder : NSObject + +@property QMetaObject::Connection newMediaAdded; +@property QMetaObject::Connection newMessage; + +@end + +@implementation MediaConnectionsHolder + +@end + +@interface ChatVC () + +@property (unsafe_unretained) IBOutlet NSTextView *chatView; +@property (unsafe_unretained) IBOutlet NSTextField *messageField; +@property (unsafe_unretained) IBOutlet NSButton *sendButton; + +@property MediaConnectionsHolder* mediaHolder; + +@end + +@implementation ChatVC +@synthesize messageField,chatView,sendButton, mediaHolder; + +- (void)awakeFromNib +{ + NSLog(@"Init ChatVC"); + + [self.view setWantsLayer:YES]; + [self.view setLayer:[CALayer layer]]; + [self.view.layer setBackgroundColor:[NSColor blackColor].CGColor]; + + mediaHolder = [[MediaConnectionsHolder alloc] init]; + + QObject::connect(CallModel::instance()->selectionModel(), + &QItemSelectionModel::currentChanged, + [=](const QModelIndex ¤t, const QModelIndex &previous) { + [self setupChat]; + }); + + // Override default style to add interline space + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + paragraphStyle.lineSpacing = 8; + [chatView setDefaultParagraphStyle:paragraphStyle]; +} + +- (void) setupChat +{ + QObject::disconnect(mediaHolder.newMediaAdded); + QObject::disconnect(mediaHolder.newMessage); + + QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex(); + + if (!callIdx.isValid()) + return; + + Call* call = CallModel::instance()->getCall(callIdx); + + /* check if text media is already present */ + if (call->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) { + Media::Text *text = call->firstMedia<Media::Text>(Media::Media::Direction::IN); + [self parseChatModel:text->recording()->instantMessagingModel()]; + } else if (call->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) { + Media::Text *text = call->firstMedia<Media::Text>(Media::Media::Direction::OUT); + [self parseChatModel:text->recording()->instantMessagingModel()]; + } else { + /* monitor media for messaging text messaging */ + mediaHolder.newMediaAdded = QObject::connect(call, + &Call::mediaAdded, + [self] (Media::Media* media) { + if (media->type() == Media::Media::Type::TEXT) { + QObject::disconnect(mediaHolder.newMediaAdded); + [self parseChatModel:((Media::Text*)media)->recording()->instantMessagingModel()]; + + } + }); + } +} + +- (void) parseChatModel:(QAbstractItemModel *)model +{ + QObject::disconnect(mediaHolder.newMessage); + [self.messageField setStringValue:@""]; + self.message = @""; + [self.chatView.textStorage.mutableString setString:@""]; + + /* put all the messages in the im model into the text view */ + for (int row = 0; row < model->rowCount(); ++row) { + [self appendNewMessage:model->index(row, 0)]; + } + + /* append new messages */ + mediaHolder.newMessage = QObject::connect(model, + &QAbstractItemModel::rowsInserted, + [self, model] (const QModelIndex &parent, int first, int last) { + for (int row = first; row <= last; ++row) { + QModelIndex idx = model->index(row, 0, parent); + [self appendNewMessage:idx]; + } + } + ); +} + +- (void) appendNewMessage:(const QModelIndex&) msgIdx +{ + if (!msgIdx.isValid()) + return; + + NSString* message = msgIdx.data(Qt::DisplayRole).value<QString>().toNSString(); + NSString* author = msgIdx.data((int)Media::TextRecording::Role::AuthorDisplayname).value<QString>().toNSString(); + + NSMutableAttributedString* attr = [[NSMutableAttributedString alloc] initWithString: + [NSString stringWithFormat:@"%@: %@\n",author, message]]; + + // put in bold type author name + [attr applyFontTraits:NSBoldFontMask range: NSMakeRange(0, [author length])]; + + [[chatView textStorage] appendAttributedString:attr]; + + // reapply paragraph style on all the text + NSRange range = NSMakeRange(0,[chatView textStorage].length); + [[self.chatView textStorage] addAttribute:NSParagraphStyleAttributeName + value:chatView.defaultParagraphStyle + range:range]; + + [chatView scrollRangeToVisible:NSMakeRange([[chatView string] length], 0)]; + +} + +- (IBAction)sendMessage:(id)sender { + + QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex(); + Call* call = CallModel::instance()->getCall(callIdx); + + /* make sure there is text to send */ + NSString* text = self.message; + if (text && text.length > 0) { + call->addOutgoingMedia<Media::Text>()->send(QString::fromNSString(text)); + // Empty the text after sending it + [self.messageField setStringValue:@""]; + self.message = @""; + } +} + +#pragma mark - NSTextFieldDelegate + +- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector +{ + if (commandSelector == @selector(insertNewline:) && self.message.length > 0) { + [self sendMessage:nil]; + return YES; + } + return NO; +} + +@end diff --git a/src/Constants.h b/src/Constants.h new file mode 100644 index 0000000000000000000000000000000000000000..a119562ffb04a48fe9a7d8887ba395897c6cec1a --- /dev/null +++ b/src/Constants.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import <Foundation/Foundation.h> + +/** + * Defines a set of preferences constants + * WARNING: If you modify a KVO compliant value, make sure to change the string associated + * in the relative xib file in IB. + */ +namespace Preferences { + /* KVO compliant */ + NSString * const HistoryLimit = @"history_limit"; + /* KVO compliant */ + NSString * const WindowBehaviour = @"window_behaviour"; + /* KVO compliant */ + NSString * const Notifications = @"enable_notifications"; + + NSString * const ShowAdvanced = @"show_advanced"; +} diff --git a/src/ConversationsViewController.h b/src/ConversationsVC.h similarity index 94% rename from src/ConversationsViewController.h rename to src/ConversationsVC.h index 09d4e3bc160062d4bc28ff283f2de9958dcef2f4..af8843d80b642c3858ccd1a6fcaa086c94586c4d 100644 --- a/src/ConversationsViewController.h +++ b/src/ConversationsVC.h @@ -33,7 +33,7 @@ #import <Cocoa/Cocoa.h> #import "QNSTreeController.h" -@interface ConversationsViewController : NSViewController <NSOutlineViewDelegate> { +@interface ConversationsVC : NSViewController <NSOutlineViewDelegate> { } diff --git a/src/ConversationsViewController.mm b/src/ConversationsVC.mm similarity index 90% rename from src/ConversationsViewController.mm rename to src/ConversationsVC.mm index 4b7ce22d28e2305a91157b226c7a281b52db9238..13aa12ff520b9b3b5384cf3361cd23edef56d550 100644 --- a/src/ConversationsViewController.mm +++ b/src/ConversationsVC.mm @@ -27,30 +27,23 @@ * shall include the source code for the parts of OpenSSL used as well * as that of the covered work. */ -#import "ConversationsViewController.h" +#import "ConversationsVC.h" #import <callmodel.h> #import <QtCore/qitemselectionmodel.h> -#import "CurrentCallVC.h" - #define COLUMNID_CONVERSATIONS @"ConversationsColumn" // the single column name in our outline view -@interface ConversationsViewController () +@interface ConversationsVC () -@property CurrentCallVC* currentVC; -@property (assign) IBOutlet NSView *currentCallView; @property QNSTreeController *treeController; @property (assign) IBOutlet NSOutlineView *conversationsView; @end -@implementation ConversationsViewController +@implementation ConversationsVC @synthesize conversationsView; @synthesize treeController; -@synthesize currentVC; -@synthesize currentCallView; - - (void)awakeFromNib { NSLog(@"INIT Conversations VC"); @@ -73,15 +66,7 @@ [conversationsView reloadDataForRowIndexes: [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(topLeft.row(), bottomRight.row() + 1)] columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, conversationsView.tableColumns.count)]]; - }); - - currentVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil]; - [currentCallView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [[currentVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - [currentCallView addSubview:[self.currentVC view]]; - [currentVC initFrame]; } #pragma mark - NSOutlineViewDelegate methods diff --git a/src/CurrentCallVC.h b/src/CurrentCallVC.h index e24cf01203d9bb9d078ebe61ddd613eb0bc6003b..7000c554eebebf2c759222ad0e10e1ea693fd432 100644 --- a/src/CurrentCallVC.h +++ b/src/CurrentCallVC.h @@ -32,9 +32,11 @@ #import <Cocoa/Cocoa.h> +#import "views/CallView.h" + class Call; -@interface CurrentCallVC : NSViewController { +@interface CurrentCallVC : NSViewController <NSSplitViewDelegate, FullScreenDelegate> { } diff --git a/src/CurrentCallVC.mm b/src/CurrentCallVC.mm index ba70063e7458d550de51879ca30365106ad1a26c..98be9aaea0c9d451efd21453840f986ba735e8a9 100644 --- a/src/CurrentCallVC.mm +++ b/src/CurrentCallVC.mm @@ -38,12 +38,13 @@ #import <qabstractitemmodel.h> #import <QItemSelectionModel> #import <QItemSelection> - #import <video/previewmanager.h> #import <video/renderer.h> +#import <media/text.h> + +#import "views/ITProgressIndicator.h" +#import "views/CallView.h" -/** FrameReceiver class - delegate for AVCaptureSession - */ @interface RendererConnectionsHolder : NSObject @property QMetaObject::Connection frameUpdated; @@ -58,21 +59,28 @@ @interface CurrentCallVC () -@property (assign) IBOutlet NSTextField *personLabel; -@property (assign) IBOutlet NSTextField *stateLabel; -@property (assign) IBOutlet NSButton *holdOnOffButton; -@property (assign) IBOutlet NSButton *hangUpButton; -@property (assign) IBOutlet NSButton *recordOnOffButton; -@property (assign) IBOutlet NSButton *pickUpButton; -@property (assign) IBOutlet NSTextField *timeSpentLabel; -@property (assign) IBOutlet NSView *controlsPanel; +@property (unsafe_unretained) IBOutlet NSTextField *personLabel; +@property (unsafe_unretained) IBOutlet NSTextField *stateLabel; +@property (unsafe_unretained) IBOutlet NSButton *holdOnOffButton; +@property (unsafe_unretained) IBOutlet NSButton *hangUpButton; +@property (unsafe_unretained) IBOutlet NSButton *recordOnOffButton; +@property (unsafe_unretained) IBOutlet NSButton *pickUpButton; +@property (unsafe_unretained) IBOutlet NSButton *muteAudioButton; +@property (unsafe_unretained) IBOutlet NSButton *muteVideoButton; + +@property (unsafe_unretained) IBOutlet ITProgressIndicator *loadingIndicator; + +@property (unsafe_unretained) IBOutlet NSTextField *timeSpentLabel; +@property (unsafe_unretained) IBOutlet NSView *controlsPanel; +@property (unsafe_unretained) IBOutlet NSSplitView *splitView; +@property (unsafe_unretained) IBOutlet NSButton *chatButton; @property QHash<int, NSButton*> actionHash; // Video -@property (assign) IBOutlet NSView *videoView; +@property (unsafe_unretained) IBOutlet CallView *videoView; @property CALayer* videoLayer; -@property (assign) IBOutlet NSView *previewView; +@property (unsafe_unretained) IBOutlet NSView *previewView; @property CALayer* previewLayer; @property RendererConnectionsHolder* previewHolder; @@ -82,19 +90,10 @@ @end @implementation CurrentCallVC -@synthesize personLabel; -@synthesize actionHash; -@synthesize stateLabel; -@synthesize holdOnOffButton; -@synthesize hangUpButton; -@synthesize recordOnOffButton; -@synthesize pickUpButton; -@synthesize timeSpentLabel; -@synthesize controlsPanel; -@synthesize videoView; -@synthesize videoLayer; -@synthesize previewLayer; -@synthesize previewView; +@synthesize personLabel, actionHash, stateLabel, holdOnOffButton, hangUpButton, + recordOnOffButton, pickUpButton, chatButton, timeSpentLabel, + muteVideoButton, muteAudioButton, controlsPanel, videoView, + videoLayer, previewLayer, previewView, splitView, loadingIndicator; @synthesize previewHolder; @synthesize videoHolder; @@ -116,7 +115,9 @@ [a setState:(idx.data(Qt::CheckStateRole) == Qt::Checked) ? NSOnState : NSOffState]; if(action == UserActionModel::Action::HOLD) { - [a setTitle:(a.state == NSOnState ? @"Hold off" : @"Hold")]; + NSString* imgName = (a.state == NSOnState ? @"ic_action_holdoff" : @"ic_action_hold"); + [a setImage:[NSImage imageNamed:imgName]]; + } if(action == UserActionModel::Action::RECORD) { [a setTitle:(a.state == NSOnState ? @"Record off" : @"Record")]; @@ -127,34 +128,45 @@ -(void) updateCall { QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex(); - [personLabel setStringValue:CallModel::instance()->data(callIdx, Qt::DisplayRole).toString().toNSString()]; - [timeSpentLabel setStringValue:CallModel::instance()->data(callIdx, (int)Call::Role::Length).toString().toNSString()]; - - Call::State state = CallModel::instance()->data(callIdx, (int)Call::Role::State).value<Call::State>(); + [personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()]; + [timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()]; + Call::State state = callIdx.data((int)Call::Role::State).value<Call::State>(); + [loadingIndicator setHidden:YES]; + [stateLabel setStringValue:callIdx.data((int)Call::Role::HumanStateName).toString().toNSString()]; switch (state) { + case Call::State::DIALING: + [loadingIndicator setHidden:NO]; + break; + case Call::State::NEW: + break; case Call::State::INITIALIZATION: - [stateLabel setStringValue:@"Initializing"]; + [videoView setShouldAcceptInteractions:NO]; + [loadingIndicator setHidden:NO]; + break; + case Call::State::CONNECTED: + [videoView setShouldAcceptInteractions:NO]; + [loadingIndicator setHidden:NO]; break; case Call::State::RINGING: - [stateLabel setStringValue:@"Ringing"]; + [videoView setShouldAcceptInteractions:NO]; break; case Call::State::CURRENT: - [stateLabel setStringValue:@"Current"]; + [videoView setShouldAcceptInteractions:YES]; break; case Call::State::HOLD: - [stateLabel setStringValue:@"On Hold"]; + [videoView setShouldAcceptInteractions:NO]; break; case Call::State::BUSY: - [stateLabel setStringValue:@"Busy"]; + [videoView setShouldAcceptInteractions:NO]; break; case Call::State::OVER: - [stateLabel setStringValue:@"Finished"]; + [videoView setShouldAcceptInteractions:NO]; + if(videoView.isInFullScreenMode) + [videoView exitFullScreenModeWithOptions:nil]; break; case Call::State::FAILURE: - [stateLabel setStringValue:@"Failure"]; - break; - default: + [videoView setShouldAcceptInteractions:NO]; break; } @@ -175,6 +187,8 @@ actionHash[ (int)UserActionModel::Action::HOLD ] = holdOnOffButton; actionHash[ (int)UserActionModel::Action::RECORD] = recordOnOffButton; actionHash[ (int)UserActionModel::Action::HANGUP] = hangUpButton; + actionHash[ (int)UserActionModel::Action::MUTE_AUDIO] = muteAudioButton; + actionHash[ (int)UserActionModel::Action::MUTE_VIDEO] = muteVideoButton; videoLayer = [CALayer layer]; [videoView setWantsLayer:YES]; @@ -198,6 +212,14 @@ previewHolder = [[RendererConnectionsHolder alloc] init]; videoHolder = [[RendererConnectionsHolder alloc] init]; + [loadingIndicator setColor:[NSColor whiteColor]]; + [loadingIndicator setNumberOfLines:100]; + [loadingIndicator setWidthOfLine:2]; + [loadingIndicator setLengthOfLine:2]; + [loadingIndicator setInnerMargin:30]; + + [self.videoView setFullScreenDelegate:self]; + [self connect]; } @@ -210,6 +232,7 @@ [self animateOut]; return; } + [self collapseRightView]; [self updateCall]; [self updateAllActions]; [self animateOut]; @@ -241,11 +264,10 @@ { QModelIndex idx = CallModel::instance()->selectionModel()->currentIndex(); Call* call = CallModel::instance()->getCall(idx); - QObject::connect(call, + self.videoStarted = QObject::connect(call, &Call::videoStarted, [=](Video::Renderer* renderer) { NSLog(@"Video started!"); - QObject::disconnect(self.videoStarted); [self connectVideoRenderer:renderer]; }); @@ -419,6 +441,8 @@ } }]; [self.view.layer addAnimation:animation forKey:animation.keyPath]; + + [self.view.layer setPosition:frame.origin]; [CATransaction commit]; } @@ -430,8 +454,37 @@ NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); } +-(void)collapseRightView +{ + NSView *right = [[splitView subviews] objectAtIndex:1]; + NSView *left = [[splitView subviews] objectAtIndex:0]; + NSRect leftFrame = [left frame]; + [right setHidden:YES]; + [splitView display]; +} + +-(void)uncollapseRightView +{ + NSView *left = [[splitView subviews] objectAtIndex:0]; + NSView *right = [[splitView subviews] objectAtIndex:1]; + [right setHidden:NO]; + + CGFloat dividerThickness = [splitView dividerThickness]; + + // get the different frames + NSRect leftFrame = [left frame]; + NSRect rightFrame = [right frame]; + + leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness); + rightFrame.origin.x = leftFrame.size.width + dividerThickness; + [left setFrameSize:leftFrame.size]; + [right setFrame:rightFrame]; + [splitView display]; +} + + +#pragma mark - Button methods -#pragma button methods - (IBAction)hangUp:(id)sender { CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::REFUSE; } @@ -441,11 +494,69 @@ } - (IBAction)toggleRecording:(id)sender { - CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::RECORD; + CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::RECORD_AUDIO; } - (IBAction)toggleHold:(id)sender { CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::HOLD; } +-(IBAction)toggleChat:(id)sender; +{ + BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]]; + if (rightViewCollapsed) { + [self uncollapseRightView]; + CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex())->addOutgoingMedia<Media::Text>(); + } else { + [self collapseRightView]; + } + [chatButton setState:rightViewCollapsed]; +} + +- (IBAction)muteAudio:(id)sender +{ + UserActionModel* uam = CallModel::instance()->userActionModel(); + uam << UserActionModel::Action::MUTE_AUDIO; +} + +- (IBAction)muteVideo:(id)sender +{ + UserActionModel* uam = CallModel::instance()->userActionModel(); + uam << UserActionModel::Action::MUTE_VIDEO; +} + +#pragma mark - NSSplitViewDelegate + +/* Return YES if the subview should be collapsed because the user has double-clicked on an adjacent divider. If a split view has a delegate, and the delegate responds to this message, it will be sent once for the subview before a divider when the user double-clicks on that divider, and again for the subview after the divider, but only if the delegate returned YES when sent -splitView:canCollapseSubview: for the subview in question. When the delegate indicates that both subviews should be collapsed NSSplitView's behavior is undefined. + */ +- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex; +{ + NSView* rightView = [[splitView subviews] objectAtIndex:1]; + return ([subview isEqual:rightView]); +} + + +- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview; +{ + NSView* rightView = [[splitView subviews] objectAtIndex:1]; + return ([subview isEqual:rightView]); +} + + +# pragma mark - FullScreenDelegate + +- (void) callShouldToggleFullScreen +{ + if(self.splitView.isInFullScreenMode) + [self.splitView exitFullScreenModeWithOptions:nil]; + else { + NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock + + NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar; + NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options], + NSFullScreenModeApplicationPresentationOptions, nil]; + + [self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts]; + } +} + @end diff --git a/src/GeneralPrefsVC.h b/src/GeneralPrefsVC.h index ef059aed3bb1760a1b1e499bf402bfec0172a8d5..cffd689d3ae0e4d24af6962e029ab5c3c925a883 100644 --- a/src/GeneralPrefsVC.h +++ b/src/GeneralPrefsVC.h @@ -32,10 +32,7 @@ #import <Cocoa/Cocoa.h> -@interface GeneralPrefsVC : NSViewController { - - NSTextField *historyChangedLabel; -} +@interface GeneralPrefsVC : NSViewController @end diff --git a/src/GeneralPrefsVC.mm b/src/GeneralPrefsVC.mm index ea6d97290ead1daef442663d95964079a527e24a..a9f88e6b325256e2be93e650c798964f40ccd3ec 100644 --- a/src/GeneralPrefsVC.mm +++ b/src/GeneralPrefsVC.mm @@ -31,20 +31,56 @@ #import <categorizedhistorymodel.h> +#if ENABLE_SPARKLE +#import <Sparkle/Sparkle.h> +#endif + +#import "Constants.h" + @interface GeneralPrefsVC () -@property (assign) IBOutlet NSTextField *historyChangedLabel; +@property (unsafe_unretained) IBOutlet NSTextField *historyChangedLabel; +@property (unsafe_unretained) IBOutlet NSView *advancedGeneralSettings; +@property (unsafe_unretained) IBOutlet NSButton *startUpButton; +@property (unsafe_unretained) IBOutlet NSButton *toggleAutomaticUpdateCheck; +@property (unsafe_unretained) IBOutlet NSPopUpButton *checkIntervalPopUp; +@property (unsafe_unretained) IBOutlet NSView *sparkleContainer; @end -@implementation GeneralPrefsVC { - -} +@implementation GeneralPrefsVC @synthesize historyChangedLabel; +@synthesize advancedGeneralSettings; +@synthesize startUpButton; +@synthesize toggleAutomaticUpdateCheck; +@synthesize checkIntervalPopUp; +@synthesize sparkleContainer; - (void)loadView { [super loadView]; - [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"history_limit" options:NSKeyValueObservingOptionNew context:NULL]; + [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:Preferences::HistoryLimit options:NSKeyValueObservingOptionNew context:NULL]; + [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:Preferences::ShowAdvanced options:NSKeyValueObservingOptionNew context:NULL]; + + [startUpButton setState:[self isLaunchAtStartup]]; + +#if ENABLE_SPARKLE + [sparkleContainer setHidden:NO]; + SUUpdater *updater = [SUUpdater sharedUpdater]; + [toggleAutomaticUpdateCheck bind:@"value" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil]; + + [checkIntervalPopUp bind:@"enabled" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil]; + [checkIntervalPopUp bind:@"selectedTag" toObject:updater withKeyPath:@"updateCheckInterval" options:nil]; +#else + [sparkleContainer setHidden:YES]; +#endif + + //[advancedGeneralSettings setHidden:![[NSUserDefaults standardUserDefaults] boolForKey:Preferences::ShowAdvanced]]; +} + +- (void) dealloc +{ + [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:Preferences::HistoryLimit]; + [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:Preferences::ShowAdvanced]; } - (IBAction)clearHistory:(id)sender { @@ -56,8 +92,76 @@ -(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject change:(NSDictionary *)aChange context:(void *)aContext { - NSLog(@"VALUE CHANGED"); - [historyChangedLabel setHidden:NO]; + if (aKeyPath == Preferences::HistoryLimit) { + [historyChangedLabel setHidden:NO]; + } else if (aKeyPath == Preferences::ShowAdvanced) { + //[advancedGeneralSettings setHidden:[[aChange objectForKey: NSKeyValueChangeNewKey] boolValue]]; + } +} + +#pragma mark - Startup API + +// MIT license by Brian Dunagan +- (BOOL)isLaunchAtStartup { + // See if the app is currently in LoginItems. + LSSharedFileListItemRef itemRef = [self itemRefInLoginItems]; + // Store away that boolean. + BOOL isInList = itemRef != nil; + // Release the reference if it exists. + if (itemRef != nil) CFRelease(itemRef); + + return isInList; +} + +- (IBAction)toggleLaunchAtStartup:(id)sender { + // Toggle the state. + BOOL shouldBeToggled = ![self isLaunchAtStartup]; + // Get the LoginItems list. + LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + if (loginItemsRef == nil) return; + if (shouldBeToggled) { + // Add the app to the LoginItems list. + CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; + LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL); + if (itemRef) CFRelease(itemRef); + } + else { + // Remove the app from the LoginItems list. + LSSharedFileListItemRef itemRef = [self itemRefInLoginItems]; + LSSharedFileListItemRemove(loginItemsRef,itemRef); + if (itemRef != nil) CFRelease(itemRef); + } + CFRelease(loginItemsRef); +} + +- (LSSharedFileListItemRef)itemRefInLoginItems { + LSSharedFileListItemRef itemRef = nil; + CFURLRef itemUrl = nil; + + // Get the app's URL. + auto appUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; + // Get the LoginItems list. + LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + if (loginItemsRef == nil) return nil; + // Iterate over the LoginItems. + NSArray *loginItems = (__bridge_transfer NSArray *)LSSharedFileListCopySnapshot(loginItemsRef, nil); + for (int currentIndex = 0; currentIndex < [loginItems count]; currentIndex++) { + // Get the current LoginItem and resolve its URL. + LSSharedFileListItemRef currentItemRef = (__bridge LSSharedFileListItemRef)[loginItems objectAtIndex:currentIndex]; + if (LSSharedFileListItemResolve(currentItemRef, 0, &itemUrl, NULL) == noErr) { + // Compare the URLs for the current LoginItem and the app. + if ([(__bridge NSURL *)itemUrl isEqual:appUrl]) { + // Save the LoginItem reference. + itemRef = currentItemRef; + } + } + } + // Retain the LoginItem reference. + if (itemRef != nil) CFRetain(itemRef); + // Release the LoginItems lists. + CFRelease(loginItemsRef); + + return itemRef; } @end diff --git a/src/HistoryViewController.h b/src/HistoryVC.h similarity index 95% rename from src/HistoryViewController.h rename to src/HistoryVC.h index 4c4dbeacce2d08e5a6e1c545d3e0f69084165e12..fe6b7b48431ec644695e52c1df2358623ee9c280 100644 --- a/src/HistoryViewController.h +++ b/src/HistoryVC.h @@ -32,7 +32,7 @@ #import <Cocoa/Cocoa.h> -@interface HistoryViewController : NSViewController <NSOutlineViewDelegate> { +@interface HistoryVC : NSViewController <NSOutlineViewDelegate> { } diff --git a/src/HistoryViewController.mm b/src/HistoryVC.mm similarity index 93% rename from src/HistoryViewController.mm rename to src/HistoryVC.mm index 5ca5dada255eab747adb442068d605963435d144..acb41c4c9dfc4868d87045b2c2fb41f1bdadc300 100644 --- a/src/HistoryViewController.mm +++ b/src/HistoryVC.mm @@ -27,29 +27,29 @@ * shall include the source code for the parts of OpenSSL used as well * as that of the covered work. */ -#import "HistoryViewController.h" +#import "HistoryVC.h" #import <categorizedhistorymodel.h> #import <QSortFilterProxyModel> #import <callmodel.h> #import <call.h> #import <contactmethod.h> +#import <localhistorycollection.h> -#import "backends/MinimalHistoryBackend.h" #import "QNSTreeController.h" #define COLUMNID_DAY @"DayColumn" // the single column name in our outline view #define COLUMNID_CONTACTMETHOD @"ContactMethodColumn" // the single column name in our outline view #define COLUMNID_DATE @"DateColumn" // the single column name in our outline view -@interface HistoryViewController() +@interface HistoryVC() -@property NSTreeController *treeController; +@property QNSTreeController *treeController; @property (assign) IBOutlet NSOutlineView *historyView; @property QSortFilterProxyModel *historyProxyModel; @end -@implementation HistoryViewController +@implementation HistoryVC @synthesize treeController; @synthesize historyView; @synthesize historyProxyModel; @@ -78,15 +78,14 @@ [historyView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil]; [historyView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil]; [historyView setTarget:self]; - [historyView setDoubleAction:@selector(placeCall:)]; + [historyView setDoubleAction:@selector(placeHistoryCall:)]; - CategorizedHistoryModel::instance()->addCollection<MinimalHistoryBackend>(LoadOptions::FORCE_ENABLED); + CategorizedHistoryModel::instance()->addCollection<LocalHistoryCollection>(LoadOptions::FORCE_ENABLED); } -- (void)placeCall:(id)sender +- (void)placeHistoryCall:(id)sender { if([[treeController selectedNodes] count] > 0) { - Call* c = CallModel::instance()->dialingCall(); QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]]; QVariant var = historyProxyModel->data(qIdx, (int)Call::Role::ContactMethod); ContactMethod* m = qvariant_cast<ContactMethod*>(var); diff --git a/src/PersonsVC.h b/src/PersonsVC.h new file mode 100644 index 0000000000000000000000000000000000000000..9d735717235252b211ab8b260ae4155868b1e85c --- /dev/null +++ b/src/PersonsVC.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import <Cocoa/Cocoa.h> + +@interface PersonsVC : NSViewController <NSOutlineViewDelegate> + +@end diff --git a/src/PersonsVC.mm b/src/PersonsVC.mm new file mode 100644 index 0000000000000000000000000000000000000000..f1d1c26fb7ed877f288223b0cb424c9ad038ba87 --- /dev/null +++ b/src/PersonsVC.mm @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import "PersonsVC.h" + +#import <personmodel.h> +#import <callmodel.h> +#import <categorizedcontactmodel.h> +#import <QSortFilterProxyModel> +#import <person.h> +#import <contactmethod.h> +#import <QtMacExtras/qmacfunctions.h> +#import <QPixmap> + +#import "backends/AddressBookBackend.h" +#import "QNSTreeController.h" +#import "delegates/ImageManipulationDelegate.h" +#import "views/PersonCell.h" + +#define COLUMNID_NAME @"NameColumn" + +class ReachablePersonModel : public QSortFilterProxyModel +{ +public: + ReachablePersonModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent) + { + setSourceModel(parent); + } + virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const + { + return sourceModel()->index(source_row,0,source_parent).flags() & Qt::ItemIsEnabled; + } +}; + + +@interface PersonsVC () + +@property QNSTreeController *treeController; +@property (assign) IBOutlet NSOutlineView *personsView; +@property QSortFilterProxyModel *contactProxyModel; + +@end + +@implementation PersonsVC +@synthesize treeController; +@synthesize personsView; +@synthesize contactProxyModel; + +-(void) awakeFromNib +{ + new ImageManipulationDelegate(); + NSLog(@"INIT PersonsVC"); + contactProxyModel = new ReachablePersonModel(CategorizedContactModel::instance()); + contactProxyModel->setSortRole(static_cast<int>(Qt::DisplayRole)); + contactProxyModel->sort(0,Qt::AscendingOrder); + treeController = [[QNSTreeController alloc] initWithQModel:contactProxyModel]; + + [treeController setAvoidsEmptySelection:NO]; + [treeController setChildrenKeyPath:@"children"]; + + [personsView bind:@"content" toObject:treeController withKeyPath:@"arrangedObjects" options:nil]; + [personsView bind:@"sortDescriptors" toObject:treeController withKeyPath:@"sortDescriptors" options:nil]; + [personsView bind:@"selectionIndexPaths" toObject:treeController withKeyPath:@"selectionIndexPaths" options:nil]; + [personsView setTarget:self]; + [personsView setDoubleAction:@selector(callContact:)]; + + CategorizedContactModel::instance()->setUnreachableHidden(YES); + PersonModel::instance()->addCollection<AddressBookBackend>(LoadOptions::FORCE_ENABLED); + +} + +- (IBAction)callContact:(id)sender +{ + if([[treeController selectedNodes] count] > 0) { + QModelIndex qIdx = [treeController toQIdx:[treeController selectedNodes][0]]; + ContactMethod* m = nil; + if(((NSTreeNode*)[treeController selectedNodes][0]).indexPath.length == 2) { + // Person + QVariant var = qIdx.data((int)Person::Role::Object); + if (var.isValid()) { + Person *c = var.value<Person*>(); + if (c->phoneNumbers().size() == 1) { + m = c->phoneNumbers().first(); + } + } + } else if (((NSTreeNode*)[treeController selectedNodes][0]).indexPath.length == 3) { + //ContactMethod + QVariant var = qIdx.data(static_cast<int>(ContactMethod::Role::Object)); + if (var.isValid()) { + m = var.value<ContactMethod *>(); + } + } + + if(m){ + Call* c = CallModel::instance()->dialingCall(); + c->setDialNumber(m); + c << Call::Action::ACCEPT; + } + } +} + +#pragma mark - NSOutlineViewDelegate methods + +// ------------------------------------------------------------------------------- +// shouldSelectItem:item +// ------------------------------------------------------------------------------- +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item; +{ + QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)]; + if(!qIdx.isValid()) + return NO; + + if(!qIdx.parent().isValid()) { + return NO; + } else { + return YES; + } +} + +// ------------------------------------------------------------------------------- +// dataCellForTableColumn:tableColumn:item +// ------------------------------------------------------------------------------- +- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)]; + PersonCell *returnCell = [tableColumn dataCell]; + if(!qIdx.isValid()) + return returnCell; + + if(!qIdx.parent().isValid()) { + [returnCell setDrawsBackground:YES]; + [returnCell setBackgroundColor:[NSColor selectedControlColor]]; + } else { + [returnCell setDrawsBackground:NO]; + } + + return returnCell; +} + +// ------------------------------------------------------------------------------- +// textShouldEndEditing:fieldEditor +// ------------------------------------------------------------------------------- +- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor +{ + if ([[fieldEditor string] length] == 0) + { + // don't allow empty node names + return NO; + } + else + { + return YES; + } +} + +// ------------------------------------------------------------------------------- +// shouldEditTableColumn:tableColumn:item +// +// Decide to allow the edit of the given outline view "item". +// ------------------------------------------------------------------------------- +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + return NO; +} + +// ------------------------------------------------------------------------------- +// outlineView:willDisplayCell:forTableColumn:item +// ------------------------------------------------------------------------------- +- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)]; + if(!qIdx.isValid()) + return; + + if ([[tableColumn identifier] isEqualToString:COLUMNID_NAME]) + { + PersonCell *pCell = (PersonCell *)cell; + [pCell setPersonImage:nil]; + if(!qIdx.parent().isValid()) { + pCell.title = qIdx.data(Qt::DisplayRole).toString().toNSString(); + } else { + pCell.title = qIdx.data(Qt::DisplayRole).toString().toNSString(); + if(((NSTreeNode*)item).indexPath.length == 2) { + Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object)); + QVariant photo = ImageManipulationDelegate::instance()->contactPhoto(p, QSize(35,35)); + [pCell setPersonImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))]; + } + } + } +} + +// ------------------------------------------------------------------------------- +// outlineViewSelectionDidChange:notification +// ------------------------------------------------------------------------------- +- (void)outlineViewSelectionDidChange:(NSNotification *)notification +{ + +} + +- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item +{ + QModelIndex qIdx = [treeController toQIdx:((NSTreeNode*)item)]; + if(!qIdx.isValid()) + return 0.0f; + + if(!qIdx.parent().isValid()) { + return 20.0; + } else { + return 45.0; + } +} + + +@end diff --git a/src/PreferencesViewController.h b/src/PreferencesVC.h similarity index 82% rename from src/PreferencesViewController.h rename to src/PreferencesVC.h index ac0400ce9db70887ba1746f09db5f4da66c355b5..4b2ecf9194601edb453e5a29eb9531bc43f24073 100644 --- a/src/PreferencesViewController.h +++ b/src/PreferencesVC.h @@ -32,15 +32,15 @@ #import <Cocoa/Cocoa.h> -@interface PreferencesViewController : NSViewController <NSToolbarDelegate> +@interface PreferencesVC : NSViewController <NSToolbarDelegate> -- (void) close; -@property (nonatomic, assign) NSViewController *currentVC; -@property (nonatomic, assign) NSViewController *accountsPrefsVC; -@property (nonatomic, assign) NSViewController *generalPrefsVC; -@property (nonatomic, assign) NSViewController *audioPrefsVC; -@property (nonatomic, assign) NSViewController *videoPrefsVC; +@property (nonatomic, strong) NSViewController *currentVC; +@property (nonatomic, strong) NSViewController *accountsPrefsVC; +@property (nonatomic, strong) NSViewController *generalPrefsVC; +@property (nonatomic, strong) NSViewController *audioPrefsVC; +@property (nonatomic, strong) NSViewController *videoPrefsVC; +- (void) close; - (void)displayGeneral:(NSToolbarItem *)sender; - (void)displayAudio:(NSToolbarItem *)sender; - (void)displayAncrage:(NSToolbarItem *)sender; diff --git a/src/PreferencesViewController.mm b/src/PreferencesVC.mm similarity index 92% rename from src/PreferencesViewController.mm rename to src/PreferencesVC.mm index 5ccde7a043827bf39ef2252a7f683e42ea333852..9edb1a6ea746a80258591a6d9c34b37fe4481ab1 100644 --- a/src/PreferencesViewController.mm +++ b/src/PreferencesVC.mm @@ -27,7 +27,7 @@ * shall include the source code for the parts of OpenSSL used as well * as that of the covered work. */ -#import "PreferencesViewController.h" +#import "PreferencesVC.h" #import <QuartzCore/QuartzCore.h> @@ -38,14 +38,15 @@ #import "GeneralPrefsVC.h" #import "AudioPrefsVC.h" #import "VideoPrefsVC.h" +#import "Constants.h" -@interface PreferencesViewController () +@interface PreferencesVC () @property NSButton* toggleAdvancedSettings; @end -@implementation PreferencesViewController +@implementation PreferencesVC @synthesize toggleAdvancedSettings; static NSString* const kProfilePrefsIdentifier = @"ProfilesPrefsIdentifier"; @@ -67,18 +68,21 @@ static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer"; // Set the layer redraw policy. This would be better done in // the initialization method of a NSView subclass instead of here. - self.view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; + self.view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; [self.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; CGRect frame = CGRectOffset(self.view.frame, 0, -self.view.frame.size.height); - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; - animation.fromValue = [NSValue valueWithPoint:frame.origin]; - animation.toValue = [NSValue valueWithPoint:self.view.frame.origin]; + [CATransaction begin]; + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; + animation.fromValue = @(frame.origin.y); + animation.toValue = @(self.view.frame.origin.y); animation.duration = 0.3f; + [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]]; [self.view.layer addAnimation:animation forKey:animation.keyPath]; + [CATransaction commit]; } - (void) close @@ -95,9 +99,9 @@ static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer"; CGRect frame = CGRectOffset(self.view.frame, 0, -self.view.frame.size.height); [CATransaction begin]; - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; - animation.fromValue = [NSValue valueWithPoint:self.view.frame.origin]; - animation.toValue = [NSValue valueWithPoint:frame.origin]; + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; + animation.fromValue = @(self.view.frame.origin.y); + animation.toValue = @(frame.origin.y); animation.duration = 0.3f; [animation setTimingFunction:[CAMediaTimingFunction functionWithControlPoints:.7 :0.9 :1 :1]]; @@ -105,8 +109,10 @@ static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer"; [self.view removeFromSuperview]; }]; - [self.view.layer addAnimation:animation forKey:animation.keyPath]; + + // set final layer position to prevent glitching back to original one + [self.view.layer setPosition:frame.origin];; [CATransaction commit]; } @@ -188,7 +194,7 @@ static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer"; toggleAdvancedSettings = [[NSButton alloc] initWithFrame:NSMakeRect(0,0,20,20)]; [toggleAdvancedSettings setButtonType:NSSwitchButton]; [toggleAdvancedSettings setTitle:@""]; - [toggleAdvancedSettings setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"]]; + [toggleAdvancedSettings setState:[[NSUserDefaults standardUserDefaults] boolForKey:Preferences::ShowAdvanced]]; [item setLabel:@"Show Advanced"]; [item setView:toggleAdvancedSettings]; [item setAction:@selector(togglePowerSettings:)]; @@ -225,7 +231,7 @@ static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer"; kDonePrefsIdentifer, nil]; - if([[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"]) { + if([[NSUserDefaults standardUserDefaults] boolForKey:Preferences::ShowAdvanced]) { [items insertObject:NSToolbarSpaceItemIdentifier atIndex:5]; [items insertObject:kProfilePrefsIdentifier atIndex:2]; } else @@ -243,7 +249,7 @@ static NSString* const kPowerSettingsIdentifer = @"PowerSettingsIdentifer"; kVideoPrefsIdentifer, nil]; - if([[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"]) + if([[NSUserDefaults standardUserDefaults] boolForKey:Preferences::ShowAdvanced]) [items insertObject:kProfilePrefsIdentifier atIndex:1]; diff --git a/src/QNSTreeController.mm b/src/QNSTreeController.mm index 75533c25b9a676f57cabeda803a54e305b61b846..f7697fb6ae8fb3cac9f1a5d44402754a30508a15 100644 --- a/src/QNSTreeController.mm +++ b/src/QNSTreeController.mm @@ -55,7 +55,7 @@ - (id) initWithQModel:(QAbstractItemModel*) model { - [super init]; + self = [super init]; self->privateQModel = model; topNodes = [[NSMutableArray alloc] init]; @@ -84,13 +84,33 @@ NSUInteger myArray[[idx length]]; [idx getIndexes:myArray]; QModelIndex toReturn; - if(idx.length == 2) - toReturn = self->privateQModel->index(myArray[1], 0, self->privateQModel->index(myArray[0], 0)); - else - toReturn = self->privateQModel->index(myArray[0], 0); + + for (int i = 0; i < idx.length; ++i) { + toReturn = self->privateQModel->index(myArray[i], 0, toReturn); + } + return toReturn; } +- (void) insertChildAtQIndex:(QModelIndex) qIdx +{ + Node* child = [[Node alloc] init]; + + QModelIndex tmp = qIdx.parent(); + NSMutableArray* allIndexes = [NSMutableArray array]; + while (tmp.isValid()) { + [allIndexes insertObject:@(tmp.row()) atIndex:0]; + tmp = tmp.parent(); + } + [allIndexes insertObject:@(qIdx.row()) atIndex:allIndexes.count]; + + NSUInteger indexes[allIndexes.count]; + for (int i = 0 ; i < allIndexes.count ; ++i) { + indexes[i] = [[allIndexes objectAtIndex:i] intValue]; + } + [self insertObject:child atArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndexes:indexes length:allIndexes.count]]; +} + - (void)connect { QObject::connect(self->privateQModel, @@ -102,9 +122,7 @@ Node* n = [[Node alloc] init]; [self insertObject:n atArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndex:row]]; } else { - Node* child = [[Node alloc] init]; - NSUInteger indexes[] = { (NSUInteger)parent.row(), (NSUInteger)row}; - [self insertObject:child atArrangedObjectIndexPath:[[NSIndexPath alloc] initWithIndexes:indexes length:2]]; + [self insertChildAtQIndex:self->privateQModel->index(row, 0, parent)]; } } } diff --git a/src/RingWindowController.h b/src/RingWindowController.h index 77c93c4e5a910ef3f7cd293b9e2a17a19f5d19db..86b104b1f0da3ea1a7526e53e4cd189c71b7d437 100644 --- a/src/RingWindowController.h +++ b/src/RingWindowController.h @@ -31,14 +31,14 @@ #define RINGWINDOWCONTROLLER_H #import <Cocoa/Cocoa.h> -#import "HistoryViewController.h" -#import "PreferencesViewController.h" +#import "HistoryVC.h" +#import "PreferencesVC.h" @interface RingWindowController : NSWindowController <NSToolbarDelegate, NSTextFieldDelegate>{ IBOutlet NSView *currentView; } @property (nonatomic, assign) NSViewController *myCurrentViewController; -@property PreferencesViewController* preferencesViewController; +@property PreferencesVC* preferencesViewController; - (IBAction)openPreferences:(id)sender; - (IBAction)closePreferences:(NSToolbarItem *)sender; diff --git a/src/RingWindowController.mm b/src/RingWindowController.mm index f46bf664220b7260874cdedbd964a3c402d0eaf7..7559041d048b5b74ab0738da5139b7bbebf1751d 100644 --- a/src/RingWindowController.mm +++ b/src/RingWindowController.mm @@ -35,15 +35,23 @@ #import <call.h> #import "AppDelegate.h" +#import "Constants.h" +#import "CurrentCallVC.h" @interface RingWindowController () @property NSSearchField* callField; +@property CurrentCallVC* currentVC; +@property (unsafe_unretained) IBOutlet NSView *callView; + @end @implementation RingWindowController @synthesize callField; +@synthesize currentVC; +@synthesize callView; + static NSString* const kSearchViewIdentifier = @"SearchViewIdentifier"; static NSString* const kPreferencesIdentifier = @"PreferencesIdentifier"; static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier"; @@ -52,6 +60,13 @@ static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier"; [super windowDidLoad]; [self.window setReleasedWhenClosed:FALSE]; [self displayMainToolBar]; + + currentVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil]; + [callView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [[currentVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + [callView addSubview:[self.currentVC view]]; + [currentVC initFrame]; } - (IBAction)openPreferences:(id)sender @@ -62,7 +77,7 @@ static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier"; } NSToolbar* tb = [[NSToolbar alloc] initWithIdentifier: @"PreferencesToolbar"]; - self.preferencesViewController = [[PreferencesViewController alloc] initWithNibName:@"PreferencesScreen" bundle:nil]; + self.preferencesViewController = [[PreferencesVC alloc] initWithNibName:@"PreferencesScreen" bundle:nil]; self.myCurrentViewController = self.preferencesViewController; NSLayoutConstraint* test = [NSLayoutConstraint constraintWithItem:self.preferencesViewController.view @@ -149,8 +164,8 @@ static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier"; - (void)togglePowerSettings:(id)sender { - BOOL advanced = [[NSUserDefaults standardUserDefaults] boolForKey:@"show_advanced"]; - [[NSUserDefaults standardUserDefaults] setBool:!advanced forKey:@"show_advanced"]; + BOOL advanced = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::ShowAdvanced]; + [[NSUserDefaults standardUserDefaults] setBool:!advanced forKey:Preferences::ShowAdvanced]; [[NSUserDefaults standardUserDefaults] synchronize]; NSToolbar* tb = [[NSToolbar alloc] initWithIdentifier: @"PreferencesToolbar"]; @@ -201,7 +216,6 @@ static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier"; - (IBAction)placeCall:(id)sender { Call* c = CallModel::instance()->dialingCall(); - // check for a valid ring hash NSCharacterSet *hexSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"]; BOOL valid = [[[callField stringValue] stringByTrimmingCharactersInSet:hexSet] isEqualToString:@""]; diff --git a/src/RingWizardWC.h b/src/RingWizardWC.h index e3ca85830e878715f887e3b7e693a0b2f3b5eda9..28f37552c0d92618e26ecc965fb8ad4186af3abd 100644 --- a/src/RingWizardWC.h +++ b/src/RingWizardWC.h @@ -30,9 +30,6 @@ #import <Cocoa/Cocoa.h> -@interface RingWizardWC : NSWindowController <NSWindowDelegate>{ - - NSButton *goToAppButton; -} +@interface RingWizardWC : NSWindowController <NSWindowDelegate> @end diff --git a/src/RingWizardWC.mm b/src/RingWizardWC.mm index 57ed1d939710bd3fc845a23cff16943ecccf2565..5868bc6a6f16b78e66ffec4c05a4724109f89fc2 100644 --- a/src/RingWizardWC.mm +++ b/src/RingWizardWC.mm @@ -38,11 +38,11 @@ @interface RingWizardWC () -@property (assign) IBOutlet NSButton *goToAppButton; -@property (assign) IBOutlet NSTextField *nickname; -@property (assign) IBOutlet NSProgressIndicator *progressBar; -@property (assign) IBOutlet NSTextField *indicationLabel; -@property (assign) IBOutlet NSButton *createButton; +@property (unsafe_unretained) IBOutlet NSButton *goToAppButton; +@property (unsafe_unretained) IBOutlet NSTextField *nickname; +@property (unsafe_unretained) IBOutlet NSProgressIndicator *progressBar; +@property (unsafe_unretained) IBOutlet NSTextField *indicationLabel; +@property (unsafe_unretained) IBOutlet NSButton *createButton; @end @implementation RingWizardWC @@ -102,6 +102,19 @@ [self setCallback]; [self performSelector:@selector(saveAccount) withObject:nil afterDelay:1]; + [self registerAutoStartup]; +} + +/** + * Enable launch at startup by default + */ +- (void) registerAutoStartup +{ + LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + if (loginItemsRef == nil) return; + CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; + LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL); + if (itemRef) CFRelease(itemRef); } - (void) saveAccount @@ -109,6 +122,7 @@ NSString* newAccName = @"My Ring"; Account* newAcc = AccountModel::instance()->add([newAccName UTF8String], Account::Protocol::RING); newAcc->setAlias([[nickname stringValue] UTF8String]); + newAcc->setUpnpEnabled(YES); // Always active upnp newAcc << Account::EditAction::SAVE; } diff --git a/src/backends/AddressBookBackend.h b/src/backends/AddressBookBackend.h new file mode 100644 index 0000000000000000000000000000000000000000..74a24c45e2588d582b5d365dd614d760b49de60e --- /dev/null +++ b/src/backends/AddressBookBackend.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#ifndef ADDRESSBOOKBACKEND_H +#define ADDRESSBOOKBACKEND_H + +#include <collectioninterface.h> +#include <collectioneditor.h> + +class Person; + +template<typename T> class CollectionMediator; + +class AddressBookBackend : public CollectionInterface +{ +public: + explicit AddressBookBackend(CollectionMediator<Person>* mediator); + virtual ~AddressBookBackend(); + + virtual bool load() override; + virtual bool reload() override; + virtual bool clear() override; + virtual QString name () const override; + virtual QString category () const override; + virtual QVariant icon () const override; + virtual bool isEnabled() const override; + virtual QByteArray id () const override; + virtual FlagPack<SupportedFeatures> supportedFeatures() const override; + +private: + CollectionMediator<Person>* m_pMediator; + + void asyncLoad(int startingPoint); +}; + +#endif // ADDRESSBOOKBACKEND_H diff --git a/src/backends/AddressBookBackend.mm b/src/backends/AddressBookBackend.mm new file mode 100644 index 0000000000000000000000000000000000000000..f98e7736059098f9dde7de49bf11b55e4e1e4fa4 --- /dev/null +++ b/src/backends/AddressBookBackend.mm @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall import the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#import "AddressBookBackend.h" + +#import <AddressBook/AddressBook.h> + +//Qt +#import <QtCore/QFile> +#import <QtCore/QDir> +#import <QtCore/QHash> +#import <QtWidgets/QApplication> +#import <QtCore/QStandardPaths> +#import <QTimer> +#import <QPixmap> +#import <QtGlobal> + +//Ring +#import <Person.h> +#import <account.h> +#import <person.h> +#import <contactmethod.h> + +/** + * + *kABFirstNameProperty + kABLastNameProperty + kABFirstNamePhoneticProperty + kABLastNamePhoneticProperty + kABBirthdayProperty + kABOrganizationProperty + kABJobTitleProperty + kABHomePageProperty + kABEmailProperty + kABAddressProperty + kABPhoneProperty + kABAIMInstantProperty + kABJabberInstantProperty + kABMSNInstantProperty + kABYahooInstantProperty + kABICQInstantProperty + kABNoteProperty + kABMiddleNameProperty + kABMiddleNamePhoneticProperty + kABTitleProperty + kABSuffixProperty + kABNicknameProperty + kABMaidenNameProperty + */ + +class AddressBookEditor : public CollectionEditor<Person> +{ +public: + AddressBookEditor(CollectionMediator<Person>* m, AddressBookBackend* parent); + virtual bool save ( const Person* item ) override; + virtual bool remove ( const Person* item ) override; + virtual bool edit ( Person* item ) override; + virtual bool addNew ( const Person* item ) override; + virtual bool addExisting( const Person* item ) override; + +private: + virtual QVector<Person*> items() const override; + + //Helpers + void savePerson(QTextStream& stream, const Person* Person); + bool regenFile(const Person* toIgnore); + + //Attributes + QVector<Person*> m_lItems; + AddressBookBackend* m_pCollection; +}; + +AddressBookEditor::AddressBookEditor(CollectionMediator<Person>* m, AddressBookBackend* parent) : +CollectionEditor<Person>(m),m_pCollection(parent) +{ + +} + +AddressBookBackend::AddressBookBackend(CollectionMediator<Person>* mediator) : +CollectionInterface(new AddressBookEditor(mediator,this)),m_pMediator(mediator) +{ + +} + +AddressBookBackend::~AddressBookBackend() +{ + +} + +void AddressBookEditor::savePerson(QTextStream& stream, const Person* Person) +{ + + qDebug() << "Saving Person!"; +} + +bool AddressBookEditor::regenFile(const Person* toIgnore) +{ + QDir dir(QString('/')); + dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString()); + + + return false; +} + +bool AddressBookEditor::save(const Person* Person) +{ + //if (Person->collection()->editor<Person>() != this) + // return addNew(Person); + + return regenFile(nullptr); +} + +bool AddressBookEditor::remove(const Person* item) +{ + return regenFile(item); +} + +bool AddressBookEditor::edit( Person* item) +{ + Q_UNUSED(item) + return false; +} + +bool AddressBookEditor::addNew(const Person* Person) +{ + QDir dir(QString('/')); + dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString()); + + return false; +} + +bool AddressBookEditor::addExisting(const Person* item) +{ + m_lItems << const_cast<Person*>(item); + mediator()->addItem(item); + return true; +} + +QVector<Person*> AddressBookEditor::items() const +{ + return m_lItems; +} + +QString AddressBookBackend::name () const +{ + return QObject::tr("AddressBook backend"); +} + +QString AddressBookBackend::category () const +{ + return QObject::tr("Persons"); +} + +QVariant AddressBookBackend::icon() const +{ + return QVariant(); +} + +bool AddressBookBackend::isEnabled() const +{ + return true; +} + +bool AddressBookBackend::load() +{ + QTimer::singleShot(100, [=] { + asyncLoad(0); + }); + return false; +} + +void AddressBookBackend::asyncLoad(int startingPoint) +{ + ABAddressBook *book = [ABAddressBook sharedAddressBook]; + NSArray *everyone = [book people]; + int endPoint = qMin(startingPoint + 10, (int)everyone.count); + + for (int i = startingPoint; i < endPoint; ++i) { + + ABPerson* abPerson = ((ABPerson*)[everyone objectAtIndex:i]); + + Person* person = new Person(QByteArray::fromNSData(abPerson.vCardRepresentation), + Person::Encoding::vCard, + this); + + if(abPerson.imageData) + person->setPhoto(QVariant(QPixmap::fromImage(QImage::fromData(QByteArray::fromNSData((abPerson.imageData)))))); + + if([person->formattedName().toNSString() isEqualToString:@""] && + [person->secondName().toNSString() isEqualToString:@""] && + [person->firstName().toNSString() isEqualToString:@""]) { + continue; + } + + person->setCollection(this); + + editor<Person>()->addExisting(person); + } + + if(endPoint < everyone.count) { + QTimer::singleShot(100, [=] { + asyncLoad(endPoint); + }); + } + +} + + +bool AddressBookBackend::reload() +{ + return false; +} + +FlagPack<AddressBookBackend::SupportedFeatures> AddressBookBackend::supportedFeatures() const +{ + return (FlagPack<SupportedFeatures>) ( + CollectionInterface::SupportedFeatures::NONE | + CollectionInterface::SupportedFeatures::LOAD | + CollectionInterface::SupportedFeatures::CLEAR | + CollectionInterface::SupportedFeatures::REMOVE| + CollectionInterface::SupportedFeatures::ADD ); +} + +bool AddressBookBackend::clear() +{ + /* TODO: insert confirm dialog? */ + return true; +} + +QByteArray AddressBookBackend::id() const +{ + return "abb"; +} diff --git a/src/backends/MinimalHistoryBackend.h b/src/backends/MinimalHistoryBackend.h deleted file mode 100644 index 50eef51dec2ad5a20cffc8a4b46aafe37650d8b1..0000000000000000000000000000000000000000 --- a/src/backends/MinimalHistoryBackend.h +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************************ - * Copyright (C) 2014-2015 by Savoir-Faire Linux * - * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***********************************************************************************/ -#ifndef MINIMALHISTORYBACKEND_H -#define MINIMALHISTORYBACKEND_H - -#import <collectioninterface.h> - -class Call; - -template<typename T> class CollectionMediator; - -class LIB_EXPORT MinimalHistoryBackend : public CollectionInterface -{ -public: - explicit MinimalHistoryBackend(CollectionMediator<Call>* mediator); - virtual ~MinimalHistoryBackend(); - - virtual bool load() override; - virtual bool reload() override; - virtual bool clear() override; - virtual QString name () const override; - virtual QString category () const override; - virtual QVariant icon () const override; - virtual bool isEnabled() const override; - virtual QByteArray id () const override; - virtual SupportedFeatures supportedFeatures() const override; - int daysSince(time_t timestamp); - - -private: - CollectionMediator<Call>* m_pMediator; -}; - -#endif diff --git a/src/backends/MinimalHistoryBackend.mm b/src/backends/MinimalHistoryBackend.mm deleted file mode 100644 index 2f126cdc972e442a65324e1b591f6234ef99e9cd..0000000000000000000000000000000000000000 --- a/src/backends/MinimalHistoryBackend.mm +++ /dev/null @@ -1,290 +0,0 @@ -/************************************************************************************ - * Copyright (C) 2014-2015 by Savoir-Faire Linux * - * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***********************************************************************************/ -#import "minimalhistorybackend.h" - -#import <Cocoa/Cocoa.h> - -//Qt -#import <QtCore/QFile> -#import <QtCore/QDir> -#import <QtCore/qlist.h> -#import <QtCore/QHash> -#import <QtWidgets/QApplication> -#import <QtCore/QStandardPaths> -#import <collectioneditor.h> - -//Ring -#import <call.h> -#import <account.h> -#import <person.h> -#import <contactmethod.h> -#import <categorizedhistorymodel.h> - -class MinimalHistoryEditor : public CollectionEditor<Call> -{ -public: - MinimalHistoryEditor(CollectionMediator<Call>* m, MinimalHistoryBackend* parent); - virtual bool save ( const Call* item ) override; - virtual bool remove ( const Call* item ) override; - virtual bool batchRemove(const QList<Call*> contacts) override; - virtual bool edit ( Call* item ) override; - virtual bool addNew ( const Call* item ) override; - virtual bool addExisting( const Call* item ) override; - -private: - virtual QVector<Call*> items() const override; - - //Helpers - void saveCall(QTextStream& stream, const Call* call); - bool regenFile(const Call* toIgnore); - - //Attributes - QVector<Call*> m_lItems; - MinimalHistoryBackend* m_pCollection; -}; - -MinimalHistoryEditor::MinimalHistoryEditor(CollectionMediator<Call>* m, MinimalHistoryBackend* parent) : -CollectionEditor<Call>(m),m_pCollection(parent) -{ - -} - -MinimalHistoryBackend::MinimalHistoryBackend(CollectionMediator<Call>* mediator) : -CollectionInterface(new MinimalHistoryEditor(mediator,this)),m_pMediator(mediator) -{ - -} - -MinimalHistoryBackend::~MinimalHistoryBackend() -{ - -} - -void MinimalHistoryEditor::saveCall(QTextStream& stream, const Call* call) -{ - const QString direction = (call->direction()==Call::Direction::INCOMING)? - Call::HistoryStateName::INCOMING : Call::HistoryStateName::OUTGOING; - - const Account* a = call->account(); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::CALLID ).arg(call->historyId() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::TIMESTAMP_START ).arg(call->startTimeStamp() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::TIMESTAMP_STOP ).arg(call->stopTimeStamp() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::ACCOUNT_ID ).arg(a?QString(a->id()):"" ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::DISPLAY_NAME ).arg(call->peerName() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::PEER_NUMBER ).arg(call->peerContactMethod()->uri() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::DIRECTION ).arg(direction ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::MISSED ).arg(call->isMissed() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::RECORDING_PATH ).arg(call->recordingPath() ); - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::CONTACT_USED ).arg(false );//TODO - if (call->peerContactMethod()->contact()) { - stream << QString("%1=%2\n").arg(Call::HistoryMapFields::CONTACT_UID ).arg( - QString(call->peerContactMethod()->contact()->uid()) - ); - } - stream << "\n"; - stream.flush(); -} - -bool MinimalHistoryEditor::regenFile(const Call* toIgnore) -{ - QDir dir(QString('/')); - dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString()); - - QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') +"history.ini"); - if ( file.open(QIODevice::WriteOnly | QIODevice::Text) ) { - QTextStream stream(&file); - for (const Call* c : CategorizedHistoryModel::instance()->getHistoryCalls()) { - if (c != toIgnore) - saveCall(stream, c); - } - file.close(); - return true; - } - return false; -} - -bool MinimalHistoryEditor::save(const Call* call) -{ - if (call->collection()->editor<Call>() != this) - return addNew(call); - - return regenFile(nullptr); -} - -bool MinimalHistoryEditor::remove(const Call* item) -{ - mediator()->removeItem(item); - return regenFile(item); -} - -bool MinimalHistoryEditor::batchRemove(const QList<Call*> calls) { - QFile::remove(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + "history.ini"); - return YES; -} - -bool MinimalHistoryEditor::edit( Call* item) -{ - Q_UNUSED(item) - return false; -} - -bool MinimalHistoryEditor::addNew(const Call* call) -{ - QDir dir(QString('/')); - dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString()); - - if ((call->collection() && call->collection()->editor<Call>() == this) || call->historyId().isEmpty()) return false; - //TODO support \r and \n\r end of line - QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/')+"history.ini"); - - if ( file.open(QIODevice::Append | QIODevice::Text) ) { - QTextStream streamFileOut(&file); - saveCall(streamFileOut, call); - file.close(); - - const_cast<Call*>(call)->setCollection(m_pCollection); - addExisting(call); - return true; - } - else - qWarning() << "Unable to save history"; - return false; -} - -bool MinimalHistoryEditor::addExisting(const Call* item) -{ - m_lItems << const_cast<Call*>(item); - mediator()->addItem(item); - return true; -} - -QVector<Call*> MinimalHistoryEditor::items() const -{ - return m_lItems; -} - -QString MinimalHistoryBackend::name () const -{ - return QObject::tr("Minimal history backend"); -} - -QString MinimalHistoryBackend::category () const -{ - return QObject::tr("History"); -} - -QVariant MinimalHistoryBackend::icon() const -{ - return QVariant(); -} - -bool MinimalHistoryBackend::isEnabled() const -{ - return true; -} - -bool MinimalHistoryBackend::load() -{ - // get history limit from our preferences set - NSInteger historyLimit = [[NSUserDefaults standardUserDefaults] integerForKey:@"history_limit"]; - - QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') +"history.ini"); - if ( file.open(QIODevice::ReadOnly | QIODevice::Text) ) { - QMap<QString,QString> hc; - while (!file.atEnd()) { - QByteArray line = file.readLine().trimmed(); - - //The item is complete - if ((line.isEmpty() || !line.size()) && hc.size()) { - Call* pastCall = Call::buildHistoryCall(hc); - if (pastCall->peerName().isEmpty()) { - pastCall->setPeerName(QObject::tr("Unknown")); - } - - if(daysSince(pastCall->startTimeStamp()) < historyLimit) { - pastCall->setRecordingPath(hc[ Call::HistoryMapFields::RECORDING_PATH ]); - pastCall->setCollection(this); - - editor<Call>()->addExisting(pastCall); - } - hc.clear(); - } - // Add to the current set - else { - const int idx = line.indexOf("="); - if (idx >= 0) - hc[line.left(idx)] = line.right(line.size()-idx-1); - } - } - return true; - } - else - qWarning() << "History doesn't exist or is not readable"; - return false; -} - -int MinimalHistoryBackend::daysSince(time_t timestamp) -{ - NSDate *fromDate; - NSDate *toDate; - - NSDate* fromDateTime = [NSDate dateWithTimeIntervalSince1970:timestamp]; - - NSCalendar *calendar = [NSCalendar currentCalendar]; - - [calendar rangeOfUnit:NSCalendarUnitDay startDate:&fromDate - interval:NULL forDate:fromDateTime]; - [calendar rangeOfUnit:NSCalendarUnitDay startDate:&toDate - interval:NULL forDate:[NSDate date]]; - - NSDateComponents *difference = [calendar components:NSCalendarUnitDay - fromDate:fromDate toDate:toDate options:0]; - - return [difference day]; -} - -bool MinimalHistoryBackend::reload() -{ - return false; -} - -CollectionInterface::SupportedFeatures MinimalHistoryBackend::supportedFeatures() const -{ - return (CollectionInterface::SupportedFeatures) ( - CollectionInterface::SupportedFeatures::NONE | - CollectionInterface::SupportedFeatures::LOAD | - CollectionInterface::SupportedFeatures::CLEAR | - CollectionInterface::SupportedFeatures::REMOVE| - CollectionInterface::SupportedFeatures::ADD ); -} - -bool MinimalHistoryBackend::clear() -{ - editor<Call>()->batchRemove(items<Call>().toList()); - QList<Call*> calls = items<Call>().toList(); - for(int i = 0 ; i < calls.count() ; ++i) { - CategorizedHistoryModel::instance()->deleteItem(calls[i]); - } - return true; -} - -QByteArray MinimalHistoryBackend::id() const -{ - return "mhb"; -} diff --git a/src/delegates/ImageManipulationDelegate.h b/src/delegates/ImageManipulationDelegate.h new file mode 100644 index 0000000000000000000000000000000000000000..2f711aea1c4086cef01ef37fa75ec869c602953d --- /dev/null +++ b/src/delegates/ImageManipulationDelegate.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#ifndef IMAGEMANIPULATION_H +#define IMAGEMANIPULATION_H + +#import <QuartzCore/QuartzCore.h> + +#import <delegates/pixmapmanipulationdelegate.h> +#import <call.h> + +class Person; +class QPixmap; + +class ImageManipulationDelegate : public PixmapManipulationDelegate { + +public: + ImageManipulationDelegate(); + QVariant contactPhoto(Person* c, const QSize& size, bool displayPresence = true) override; + virtual QByteArray toByteArray(const QVariant& pxm) override; + virtual QVariant personPhoto(const QByteArray& data, const QString& type = "PNG") override; + +private: + //Helper + QPixmap drawDefaultUserPixmap(const QSize& size, bool displayPresence, bool isPresent); + CGImageRef resizeCGImage(CGImageRef image, const QSize& size); + + +}; + +#endif // IMAGEMANIPULATION_H diff --git a/src/delegates/ImageManipulationDelegate.mm b/src/delegates/ImageManipulationDelegate.mm new file mode 100644 index 0000000000000000000000000000000000000000..40f7a0a60384386eb656da4b4bf6035b949e405a --- /dev/null +++ b/src/delegates/ImageManipulationDelegate.mm @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall import the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#import "ImageManipulationDelegate.h" + +#import <Cocoa/Cocoa.h> +#import <Foundation/Foundation.h> + +//Qt +#import <QSize> +#import <QBuffer> +#import <QtGui/QColor> +#import <QtGui/QPainter> +#import <QtGui/QBitmap> +#import <QtWidgets/QApplication> +#import <QtGui/QImage> +#import <QtMacExtras/qmacfunctions.h> +#import <QtGui/QPalette> + +//Ring +#import <person.h> +#import <contactmethod.h> + +ImageManipulationDelegate::ImageManipulationDelegate() : PixmapManipulationDelegate() +{ + +} + +QVariant ImageManipulationDelegate::contactPhoto(Person* c, const QSize& size, bool displayPresence) { + const int radius = (size.height() > 35) ? 7 : 5; + + QPixmap pxm; + if (c->photo().isValid()) { + QPixmap contactPhoto((qvariant_cast<QPixmap>(c->photo())).scaledToWidth(size.height()-6)); + pxm = QPixmap(size); + pxm.fill(Qt::transparent); + QPainter painter(&pxm); + + //Clear the pixmap + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(0,0,size.width(),size.height(),QBrush(Qt::white)); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + //Add corner radius to the Pixmap + QRect pxRect = contactPhoto.rect(); + QBitmap mask(pxRect.size()); + QPainter customPainter(&mask); + customPainter.setRenderHint (QPainter::Antialiasing, true ); + customPainter.fillRect (pxRect , Qt::white ); + customPainter.setBackground (Qt::black ); + customPainter.setBrush (Qt::black ); + customPainter.drawRoundedRect(pxRect,radius,radius); + contactPhoto.setMask(mask); + painter.drawPixmap(3,3,contactPhoto); + painter.setBrush(Qt::NoBrush); + painter.setPen(Qt::white); + painter.setRenderHint (QPainter::Antialiasing, true ); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + painter.drawRoundedRect(3,3,pxm.height()-6,pxm.height()-6,radius,radius); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + } + else { + pxm = drawDefaultUserPixmap(size, false, false); + } + + return pxm; +} + +QVariant ImageManipulationDelegate::personPhoto(const QByteArray& data, const QString& type) +{ + QImage image; + //For now, ENCODING is only base64 and image type PNG or JPG + const bool ret = image.loadFromData(QByteArray::fromBase64(data),type.toLatin1()); + if (!ret) + qDebug() << "vCard image loading failed"; + + return QPixmap::fromImage(image); +} + +QByteArray ImageManipulationDelegate::toByteArray(const QVariant& pxm) +{ + //Preparation of our QPixmap + QByteArray bArray; + QBuffer buffer(&bArray); + buffer.open(QIODevice::WriteOnly); + + //PNG ? + (qvariant_cast<QPixmap>(pxm)).save(&buffer, "PNG"); + buffer.close(); + + return bArray; +} + +QPixmap ImageManipulationDelegate::drawDefaultUserPixmap(const QSize& size, bool displayPresence, bool isPresent) { + + QPixmap pxm(size); + pxm.fill(Qt::transparent); + QPainter painter(&pxm); + + // create the image somehow, load from file, draw into it... + auto sourceImgRef = CGImageSourceCreateWithData((CFDataRef)[[NSImage imageNamed:@"NSUser"] TIFFRepresentation], NULL); + auto imgRef = CGImageSourceCreateImageAtIndex(sourceImgRef, 0, NULL); + auto finalImgRef = resizeCGImage(imgRef, size); + painter.drawPixmap(3,3,QtMac::fromCGImageRef(finalImgRef)); + + CFRelease(sourceImgRef); + CFRelease(imgRef); + CFRelease(finalImgRef); + + return pxm; +} + +CGImageRef ImageManipulationDelegate::resizeCGImage(CGImageRef image, const QSize& size) { + // create context, keeping original image properties + CGColorSpaceRef colorspace = CGImageGetColorSpace(image); + + CGContextRef context = CGBitmapContextCreate(NULL, size.width(), size.height(), + CGImageGetBitsPerComponent(image), + size.width() * CGImageGetBitsPerComponent(image), + colorspace, + CGImageGetAlphaInfo(image)); + + if(context == NULL) + return nil; + + // draw image to context (resizing it) + CGContextDrawImage(context, CGRectMake(0, 0, size.width(), size.height()), image); + // extract resulting image from context + CGImageRef imgRef = CGBitmapContextCreateImage(context); + CGContextRelease(context); + + return imgRef; +} diff --git a/src/views/CallView.h b/src/views/CallView.h new file mode 100644 index 0000000000000000000000000000000000000000..b15d5251430f2938aceda9a8f79050e2bd23c64c --- /dev/null +++ b/src/views/CallView.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#import <Cocoa/Cocoa.h> + +@protocol FullScreenDelegate; +@protocol FullScreenDelegate + +@optional + +-(void) callShouldToggleFullScreen; + +@end + +@interface CallView : NSView <NSDraggingDestination, NSOpenSavePanelDelegate> +{ + //highlight the drop zone + BOOL highlight; +} + +- (id)initWithCoder:(NSCoder *)coder; + +/** + * Sets weither this view allow first click interactions + */ +@property BOOL shouldAcceptInteractions; + +/** + * Delegate to inform about desire to move + */ +@property (nonatomic) id <FullScreenDelegate> fullScreenDelegate; + +@end diff --git a/src/views/CallView.mm b/src/views/CallView.mm new file mode 100644 index 0000000000000000000000000000000000000000..8fe60c63bf5591d6e7533eee826968c0939c34c0 --- /dev/null +++ b/src/views/CallView.mm @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import "CallView.h" + +#import <QItemSelectionModel> +#import <QAbstractProxyModel> +#import <QUrl> + +#import <video/configurationproxy.h> +#import <video/sourcemodel.h> +#import <video/previewmanager.h> +#import <video/renderer.h> +#import <video/device.h> +#import <video/devicemodel.h> + +@interface CallView () + +@property NSMenu *contextualMenu; + +@end + +@implementation CallView +@synthesize contextualMenu; +@synthesize shouldAcceptInteractions; + + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]]; + } + return self; +} + + +#pragma mark - Destination Operations + +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + /*------------------------------------------------------ + method called whenever a drag enters our drop zone + --------------------------------------------------------*/ + NSLog(@"Dragging entered"); + + NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]]; + CFStringRef fileExtension = (__bridge CFStringRef) [fileURL.path pathExtension]; + CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL); + + // Check if the pasteboard contains image data and source/user wants it copied + if ( [sender draggingSourceOperationMask] & NSDragOperationCopy && + (UTTypeConformsTo(fileUTI, kUTTypeVideo)) || (UTTypeConformsTo(fileUTI, kUTTypeMovie))) { + + //highlight our drop zone + highlight=YES; + + [self setNeedsDisplay: YES]; + + /* When an image from one window is dragged over another, we want to resize the dragging item to + * preview the size of the image as it would appear if the user dropped it in. */ + [sender enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent + forView:self + classes:[NSArray arrayWithObject:[NSPasteboardItem class]] + searchOptions:nil + usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) { + *stop = YES; + }]; + CFRelease(fileUTI); + //accept data as a copy operation + return NSDragOperationCopy; + } + + CFRelease(fileUTI); + return NSDragOperationNone; +} + +- (void)draggingExited:(id <NSDraggingInfo>)sender +{ + /*------------------------------------------------------ + method called whenever a drag exits our drop zone + --------------------------------------------------------*/ + //remove highlight of the drop zone + highlight=NO; + + [self setNeedsDisplay: YES]; +} + +-(void)drawRect:(NSRect)rect +{ + /*------------------------------------------------------ + draw method is overridden to do drop highlighing + --------------------------------------------------------*/ + //do the usual draw operation to display the image + [super drawRect:rect]; + + if ( highlight ) { + //highlight by overlaying a gray border + [[NSColor blueColor] set]; + [NSBezierPath setDefaultLineWidth: 5]; + [NSBezierPath strokeRect: rect]; + } +} + +- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender +{ + /*------------------------------------------------------ + method to determine if we can accept the drop + --------------------------------------------------------*/ + //finished with the drag so remove any highlighting + highlight=NO; + + [self setNeedsDisplay: YES]; + + NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]]; + CFStringRef fileExtension = (__bridge CFStringRef) [fileURL.path pathExtension]; + CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL); + + BOOL conforms = (UTTypeConformsTo(fileUTI, kUTTypeVideo)) || (UTTypeConformsTo(fileUTI, kUTTypeMovie)); + CFRelease(fileUTI); + //check to see if we can accept the data + return conforms; +} + +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + /*------------------------------------------------------ + method that should handle the drop data + --------------------------------------------------------*/ + if ( [sender draggingSource] != self ) { + NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]]; + Video::SourceModel::instance()->setFile(QUrl::fromLocalFile(QString::fromUtf8([fileURL.path UTF8String]))); + } + + return YES; +} + +- (void)showContextualMenu:(NSEvent *)theEvent { + + contextualMenu = [[NSMenu alloc] initWithTitle:@"Switch camera"]; + + for(int i = 0 ; i < Video::DeviceModel::instance()->devices().size() ; ++i) { + Video::Device* device = Video::DeviceModel::instance()->devices()[i]; + [contextualMenu insertItemWithTitle:device->name().toNSString() action:@selector(switchInput:) keyEquivalent:@"" atIndex:i]; + } + + [contextualMenu addItem:[NSMenuItem separatorItem]]; + [contextualMenu insertItemWithTitle:@"Choose file" action:@selector(chooseFile:) keyEquivalent:@"" atIndex:contextualMenu.itemArray.count]; + + [NSMenu popUpContextMenu:contextualMenu withEvent:theEvent forView:self]; +} + +- (void)mouseUp:(NSEvent *)theEvent +{ + if([theEvent clickCount] == 1 && shouldAcceptInteractions) { + if(!contextualMenu) + [self performSelector:@selector(showContextualMenu:) withObject:theEvent afterDelay:[NSEvent doubleClickInterval]]; + else + contextualMenu = nil; + } + else if([theEvent clickCount] == 2) + { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel showContextualMenu + if(self.fullScreenDelegate) + [self.fullScreenDelegate callShouldToggleFullScreen]; + } +} + +- (void) switchInput:(NSMenuItem*) sender +{ + int index = [contextualMenu indexOfItem:sender]; + Video::SourceModel::instance()->switchTo(Video::DeviceModel::instance()->devices()[index]); +} + +- (void) chooseFile:(NSMenuItem*) sender +{ + NSOpenPanel *browsePanel = [[NSOpenPanel alloc] init]; + [browsePanel setDirectoryURL:[NSURL URLWithString:NSHomeDirectory()]]; + [browsePanel setCanChooseFiles:YES]; + [browsePanel setCanChooseDirectories:NO]; + [browsePanel setCanCreateDirectories:NO]; + + //NSMutableArray* fileTypes = [[NSMutableArray alloc] initWithArray:[NSImage imageTypes]]; + NSMutableArray* fileTypes = [NSMutableArray array]; + [fileTypes addObject:(__bridge NSString *)kUTTypeVideo]; + [fileTypes addObject:(__bridge NSString *)kUTTypeMovie]; + [browsePanel setAllowedFileTypes:fileTypes]; + + [browsePanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) { + NSURL* theDoc = [[browsePanel URLs] objectAtIndex:0]; + Video::SourceModel::instance()->setFile(QUrl::fromLocalFile(QString::fromUtf8([theDoc.path UTF8String]))); + } + }]; + +} + +@end diff --git a/src/views/ITProgressIndicator.h b/src/views/ITProgressIndicator.h new file mode 100644 index 0000000000000000000000000000000000000000..59ee8ea06f75e6f3b8f171ed5c971b224635b88c --- /dev/null +++ b/src/views/ITProgressIndicator.h @@ -0,0 +1,88 @@ +//Copyright 2013-2015 Ilija Tovilo +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + + +#import <Cocoa/Cocoa.h> +#import <QuartzCore/QuartzCore.h> + +// +// !!!IMPORTANT!!! - Embedd ITProgressIndicator in a layer-backed view to avoid side-effects! +// + +/** + * @class ITProgressIndicator + * + * A replacement for `NSProgressIndicator`. + * It's a highly customizable control, driven by Core Animation, which makes it much more performant. + * + * So basically, it's awesome. + * + */ +@interface ITProgressIndicator : NSView + +#pragma mark - Methods + +/** + * Override this method to achieve a custom animation + * + * @return CAKeyframeAnimation - animation which will be put on the progress indicator layer + */ +- (CAKeyframeAnimation *)keyFrameAnimationForCurrentPreferences; + +#pragma mark - Properties + + +/// @property isIndeterminate - Indicates if the view will show the progress, or just spin +@property (nonatomic, setter = setIndeterminate:) BOOL isIndeterminate; + + +/// @property progress - The amount that should be shown when `isIndeterminate` is set to `YES` +@property (nonatomic) CGFloat progress; + + +/// @property animates - Indicates if the view is animating +@property (nonatomic) BOOL animates; + + +/// @property hideWhenStopped - Indicates if the view will be hidden if it's stopped +@property (nonatomic) BOOL hideWhenStopped; + + +/// @property lengthOfLine - The length of a single line +@property (nonatomic) CGFloat lengthOfLine; + + +/// @property widthOfLine - The width of a single line +@property (nonatomic) CGFloat widthOfLine; + + +/// @property numberOfLines - The number of lines of the indicator +@property (nonatomic) NSUInteger numberOfLines; + + +/// @property innerMargin - The distance of the lines from the middle +@property (nonatomic) CGFloat innerMargin; + + +/// @property animationDuration - Duration of a single rotation +@property (nonatomic) CGFloat animationDuration; + + +/// @property gradualAnimation - Defines if the animation is smooth or gradual +@property (nonatomic) BOOL steppedAnimation; + +/// @property color - The color of the progress indicator +@property (nonatomic, strong) NSColor *color; + +@end diff --git a/src/views/ITProgressIndicator.mm b/src/views/ITProgressIndicator.mm new file mode 100644 index 0000000000000000000000000000000000000000..189ea79d0fab4ebba6522ba656346d218365b92d --- /dev/null +++ b/src/views/ITProgressIndicator.mm @@ -0,0 +1,349 @@ +//Copyright 2013-2015 Ilija Tovilo +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +#if !__has_feature(objc_arc) +#error ARC needs to be enabled! +#endif + + +#import "ITProgressIndicator.h" + + +#pragma mark - Consts +#define kITSpinAnimationKey @"spinAnimation" +#define kITProgressPropertyKey @"progress" + + +// ---------------------------------------------------------------------------------------- +#pragma mark - NSBezierPath+IT_Geometry +// ---------------------------------------------------------------------------------------- + +@interface NSBezierPath (IT_Geometry) + +- (NSBezierPath*)it_rotatedBezierPath:(float) angle; +- (NSBezierPath*)it_rotatedBezierPath:(float) angle aboutPoint:(NSPoint)point; + +@end + +@implementation NSBezierPath (IT_Geometry) + +- (NSBezierPath *)it_rotatedBezierPath:(float)angle { + return [self it_rotatedBezierPath:angle aboutPoint:NSMakePoint(NSMidX(self.bounds), NSMidY(self.bounds))]; +} + +- (NSBezierPath*)it_rotatedBezierPath:(float)angle aboutPoint:(NSPoint)point { + if(angle == 0.0) return self; + else + { + NSBezierPath* copy = [self copy]; + NSAffineTransform *xfm = [self it_rotationTransformWithAngle:angle aboutPoint:point]; + [copy transformUsingAffineTransform:xfm]; + + return copy; + } +} + +- (NSAffineTransform *)it_rotationTransformWithAngle:(const float)angle aboutPoint:(const NSPoint)aboutPoint { + NSAffineTransform *xfm = [NSAffineTransform transform]; + [xfm translateXBy:aboutPoint.x yBy:aboutPoint.y]; + [xfm rotateByRadians:angle]; + [xfm translateXBy:-aboutPoint.x yBy:-aboutPoint.y]; + + return xfm; +} + +@end + + + + +// ---------------------------------------------------------------------------------------- +#pragma mark - ITProgressIndicator +// ---------------------------------------------------------------------------------------- + +#pragma mark - Private Interface + +@interface ITProgressIndicator () +@property (nonatomic, strong, readonly) CALayer *rootLayer; +@property (nonatomic, strong, readonly) CALayer *progressIndicatorLayer; +@end + + +#pragma mark - Implementation + +@implementation ITProgressIndicator +@synthesize progressIndicatorLayer = _progressIndicatorLayer; + + +#pragma mark - Init + +- (id)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (self) { + [self initLayers]; + } + return self; +} + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self initLayers]; + } + return self; +} + +- (void)initLayers { + // Setting initial values + self.color = [NSColor blackColor]; + self.innerMargin = 4; + self.widthOfLine = 3; + self.lengthOfLine = 6; + self.numberOfLines = 8; + self.animationDuration = 0.6; + self.isIndeterminate = YES; + self.steppedAnimation = YES; + self.hideWhenStopped = YES; + self.animates = YES; + + // Init layers + _rootLayer = [CALayer layer]; + self.layer = _rootLayer; + [self setWantsLayer:YES]; + self.progressIndicatorLayer.frame = _rootLayer.bounds; + [_rootLayer addSublayer:self.progressIndicatorLayer]; + + [self reloadIndicatorContent]; + [self reloadAnimation]; +} + +- (void)awakeFromNib { + [self reloadAnimation]; +} + +- (void)reloadIndicatorContent { + self.progressIndicatorLayer.contents = [self progressImage]; +} + +- (void)reloadAnimation { + [self.progressIndicatorLayer removeAnimationForKey:kITSpinAnimationKey]; + + if (self.animates) { + [self.progressIndicatorLayer addAnimation:[self keyFrameAnimationForCurrentPreferences] forKey:kITSpinAnimationKey]; + } +} + + +#pragma mark - Drawing + +- (NSImage *)progressImage { + NSImage *progressImage = [[NSImage alloc] initWithSize:self.bounds.size]; + [progressImage lockFocus]; + { + [NSGraphicsContext saveGraphicsState]; + { + [self.color set]; + + NSRect r = self.bounds; + NSBezierPath *line = [NSBezierPath bezierPathWithRoundedRect: + NSMakeRect((NSWidth(r) / 2) - (self.widthOfLine / 2), + (NSHeight(r) / 2) - self.innerMargin - self.lengthOfLine, + self.widthOfLine, self.lengthOfLine) + xRadius:self.widthOfLine / 2 + yRadius:self.widthOfLine / 2]; + + void (^lineDrawingBlock)(NSUInteger line) = + ^(NSUInteger lineNumber) { + NSBezierPath *lineInstance = [line copy]; + lineInstance = [lineInstance it_rotatedBezierPath:((2 * M_PI) / self.numberOfLines * lineNumber) + M_PI + aboutPoint:NSMakePoint(NSWidth(r) / 2, NSHeight(r) / 2)]; + + if (_isIndeterminate) [[self.color colorWithAlphaComponent:1.0 - (1.0 / self.numberOfLines * lineNumber)] set]; + + [lineInstance fill]; + }; + + if (!self.isIndeterminate) { + for (NSUInteger i = self.numberOfLines; + i > round(self.numberOfLines - (self.numberOfLines * self.progress)); + i--) + { + lineDrawingBlock(i); + } + } else { + for (NSUInteger i = 0; i < self.numberOfLines; i++) { + lineDrawingBlock(i); + } + } + } + [NSGraphicsContext restoreGraphicsState]; + } + [progressImage unlockFocus]; + + return progressImage; +} + + +#pragma mark - Helpers + +- (CAKeyframeAnimation *)keyFrameAnimationForCurrentPreferences { + NSMutableArray* keyFrameValues = [NSMutableArray array]; + NSMutableArray* keyTimeValues; + + if (self.steppedAnimation) { + { + [keyFrameValues addObject:[NSNumber numberWithFloat:0.0]]; + for (NSUInteger i = 0; i < self.numberOfLines; i++) { + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI * (2.0 / self.numberOfLines * i)]]; + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI * (2.0 / self.numberOfLines * i)]]; + } + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI*2.0]]; + } + + keyTimeValues = [NSMutableArray array]; + { + [keyTimeValues addObject:[NSNumber numberWithFloat:0.0]]; + for (NSUInteger i = 0; i < (self.numberOfLines - 1); i++) { + [keyTimeValues addObject:[NSNumber numberWithFloat:1.0 / self.numberOfLines * i]]; + [keyTimeValues addObject:[NSNumber numberWithFloat:1.0 / self.numberOfLines * (i + 1)]]; + } + [keyTimeValues addObject:[NSNumber numberWithFloat:1.0 / self.numberOfLines * (self.numberOfLines - 1)]]; + } + } else { + { + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI*0.0]]; + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI*0.5]]; + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI*1.0]]; + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI*1.5]]; + [keyFrameValues addObject:[NSNumber numberWithFloat:-M_PI*2.0]]; + } + } + + + CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; + + [animation setRepeatCount:HUGE_VALF]; + [animation setValues:keyFrameValues]; + [animation setKeyTimes:keyTimeValues]; + [animation setValueFunction:[CAValueFunction functionWithName: kCAValueFunctionRotateZ]]; + [animation setDuration:self.animationDuration]; + + return animation; +} + +- (void)reloadVisibility { + if (_hideWhenStopped && !_animates && _isIndeterminate) { + [self setHidden:YES]; + } else { + [self setHidden:NO]; + } +} + + +#pragma mark - NSView methods + +// Animatible proxy ++ (id)defaultAnimationForKey:(NSString *)key +{ + if ([key isEqualToString:kITProgressPropertyKey]) { + return [CABasicAnimation animation]; + } else { + return [super defaultAnimationForKey:key]; + } +} + + +#pragma mark - Setters & Getters + +- (void)setIndeterminate:(BOOL)isIndeterminate { + _isIndeterminate = isIndeterminate; + + if (!_isIndeterminate) { + self.animates = NO; + } +} + +- (void)setProgress:(CGFloat)progress { + if (progress < 0 || progress > 1) { + @throw [NSException exceptionWithName:@"Invalid `progress` property value" + reason:@"`progress` property needs to be between 0 and 1" + userInfo:nil]; + } + + _progress = progress; + + if (!self.isIndeterminate) { + [self reloadIndicatorContent]; + } +} + +- (void)setAnimates:(BOOL)animates { + _animates = animates; + [self reloadIndicatorContent]; + [self reloadAnimation]; + [self reloadVisibility]; +} + +- (void)setHideWhenStopped:(BOOL)hideWhenStopped { + _hideWhenStopped = hideWhenStopped; + [self reloadVisibility]; +} + +- (CALayer *)progressIndicatorLayer { + if (!_progressIndicatorLayer) { + _progressIndicatorLayer = [CALayer layer]; + } + + return _progressIndicatorLayer; +} + +- (void)setLengthOfLine:(CGFloat)lengthOfLine { + _lengthOfLine = lengthOfLine; + [self reloadIndicatorContent]; +} + +- (void)setWidthOfLine:(CGFloat)widthOfLine { + _widthOfLine = widthOfLine; + [self reloadIndicatorContent]; +} + +- (void)setInnerMargin:(CGFloat)innerMargin { + _innerMargin = innerMargin; + [self reloadIndicatorContent]; +} + +- (void)setAnimationDuration:(CGFloat)animationDuration { + _animationDuration = animationDuration; + [self reloadAnimation]; +} + +- (void)setNumberOfLines:(NSUInteger)numberOfLines { + _numberOfLines = numberOfLines; + [self reloadIndicatorContent]; + [self reloadAnimation]; +} + +- (void)setSteppedAnimation:(BOOL)steppedAnimation { + _steppedAnimation = steppedAnimation; + [self reloadAnimation]; +} + +- (void)setColor:(NSColor *)color { + _color = color; + [self reloadIndicatorContent]; +} + +@end diff --git a/src/views/PersonCell.h b/src/views/PersonCell.h new file mode 100644 index 0000000000000000000000000000000000000000..34aeecdbc48348f42dd6cac8ea5f976d3f73398c --- /dev/null +++ b/src/views/PersonCell.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import <Cocoa/Cocoa.h> + + +@interface PersonCell : NSTextFieldCell +{ + BOOL mIsEditingOrSelecting; + +} +@property NSImage *personImage; +@property NSString* primaryText; +@property NSString* secondaryText; + +@end diff --git a/src/views/PersonCell.mm b/src/views/PersonCell.mm new file mode 100644 index 0000000000000000000000000000000000000000..9b6bede5f98fec99a2f7c2eb31fc9402789cbe92 --- /dev/null +++ b/src/views/PersonCell.mm @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2004-2015 Savoir-Faire Linux Inc. + * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#import "PersonCell.h" + +#define kImageOriginXOffset 3 +#define kImageOriginYOffset 1 + +#define kTextOriginXOffset 2 +#define kTextOriginYOffset 2 +#define kTextHeightAdjust 4 + +@implementation PersonCell + +// ------------------------------------------------------------------------------- +// initTextCell:aString +// ------------------------------------------------------------------------------- +- (instancetype)initTextCell:(NSString *)aString +{ + self = [super initTextCell:aString]; + if (self != nil) + { + // we want a smaller font + [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + } + return self; +} + +// ------------------------------------------------------------------------------- +// copyWithZone:zone +// ------------------------------------------------------------------------------- +- (id)copyWithZone:(NSZone *)zone +{ + PersonCell *cell = (PersonCell *)[super copyWithZone:zone]; + cell.personImage = self.personImage; + return cell; +} + +// ------------------------------------------------------------------------------- +// titleRectForBounds:cellRect +// +// Returns the proper bound for the cell's title while being edited +// ------------------------------------------------------------------------------- +- (NSRect)titleRectForBounds:(NSRect)cellRect +{ + // the cell has an image: draw the normal item cell + NSSize imageSize; + NSRect imageFrame; + + imageSize = [self.personImage size]; + NSDivideRect(cellRect, &imageFrame, &cellRect, 3 + imageSize.width, NSMinXEdge); + + imageFrame.origin.x += kImageOriginXOffset; + imageFrame.origin.y -= kImageOriginYOffset; + imageFrame.size = imageSize; + + imageFrame.origin.y += ceil((cellRect.size.height - imageFrame.size.height) / 2); + + NSRect newFrame = cellRect; + newFrame.origin.x += kTextOriginXOffset; + newFrame.origin.y += kTextOriginYOffset; + newFrame.size.height -= kTextHeightAdjust; + + return newFrame; +} + +// ------------------------------------------------------------------------------- +// editWithFrame:inView:editor:delegate:event +// ------------------------------------------------------------------------------- +- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent +{ + NSRect textFrame = [self titleRectForBounds:aRect]; + [super editWithFrame:textFrame inView:controlView editor:textObj delegate:anObject event:theEvent]; +} + +// ------------------------------------------------------------------------------- +// selectWithFrame:inView:editor:delegate:event:start:length +// ------------------------------------------------------------------------------- +- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength +{ + NSRect textFrame = [self titleRectForBounds:aRect]; + [super selectWithFrame:textFrame inView:controlView editor:textObj delegate:anObject start:selStart length:selLength]; +} + +// ------------------------------------------------------------------------------- +// drawWithFrame:cellFrame:controlView +// ------------------------------------------------------------------------------- +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + NSRect newCellFrame = cellFrame; + + if (self.personImage != nil) + { + NSSize imageSize; + NSRect imageFrame; + + imageSize = [self.personImage size]; + NSDivideRect(newCellFrame, &imageFrame, &newCellFrame, imageSize.width, NSMinXEdge); + if ([self drawsBackground]) + { + [[self backgroundColor] set]; + NSRectFill(imageFrame); + } + + imageFrame.origin.y += 2; + imageFrame.size = imageSize; + + [self.personImage drawInRect:imageFrame + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1.0 + respectFlipped:YES + hints:nil]; + } + + [super drawWithFrame:newCellFrame inView:controlView]; +} + +- (NSRect)drawingRectForBounds:(NSRect)theRect +{ + // Get the parent's idea of where we should draw + NSRect newRect = [super drawingRectForBounds:theRect]; + + // When the text field is being + // edited or selected, we have to turn off the magic because it screws up + // the configuration of the field editor. We sneak around this by + // intercepting selectWithFrame and editWithFrame and sneaking a + // reduced, centered rect in at the last minute. + if (mIsEditingOrSelecting == NO) + { + // Get our ideal size for current text + NSSize textSize = [self cellSizeForBounds:theRect]; + + // Center that in the proposed rect + float heightDelta = newRect.size.height - textSize.height; + if (heightDelta > 0) + { + newRect.size.height -= heightDelta; + newRect.origin.y += (heightDelta / 2); + } + } + return newRect; +} + +// ------------------------------------------------------------------------------- +// cellSize +// ------------------------------------------------------------------------------- +- (NSSize)cellSize +{ + NSSize cellSize = [super cellSize]; + cellSize.width += (self.personImage ? [self.personImage size].width : 0) + 3; + return cellSize; +} + +@end diff --git a/ui/AccAdvanced.xib b/ui/AccAdvanced.xib index 5edd143ff61121daae0b96186e13219ec1d4cbee..291a17d17cf8799cb244bc48bd981580d30d1efc 100644 --- a/ui/AccAdvanced.xib +++ b/ui/AccAdvanced.xib @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> </dependencies> @@ -44,15 +44,16 @@ </textFieldCell> </textField> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="A98-ed-6iq"> - <rect key="frame" x="266" y="434" width="19" height="27"/> + <rect key="frame" x="219" y="434" width="19" height="27"/> <stepperCell key="cell" continuous="YES" alignment="left" maxValue="200" id="3kR-uk-WAl"/> <connections> <action selector="valueDidChange:" target="-2" id="eL2-tT-Wdd"/> </connections> </stepper> <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vy9-CY-MrG"> - <rect key="frame" x="165" y="437" width="96" height="22"/> + <rect key="frame" x="165" y="437" width="49" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="rtX-dk-eMQ"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="99r-pE-lsZ"/> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> @@ -105,7 +106,7 @@ </button> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e6E-9n-08s"> <rect key="frame" x="266" y="365" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="99999" id="sr0-PX-dXX"/> + <stepperCell key="cell" continuous="YES" alignment="left" maxValue="65534" id="sr0-PX-dXX"/> <connections> <action selector="valueDidChange:" target="-2" id="5GF-aO-SNO"/> </connections> @@ -113,6 +114,9 @@ <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TgP-vt-AeS"> <rect key="frame" x="165" y="368" width="96" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="x39-hY-AAQ"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="7Cv-xY-6X5"> + <real key="maximum" value="65534"/> + </numberFormatter> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> @@ -142,7 +146,7 @@ </textField> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bnh-Mc-8kR"> <rect key="frame" x="198" y="153" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="99999" id="IDZ-ip-UEs"/> + <stepperCell key="cell" continuous="YES" alignment="left" maxValue="65534" id="IDZ-ip-UEs"/> <connections> <action selector="valueDidChange:" target="-2" id="Daw-BV-Edf"/> </connections> @@ -150,6 +154,9 @@ <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="b3n-gl-1th"> <rect key="frame" x="97" y="156" width="96" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="fsG-bB-ubb"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="oaP-iC-Alq"> + <real key="maximum" value="65534"/> + </numberFormatter> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> @@ -168,7 +175,7 @@ </textField> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FI1-5p-EcC"> <rect key="frame" x="364" y="153" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="99999" id="6DL-JU-9vo"/> + <stepperCell key="cell" continuous="YES" alignment="left" maxValue="65534" id="6DL-JU-9vo"/> <connections> <action selector="valueDidChange:" target="-2" id="qZ3-R2-m60"/> </connections> @@ -176,6 +183,9 @@ <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GOf-0V-pyS"> <rect key="frame" x="263" y="156" width="96" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="rcL-uY-sov"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="nBc-fC-RAm"> + <real key="maximum" value="65534"/> + </numberFormatter> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> @@ -202,7 +212,7 @@ </textField> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="caC-em-Z8T"> <rect key="frame" x="197" y="92" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="99999" id="uSL-4H-pJJ"/> + <stepperCell key="cell" continuous="YES" alignment="left" maxValue="65534" id="uSL-4H-pJJ"/> <connections> <action selector="valueDidChange:" target="-2" id="gab-7r-VfC"/> </connections> @@ -210,6 +220,9 @@ <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="khU-Ue-Hen"> <rect key="frame" x="96" y="95" width="96" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="J1A-gZ-lMv"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="7Vg-kX-7gY"> + <real key="maximum" value="65534"/> + </numberFormatter> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> @@ -228,7 +241,7 @@ </textField> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uac-nZ-Hcw"> <rect key="frame" x="363" y="92" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="99999" id="heA-0J-Vqe"/> + <stepperCell key="cell" continuous="YES" alignment="left" maxValue="65534" id="heA-0J-Vqe"/> <connections> <action selector="valueDidChange:" target="-2" id="uJU-9R-UJR"/> </connections> @@ -236,6 +249,9 @@ <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IJS-5x-riX"> <rect key="frame" x="262" y="95" width="96" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="L2A-qa-yTI"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="qXb-fv-xC6"> + <real key="maximum" value="65534"/> + </numberFormatter> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> @@ -255,7 +271,7 @@ <matrix verticalHuggingPriority="750" fixedFrame="YES" allowsEmptySelection="NO" autorecalculatesCellSize="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2yz-X7-dVm"> <rect key="frame" x="64" y="255" width="320" height="38"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - <size key="cellSize" width="216" height="18"/> + <size key="cellSize" width="217" height="18"/> <size key="intercellSpacing" width="4" height="2"/> <buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" inset="2" id="fB0-X8-GXk"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> @@ -309,6 +325,14 @@ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> </textFieldCell> </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kpt-7R-pUj"> + <rect key="frame" x="240" y="440" width="55" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="seconds" id="xGw-j0-4ID"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> </subviews> </customView> </subviews> diff --git a/ui/AccGeneral.xib b/ui/AccGeneral.xib index 4e8f5167e739c0a008b27e8eafc633d197a24209..b4d5ad2d8b1152ee223fb463dc9e6b6e53e37247 100644 --- a/ui/AccGeneral.xib +++ b/ui/AccGeneral.xib @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> - <deployment identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="AccGeneralVC"> @@ -147,6 +146,26 @@ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> </textFieldCell> </textField> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SEc-jZ-TUE"> + <rect key="frame" x="327" y="5" width="129" height="18"/> + <buttonCell key="cell" type="check" title="Show password" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="Ue3-ai-FBV"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="showPassword:" target="-2" id="5zI-is-yfV"/> + </connections> + </button> + <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jgg-ZS-LyJ"> + <rect key="frame" x="323" y="37" width="137" height="32"/> + <buttonCell key="cell" type="push" title="Try Registration" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="j6w-ZG-17T"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="tryRegistration:" target="-2" id="KIF-Ps-esl"/> + </connections> + </button> </subviews> </customView> <customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7NY-Hh-Icd"> diff --git a/ui/AccSecurity.xib b/ui/AccSecurity.xib index 26fd39d19b5346520af55a0fbd0175414f3cb56f..30b8bc620e6cabcf3ac9d37132e97bacb4188d1f 100644 --- a/ui/AccSecurity.xib +++ b/ui/AccSecurity.xib @@ -1,227 +1,337 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="AccSecurityVC"> <connections> + <outlet property="caListPathControl" destination="D3W-lQ-lnP" id="LS8-ny-clH"/> + <outlet property="certificatePathControl" destination="nm6-E0-NSH" id="fCd-di-v07"/> + <outlet property="cipherListView" destination="J53-ev-CMr" id="G0A-j6-uHa"/> + <outlet property="outgoingTlsServerName" destination="Zw6-Ys-Kie" id="JQC-aS-9eu"/> + <outlet property="pvkPasswordField" destination="bZ4-7D-4l0" id="EEN-a1-4oT"/> + <outlet property="pvkPathControl" destination="88W-qC-bQk" id="rWY-57-6pv"/> + <outlet property="requireCertButton" destination="oUu-x4-vV2" id="PNH-3v-phG"/> + <outlet property="srtpRTPFallback" destination="uPY-dc-wnh" id="mqd-Uf-g1o"/> + <outlet property="tlsContainer" destination="Ubf-Oe-BbE" id="a5m-qR-9n2"/> + <outlet property="tlsMethodList" destination="OzX-Lm-vTo" id="t16-Ed-be0"/> + <outlet property="tlsNegotiationTimeout" destination="Vei-qo-Elj" id="aHP-q0-cLi"/> + <outlet property="tlsNegotiationTimeoutStepper" destination="HTb-Hz-UsO" id="nAt-yM-bvQ"/> + <outlet property="useSRTP" destination="MDR-gI-NEM" id="cEh-uu-v1A"/> + <outlet property="useTLS" destination="KkG-2O-ExR" id="Ain-yM-iXd"/> + <outlet property="verifyCertAsClientButton" destination="IEc-xs-DVB" id="jxt-UK-0Pr"/> + <outlet property="verifyCertAsServerButton" destination="zc3-fU-bFt" id="DmT-0V-uWA"/> <outlet property="view" destination="Hz6-mo-xeY" id="zzq-0g-nOy"/> </connections> </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> <customView id="Hz6-mo-xeY"> - <rect key="frame" x="0.0" y="0.0" width="498" height="477"/> + <rect key="frame" x="0.0" y="0.0" width="694" height="628"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <subviews> <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KkG-2O-ExR"> - <rect key="frame" x="18" y="441" width="133" height="18"/> + <rect key="frame" x="18" y="532" width="133" height="18"/> <buttonCell key="cell" type="check" title="Use TLS transport" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="oTT-gO-fqM"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <font key="font" metaFont="system"/> </buttonCell> - </button> - <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pMZ-IN-XqN"> - <rect key="frame" x="213" y="413" width="96" height="22"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="OlW-vM-KEy"> - <font key="font" metaFont="system"/> - <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <secureTextField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bZ4-7D-4l0"> - <rect key="frame" x="213" y="278" width="192" height="22"/> - <secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="7c2-kF-dcs"> - <font key="font" metaFont="system"/> - <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> - <allowedInputSourceLocales> - <string>NSAllRomanInputSourcesLocaleIdentifier</string> - </allowedInputSourceLocales> - </secureTextFieldCell> - </secureTextField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tde-PT-YmG"> - <rect key="frame" x="68" y="281" width="136" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Private key password" id="gRf-gt-1lS"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vei-qo-Elj"> - <rect key="frame" x="210" y="154" width="96" height="22"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="wIn-YV-Pbm"> - <font key="font" metaFont="system"/> - <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JIN-cT-eC7"> - <rect key="frame" x="69" y="251" width="135" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="TLS protocol method" id="uOE-XA-5gW"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zw6-Ys-Kie"> - <rect key="frame" x="361" y="184" width="96" height="22"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="CAB-Dc-UQr"> - <font key="font" metaFont="system"/> - <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D3W-lQ-lnP"> - <rect key="frame" x="208" y="381" width="200" height="26"/> - <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="onD-BW-yiQ"> - <font key="font" metaFont="system"/> - </pathCell> <connections> - <outlet property="delegate" destination="-2" id="xmv-cI-kXi"/> + <action selector="toggleUseTLS:" target="-2" id="ThA-al-8l0"/> </connections> - </pathControl> - <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nm6-E0-NSH"> - <rect key="frame" x="208" y="350" width="200" height="26"/> - <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="jgm-ZK-yDf"> - <font key="font" metaFont="system"/> - </pathCell> - <connections> - <outlet property="delegate" destination="-2" id="7dO-SK-pkt"/> - </connections> - </pathControl> - <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="88W-qC-bQk"> - <rect key="frame" x="208" y="315" width="200" height="26"/> - <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="peP-JR-p0s"> - <font key="font" metaFont="system"/> - </pathCell> - <connections> - <outlet property="delegate" destination="-2" id="Lg6-ti-eMl"/> - </connections> - </pathControl> - <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GXt-f8-Yr7"> - <rect key="frame" x="314" y="410" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="100" id="RDj-lm-kWd"/> - </stepper> - <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5gr-bk-6ak"> - <rect key="frame" x="462" y="181" width="19" height="27"/> - <stepperCell key="cell" continuous="YES" alignment="left" maxValue="100" id="ExI-j5-7st"/> - </stepper> - <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IEc-xs-DVB"> - <rect key="frame" x="42" y="96" width="198" height="18"/> - <buttonCell key="cell" type="check" title="Verify certificates, as a client" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="1xi-9G-zOX"> - <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - </button> - <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zc3-fU-bFt"> - <rect key="frame" x="42" y="124" width="263" height="18"/> - <buttonCell key="cell" type="check" title="Verify incoming certificates, as a server" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="x2s-ov-cVQ"> - <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> </button> - <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oUu-x4-vV2"> - <rect key="frame" x="42" y="66" width="329" height="18"/> - <buttonCell key="cell" type="check" title="Require certificates for incoming TLS connections" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="1f9-X5-ykb"> + <customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ubf-Oe-BbE"> + <rect key="frame" x="27" y="305" width="625" height="221"/> + <subviews> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JIN-cT-eC7"> + <rect key="frame" x="54" y="85" width="135" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="TLS protocol method" id="uOE-XA-5gW"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D3W-lQ-lnP"> + <rect key="frame" x="193" y="177" width="200" height="26"/> + <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="onD-BW-yiQ"> + <font key="font" metaFont="system"/> + </pathCell> + <connections> + <action selector="caListPathControlSingleClick:" target="-2" id="q3F-hD-ivO"/> + <outlet property="delegate" destination="-2" id="xmv-cI-kXi"/> + </connections> + </pathControl> + <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nm6-E0-NSH"> + <rect key="frame" x="193" y="146" width="200" height="26"/> + <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="jgm-ZK-yDf"> + <font key="font" metaFont="system"/> + </pathCell> + <connections> + <action selector="certificatePathControlSingleClick:" target="-2" id="QEp-Pf-AmX"/> + <outlet property="delegate" destination="-2" id="7dO-SK-pkt"/> + </connections> + </pathControl> + <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="88W-qC-bQk"> + <rect key="frame" x="193" y="111" width="200" height="26"/> + <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="peP-JR-p0s"> + <font key="font" metaFont="system"/> + </pathCell> + <connections> + <action selector="pvkFilePathControlSingleClick:" target="-2" id="zsf-fI-Wms"/> + <outlet property="delegate" destination="-2" id="Lg6-ti-eMl"/> + </connections> + </pathControl> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oPj-Ol-4rt"> + <rect key="frame" x="92" y="150" width="101" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="User Certificate" id="Jqz-P0-zAz"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Si-LL-vAi"> + <rect key="frame" x="121" y="117" width="72" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Private key" id="JHb-iu-ZwI"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OzX-Lm-vTo"> + <rect key="frame" x="194" y="80" width="204" height="26"/> + <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="XAT-9Y-9e0"> + <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="menu"/> + <menu key="menu" title="tlsmethodlist" id="hOG-SW-CBI"> + <connections> + <outlet property="delegate" destination="-2" id="ri6-UD-0cj"/> + </connections> + </menu> + </popUpButtonCell> + <connections> + <action selector="chooseTlsMethod:" target="-2" id="Mas-vi-T6e"/> + </connections> + </popUpButton> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vei-qo-Elj"> + <rect key="frame" x="346" y="17" width="96" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="wIn-YV-Pbm"> + <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="Duh-um-Evj"/> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <outlet property="delegate" destination="-2" id="vaP-6X-fFT"/> + </connections> + </textField> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zw6-Ys-Kie"> + <rect key="frame" x="346" y="46" width="244" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="CAB-Dc-UQr"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <outlet property="delegate" destination="-2" id="OPQ-A6-Llk"/> + </connections> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnB-RJ-T6u"> + <rect key="frame" x="23" y="49" width="317" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Server name instance for outgoing TLS connection" id="UxS-et-whJ"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9Nq-Sf-LSO"> + <rect key="frame" x="211" y="20" width="129" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Negociation timeout" id="9Q3-1W-Lh3"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bea-Hp-LP1"> + <rect key="frame" x="392" y="169" width="39" height="38"/> + <buttonCell key="cell" type="round" bezelStyle="circular" image="NSInfo" imagePosition="above" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="un1-35-2si"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="label"/> + </buttonCell> + <connections> + <action selector="showCA:" target="-2" id="tfO-R7-Odx"/> + </connections> + </button> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aTq-AA-kGe"> + <rect key="frame" x="392" y="138" width="39" height="38"/> + <buttonCell key="cell" type="round" bezelStyle="circular" image="NSInfo" imagePosition="above" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="lFn-d8-HkS"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="label"/> + </buttonCell> + <connections> + <action selector="showEndpointCertificate:" target="-2" id="1dC-9g-Uax"/> + </connections> + </button> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="J7P-Gf-Pq8"> + <rect key="frame" x="38" y="181" width="155" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Certificate Authority list" id="vPF-Rp-aR6"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <secureTextField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bZ4-7D-4l0"> + <rect key="frame" x="398" y="112" width="192" height="22"/> + <secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Password..." drawsBackground="YES" usesSingleLineMode="YES" id="7c2-kF-dcs"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + <allowedInputSourceLocales> + <string>NSAllRomanInputSourcesLocaleIdentifier</string> + </allowedInputSourceLocales> + </secureTextFieldCell> + <connections> + <outlet property="delegate" destination="-2" id="xf5-zS-Th0"/> + </connections> + </secureTextField> + <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HTb-Hz-UsO"> + <rect key="frame" x="447" y="14" width="19" height="27"/> + <stepperCell key="cell" continuous="YES" alignment="left" maxValue="100" id="NiO-pL-W7t"/> + <connections> + <action selector="valueDidChange:" target="-2" id="SZI-r3-CRu"/> + </connections> + </stepper> + </subviews> + </customView> + <customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hb0-lT-WfA"> + <rect key="frame" x="27" y="35" width="625" height="262"/> + <subviews> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3df-06-qZy"> + <rect key="frame" x="44" y="178" width="160" height="18"/> + <buttonCell key="cell" type="check" title="Use custom cipher list" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="2e5-zS-ghg"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + </button> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zc3-fU-bFt"> + <rect key="frame" x="44" y="63" width="290" height="18"/> + <buttonCell key="cell" type="check" title="Verify incoming certificates, as a server" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="x2s-ov-cVQ"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleVerifyCertServer:" target="-2" id="c4y-hN-v0d"/> + </connections> + </button> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oUu-x4-vV2"> + <rect key="frame" x="44" y="42" width="333" height="18"/> + <buttonCell key="cell" type="check" title="Require certificates for incoming TLS connections" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="1f9-X5-ykb"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleRequireCert:" target="-2" id="4Vu-Hg-beA"/> + </connections> + </button> + <scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rf5-DP-g8u"> + <rect key="frame" x="230" y="113" width="395" height="149"/> + <clipView key="contentView" misplaced="YES" id="5qx-CS-RZZ"> + <rect key="frame" x="1" y="17" width="238" height="117"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" headerView="d0e-h3-O5f" indentationPerLevel="16" outlineTableColumn="n3A-3A-ad2" id="J53-ev-CMr"> + <rect key="frame" x="0.0" y="0.0" width="393" height="19"/> + <autoresizingMask key="autoresizingMask"/> + <size key="intercellSpacing" width="3" height="2"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> + <tableColumns> + <tableColumn identifier="StateColumn" width="30.08984375" minWidth="10" maxWidth="3.4028234663852886e+38" id="Ctd-77-A6e" userLabel="State"> + <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/> + </tableHeaderCell> + <buttonCell key="dataCell" type="check" bezelStyle="regularSquare" imagePosition="overlaps" alignment="left" inset="2" id="lbr-Bo-TJZ"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + <connections> + <action selector="toggleCipher:" target="-2" id="IJu-SC-UzR"/> + </connections> + </buttonCell> + <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + </tableColumn> + <tableColumn identifier="CipherNameColumn" width="357" minWidth="40" maxWidth="1000" id="n3A-3A-ad2"> + <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/> + </tableHeaderCell> + <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="5LC-T6-hpk"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + </tableColumn> + </tableColumns> + <connections> + <outlet property="delegate" destination="-2" id="wwd-1j-4m9"/> + </connections> + </outlineView> + </subviews> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </clipView> + <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="IPW-7g-Ykq"> + <rect key="frame" x="1" y="119" width="223" height="15"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="P9Y-fn-MnT"> + <rect key="frame" x="224" y="17" width="15" height="102"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + <tableHeaderView key="headerView" id="d0e-h3-O5f"> + <rect key="frame" x="0.0" y="0.0" width="238" height="17"/> + <autoresizingMask key="autoresizingMask"/> + </tableHeaderView> + </scrollView> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IEc-xs-DVB"> + <rect key="frame" x="44" y="83" width="243" height="18"/> + <buttonCell key="cell" type="check" title="Verify certificates, as a client" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="1xi-9G-zOX"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleVerifyCertAsClient:" target="-2" id="fau-7t-gD2"/> + </connections> + </button> + </subviews> + </customView> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uPY-dc-wnh"> + <rect key="frame" x="38" y="564" width="149" height="18"/> + <buttonCell key="cell" type="check" title="Fallback on RTP" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Ig9-B1-7N3"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <font key="font" metaFont="system"/> </buttonCell> + <connections> + <action selector="toggleRTPFallback:" target="-2" id="UOi-fW-eWB"/> + </connections> </button> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c3y-9n-Gpj"> - <rect key="frame" x="98" y="416" width="106" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="TLS listener port" id="lKC-W6-rAb"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="J7P-Gf-Pq8"> - <rect key="frame" x="39" y="385" width="165" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Certificate of Authority list" id="vPF-Rp-aR6"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oPj-Ol-4rt"> - <rect key="frame" x="38" y="354" width="166" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Public endpoint certificate" id="Jqz-P0-zAz"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Si-LL-vAi"> - <rect key="frame" x="110" y="321" width="94" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Private key file" id="JHb-iu-ZwI"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OzX-Lm-vTo"> - <rect key="frame" x="209" y="246" width="199" height="26"/> - <popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="AR3-KR-uq5" id="XAT-9Y-9e0"> - <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="menu"/> - <menu key="menu" id="hOG-SW-CBI"> - <items> - <menuItem title="Item 1" state="on" id="AR3-KR-uq5"/> - <menuItem title="Item 2" id="y3X-lJ-AUe"/> - <menuItem title="Item 3" id="Jgr-gX-SAn"/> - </items> - </menu> - </popUpButtonCell> - </popUpButton> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HA0-ix-NrF"> - <rect key="frame" x="112" y="219" width="93" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="TLS cipher list" id="R0v-ng-LPE"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnB-RJ-T6u"> - <rect key="frame" x="38" y="187" width="317" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Server name instance for outgoing TLS connection" id="UxS-et-whJ"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9Nq-Sf-LSO"> - <rect key="frame" x="75" y="157" width="129" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Negociation timeout" id="9Q3-1W-Lh3"> - <font key="font" metaFont="system"/> - <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zfa-Ks-xJm"> - <rect key="frame" x="208" y="214" width="200" height="26"/> - <popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="5TG-WY-fVo" id="uk0-kX-M0u"> - <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="menu"/> - <menu key="menu" id="WO9-SC-D1y"> - <items> - <menuItem title="Item 1" state="on" id="5TG-WY-fVo"/> - <menuItem title="Item 2" id="168-7q-W1h"/> - <menuItem title="Item 3" id="BVX-Y0-vyG"/> - </items> - </menu> - </popUpButtonCell> - </popUpButton> <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MDR-gI-NEM"> - <rect key="frame" x="18" y="21" width="85" height="18"/> + <rect key="frame" x="18" y="592" width="85" height="18"/> <buttonCell key="cell" type="check" title="Use SRTP" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="YtQ-hx-hih"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <font key="font" metaFont="system"/> </buttonCell> + <connections> + <action selector="toggleUseSRTP:" target="-2" id="crs-ka-i2O"/> + </connections> </button> </subviews> - <point key="canvasLocation" x="222" y="251.5"/> + <point key="canvasLocation" x="263" y="156"/> </customView> </objects> + <resources> + <image name="NSInfo" width="32" height="32"/> + </resources> </document> diff --git a/ui/AccVideo.xib b/ui/AccVideo.xib index 66ca7e6826599c641d698518d14159f248335d93..d6b02dbd0966f7123fae0fbd585ca4096ef84398 100644 --- a/ui/AccVideo.xib +++ b/ui/AccVideo.xib @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> - <deployment identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="AccVideoVC"> @@ -30,7 +29,7 @@ </connections> </button> <customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qN4-Se-Waf"> - <rect key="frame" x="33" y="55" width="322" height="216"/> + <rect key="frame" x="33" y="55" width="379" height="245"/> <subviews> <scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Xq-Du-JwG"> <rect key="frame" x="0.0" y="46" width="315" height="170"/> @@ -140,16 +139,16 @@ <action selector="moveUp:" target="-2" id="y2e-P5-leK"/> </connections> </button> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="flI-L9-J7Q"> + <rect key="frame" x="3" y="224" width="53" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Codecs" id="DlW-RF-2xv"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> </subviews> </customView> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="flI-L9-J7Q"> - <rect key="frame" x="31" y="279" width="53" height="17"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Codecs" id="DlW-RF-2xv"> - <font key="font" metaFont="systemBold"/> - <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> </subviews> <point key="canvasLocation" x="273" y="167"/> </customView> diff --git a/ui/AudioPrefs.xib b/ui/AudioPrefs.xib index d40e68c23377914b982ee7123b3f04bb7b22836e..3dcd213eb155362640d80d312afc8f1e28f3f186 100644 --- a/ui/AudioPrefs.xib +++ b/ui/AudioPrefs.xib @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> </dependencies> @@ -72,17 +72,6 @@ <action selector="chooseOutput:" target="-2" id="SIh-kG-rV5"/> </connections> </popUpButton> - <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nyE-tZ-v4C"> - <rect key="frame" x="95" y="316" width="210" height="26"/> - <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="SPB-Bz-vDM"> - <font key="font" metaFont="system"/> - <url key="url" string="file://localhost/Applications/"/> - </pathCell> - <connections> - <action selector="pathControlSingleClick:" target="-2" id="a8r-s5-lxq"/> - <outlet property="delegate" destination="-2" id="JXx-Le-f5R"/> - </connections> - </pathControl> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bMB-Zm-k0Z"> <rect key="frame" x="20" y="253" width="99" height="17"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Audio devices" id="B0r-wh-wc3"> @@ -132,6 +121,17 @@ <action selector="chooseInput:" target="-2" id="ADO-Qj-tAz"/> </connections> </popUpButton> + <pathControl verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nyE-tZ-v4C"> + <rect key="frame" x="95" y="318" width="210" height="26"/> + <pathCell key="cell" selectable="YES" editable="YES" alignment="left" pathStyle="popUp" id="SPB-Bz-vDM"> + <font key="font" metaFont="system"/> + <url key="url" string="file://localhost/Applications/"/> + </pathCell> + <connections> + <action selector="pathControlSingleClick:" target="-2" id="jyC-qZ-OzW"/> + <outlet property="delegate" destination="-2" id="JXx-Le-f5R"/> + </connections> + </pathControl> </subviews> </customView> </subviews> diff --git a/ui/CertificateWindow.xib b/ui/CertificateWindow.xib new file mode 100644 index 0000000000000000000000000000000000000000..107b06f386478abd307ba9eb316b21821fde203f --- /dev/null +++ b/ui/CertificateWindow.xib @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1077" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> + <dependencies> + <deployment identifier="macosx"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> + </dependencies> + <objects> + <customObject id="-2" userLabel="File's Owner" customClass="CertificateWC"> + <connections> + <outlet property="window" destination="QvC-M9-y7g" id="bos-rN-Jgz"/> + </connections> + </customObject> + <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> + <customObject id="-3" userLabel="Application" customClass="NSObject"/> + <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g"> + <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> + <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> + <rect key="contentRect" x="196" y="240" width="601" height="388"/> + <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1050"/> + <view key="contentView" id="EiT-Mj-1SZ"> + <rect key="frame" x="0.0" y="0.0" width="601" height="388"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oot-0X-jju"> + <rect key="frame" x="20" y="54" width="561" height="135"/> + <clipView key="contentView" misplaced="YES" id="rbk-gh-ueD"> + <rect key="frame" x="1" y="17" width="238" height="117"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="MKo-as-RdY" viewBased="YES" indentationPerLevel="16" outlineTableColumn="UEk-iW-QGt" id="GQE-up-gwL"> + <rect key="frame" x="0.0" y="0.0" width="238" height="117"/> + <autoresizingMask key="autoresizingMask"/> + <size key="intercellSpacing" width="3" height="2"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> + <tableColumns> + <tableColumn width="116" minWidth="40" maxWidth="1000" id="UEk-iW-QGt"> + <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/> + </tableHeaderCell> + <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="G7a-wY-fOS"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + <prototypeCellViews> + <tableCellView id="EQf-hj-0zK"> + <rect key="frame" x="1" y="1" width="116" height="17"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qaS-Bh-sxP"> + <rect key="frame" x="0.0" y="0.0" width="100" height="17"/> + <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Od1-pH-TGO"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + </subviews> + <connections> + <outlet property="textField" destination="qaS-Bh-sxP" id="OtI-5l-esy"/> + </connections> + </tableCellView> + </prototypeCellViews> + </tableColumn> + <tableColumn width="437" minWidth="40" maxWidth="1000" id="09Y-Vz-ax6"> + <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/> + </tableHeaderCell> + <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="RMj-fd-OsX"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + <prototypeCellViews> + <tableCellView id="ekp-aB-Wuv"> + <rect key="frame" x="120" y="1" width="437" height="17"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="A3p-Z8-Fhy"> + <rect key="frame" x="0.0" y="0.0" width="100" height="17"/> + <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="khh-uZ-Keh"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + </subviews> + <connections> + <outlet property="textField" destination="A3p-Z8-Fhy" id="5Ui-kq-mhW"/> + </connections> + </tableCellView> + </prototypeCellViews> + </tableColumn> + </tableColumns> + </outlineView> + </subviews> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </clipView> + <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="Gim-9c-e4f"> + <rect key="frame" x="1" y="119" width="223" height="15"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="Gdk-7j-Chx"> + <rect key="frame" x="224" y="17" width="15" height="102"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + <tableHeaderView key="headerView" id="MKo-as-RdY"> + <rect key="frame" x="0.0" y="0.0" width="238" height="17"/> + <autoresizingMask key="autoresizingMask"/> + </tableHeaderView> + </scrollView> + <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pXw-HJ-Apl"> + <rect key="frame" x="505" y="13" width="82" height="32"/> + <buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="x6p-u4-4Ot"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + <string key="keyEquivalent" base64-UTF8="YES"> +DQ +</string> + </buttonCell> + <connections> + <action selector="closePanel:" target="-2" id="0f6-l1-xBw"/> + </connections> + </button> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yvG-8F-MvY"> + <rect key="frame" x="18" y="351" width="177" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Certificate information" id="BrE-Hz-sVE"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + </subviews> + </view> + <connections> + <outlet property="delegate" destination="-2" id="3wn-SC-48D"/> + </connections> + <point key="canvasLocation" x="167.5" y="128"/> + </window> + </objects> +</document> diff --git a/ui/CurrentCall.xib b/ui/CurrentCall.xib index c241f902bbc6f8125cc433e5718b9e7e74e0da21..49907df94bc37ef120bad53917d6846f37788c4d 100644 --- a/ui/CurrentCall.xib +++ b/ui/CurrentCall.xib @@ -1,18 +1,23 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D2134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="CurrentCallVC"> <connections> + <outlet property="chatButton" destination="fmp-x4-Pef" id="ARt-dr-TRo"/> <outlet property="controlsPanel" destination="Eoi-B8-iL6" id="4xn-3b-SNn"/> <outlet property="hangUpButton" destination="Kjq-iM-NBL" id="Puz-4L-Okl"/> <outlet property="holdOnOffButton" destination="anb-Y8-JQi" id="HSl-pE-Kwg"/> + <outlet property="loadingIndicator" destination="JwW-2h-DyZ" id="EEb-50-oSJ"/> + <outlet property="muteAudioButton" destination="tQl-cT-0Lb" id="qV4-Ef-UTx"/> + <outlet property="muteVideoButton" destination="LVS-yZ-98V" id="qQs-zP-wQ4"/> <outlet property="personLabel" destination="bg3-hB-nE8" id="t6l-1B-JxI"/> <outlet property="pickUpButton" destination="qgD-3D-nD5" id="mkD-IT-22E"/> <outlet property="previewView" destination="6y6-RH-qOp" id="1PY-sd-mh4"/> <outlet property="recordOnOffButton" destination="oRa-pS-HN2" id="N7C-wn-0le"/> + <outlet property="splitView" destination="GIJ-gB-FZo" id="PM0-az-Q8X"/> <outlet property="stateLabel" destination="kFD-FB-vig" id="SSO-14-q2t"/> <outlet property="timeSpentLabel" destination="cIU-M7-xpN" id="9Rl-t3-gjY"/> <outlet property="videoView" destination="2wf-Py-l6B" id="dEF-Gx-w6x"/> @@ -22,616 +27,346 @@ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> <customView id="Hz6-mo-xeY"> - <rect key="frame" x="0.0" y="0.0" width="608" height="493"/> + <rect key="frame" x="0.0" y="0.0" width="1014" height="509"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <subviews> - <customView translatesAutoresizingMaskIntoConstraints="NO" id="2wf-Py-l6B"> - <rect key="frame" x="0.0" y="0.0" width="608" height="493"/> + <splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GIJ-gB-FZo"> + <rect key="frame" x="0.0" y="0.0" width="1014" height="509"/> <subviews> - <customView translatesAutoresizingMaskIntoConstraints="NO" id="6y6-RH-qOp" userLabel="Preview"> - <rect key="frame" x="413" y="20" width="175" height="120"/> - <constraints> - <constraint firstAttribute="height" constant="120" id="BvU-kV-0uD"/> - <constraint firstAttribute="width" constant="175" id="aEv-Tt-tSD"/> - </constraints> - </customView> - <customView translatesAutoresizingMaskIntoConstraints="NO" id="Eoi-B8-iL6" userLabel="Controls"> - <rect key="frame" x="20" y="20" width="385" height="77"/> + <customView id="2wf-Py-l6B" customClass="CallView"> + <rect key="frame" x="0.0" y="0.0" width="675" height="509"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <subviews> - <button horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kjq-iM-NBL"> - <rect key="frame" x="187" y="8" width="80" height="60"/> + <customView translatesAutoresizingMaskIntoConstraints="NO" id="d0X-cW-Xgz"> + <rect key="frame" x="20" y="438" width="635" height="71"/> + <subviews> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kFD-FB-vig"> + <rect key="frame" x="18" y="42" width="37" height="17"/> + <constraints> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="29" id="pft-oc-ZNh"/> + </constraints> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="State" id="ugy-uK-901"> + <font key="font" metaFont="system"/> + <color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="highlightColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cIU-M7-xpN"> + <rect key="frame" x="513" y="23" width="104" height="24"/> + <constraints> + <constraint firstAttribute="width" constant="100" id="9vz-kb-6L6"/> + </constraints> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Time" id="zsO-T7-9yi"> + <font key="font" size="20" name="HelveticaNeue"/> + <color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="highlightColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="string" keyPath="layer.cornerRadius" value="15"/> + </userDefinedRuntimeAttributes> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bg3-hB-nE8"> + <rect key="frame" x="18" y="17" width="85" height="17"/> + <constraints> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="81" id="gT7-Wu-XtU"/> + </constraints> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Person name" id="osk-LS-0Qg"> + <font key="font" metaFont="system"/> + <color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + </subviews> <constraints> - <constraint firstAttribute="width" constant="76" id="7Ja-wI-kLL"/> - <constraint firstAttribute="height" constant="55" id="E2W-LK-NfM"/> + <constraint firstItem="kFD-FB-vig" firstAttribute="leading" secondItem="bg3-hB-nE8" secondAttribute="leading" id="LXG-QI-oPf"/> + <constraint firstItem="cIU-M7-xpN" firstAttribute="top" secondItem="d0X-cW-Xgz" secondAttribute="top" constant="24" id="Qc7-qp-qSV"/> + <constraint firstAttribute="trailing" secondItem="cIU-M7-xpN" secondAttribute="trailing" constant="20" id="RXf-xZ-4f9"/> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="568" id="Xeq-Aa-f1W"/> + <constraint firstItem="kFD-FB-vig" firstAttribute="top" secondItem="bg3-hB-nE8" secondAttribute="bottom" constant="-42" id="Z06-5v-81Q"/> + <constraint firstItem="kFD-FB-vig" firstAttribute="top" secondItem="bg3-hB-nE8" secondAttribute="bottom" constant="-42" id="gRn-E6-o6O"/> + <constraint firstItem="kFD-FB-vig" firstAttribute="leading" secondItem="d0X-cW-Xgz" secondAttribute="leading" constant="20" id="i5C-8o-qKp"/> + <constraint firstAttribute="bottom" secondItem="kFD-FB-vig" secondAttribute="bottom" constant="42" id="l71-7V-oLx"/> + <constraint firstItem="bg3-hB-nE8" firstAttribute="leading" secondItem="d0X-cW-Xgz" secondAttribute="leading" constant="20" id="nV4-Vy-vqK"/> + <constraint firstAttribute="centerY" secondItem="cIU-M7-xpN" secondAttribute="centerY" id="yvc-8B-cEu"/> </constraints> - <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="7B8EA957-C2D1-40F0-ACD8-5F72C84E3E15" imagePosition="overlaps" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="kR5-bV-2KY"> - <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - <connections> - <action selector="hangUp:" target="-2" id="1Fj-b8-nfh"/> - </connections> - </button> - <button horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgD-3D-nD5"> - <rect key="frame" x="103" y="8" width="80" height="60"/> + </customView> + <customView translatesAutoresizingMaskIntoConstraints="NO" id="6y6-RH-qOp" userLabel="Preview"> + <rect key="frame" x="480" y="20" width="175" height="120"/> <constraints> - <constraint firstAttribute="width" constant="76" id="9Aq-GM-wT2"/> - <constraint firstAttribute="height" constant="55" id="mnN-fs-Rr6"/> + <constraint firstAttribute="height" constant="120" id="BvU-kV-0uD"/> + <constraint firstAttribute="width" constant="175" id="aEv-Tt-tSD"/> </constraints> - <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="81E4DCCA-D15B-4820-A27C-9F5B15440C8A" imagePosition="overlaps" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="CoO-HS-nEB"> - <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - <connections> - <action selector="accept:" target="-2" id="maS-G8-eY7"/> - </connections> - </button> - <button horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="anb-Y8-JQi"> - <rect key="frame" x="269" y="7" width="84" height="62"/> + </customView> + <customView translatesAutoresizingMaskIntoConstraints="NO" id="Eoi-B8-iL6" userLabel="Controls"> + <rect key="frame" x="20" y="20" width="452" height="81"/> + <subviews> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Kjq-iM-NBL" userLabel="Hang Up"> + <rect key="frame" x="162" y="40" width="58" height="32"/> + <constraints> + <constraint firstAttribute="width" constant="54" id="7Ja-wI-kLL"/> + <constraint firstAttribute="height" constant="27" id="E2W-LK-NfM"/> + </constraints> + <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="ic_action_hangup" imagePosition="overlaps" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="kR5-bV-2KY"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="hangUp:" target="-2" id="1Fj-b8-nfh"/> + </connections> + </button> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tQl-cT-0Lb" userLabel="Mute Audio"> + <rect key="frame" x="197" y="5" width="58" height="32"/> + <constraints> + <constraint firstAttribute="height" constant="27" id="TbE-0y-0UT"/> + <constraint firstAttribute="width" constant="54" id="eIi-Jg-kdC"/> + </constraints> + <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="ic_action_mute_audio" imagePosition="overlaps" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="7wg-Q4-mbD"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="muteAudio:" target="-2" id="DBk-mG-FLj"/> + </connections> + </button> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LVS-yZ-98V" userLabel="Mute Video"> + <rect key="frame" x="259" y="5" width="58" height="32"/> + <constraints> + <constraint firstAttribute="width" constant="54" id="D26-l1-SRe"/> + <constraint firstAttribute="height" constant="27" id="qDd-Wq-7QG"/> + </constraints> + <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="ic_action_mute_video" imagePosition="overlaps" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="sSe-V6-C7i"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="muteVideo:" target="-2" id="a6W-aB-zWX"/> + </connections> + </button> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qgD-3D-nD5" userLabel="Accept"> + <rect key="frame" x="100" y="40" width="58" height="32"/> + <constraints> + <constraint firstAttribute="width" constant="54" id="9Aq-GM-wT2"/> + <constraint firstAttribute="height" constant="27" id="mnN-fs-Rr6"/> + </constraints> + <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="ic_action_call" imagePosition="overlaps" alignment="left" borderStyle="border" imageScaling="proportionallyDown" id="CoO-HS-nEB"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="accept:" target="-2" id="maS-G8-eY7"/> + </connections> + </button> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oRa-pS-HN2"> + <rect key="frame" x="124" y="5" width="69" height="32"/> + <constraints> + <constraint firstAttribute="width" constant="65" id="Afw-2T-aY9"/> + <constraint firstAttribute="height" constant="27" id="t21-HC-Wvs"/> + </constraints> + <buttonCell key="cell" type="bevel" title="Record" bezelStyle="regularSquare" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" id="rhz-4Z-avV"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleRecording:" target="-2" id="gAc-ZJ-9PN"/> + </connections> + </button> + <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="anb-Y8-JQi" userLabel="Hold"> + <rect key="frame" x="224" y="40" width="53" height="32"/> + <constraints> + <constraint firstAttribute="width" constant="49" id="QBx-uC-sub"/> + <constraint firstAttribute="height" constant="27" id="nPV-iA-aaw"/> + </constraints> + <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="ic_action_hold" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" id="7w5-d1-mNe"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleHold:" target="-2" id="O18-nN-hHE"/> + </connections> + </button> + <button wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fmp-x4-Pef" userLabel="Chat"> + <rect key="frame" x="281" y="40" width="54" height="32"/> + <constraints> + <constraint firstAttribute="width" constant="50" id="eC5-B4-omb"/> + <constraint firstAttribute="height" constant="27" id="wYS-TH-4rc"/> + </constraints> + <backgroundFilters> + <ciFilter name="CIColorMonochrome"> + <configuration> + <ciColor key="inputColor" red="1" green="0.53908981990000004" blue="0.1518531061" alpha="1"/> + <null key="inputImage"/> + <real key="inputIntensity" value="1"/> + </configuration> + </ciFilter> + </backgroundFilters> + <buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="ic_action_chat" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" id="1fJ-X6-Rza"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleChat:" target="-2" id="7HN-HS-oqT"/> + </connections> + </button> + </subviews> <constraints> - <constraint firstAttribute="width" constant="76" id="QBx-uC-sub"/> - <constraint firstAttribute="height" constant="55" id="nPV-iA-aaw"/> + <constraint firstItem="qgD-3D-nD5" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="1lr-kB-A5h"/> + <constraint firstItem="Kjq-iM-NBL" firstAttribute="leading" secondItem="qgD-3D-nD5" secondAttribute="trailing" constant="8" id="3iK-1x-F2q"/> + <constraint firstItem="anb-Y8-JQi" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="4C3-og-43v"/> + <constraint firstAttribute="bottom" secondItem="fmp-x4-Pef" secondAttribute="bottom" constant="43" id="4qt-Sw-3oV"/> + <constraint firstItem="LVS-yZ-98V" firstAttribute="top" secondItem="fmp-x4-Pef" secondAttribute="bottom" constant="8" id="6Tt-Zj-Gnf"/> + <constraint firstAttribute="bottom" secondItem="LVS-yZ-98V" secondAttribute="bottom" constant="8" id="9xT-hf-H7Q"/> + <constraint firstAttribute="bottom" secondItem="Kjq-iM-NBL" secondAttribute="bottom" constant="43" id="IPS-1V-PVm"/> + <constraint firstItem="LVS-yZ-98V" firstAttribute="leading" secondItem="tQl-cT-0Lb" secondAttribute="trailing" constant="8" id="IUv-fA-Zva"/> + <constraint firstAttribute="bottom" secondItem="qgD-3D-nD5" secondAttribute="bottom" constant="43" id="KYy-za-dDq"/> + <constraint firstItem="anb-Y8-JQi" firstAttribute="leading" secondItem="Kjq-iM-NBL" secondAttribute="trailing" constant="8" id="Mcj-US-rZq"/> + <constraint firstAttribute="bottom" secondItem="anb-Y8-JQi" secondAttribute="bottom" constant="43" id="MwL-3I-lJv"/> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="385" id="TSJ-9A-brf"/> + <constraint firstItem="fmp-x4-Pef" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="Wmf-0A-jba"/> + <constraint firstItem="Kjq-iM-NBL" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="a1R-g8-5gl"/> + <constraint firstAttribute="bottom" secondItem="oRa-pS-HN2" secondAttribute="bottom" constant="8" id="aOv-xQ-1pk"/> + <constraint firstItem="fmp-x4-Pef" firstAttribute="leading" secondItem="anb-Y8-JQi" secondAttribute="trailing" constant="8" id="bSy-yw-J5C"/> + <constraint firstItem="tQl-cT-0Lb" firstAttribute="leading" secondItem="oRa-pS-HN2" secondAttribute="trailing" constant="8" id="d6a-bF-2d5"/> + <constraint firstItem="oRa-pS-HN2" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="46" id="faC-l8-iCU"/> + <constraint firstItem="tQl-cT-0Lb" firstAttribute="top" secondItem="anb-Y8-JQi" secondAttribute="bottom" constant="8" id="gZK-fs-Aml"/> + <constraint firstItem="oRa-pS-HN2" firstAttribute="leading" secondItem="Eoi-B8-iL6" secondAttribute="leading" constant="126" id="htl-he-rlg"/> + <constraint firstAttribute="bottom" secondItem="tQl-cT-0Lb" secondAttribute="bottom" constant="8" id="iWQ-V7-2PF"/> + <constraint firstItem="fmp-x4-Pef" firstAttribute="leading" secondItem="anb-Y8-JQi" secondAttribute="trailing" constant="8" id="jfN-xU-POX"/> + <constraint firstItem="qgD-3D-nD5" firstAttribute="leading" secondItem="oRa-pS-HN2" secondAttribute="trailing" constant="-89" id="wQF-FD-dbj"/> </constraints> - <buttonCell key="cell" type="bevel" title="Hold" bezelStyle="regularSquare" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" id="7w5-d1-mNe"> - <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - <connections> - <action selector="toggleHold:" target="-2" id="O18-nN-hHE"/> - </connections> - </button> - <button horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oRa-pS-HN2"> - <rect key="frame" x="18" y="8" width="80" height="60"/> + </customView> + <customView translatesAutoresizingMaskIntoConstraints="NO" id="JwW-2h-DyZ" customClass="ITProgressIndicator"> + <rect key="frame" x="287" y="205" width="100" height="100"/> <constraints> - <constraint firstAttribute="width" constant="76" id="Afw-2T-aY9"/> - <constraint firstAttribute="height" constant="55" id="t21-HC-Wvs"/> + <constraint firstAttribute="height" constant="100" id="gpl-re-hHE"/> + <constraint firstAttribute="width" constant="100" id="nmo-HF-lhL"/> </constraints> - <buttonCell key="cell" type="bevel" title="Record" bezelStyle="regularSquare" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" id="rhz-4Z-avV"> - <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - <connections> - <action selector="toggleRecording:" target="-2" id="gAc-ZJ-9PN"/> - </connections> - </button> + </customView> </subviews> <constraints> - <constraint firstItem="qgD-3D-nD5" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="1lr-kB-A5h"/> - <constraint firstItem="anb-Y8-JQi" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="4C3-og-43v"/> - <constraint firstAttribute="bottom" secondItem="Kjq-iM-NBL" secondAttribute="bottom" constant="11" id="IPS-1V-PVm"/> - <constraint firstAttribute="bottom" secondItem="qgD-3D-nD5" secondAttribute="bottom" constant="11" id="KYy-za-dDq"/> - <constraint firstAttribute="bottom" secondItem="anb-Y8-JQi" secondAttribute="bottom" constant="11" id="MwL-3I-lJv"/> - <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="385" id="TSJ-9A-brf"/> - <constraint firstItem="Kjq-iM-NBL" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="a1R-g8-5gl"/> - <constraint firstAttribute="bottom" secondItem="oRa-pS-HN2" secondAttribute="bottom" constant="11" id="aOv-xQ-1pk"/> - <constraint firstItem="oRa-pS-HN2" firstAttribute="top" secondItem="Eoi-B8-iL6" secondAttribute="top" constant="11" id="faC-l8-iCU"/> + <constraint firstAttribute="centerX" secondItem="JwW-2h-DyZ" secondAttribute="centerX" id="4eh-az-oI5"/> + <constraint firstItem="6y6-RH-qOp" firstAttribute="leading" secondItem="Eoi-B8-iL6" secondAttribute="trailing" constant="8" id="7wV-uh-Xb7"/> + <constraint firstAttribute="trailing" secondItem="d0X-cW-Xgz" secondAttribute="trailing" constant="20" id="G79-Jv-EYw"/> + <constraint firstAttribute="bottom" secondItem="6y6-RH-qOp" secondAttribute="bottom" constant="20" id="HOt-7O-FU2"/> + <constraint firstAttribute="trailing" secondItem="6y6-RH-qOp" secondAttribute="trailing" constant="20" id="KTx-SN-RUg"/> + <constraint firstItem="d0X-cW-Xgz" firstAttribute="top" secondItem="2wf-Py-l6B" secondAttribute="top" id="MKB-zm-C75"/> + <constraint firstAttribute="centerY" secondItem="JwW-2h-DyZ" secondAttribute="centerY" id="Na1-o4-4Ds"/> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="675" id="ciq-ed-2FK"/> + <constraint firstItem="d0X-cW-Xgz" firstAttribute="leading" secondItem="2wf-Py-l6B" secondAttribute="leading" constant="20" id="efy-70-qsJ"/> + <constraint firstAttribute="bottom" secondItem="Eoi-B8-iL6" secondAttribute="bottom" constant="20" id="glQ-Is-Pk6"/> + <constraint firstItem="Eoi-B8-iL6" firstAttribute="leading" secondItem="2wf-Py-l6B" secondAttribute="leading" constant="20" id="sHw-xg-QAo"/> </constraints> </customView> - <customView ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="d0X-cW-Xgz"> - <rect key="frame" x="20" y="422" width="568" height="71"/> + <customView id="TdD-3L-553"> + <rect key="frame" x="676" y="0.0" width="338" height="509"/> + <autoresizingMask key="autoresizingMask"/> <subviews> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cIU-M7-xpN"> - <rect key="frame" x="446" y="23" width="104" height="24"/> + <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kW2-Cx-fNv"> + <rect key="frame" x="274" y="13" width="50" height="32"/> <constraints> - <constraint firstAttribute="width" constant="100" id="9vz-kb-6L6"/> + <constraint firstAttribute="width" constant="38" id="0Qx-5g-ThL"/> </constraints> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Time" id="zsO-T7-9yi"> - <font key="font" size="20" name="HelveticaNeue"/> - <color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="highlightColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="layer.cornerRadius" value="15"/> - </userDefinedRuntimeAttributes> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kFD-FB-vig"> - <rect key="frame" x="18" y="20" width="37" height="17"/> - <constraints> - <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="29" id="pft-oc-ZNh"/> - </constraints> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="State" id="ugy-uK-901"> + <buttonCell key="cell" type="push" bezelStyle="rounded" image="NSGoRightTemplate" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5Nl-aV-9kl"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <font key="font" metaFont="system"/> - <color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="highlightColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bg3-hB-nE8"> - <rect key="frame" x="18" y="40" width="139" height="17"/> - <constraints> - <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="81" id="gT7-Wu-XtU"/> - </constraints> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Person name" id="osk-LS-0Qg"> + </buttonCell> + <connections> + <action selector="sendMessage:" target="LWe-df-dS6" id="Hlj-og-5rV"/> + <binding destination="LWe-df-dS6" name="enabled" keyPath="self.message.length" id="ec2-s1-Hpt"/> + </connections> + </button> + <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OBX-o0-u1k"> + <rect key="frame" x="20" y="20" width="252" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="QW9-Ty-ZEe"> <font key="font" metaFont="system"/> - <color key="textColor" name="highlightColor" catalog="System" colorSpace="catalog"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> </textFieldCell> + <connections> + <binding destination="LWe-df-dS6" name="value" keyPath="self.message" id="suS-k6-ypU"> + <dictionary key="options"> + <bool key="NSContinuouslyUpdatesValue" value="YES"/> + </dictionary> + </binding> + <outlet property="delegate" destination="LWe-df-dS6" id="9by-zr-IW0"/> + </connections> </textField> + <scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="i3X-6S-mKF"> + <rect key="frame" x="0.0" y="50" width="338" height="459"/> + <clipView key="contentView" id="Tbz-Bj-Y3K"> + <rect key="frame" x="1" y="1" width="223" height="133"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <textView editable="NO" importsGraphics="NO" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" usesFontPanel="YES" verticallyResizable="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dataDetection="YES" dashSubstitution="YES" spellingCorrection="YES" smartInsertDelete="YES" id="9jD-8j-lXO"> + <rect key="frame" x="0.0" y="0.0" width="223" height="459"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <size key="minSize" width="338" height="459"/> + <size key="maxSize" width="463" height="10000000"/> + <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <size key="minSize" width="338" height="459"/> + <size key="maxSize" width="463" height="10000000"/> + </textView> + </subviews> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </clipView> + <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="Uo5-G8-d2s"> + <rect key="frame" x="-100" y="-100" width="87" height="18"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + <scroller key="verticalScroller" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="eno-qS-nJm"> + <rect key="frame" x="224" y="1" width="15" height="133"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + </scrollView> </subviews> <constraints> - <constraint firstItem="kFD-FB-vig" firstAttribute="leading" secondItem="bg3-hB-nE8" secondAttribute="leading" id="LXG-QI-oPf"/> - <constraint firstAttribute="trailing" secondItem="cIU-M7-xpN" secondAttribute="trailing" constant="20" id="RXf-xZ-4f9"/> - <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="568" id="Xeq-Aa-f1W"/> - <constraint firstItem="kFD-FB-vig" firstAttribute="top" secondItem="bg3-hB-nE8" secondAttribute="bottom" constant="3" id="Z06-5v-81Q"/> - <constraint firstItem="kFD-FB-vig" firstAttribute="top" secondItem="bg3-hB-nE8" secondAttribute="bottom" constant="3" id="gRn-E6-o6O"/> - <constraint firstItem="kFD-FB-vig" firstAttribute="leading" secondItem="d0X-cW-Xgz" secondAttribute="leading" constant="20" id="i5C-8o-qKp"/> - <constraint firstAttribute="bottom" secondItem="kFD-FB-vig" secondAttribute="bottom" constant="20" id="l71-7V-oLx"/> - <constraint firstItem="bg3-hB-nE8" firstAttribute="leading" secondItem="d0X-cW-Xgz" secondAttribute="leading" constant="20" id="nV4-Vy-vqK"/> - <constraint firstAttribute="centerY" secondItem="cIU-M7-xpN" secondAttribute="centerY" id="yvc-8B-cEu"/> + <constraint firstItem="kW2-Cx-fNv" firstAttribute="leading" secondItem="OBX-o0-u1k" secondAttribute="trailing" constant="8" id="5sg-0N-YSw"/> + <constraint firstAttribute="bottom" secondItem="kW2-Cx-fNv" secondAttribute="bottom" constant="20" id="EeC-o1-xNE"/> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="250" id="KI6-XT-afu"/> + <constraint firstItem="i3X-6S-mKF" firstAttribute="top" secondItem="TdD-3L-553" secondAttribute="top" id="LoI-gZ-Gp6"/> + <constraint firstItem="i3X-6S-mKF" firstAttribute="leading" secondItem="TdD-3L-553" secondAttribute="leading" id="NVH-57-1Yw"/> + <constraint firstAttribute="width" relation="lessThanOrEqual" constant="400" id="Szn-hz-Zcv"/> + <constraint firstAttribute="bottom" secondItem="OBX-o0-u1k" secondAttribute="bottom" constant="20" id="eOH-Wv-Zuo"/> + <constraint firstAttribute="trailing" secondItem="kW2-Cx-fNv" secondAttribute="trailing" constant="20" id="g3z-YY-hyn"/> + <constraint firstItem="OBX-o0-u1k" firstAttribute="leading" secondItem="TdD-3L-553" secondAttribute="leading" constant="20" id="wTD-wE-axZ"/> + <constraint firstItem="OBX-o0-u1k" firstAttribute="top" secondItem="i3X-6S-mKF" secondAttribute="bottom" constant="8" id="yKJ-bq-2wk"/> + <constraint firstAttribute="trailing" secondItem="i3X-6S-mKF" secondAttribute="trailing" id="zdl-DO-8e8"/> </constraints> </customView> </subviews> - <constraints> - <constraint firstItem="6y6-RH-qOp" firstAttribute="leading" secondItem="Eoi-B8-iL6" secondAttribute="trailing" constant="8" id="7wV-uh-Xb7"/> - <constraint firstAttribute="trailing" secondItem="d0X-cW-Xgz" secondAttribute="trailing" constant="20" id="G79-Jv-EYw"/> - <constraint firstAttribute="bottom" secondItem="6y6-RH-qOp" secondAttribute="bottom" constant="20" id="HOt-7O-FU2"/> - <constraint firstAttribute="trailing" secondItem="6y6-RH-qOp" secondAttribute="trailing" constant="20" id="KTx-SN-RUg"/> - <constraint firstItem="d0X-cW-Xgz" firstAttribute="top" secondItem="2wf-Py-l6B" secondAttribute="top" id="MKB-zm-C75"/> - <constraint firstItem="d0X-cW-Xgz" firstAttribute="leading" secondItem="2wf-Py-l6B" secondAttribute="leading" constant="20" id="efy-70-qsJ"/> - <constraint firstAttribute="bottom" secondItem="Eoi-B8-iL6" secondAttribute="bottom" constant="20" id="glQ-Is-Pk6"/> - <constraint firstItem="Eoi-B8-iL6" firstAttribute="leading" secondItem="2wf-Py-l6B" secondAttribute="leading" constant="20" id="sHw-xg-QAo"/> - </constraints> - </customView> + <holdingPriorities> + <real value="250"/> + <real value="250"/> + </holdingPriorities> + <connections> + <outlet property="delegate" destination="-2" id="Srr-ek-32R"/> + </connections> + </splitView> </subviews> <constraints> - <constraint firstAttribute="trailing" secondItem="2wf-Py-l6B" secondAttribute="trailing" id="55Z-0g-5TN"/> - <constraint firstItem="2wf-Py-l6B" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="X7p-et-pnn"/> - <constraint firstAttribute="trailing" secondItem="2wf-Py-l6B" secondAttribute="trailing" id="YzN-ww-GnM"/> - <constraint firstAttribute="bottom" secondItem="2wf-Py-l6B" secondAttribute="bottom" id="a7w-Ws-Gv9"/> - <constraint firstItem="2wf-Py-l6B" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="fQz-Y7-SMk"/> - <constraint firstAttribute="bottom" secondItem="2wf-Py-l6B" secondAttribute="bottom" id="iMg-yV-adv"/> - <constraint firstItem="2wf-Py-l6B" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="mf8-S2-fcv"/> + <constraint firstAttribute="trailing" secondItem="GIJ-gB-FZo" secondAttribute="trailing" id="0em-qy-QDF"/> + <constraint firstItem="GIJ-gB-FZo" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="BMA-1U-7qS"/> + <constraint firstAttribute="bottom" secondItem="GIJ-gB-FZo" secondAttribute="bottom" id="cSp-R2-2P7"/> + <constraint firstItem="GIJ-gB-FZo" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="gYP-xn-tdT"/> </constraints> - <point key="canvasLocation" x="1225" y="13.5"/> + <point key="canvasLocation" x="1428" y="21.5"/> </customView> + <viewController id="LWe-df-dS6" customClass="ChatVC"> + <connections> + <outlet property="chatView" destination="9jD-8j-lXO" id="nRK-qZ-xex"/> + <outlet property="messageField" destination="OBX-o0-u1k" id="MS2-Hl-as4"/> + <outlet property="sendButton" destination="kW2-Cx-fNv" id="7iO-d1-LsM"/> + <outlet property="view" destination="TdD-3L-553" id="HQf-B1-D8b"/> + </connections> + </viewController> </objects> <resources> - <image name="7B8EA957-C2D1-40F0-ACD8-5F72C84E3E15" width="48" height="48"> - <mutableData key="keyedArchiveRepresentation"> -YnBsaXN0MDDUAQIDBAUGOzxYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK0HCBEW -GxwgISksLzU4VSRudWxs1AkKCwwNDg8QViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzV05TQ29sb3KA -DBIAwAAAgAKACtISCRMVWk5TLm9iamVjdHOhFIADgAnSEgkXGqIYGYAEgAWACBAA0h0JHh9fEBROU1RJ -RkZSZXByZXNlbnRhdGlvboAGgAdPETEOTU0AKgAAJAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzNDMzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM0MAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM2AzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMK -MzMzfDMzM5kzMzNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzQzMzM5kzMzOZMzMzmTMzM3wzMzMK -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZ -MzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8 -MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8 -MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8 -MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZ -MzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8 -MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAA -MzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8MzMzmTMzM5kzMzOZ -MzMzfDMzMwozMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMK -MzMzfDMzM5kzMzOZMzMzmTMzM3wzMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzmTMzM3wzMzMK -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8MzMzmTMzM5kzMzOZ -MzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMK -MzMzfDMzM5kzMzOZMzMzmTMzM3wzMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwozMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMK -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAMzMzCjMzM3wzMzOZ -MzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAA -AAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8 -MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8 -MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZ -MzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8 -MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8 -MzMzmTMzM5kzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -MzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzQzMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8MzMzmTMzM5kzMzOZ -MzMzQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM2AzMzOZMzMzfDMzMwoAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMK -MzMzfDMzM5kzMzNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzNDMzMzCgAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzCjMzM0MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAADgEAAAMAAAABADAAAAEBAAMAAAABADAAAAECAAMAAAAEAAAktgEDAAMAAAABAAEAAAEG -AAMAAAABAAIAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABADAAAAEX -AAQAAAABAAAkAAEcAAMAAAABAAEAAAFSAAMAAAABAAIAAAFTAAMAAAAEAAAkvodzAAcAAAxIAAAkxgAA -AAAACAAIAAgACAABAAEAAQABAAAMSExpbm8CEAAAbW50clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1T -RlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAA -AfAAAAAUYmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA -AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gAAAAUbWVhcwAA -BAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgMYlRSQwAABDwAAAgMdGV4dAAA -AABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNrYXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNS -R0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAA -AAAAAAAAAAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAA -AAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAABZJ -RUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBz -UkdCAAAAAAAAAAAAAAAuSUVDIDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdC -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv -biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g -SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAAAAATpP4AFF8uABDPFAAD -7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521lYXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAA -AAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7 -AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQ -ANUA2wDgAOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL -AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJxAnoChAKO -ApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34DigOWA6IDrgO6A8cD0wPg -A+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWG -BZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeG -B5kHrAe/B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnl -CfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgyn -DMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/P -D+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNj -E4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdl -F4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshva -HAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE -IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYn -Jlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitpK50r0SwF -LDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJj -Mpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlC -OX85vDn5OjY6dDqyOu87LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECm -QOdBKUFqQaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR -SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAnUHFQu1EG -UVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoH -WlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OX -Y+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25 -bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhu -eMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6 -hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+e -kAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wc -nImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3 -qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbw -t2i34LhZuNG5SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL -xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ -1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC94UThzOJT4tvjY+Pr -5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q0 -9ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY/Sn9uv5L/tz/bf//0iIjJCVaJGNsYXNzbmFtZVgk -Y2xhc3Nlc18QEE5TQml0bWFwSW1hZ2VSZXCjJicoXxAQTlNCaXRtYXBJbWFnZVJlcFpOU0ltYWdlUmVw -WE5TT2JqZWN00iIjKitXTlNBcnJheaIqKNIiIy0uXk5TTXV0YWJsZUFycmF5oy0qKNMwMQkyMzRXTlNX -aGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAvSIiM2N1dOU0NvbG9yojYo0iIjOTpXTlNJbWFnZaI5KF8Q -D05TS2V5ZWRBcmNoaXZlctE9PlRyb290gAEACAARABoAIwAtADIANwBFAEsAVABbAGgAbwB3AHkAfgCA -AIIAhwCSAJQAlgCYAJ0AoACiAKQApgCoAK0AxADGAMgx2jHfMeox8zIGMgoyHTIoMjEyNjI+MkEyRjJV -MlkyYDJoMnUyejJ8Mn4ygzKLMo4ykzKbMp4ysDKzMrgAAAAAAAACAQAAAAAAAAA/AAAAAAAAAAAAAAAA -AAAyug -</mutableData> - </image> - <image name="81E4DCCA-D15B-4820-A27C-9F5B15440C8A" width="48" height="48"> - <mutableData key="keyedArchiveRepresentation"> -YnBsaXN0MDDUAQIDBAUGOzxYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoK0HCBEW -GxwgISksLzU4VSRudWxs1AkKCwwNDg8QViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzV05TQ29sb3KA -DBIAwAAAgAKACtISCRMVWk5TLm9iamVjdHOhFIADgAnSEgkXGqIYGYAEgAWACBAA0h0JHh9fEBROU1RJ -RkZSZXByZXNlbnRhdGlvboAGgAdPETEOTU0AKgAAJAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzNDMzMzTTMzM00zMzNN -MzMzTTMzM00zMzNNMzMzTTMzM00zMzMdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM2kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzM3MzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAADMzM3MzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM3MzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM00zMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMzkzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzfAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMxMzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzMxMAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzOGMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM0MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzNgMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzMzkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMwMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -MzMzjzMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzVjMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM3wzMzMKAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzEzMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzjzMzMwoAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM2AzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM0MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMxMzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM48zMzMTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAzMzNNMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzN8MzMzCgAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzhjMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzaQAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzJjMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzM00zMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMK -MzMzJjMzMwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAzMzNgMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzaTMzMwoAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMwozMzN8MzMzmTMzM5kzMzNpMzMzQzMzMyYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzfDMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM3wzMzMd -AAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzjzMzM3MzMzMm -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzCjMzM3wzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOPMzMzQwAAAAAzMzMTMzMzfDMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMwozMzN8 -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM3wzMzOPMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAMzMzYDMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMzMzkzMzOPMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMTMzMzczMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMzkzMzOPMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMKMzMzTTMzM48zMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzM5MzMzczMzM5kzMzOZ -MzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzOGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAADMzMxMzMzM5MzMzYDMzM4YzMzOZMzMzmTMzM5kzMzOZMzMzmTMzM5kzMzNWAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMTMzMzJjMzM00zMzNNMzMzTTMzM0MzMzMK -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAADgEAAAMAAAABADAAAAEBAAMAAAABADAAAAECAAMAAAAEAAAktgEDAAMAAAABAAEAAAEG -AAMAAAABAAIAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABADAAAAEX -AAQAAAABAAAkAAEcAAMAAAABAAEAAAFSAAMAAAABAAIAAAFTAAMAAAAEAAAkvodzAAcAAAxIAAAkxgAA -AAAACAAIAAgACAABAAEAAQABAAAMSExpbm8CEAAAbW50clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1T -RlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAA -AfAAAAAUYmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA -AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gAAAAUbWVhcwAA -BAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgMYlRSQwAABDwAAAgMdGV4dAAA -AABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNrYXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNS -R0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAA -AAAAAAAAAAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAA -AAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAABZJ -RUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBz -UkdCAAAAAAAAAAAAAAAuSUVDIDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdC -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv -biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g -SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAAAAATpP4AFF8uABDPFAAD -7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521lYXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAA -AAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7 -AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQ -ANUA2wDgAOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL -AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJxAnoChAKO -ApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34DigOWA6IDrgO6A8cD0wPg -A+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWG -BZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeG -B5kHrAe/B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnl -CfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgyn -DMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/P -D+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNj -E4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdl -F4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshva -HAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE -IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYn -Jlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitpK50r0SwF -LDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJj -Mpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlC -OX85vDn5OjY6dDqyOu87LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECm -QOdBKUFqQaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR -SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAnUHFQu1EG -UVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoH -WlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OX -Y+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25 -bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhu -eMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6 -hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+e -kAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wc -nImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3 -qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbw -t2i34LhZuNG5SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL -xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ -1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC94UThzOJT4tvjY+Pr -5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q0 -9ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY/Sn9uv5L/tz/bf//0iIjJCVaJGNsYXNzbmFtZVgk -Y2xhc3Nlc18QEE5TQml0bWFwSW1hZ2VSZXCjJicoXxAQTlNCaXRtYXBJbWFnZVJlcFpOU0ltYWdlUmVw -WE5TT2JqZWN00iIjKitXTlNBcnJheaIqKNIiIy0uXk5TTXV0YWJsZUFycmF5oy0qKNMwMQkyMzRXTlNX -aGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAvSIiM2N1dOU0NvbG9yojYo0iIjOTpXTlNJbWFnZaI5KF8Q -D05TS2V5ZWRBcmNoaXZlctE9PlRyb290gAEACAARABoAIwAtADIANwBFAEsAVABbAGgAbwB3AHkAfgCA -AIIAhwCSAJQAlgCYAJ0AoACiAKQApgCoAK0AxADGAMgx2jHfMeox8zIGMgoyHTIoMjEyNjI+MkEyRjJV -MlkyYDJoMnUyejJ8Mn4ygzKLMo4ykzKbMp4ysDKzMrgAAAAAAAACAQAAAAAAAAA/AAAAAAAAAAAAAAAA -AAAyug -</mutableData> - </image> + <image name="NSGoRightTemplate" width="9" height="12"/> + <image name="ic_action_call" width="72" height="72"/> + <image name="ic_action_chat" width="72" height="72"/> + <image name="ic_action_hangup" width="72" height="72"/> + <image name="ic_action_hold" width="72" height="72"/> + <image name="ic_action_mute_audio" width="72" height="72"/> + <image name="ic_action_mute_video" width="72" height="72"/> </resources> </document> diff --git a/ui/GeneralPrefs.xib b/ui/GeneralPrefs.xib index 83d3111956a3622cceaeb7f978a962d73e272462..3167824c8b3fac66cc56a0984f9eaa16294256ff 100644 --- a/ui/GeneralPrefs.xib +++ b/ui/GeneralPrefs.xib @@ -1,23 +1,28 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D2134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="GeneralPrefsVC"> <connections> + <outlet property="advancedGeneralSettings" destination="VH6-yw-pgr" id="lSD-RY-Puf"/> + <outlet property="checkIntervalPopUp" destination="RYP-3d-PCa" id="JNO-GR-CV8"/> <outlet property="historyChangedLabel" destination="Gyi-ID-Z3v" id="aoO-Fh-UCQ"/> + <outlet property="sparkleContainer" destination="yVO-jk-ay3" id="zni-hI-88D"/> + <outlet property="startUpButton" destination="1Nr-L4-fcd" id="veu-Hi-c7L"/> + <outlet property="toggleAutomaticUpdateCheck" destination="MCd-PD-kd7" id="rSB-ac-Nm2"/> <outlet property="view" destination="c22-O7-iKe" id="kqH-6G-Ohq"/> </connections> </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> <customView id="c22-O7-iKe"> - <rect key="frame" x="0.0" y="0.0" width="562" height="249"/> + <rect key="frame" x="0.0" y="0.0" width="899" height="583"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <subviews> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kYQ-jU-skU"> - <rect key="frame" x="57" y="203" width="143" height="17"/> + <rect key="frame" x="57" y="528" width="143" height="17"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Desktop Notifications:" id="xlz-zw-IJI"> <font key="font" metaFont="system"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> @@ -25,7 +30,7 @@ </textFieldCell> </textField> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lIm-zX-RIV"> - <rect key="frame" x="76" y="167" width="124" height="17"/> + <rect key="frame" x="76" y="492" width="124" height="17"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Window behaviour:" id="sog-Ok-Y0N"> <font key="font" metaFont="system"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> @@ -33,7 +38,7 @@ </textFieldCell> </textField> <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Oth-up-2k2"> - <rect key="frame" x="204" y="201" width="147" height="18"/> + <rect key="frame" x="204" y="526" width="147" height="18"/> <buttonCell key="cell" type="check" title="Enable Notifications" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="uCL-ye-tsv"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <font key="font" metaFont="system"/> @@ -43,7 +48,7 @@ </connections> </button> <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Is4-pD-LOT"> - <rect key="frame" x="204" y="166" width="294" height="18"/> + <rect key="frame" x="204" y="491" width="294" height="18"/> <buttonCell key="cell" type="check" title="Bring Ring to foreground on incoming calls" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="uYI-hA-JHk"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <font key="font" metaFont="system"/> @@ -53,7 +58,7 @@ </connections> </button> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fC0-ce-Yiz"> - <rect key="frame" x="38" y="127" width="54" height="17"/> + <rect key="frame" x="38" y="423" width="54" height="17"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="History" id="DSD-yl-noX"> <font key="font" metaFont="systemBold"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> @@ -61,21 +66,21 @@ </textFieldCell> </textField> <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DgD-2y-4g5"> - <rect key="frame" x="56" y="100" width="195" height="18"/> + <rect key="frame" x="56" y="396" width="195" height="18"/> <buttonCell key="cell" type="check" title="Keep my history for at least" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="3Pb-Ec-zl5"> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <font key="font" metaFont="system"/> </buttonCell> </button> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QmA-ZI-ZL5"> - <rect key="frame" x="302" y="94" width="19" height="27"/> + <rect key="frame" x="302" y="390" width="19" height="27"/> <stepperCell key="cell" continuous="YES" alignment="left" maxValue="100" doubleValue="30" id="30B-YQ-Opa"/> <connections> <binding destination="Sz0-vm-i3t" name="value" keyPath="values.history_limit" id="c2j-mK-kYa"/> </connections> </stepper> <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tHZ-7Q-5iP"> - <rect key="frame" x="257" y="97" width="40" height="22"/> + <rect key="frame" x="257" y="393" width="40" height="22"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="center" title="30" drawsBackground="YES" id="JvS-c4-OeT"> <font key="font" metaFont="system"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> @@ -86,7 +91,7 @@ </connections> </textField> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nah-Jm-ZYB"> - <rect key="frame" x="324" y="101" width="33" height="17"/> + <rect key="frame" x="324" y="397" width="33" height="17"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="days" id="e5K-l0-Nfw"> <font key="font" metaFont="system"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> @@ -94,7 +99,7 @@ </textFieldCell> </textField> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="U84-le-Iy4"> - <rect key="frame" x="58" y="63" width="122" height="19"/> + <rect key="frame" x="58" y="359" width="122" height="19"/> <buttonCell key="cell" type="roundRect" title="Clear History" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cd5-9L-Bbb"> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <font key="font" metaFont="cellTitle"/> @@ -104,16 +109,229 @@ </connections> </button> <textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Gyi-ID-Z3v"> - <rect key="frame" x="361" y="97" width="167" height="17"/> + <rect key="frame" x="361" y="393" width="167" height="17"/> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="(Applied on application restart)" id="OTl-vx-S43"> <font key="font" metaFont="smallSystem"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> </textFieldCell> </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="j3T-5j-Fom"> + <rect key="frame" x="38" y="553" width="94" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Interruptions" id="Rzy-Gh-3wQ"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Nr-L4-fcd"> + <rect key="frame" x="204" y="455" width="136" height="18"/> + <buttonCell key="cell" type="check" title="Launch on Startup" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="O2C-xR-mHF"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <connections> + <action selector="toggleLaunchAtStartup:" target="-2" id="Rky-YK-2yk"/> + </connections> + </button> + <customView hidden="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VH6-yw-pgr"> + <rect key="frame" x="20" y="69" width="859" height="181"/> + <subviews> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kAy-G5-0gJ"> + <rect key="frame" x="65" y="116" width="104" height="18"/> + <buttonCell key="cell" type="check" title="Using STUN" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="J3i-Kd-ufy"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + </button> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sMR-LQ-v1D"> + <rect key="frame" x="166" y="114" width="216" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Server url..." drawsBackground="YES" id="MzH-CP-Tf3"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <matrix verticalHuggingPriority="750" fixedFrame="YES" allowsEmptySelection="NO" autorecalculatesCellSize="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vSL-q3-yQ7"> + <rect key="frame" x="67" y="61" width="320" height="38"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + <size key="cellSize" width="216" height="18"/> + <size key="intercellSpacing" width="4" height="2"/> + <buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" inset="2" id="JPG-Km-0oR"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <cells> + <column> + <buttonCell type="radio" title="Same as local parameters" imagePosition="left" alignment="left" state="on" tag="1" inset="2" id="jLT-dL-1vJ"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <buttonCell type="radio" title="Set published address and port" imagePosition="left" alignment="left" inset="2" id="qQz-Qg-AcZ"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + </column> + </cells> + </matrix> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zjo-JQ-eys"> + <rect key="frame" x="87" y="20" width="187" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="mWy-F0-AW6"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Tf-sb-mk9"> + <rect key="frame" x="316" y="20" width="63" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="u2t-ps-CAH"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="asH-TC-Vjh"> + <rect key="frame" x="280" y="23" width="30" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Port" id="mfD-5d-YAO"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Rcn-X4-Z0S"> + <rect key="frame" x="30" y="23" width="55" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Address" id="mDR-e7-DwU"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="v1V-gL-Qea"> + <rect key="frame" x="18" y="144" width="87" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="STUN server" id="5BD-IL-feS"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tlg-3Y-SwP"> + <rect key="frame" x="564" y="114" width="216" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Server url..." drawsBackground="YES" id="5IL-CF-xNE"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <matrix verticalHuggingPriority="750" fixedFrame="YES" allowsEmptySelection="NO" autorecalculatesCellSize="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jxr-u3-BHN"> + <rect key="frame" x="465" y="61" width="320" height="38"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + <size key="cellSize" width="216" height="18"/> + <size key="intercellSpacing" width="4" height="2"/> + <buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" inset="2" id="5or-Uc-aW4"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <cells> + <column> + <buttonCell type="radio" title="Same as local parameters" imagePosition="left" alignment="left" state="on" tag="1" inset="2" id="AyQ-SW-wnT"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <buttonCell type="radio" title="Set published address and port" imagePosition="left" alignment="left" inset="2" id="S5X-FW-4tQ"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + </column> + </cells> + </matrix> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="orf-tH-Ryt"> + <rect key="frame" x="485" y="20" width="187" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="DxB-Pf-IB5"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L7A-C3-OaS"> + <rect key="frame" x="714" y="20" width="63" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="qBg-Z7-cPZ"> + <font key="font" metaFont="system"/> + <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cy7-Hu-7rs"> + <rect key="frame" x="678" y="23" width="30" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Port" id="Kby-vh-BDl"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qwZ-OU-p2U"> + <rect key="frame" x="428" y="23" width="55" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Address" id="fsN-LH-LkL"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IVO-Du-c46"> + <rect key="frame" x="416" y="144" width="88" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="TURN server" id="RdV-YO-mtx"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="H4W-dK-kUO"> + <rect key="frame" x="463" y="116" width="104" height="18"/> + <buttonCell key="cell" type="check" title="Using TURN" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="jr8-R1-kiL"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + </button> + </subviews> + </customView> + <customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yVO-jk-ay3"> + <rect key="frame" x="20" y="270" width="506" height="82"/> + <subviews> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="diX-uH-Ce8"> + <rect key="frame" x="14" y="45" width="52" height="17"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Update" id="r4u-t3-gBc"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RYP-3d-PCa" userLabel="Update interval"> + <rect key="frame" x="265" y="17" width="100" height="26"/> + <popUpButtonCell key="cell" type="push" title="Monthly" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="2629800" imageScaling="proportionallyDown" inset="2" selectedItem="42E-UY-qlP" id="tTF-gp-Rti"> + <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="menu"/> + <menu key="menu" id="GJ4-0X-PTn"> + <items> + <menuItem title="Hourly" tag="3600" id="mbu-TH-bs8"/> + <menuItem title="Daily" tag="86400" id="cB6-4m-9MU"/> + <menuItem title="Weekly" tag="604800" id="AId-am-73Q"/> + <menuItem title="Monthly" state="on" tag="2629800" id="42E-UY-qlP"/> + </items> + </menu> + </popUpButtonCell> + </popUpButton> + <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MCd-PD-kd7"> + <rect key="frame" x="31" y="21" width="221" height="18"/> + <buttonCell key="cell" type="check" title="Automatically check for updates" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="T3a-yx-ZaW"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + </button> + </subviews> + </customView> </subviews> - <point key="canvasLocation" x="383" y="124.5"/> + <point key="canvasLocation" x="523.5" y="220.5"/> </customView> <userDefaultsController representsSharedInstance="YES" id="Sz0-vm-i3t"/> + <customObject id="VEJ-ic-3Ub" customClass="SUUpdater"/> </objects> </document> diff --git a/ui/MainMenu.xib b/ui/MainMenu.xib index a7263c76a355510eea99e2d0f5a0e7e3f4359977..5c46bfe9e15888db22ee4279c156a8066990706f 100644 --- a/ui/MainMenu.xib +++ b/ui/MainMenu.xib @@ -2,13 +2,13 @@ <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00"> <data> <int key="IBDocument.SystemTarget">101000</int> - <string key="IBDocument.SystemVersion">13F34</string> - <string key="IBDocument.InterfaceBuilderVersion">6254</string> - <string key="IBDocument.AppKitVersion">1265.21</string> - <string key="IBDocument.HIToolboxVersion">698.00</string> + <string key="IBDocument.SystemVersion">14D2134</string> + <string key="IBDocument.InterfaceBuilderVersion">7706</string> + <string key="IBDocument.AppKitVersion">1347.57</string> + <string key="IBDocument.HIToolboxVersion">758.70</string> <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> - <string key="NS.object.0">6254</string> + <string key="NS.object.0">7706</string> </object> <array key="IBDocument.IntegratedClassDependencies"> <string>NSCustomObject</string> @@ -70,6 +70,14 @@ <reference key="NSOnImage" ref="35465992"/> <reference key="NSMixedImage" ref="502551668"/> </object> + <object class="NSMenuItem" id="709467494"> + <reference key="NSMenu" ref="110575045"/> + <string key="NSTitle">Check for Update...</string> + <string key="NSKeyEquiv"/> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="35465992"/> + <reference key="NSMixedImage" ref="502551668"/> + </object> <object class="NSMenuItem" id="304266470"> <reference key="NSMenu" ref="110575045"/> <bool key="NSIsDisabled">YES</bool> @@ -710,6 +718,9 @@ <object class="NSCustomObject" id="755631768"> <string key="NSClassName">NSFontManager</string> </object> + <object class="NSCustomObject" id="931776921"> + <string key="NSClassName">SUUpdater</string> + </object> </array> <object class="IBObjectContainer" key="IBDocument.Objects"> <array key="connectionRecords"> @@ -1065,6 +1076,14 @@ </object> <int key="connectionID">591</int> </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">checkForUpdates:</string> + <reference key="source" ref="931776921"/> + <reference key="destination" ref="709467494"/> + </object> + <int key="connectionID">595</int> + </object> </array> <object class="IBMutableOrderedSet" key="objectRecords"> <array key="orderedObjects"> @@ -1293,6 +1312,7 @@ <reference ref="238522557"/> <reference ref="342932134"/> <reference ref="478989275"/> + <reference ref="709467494"/> </array> <reference key="parent" ref="694149608"/> </object> @@ -1595,6 +1615,16 @@ <reference key="object" ref="478989275"/> <reference key="parent" ref="110575045"/> </object> + <object class="IBObjectRecord"> + <int key="objectID">592</int> + <reference key="object" ref="931776921"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">593</int> + <reference key="object" ref="709467494"/> + <reference key="parent" ref="110575045"/> + </object> </array> </object> <dictionary class="NSMutableDictionary" key="flattenedProperties"> @@ -1677,103 +1707,17 @@ <string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="589.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="592.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="593.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="92.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> </dictionary> <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/> <nil key="activeLocalization"/> <dictionary class="NSMutableDictionary" key="localizations"/> <nil key="sourceID"/> - <int key="maxID">591</int> - </object> - <object class="IBClassDescriber" key="IBDocument.Classes"> - <array class="NSMutableArray" key="referencedPartialClassDescriptions"> - <object class="IBPartialClassDescription"> - <string key="className">AppDelegate</string> - <string key="superclassName">NSObject</string> - <object class="IBClassDescriptionSource" key="sourceIdentifier"> - <string key="majorKey">IBProjectSource</string> - <string key="minorKey">../../AppDelegate.h</string> - </object> - </object> - <object class="IBPartialClassDescription"> - <string key="className">AppDelegate</string> - <object class="NSMutableDictionary" key="actions"> - <string key="NS.key.0">showWizard:</string> - <string key="NS.object.0">id</string> - </object> - <object class="NSMutableDictionary" key="actionInfosByName"> - <string key="NS.key.0">showWizard:</string> - <object class="IBActionInfo" key="NS.object.0"> - <string key="name">showWizard:</string> - <string key="candidateClassName">id</string> - </object> - </object> - <object class="IBClassDescriptionSource" key="sourceIdentifier"> - <string key="majorKey">IBProjectSource</string> - <string key="minorKey">../../AppDelegate.mm</string> - </object> - </object> - <object class="IBPartialClassDescription"> - <string key="className">RingWindowController</string> - <string key="superclassName">NSWindowController</string> - <dictionary class="NSMutableDictionary" key="actions"> - <string key="closePreferences:">NSToolbarItem</string> - <string key="openPreferences:">id</string> - </dictionary> - <dictionary class="NSMutableDictionary" key="actionInfosByName"> - <object class="IBActionInfo" key="closePreferences:"> - <string key="name">closePreferences:</string> - <string key="candidateClassName">NSToolbarItem</string> - </object> - <object class="IBActionInfo" key="openPreferences:"> - <string key="name">openPreferences:</string> - <string key="candidateClassName">id</string> - </object> - </dictionary> - <object class="NSMutableDictionary" key="outlets"> - <string key="NS.key.0">currentView</string> - <string key="NS.object.0">NSView</string> - </object> - <object class="NSMutableDictionary" key="toOneOutletInfosByName"> - <string key="NS.key.0">currentView</string> - <object class="IBToOneOutletInfo" key="NS.object.0"> - <string key="name">currentView</string> - <string key="candidateClassName">NSView</string> - </object> - </object> - <object class="IBClassDescriptionSource" key="sourceIdentifier"> - <string key="majorKey">IBProjectSource</string> - <string key="minorKey">../../RingWindowController.h</string> - </object> - </object> - <object class="IBPartialClassDescription"> - <string key="className">RingWindowController</string> - <dictionary class="NSMutableDictionary" key="actions"> - <string key="closePreferences:">NSToolbarItem</string> - <string key="openPreferences:">id</string> - <string key="placeCall:">id</string> - </dictionary> - <dictionary class="NSMutableDictionary" key="actionInfosByName"> - <object class="IBActionInfo" key="closePreferences:"> - <string key="name">closePreferences:</string> - <string key="candidateClassName">NSToolbarItem</string> - </object> - <object class="IBActionInfo" key="openPreferences:"> - <string key="name">openPreferences:</string> - <string key="candidateClassName">id</string> - </object> - <object class="IBActionInfo" key="placeCall:"> - <string key="name">placeCall:</string> - <string key="candidateClassName">id</string> - </object> - </dictionary> - <object class="IBClassDescriptionSource" key="sourceIdentifier"> - <string key="majorKey">IBProjectSource</string> - <string key="minorKey">../../RingWindowController.mm</string> - </object> - </object> - </array> + <int key="maxID">595</int> </object> + <object class="IBClassDescriber" key="IBDocument.Classes"/> <int key="IBDocument.localizationMode">0</int> <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string> <bool key="IBDocument.previouslyAttemptedUpgradeToXcode5">NO</bool> @@ -1784,8 +1728,8 @@ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> <int key="IBDocument.defaultPropertyAccessControl">3</int> <dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes"> - <string key="NSMenuCheckmark">{11, 11}</string> - <string key="NSMenuMixedState">{10, 3}</string> + <string key="NSMenuCheckmark">{12, 12}</string> + <string key="NSMenuMixedState">{10, 2}</string> </dictionary> <bool key="IBDocument.UseAutolayout">YES</bool> </data> diff --git a/ui/RingWindow.xib b/ui/RingWindow.xib index df2e1367af117c21785b20e77406bc90e8b0cb7c..af920b8f9f71036061d1375714d40559eb3c407d 100644 --- a/ui/RingWindow.xib +++ b/ui/RingWindow.xib @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D2134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="RingWindowController"> <connections> + <outlet property="callView" destination="tSW-YT-asL" id="NRy-rM-XW4"/> <outlet property="currentView" destination="se5-gp-TjO" id="Sae-7F-MB3"/> <outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/> </connections> @@ -17,25 +18,25 @@ <windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <rect key="contentRect" x="196" y="240" width="1053" height="626"/> - <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/> + <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1050"/> <view key="contentView" autoresizesSubviews="NO" id="se5-gp-TjO"> <rect key="frame" x="0.0" y="0.0" width="1053" height="626"/> <autoresizingMask key="autoresizingMask"/> <subviews> <scrollView focusRingType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bqQ-DB-Z0g"> - <rect key="frame" x="20" y="461" width="300" height="145"/> - <clipView key="contentView" focusRingType="none" misplaced="YES" id="1so-Pz-QB2"> + <rect key="frame" x="20" y="461" width="295" height="145"/> + <clipView key="contentView" focusRingType="none" id="1so-Pz-QB2"> <rect key="frame" x="1" y="17" width="238" height="117"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" headerView="nuM-bu-a6l" indentationPerLevel="16" outlineTableColumn="VDO-Cu-h8f" id="zcl-pp-rGb"> - <rect key="frame" x="0.0" y="0.0" width="298" height="19"/> + <rect key="frame" x="0.0" y="0.0" width="293" height="19"/> <autoresizingMask key="autoresizingMask"/> <size key="intercellSpacing" width="3" height="2"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> <tableColumns> - <tableColumn identifier="ConversationsColumn" width="295" minWidth="40" maxWidth="1000" id="VDO-Cu-h8f"> + <tableColumn identifier="ConversationsColumn" width="290" minWidth="40" maxWidth="1000" id="VDO-Cu-h8f"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Conversations"> <font key="font" metaFont="smallSystem"/> <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> @@ -57,7 +58,7 @@ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> </clipView> <constraints> - <constraint firstAttribute="width" constant="300" id="eCz-d8-35b"/> + <constraint firstAttribute="width" constant="295" id="eCz-d8-35b"/> <constraint firstAttribute="height" constant="145" id="lWc-BG-yhJ"/> </constraints> <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="Fze-JQ-8rU"> @@ -73,18 +74,18 @@ <autoresizingMask key="autoresizingMask"/> </tableHeaderView> </scrollView> - <tabView focusRingType="none" ambiguous="YES" misplaced="YES" type="bottomTabsBezelBorder" translatesAutoresizingMaskIntoConstraints="NO" id="xXW-iq-GcP"> + <tabView focusRingType="none" type="bottomTabsBezelBorder" translatesAutoresizingMaskIntoConstraints="NO" id="xXW-iq-GcP"> <rect key="frame" x="13" y="14" width="309" height="443"/> <font key="font" metaFont="system"/> <tabViewItems> <tabViewItem label="History" identifier="1" id="Wi9-Zd-O1N"> - <view key="view" ambiguous="YES" id="h2E-qI-upQ"> - <rect key="frame" x="10" y="7" width="289" height="405"/> + <view key="view" id="h2E-qI-upQ"> + <rect key="frame" x="10" y="7" width="295" height="405"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <scrollView ambiguous="YES" misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9uI-D5-KRt"> + <scrollView misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9uI-D5-KRt"> <rect key="frame" x="-3" y="0.0" width="295" height="408"/> - <clipView key="contentView" ambiguous="YES" misplaced="YES" id="eME-fQ-3QX"> + <clipView key="contentView" misplaced="YES" id="eME-fQ-3QX"> <rect key="frame" x="1" y="17" width="238" height="117"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> @@ -92,7 +93,7 @@ <rect key="frame" x="0.0" y="0.0" width="226" height="19"/> <autoresizingMask key="autoresizingMask"/> <size key="intercellSpacing" width="3" height="2"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> <tableColumns> <tableColumn identifier="DayColumn" width="87.5703125" minWidth="40" maxWidth="1000" id="pTT-gU-NVa"> @@ -142,6 +143,9 @@ </subviews> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> </clipView> + <constraints> + <constraint firstAttribute="width" constant="295" id="x0a-p6-WiG"/> + </constraints> <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="9To-im-dx7"> <rect key="frame" x="1" y="119" width="223" height="15"/> <autoresizingMask key="autoresizingMask"/> @@ -157,17 +161,77 @@ </scrollView> </subviews> <constraints> - <constraint firstAttribute="trailing" secondItem="9uI-D5-KRt" secondAttribute="trailing" constant="3" id="7Oo-Z3-JeE"/> - <constraint firstItem="9uI-D5-KRt" firstAttribute="top" secondItem="h2E-qI-upQ" secondAttribute="top" constant="3" id="Fu4-zc-WvO"/> - <constraint firstItem="9uI-D5-KRt" firstAttribute="leading" secondItem="h2E-qI-upQ" secondAttribute="leading" constant="3" id="mO1-Tn-dFB"/> - <constraint firstAttribute="bottom" secondItem="9uI-D5-KRt" secondAttribute="bottom" id="mRP-eB-8tk"/> + <constraint firstItem="9uI-D5-KRt" firstAttribute="leading" secondItem="h2E-qI-upQ" secondAttribute="leading" id="IKi-Kl-zdl"/> + <constraint firstItem="9uI-D5-KRt" firstAttribute="top" secondItem="h2E-qI-upQ" secondAttribute="top" id="SNf-F0-6HX"/> + <constraint firstAttribute="trailing" secondItem="9uI-D5-KRt" secondAttribute="trailing" id="adg-yP-eVY"/> + <constraint firstAttribute="bottom" secondItem="9uI-D5-KRt" secondAttribute="bottom" id="nLK-Pj-h7B"/> + </constraints> + </view> + </tabViewItem> + <tabViewItem label="Contacts" identifier="" id="Zbi-X6-DLT"> + <view key="view" id="sag-tS-7Jw"> + <rect key="frame" x="10" y="7" width="295" height="405"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <scrollView focusRingType="none" misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="48" horizontalPageScroll="10" verticalLineScroll="48" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rJv-ju-DFe"> + <rect key="frame" x="-3" y="5" width="295" height="403"/> + <clipView key="contentView" focusRingType="none" misplaced="YES" id="S00-xr-jYM"> + <rect key="frame" x="1" y="0.0" width="238" height="134"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="46" indentationPerLevel="16" autoresizesOutlineColumn="YES" outlineTableColumn="8Ve-L0-o7V" id="Hrg-Fe-uGq"> + <rect key="frame" x="0.0" y="0.0" width="293" height="48"/> + <autoresizingMask key="autoresizingMask"/> + <size key="intercellSpacing" width="3" height="2"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> + <tableColumns> + <tableColumn identifier="NameColumn" width="289.7734375" minWidth="40" maxWidth="1000" id="8Ve-L0-o7V"> + <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left"> + <font key="font" metaFont="system"/> + <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/> + </tableHeaderCell> + <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="GAa-pQ-t4g" customClass="PersonCell"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/> + </tableColumn> + </tableColumns> + <connections> + <outlet property="delegate" destination="9RF-6W-vEW" id="5bm-gu-2q9"/> + </connections> + </outlineView> + </subviews> + <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> + </clipView> + <constraints> + <constraint firstAttribute="width" constant="295" id="2Ia-36-f20"/> + </constraints> + <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="TGe-ww-q1j"> + <rect key="frame" x="1" y="119" width="223" height="15"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="dD5-3G-tUh"> + <rect key="frame" x="224" y="17" width="15" height="102"/> + <autoresizingMask key="autoresizingMask"/> + </scroller> + </scrollView> + </subviews> + <constraints> + <constraint firstAttribute="bottom" secondItem="rJv-ju-DFe" secondAttribute="bottom" constant="5" id="84Q-se-prS"/> + <constraint firstItem="rJv-ju-DFe" firstAttribute="leading" secondItem="sag-tS-7Jw" secondAttribute="leading" id="FHh-Sy-7VP"/> + <constraint firstItem="rJv-ju-DFe" firstAttribute="top" secondItem="sag-tS-7Jw" secondAttribute="top" id="VST-yM-sIz"/> + <constraint firstAttribute="trailing" secondItem="rJv-ju-DFe" secondAttribute="trailing" id="rZG-K9-Bc8"/> </constraints> </view> </tabViewItem> </tabViewItems> </tabView> - <customView focusRingType="none" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tSW-YT-asL"> - <rect key="frame" x="323" y="0.0" width="725" height="626"/> + <customView focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="tSW-YT-asL"> + <rect key="frame" x="323" y="0.0" width="730" height="626"/> <constraints> <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="626" id="5yo-rb-X1O"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="725" id="ccX-uq-zuj"/> @@ -189,23 +253,25 @@ <connections> <outlet property="delegate" destination="-2" id="0bl-1N-AYu"/> </connections> - <point key="canvasLocation" x="530" y="361"/> + <point key="canvasLocation" x="529.5" y="361"/> </window> - <viewController id="jzj-dD-ryc" customClass="HistoryViewController"> + <viewController id="jzj-dD-ryc" customClass="HistoryVC"> <connections> <outlet property="historyView" destination="rr8-UC-gGF" id="8NW-8O-X5L"/> <outlet property="view" destination="9uI-D5-KRt" id="hCt-k1-UV2"/> </connections> </viewController> - <viewController id="txt-J3-pzW" customClass="ConversationsViewController"> + <viewController id="txt-J3-pzW" customClass="ConversationsVC"> <connections> <outlet property="conversationsView" destination="zcl-pp-rGb" id="YXp-WN-UmC"/> - <outlet property="currentCallView" destination="tSW-YT-asL" id="r2r-Q3-clc"/> - <outlet property="view" destination="tSW-YT-asL" id="fv5-ly-rk8"/> + <outlet property="view" destination="bqQ-DB-Z0g" id="nJI-EM-4z2"/> + </connections> + </viewController> + <viewController id="9RF-6W-vEW" customClass="PersonsVC"> + <connections> + <outlet property="personsView" destination="Hrg-Fe-uGq" id="nMb-nK-g6a"/> + <outlet property="view" destination="rJv-ju-DFe" id="A8H-6d-bW2"/> </connections> </viewController> </objects> - <resources> - <image name="NSActionTemplate" width="14" height="14"/> - </resources> </document>