diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 0c988fa9f..f48490640 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -1,625 +1,626 @@ # Copyright (c) 2018 The Bitcoin developers project(bitcoin-qt) include(BrewHelper) find_brew_prefix(QT5_PREFIX qt5) set(QT_REQUIRED_COMPONENTS Core Widgets Network Test) if(ENABLE_DBUS_NOTIFICATIONS) list(APPEND QT_REQUIRED_COMPONENTS DBus) endif() find_package(Qt5 5.5.1 COMPONENTS ${QT_REQUIRED_COMPONENTS} REQUIRED HINTS "${QT5_PREFIX}") # Localisation add_subdirectory(locale) add_custom_command(OUTPUT temp_bitcoin_locale.qrc COMMAND cmake ARGS -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bitcoin_locale.qrc" temp_bitcoin_locale.qrc MAIN_DEPENDENCY bitcoin_locale.qrc VERBATIM ) add_custom_command(OUTPUT qrc_bitcoin_locale.cpp COMMAND Qt5::rcc ARGS temp_bitcoin_locale.qrc -name bitcoin_locale -o qrc_bitcoin_locale.cpp MAIN_DEPENDENCY temp_bitcoin_locale.qrc DEPENDS locales VERBATIM ) # UI elements # qt5_wrap_ui() generates the files in the CMAKE_CURRENT_BINARY_DIR. As there # is no option to change the output directory, moving the files to the forms # subdirectory requires to override the variable. It is reset to its actual # value after the call so it does not impact the other sections of this # CMakeLists.txt file. set(SAVE_CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/forms") # It seems that some generators (at least the Unix Makefiles one) doesn't create # the build directory required by a custom command, so do it manually. file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) qt5_wrap_ui(UI_GENERATED_HEADERS forms/addressbookpage.ui forms/askpassphrasedialog.ui forms/coincontroldialog.ui forms/createwalletdialog.ui forms/editaddressdialog.ui forms/helpmessagedialog.ui forms/intro.ui forms/modaloverlay.ui forms/openuridialog.ui forms/optionsdialog.ui forms/overviewpage.ui forms/receivecoinsdialog.ui forms/receiverequestdialog.ui forms/debugwindow.ui forms/sendcoinsdialog.ui forms/sendcoinsentry.ui forms/signverifymessagedialog.ui forms/transactiondescdialog.ui ) set(CMAKE_CURRENT_BINARY_DIR ${SAVE_CMAKE_CURRENT_BINARY_DIR}) # Qt MOC set(CMAKE_AUTOMOC ON) # Handle qrc resources qt5_add_resources(QRC_BITCOIN_CPP bitcoin.qrc) add_library(bitcoin-qt-base bantablemodel.cpp bitcoin.cpp bitcoinaddressvalidator.cpp bitcoinamountfield.cpp bitcoingui.cpp bitcoinunits.cpp clientmodel.cpp csvmodelwriter.cpp guiutil.cpp intro.cpp modaloverlay.cpp networkstyle.cpp notificator.cpp optionsdialog.cpp optionsmodel.cpp peertablemodel.cpp platformstyle.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp rpcconsole.cpp splashscreen.cpp trafficgraphwidget.cpp utilitydialog.cpp # Handle ui files ${UI_GENERATED_HEADERS} # Translations ${BITCOIN_QM_FILES} # Handle qrc files ${QRC_BITCOIN_CPP} qrc_bitcoin_locale.cpp ) # Add the minimal integration plugin, and other plugins according to the target # platform. set(QT_PLUGIN_COMPONENTS QMinimalIntegrationPlugin) set(QT_PLUGIN_PLATFORM_DEFINITIONS -DQT_QPA_PLATFORM_MINIMAL=1) # Linux support if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") list(APPEND QT_PLUGIN_COMPONENTS QXcbIntegrationPlugin) list(APPEND QT_PLUGIN_PLATFORM_DEFINITIONS -DQT_QPA_PLATFORM_XCB=1) endif() # Windows support if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") list(APPEND QT_PLUGIN_COMPONENTS QWindowsIntegrationPlugin) list(APPEND QT_PLUGIN_PLATFORM_DEFINITIONS -DQT_QPA_PLATFORM_WINDOWS=1) target_sources(bitcoin-qt-base PRIVATE winshutdownmonitor.cpp) endif() # OSX support if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") list(APPEND QT_PLUGIN_COMPONENTS QCocoaIntegrationPlugin) list(APPEND QT_PLUGIN_PLATFORM_DEFINITIONS -DQT_QPA_PLATFORM_COCOA=1) target_sources(bitcoin-qt-base PRIVATE macdockiconhandler.mm macnotificationhandler.mm ) set_property(TARGET bitcoin-qt-base PROPERTY AUTOMOC_MOC_OPTIONS "-DQ_OS_MAC") target_link_libraries(bitcoin-qt-base "-framework Foundation" "-framework ApplicationServices" "-framework AppKit" ) endif() # Find out more about Qt. This is similar to # http://code.qt.io/cgit/qt/qtwebkit.git/tree/Source/cmake/OptionsQt.cmake get_target_property(QT_CORE_TYPE Qt5::Core TYPE) if(QT_CORE_TYPE MATCHES STATIC) set(QT_STATIC_BUILD ON) endif() # Determine the Qt libraries directory from the QT5::Core library location get_target_property(QT_CORE_LIB_LOCATION Qt5::Core LOCATION) get_filename_component(QT5_LIB_DIR "${QT_CORE_LIB_LOCATION}" DIRECTORY) set(STATIC_DEPENDENCIES_CMAKE_FILE "${CMAKE_BINARY_DIR}/QtStaticDependencies.cmake") if(EXISTS ${STATIC_DEPENDENCIES_CMAKE_FILE}) file(REMOVE ${STATIC_DEPENDENCIES_CMAKE_FILE}) endif() set(CONVERT_PRL_PATH "${CONTRIB_PATH}/qt/convert-prl-libs-to-cmake.pl") macro(CONVERT_PRL_LIBS_TO_CMAKE _qt_component) if(TARGET Qt5::${_qt_component}) get_target_property(_lib_location Qt5::${_qt_component} LOCATION) execute_process(COMMAND ${PERL_EXECUTABLE} "${CONVERT_PRL_PATH}" --lib "${_lib_location}" --qt_lib_install_dir "${QT5_LIB_DIR}" --out "${STATIC_DEPENDENCIES_CMAKE_FILE}" --component "${_qt_component}" --compiler "${CMAKE_CXX_COMPILER_ID}" ) endif() endmacro() if(QT_STATIC_BUILD) list(APPEND QT_REQUIRED_COMPONENTS ${QT_PLUGIN_COMPONENTS}) foreach(qt_module ${QT_REQUIRED_COMPONENTS}) CONVERT_PRL_LIBS_TO_CMAKE(${qt_module}) endforeach() # HACK: We must explicitly add LIB path of the Qt installation # to correctly find qtpcre link_directories("${QT5_LIB_DIR}") # Now that we generated the dependencies, import them. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CONVERT_PRL_PATH}") if(NOT EXISTS ${STATIC_DEPENDENCIES_CMAKE_FILE}) message(FATAL_ERROR "Unable to find ${STATIC_DEPENDENCIES_CMAKE_FILE}") endif() include(${STATIC_DEPENDENCIES_CMAKE_FILE}) list(REMOVE_DUPLICATES STATIC_LIB_DEPENDENCIES) # According to Qt documentation (https://doc.qt.io/qt-5/plugins-howto.html): # "Plugins can be linked statically into your application. # If you build the static version of Qt, this is the only option for # including Qt's predefined plugins." # So if the Qt build is static, the plugins should also be static and we # need to define QT_STATICPLUGIN to tell the code to import . target_compile_definitions(bitcoin-qt-base PUBLIC -DQT_STATICPLUGIN=1) # Add the platform plugin definition if required # Setting this definition tells the code what is the target for Q_IMPORT_PLUGIN(). foreach(qt_platform_definition ${QT_PLUGIN_PLATFORM_DEFINITIONS}) target_compile_definitions(bitcoin-qt-base PUBLIC "${qt_platform_definition}") endforeach() # Link the required plugins foreach(qt_plugin ${QT_PLUGIN_COMPONENTS}) target_link_libraries(bitcoin-qt-base Qt5::${qt_plugin}) endforeach() endif() target_link_libraries(bitcoin-qt-base server rpcclient Qt5::Widgets Qt5::Network ) if(ENABLE_DBUS_NOTIFICATIONS) target_link_libraries(bitcoin-qt-base Qt5::DBus) endif() if(ENABLE_BIP70) # Do protobuf codegen find_package(Protobuf REQUIRED) protobuf_generate_cpp(PROTOBUF_SOURCES PROTOBUF_HEADERS paymentrequest.proto) add_library(bitcoin-qt-protobuf OBJECT # Protobuf codegen ${PROTOBUF_HEADERS} ${PROTOBUF_SOURCES} ) target_include_directories(bitcoin-qt-protobuf PUBLIC ${Protobuf_INCLUDE_DIRS}) target_link_libraries(bitcoin-qt-protobuf ${Protobuf_LIBRARIES}) # Don't run clang-tidy on generated files if(ENABLE_CLANG_TIDY) include(ClangTidy) target_disable_clang_tidy(bitcoin-qt-protobuf) endif() # Message::ByteSize() is deprecated and replaced by ByteSizeLong() since # protobuf 3.1. if(Protobuf_VERSION GREATER_EQUAL "3.1.0") target_compile_definitions(bitcoin-qt-base PRIVATE USE_PROTOBUF_MESSAGE_BYTESIZELONG) endif() # OpenSSL functionality include(BrewHelper) find_brew_prefix(OPENSSL_ROOT_DIR openssl) find_package(OpenSSL REQUIRED) include(CheckSymbolExists) set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) check_symbol_exists(EVP_MD_CTX_new "openssl/evp.h" HAVE_DECL_EVP_MD_CTX_NEW) if(HAVE_DECL_EVP_MD_CTX_NEW) target_compile_definitions(bitcoin-qt-base PRIVATE HAVE_DECL_EVP_MD_CTX_NEW=1) endif() target_link_libraries(bitcoin-qt-base OpenSSL::SSL bitcoin-qt-protobuf ) endif() # Wallet if(BUILD_BITCOIN_WALLET) # Automoc option. set(AUTOMOC_MOC_OPTIONS -DENABLE_WALLET=1) # Add wallet functionality to bitcoin-qt target_sources(bitcoin-qt-base PRIVATE addressbookpage.cpp addresstablemodel.cpp askpassphrasedialog.cpp coincontroldialog.cpp coincontroltreewidget.cpp createwalletdialog.cpp editaddressdialog.cpp openuridialog.cpp overviewpage.cpp paymentserver.cpp + qrimagewidget.cpp receivecoinsdialog.cpp receiverequestdialog.cpp recentrequeststablemodel.cpp sendcoinsdialog.cpp sendcoinsentry.cpp signverifymessagedialog.cpp transactiondesc.cpp transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp transactiontablemodel.cpp transactionview.cpp walletcontroller.cpp walletframe.cpp walletmodel.cpp walletmodeltransaction.cpp walletview.cpp ) # Add BIP70 functionality to bitcoin-qt if(ENABLE_BIP70) target_sources(bitcoin-qt-base PRIVATE paymentrequestplus.cpp ) endif() target_link_libraries(bitcoin-qt-base wallet) if(ENABLE_QRCODE) find_package(QREncode REQUIRED) target_link_libraries(bitcoin-qt-base QREncode::qrencode) endif() endif() # The executable add_executable(bitcoin-qt WIN32 main.cpp) include(WindowsVersionInfo) generate_windows_version_info(bitcoin-qt DESCRIPTION "GUI node for Bitcoin" ICONS "res/icons/bitcoin.ico" "res/icons/bitcoin_testnet.ico" ) target_link_libraries(bitcoin-qt bitcoin-qt-base) include(BinaryTest) add_to_symbols_check(bitcoin-qt) add_to_security_check(bitcoin-qt) include(InstallationHelper) install_target(bitcoin-qt) install_manpages(bitcoin-qt) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(BITCOINQT_BUNDLE_ICON "res/icons/bitcoin.icns") get_filename_component(BITCOINQT_BUNDLE_ICON_NAME "${BITCOINQT_BUNDLE_ICON}" NAME ) set(INFO_PLIST_STRINGS_FILE "Base.lproj/InfoPlist.strings") set(INFO_PLIST_STRINGS_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INFO_PLIST_STRINGS_FILE}") file(WRITE "${INFO_PLIST_STRINGS_PATH}" "{ CFBundleDisplayName = \"${PACKAGE_NAME}\"; CFBundleName = \"${PACKAGE_NAME}\"; }" ) set(EMPTY_LPROJ_FILE "${CMAKE_CURRENT_BINARY_DIR}/empty.lproj") file(TOUCH "${EMPTY_LPROJ_FILE}") target_sources(bitcoin-qt PRIVATE "${BITCOINQT_BUNDLE_ICON}" "${INFO_PLIST_STRINGS_PATH}" "${EMPTY_LPROJ_FILE}" ) string(JOIN ";" BITCOINQT_BUNDLE_RESOURCES "${BITCOINQT_BUNDLE_ICON}" "${EMPTY_LPROJ_FILE}" ) set(BITCOIN_QT_OSX_BUNDLE_NAME "BitcoinABC-Qt") set_target_properties(bitcoin-qt PROPERTIES MACOSX_BUNDLE ON OUTPUT_NAME "${BITCOIN_QT_OSX_BUNDLE_NAME}" MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/share/qt/Info.plist.cmake.in" MACOSX_BUNDLE_BUNDLE_NAME "${BITCOIN_QT_OSX_BUNDLE_NAME}" MACOSX_BUNDLE_BUNDLE_VERSION "${bitcoin-abc_VERSION}" MACOSX_BUNDLE_GUI_IDENTIFIER "org.bitcoinabc.${BITCOIN_QT_OSX_BUNDLE_NAME}" MACOSX_BUNDLE_ICON_FILE "${BITCOINQT_BUNDLE_ICON_NAME}" MACOSX_BUNDLE_INFO_STRING "${bitcoin-abc_VERSION}, Copyright © 2009-${COPYRIGHT_YEAR} ${COPYRIGHT_HOLDERS_FINAL}" MACOSX_BUNDLE_LONG_VERSION_STRING "${bitcoin-abc_VERSION}" MACOSX_BUNDLE_SHORT_VERSION_STRING "${bitcoin-abc_VERSION}" RESOURCE "${BITCOINQT_BUNDLE_RESOURCES}" ) # The InfoPlist.strings files should be located in a resource subdirectory. # This is not supported by the RESOURCE property and require the use of the # MACOSX_PACKAGE_LOCATION property instead. The RESOURCE documentation has # an example demonstrating this behavior (see the appres.txt file): # https://cmake.org/cmake/help/latest/prop_tgt/RESOURCE.html set_source_files_properties( "${INFO_PLIST_STRINGS_PATH}" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/${INFO_PLIST_STRINGS_FILE}" ) # Create a stripped version of the application bundle to be used in the DMG. # Since the LOCATION property and the BundleUtilities package are deprecated # by cmake, only generator expressions can be used to determine the path to # the bundle and its executable. However the generator expressions are # solved at build time, making them unusable to do path computation at # configuration time. # The paths here are then hard-coded, which is safe since the structure of # an application bundle is well-known and specified by Apple. Note that this # will only work for building MacOS application bundle as the IOS structure # is slightly different. set(STRIPPED_BUNDLE "${CMAKE_CURRENT_BINARY_DIR}/stripped/${BITCOIN_QT_OSX_BUNDLE_NAME}.app") add_custom_command( OUTPUT "${STRIPPED_BUNDLE}" COMMAND ${CMAKE_COMMAND} -E copy_directory "$" "${STRIPPED_BUNDLE}" COMMAND ${CMAKE_STRIP} -u -r "${STRIPPED_BUNDLE}/Contents/MacOS/${BITCOIN_QT_OSX_BUNDLE_NAME}" DEPENDS bitcoin-qt ) include(DoOrFail) find_program_or_fail(CMAKE_INSTALL_NAME_TOOL "install_name_tool") find_program_or_fail(CMAKE_OTOOL "otool") set(QT_BASE_TRANSLATIONS "ar" "bg" "ca" "cs" "da" "de" "es" "fa" "fi" "fr" "gd" "gl" "he" "hu" "it" "ja" "ko" "lt" "lv" "pl" "pt" "ru" "sk" "sl" "sv" "uk" "zh_CN" "zh_TW" ) string(JOIN "," QT_LOCALES ${QT_BASE_TRANSLATIONS}) get_target_property(QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) execute_process( COMMAND "${QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS OUTPUT_VARIABLE QT_TRANSLATION_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) function(get_qt_translation_dir QT_TRANSLATION_DIR) foreach(_locale ${ARGN}) find_path(_qt_translation_dir "qt_${_locale}.qm" HINTS "${QT_TRANSLATION_DIR}" PATH_SUFFIXES "translations" ) # Ensure that all the translation files are found, and are located # in the same directory. if(NOT _qt_translation_dir OR (_qt_translation_dir_previous AND (NOT _qt_translation_dir_previous STREQUAL _qt_translation_dir))) return() endif() set(_qt_translation_dir_previous _qt_translation_dir) endforeach() set(QT_TRANSLATION_DIR ${_qt_translation_dir} PARENT_SCOPE) endfunction() get_qt_translation_dir(QT_TRANSLATION_DIR ${QT_BASE_TRANSLATIONS}) if(NOT QT_TRANSLATION_DIR) message(FATAL_ERROR "Qt translation files are not found") endif() set(MACDEPLOY_DIR "${CMAKE_SOURCE_DIR}/contrib/macdeploy") set(MACDEPLOYQTPLUS "${MACDEPLOY_DIR}/macdeployqtplus") set(DMG_DIST "${CMAKE_BINARY_DIR}/dist") add_custom_command( OUTPUT "${DMG_DIST}" COMMAND "INSTALLNAMETOOL=${CMAKE_INSTALL_NAME_TOOL}" "OTOOL=${CMAKE_OTOOL}" "STRIP=${CMAKE_STRIP}" "${Python_EXECUTABLE}" "${MACDEPLOYQTPLUS}" "${STRIPPED_BUNDLE}" -translations-dir "${QT_TRANSLATION_DIR}" -add-qt-tr "${QT_LOCALES}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS "${STRIPPED_BUNDLE}" ) # Building the DMG background image requires several steps: # 1/ The SVG file must be edited to display the package name # 2/ The SVG file should be transformed into a couple PNG files, on for # low resolution screens and one for high resolution screens. # 3/ The PNG files must be transformed into a multi-resolution TIFF file. # The names are not set arbitrarily, they follow Apple's guidelines for # resolution independent bitmap images (see `man tiffutil`). set(BACKGROUND_SVG "background.svg") configure_file( "${CMAKE_SOURCE_DIR}/contrib/macdeploy/background.svg.cmake.in" "${BACKGROUND_SVG}" ) include(ImageHelper) set(BACKGROUND_PNG_LOWRES "${CMAKE_CURRENT_BINARY_DIR}/background_temp.png") set(BACKGROUND_PNG_HIRES "${CMAKE_CURRENT_BINARY_DIR}/background_temp@2x.png") set(BACKGROUND_TIFF_LOWRES "${CMAKE_CURRENT_BINARY_DIR}/background_temp.tiff") set(BACKGROUND_TIFF_HIRES "${CMAKE_CURRENT_BINARY_DIR}/background_temp@2x.tiff") set(BACKGROUND_TIFF_NAME "background.tiff") set(BACKGROUND_TIFF_MULTIRES "${CMAKE_BINARY_DIR}/${BACKGROUND_TIFF_NAME}") convert_svg_to_png("${BACKGROUND_SVG}" "${BACKGROUND_PNG_LOWRES}" 36) convert_svg_to_png("${BACKGROUND_SVG}" "${BACKGROUND_PNG_HIRES}" 72) convert_png_to_tiff("${BACKGROUND_PNG_LOWRES}" "${BACKGROUND_TIFF_LOWRES}") convert_png_to_tiff("${BACKGROUND_PNG_HIRES}" "${BACKGROUND_TIFF_HIRES}") cat_multi_resolution_tiff("${BACKGROUND_TIFF_MULTIRES}" "${BACKGROUND_TIFF_LOWRES}" "${BACKGROUND_TIFF_HIRES}") set(BACKGROUND_DIST_DIR "${DMG_DIST}/.background") set(BACKGROUND_DIST_TIFF "${BACKGROUND_DIST_DIR}/${BACKGROUND_TIFF_NAME}") add_custom_command( OUTPUT "${BACKGROUND_DIST_TIFF}" COMMAND ${CMAKE_COMMAND} -E make_directory "${BACKGROUND_DIST_DIR}" COMMAND ${CMAKE_COMMAND} -E copy "${BACKGROUND_TIFF_MULTIRES}" "${BACKGROUND_DIST_TIFF}" DEPENDS "${BACKGROUND_TIFF_MULTIRES}" "${DMG_DIST}" ) string(REPLACE " " "-" OSX_VOLNAME "${PACKAGE_NAME}") file(WRITE "${CMAKE_BINARY_DIR}/osx_volname" "${OSX_VOLNAME}") set(DMG_DSSTORE "${DMG_DIST}/.DS_Store") set(GEN_DSSTORE "${MACDEPLOY_DIR}/custom_dsstore.py") add_custom_command( OUTPUT "${DMG_DSSTORE}" COMMAND "${Python_EXECUTABLE}" "${GEN_DSSTORE}" "${DMG_DSSTORE}" "${OSX_VOLNAME}" DEPENDS "${GEN_DSSTORE}" "${DMG_DIST}" ) set(OSX_APPLICATION_DIR "Applications") set(OSX_APPLICATION_SYMLINK "${DMG_DIST}/${OSX_APPLICATION_DIR}") add_custom_command( OUTPUT "${OSX_APPLICATION_SYMLINK}" COMMAND ${CMAKE_COMMAND} -E create_symlink "/${OSX_APPLICATION_DIR}" "${OSX_APPLICATION_SYMLINK}" DEPENDS "${DMG_DIST}" ) add_custom_target(osx-deploydir DEPENDS "${OSX_APPLICATION_SYMLINK}" "${DMG_DSSTORE}" "${BACKGROUND_DIST_TIFF}" ) if(CMAKE_CROSSCOMPILING) find_program_or_fail(GENISOIMAGE_EXECUTABLE genisoimage) add_custom_target(osx-dmg COMMAND "${GENISOIMAGE_EXECUTABLE}" -no-cache-inodes -D -l -probe -V "${OSX_VOLNAME}" -no-pad -r -dir-mode 0755 -apple -o "${OSX_VOLNAME}.dmg" "${DMG_DIST}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" ) add_dependencies(osx-dmg osx-deploydir) else() add_custom_target(osx-dmg COMMAND "${Python_EXECUTABLE}" "${MACDEPLOYQTPLUS}" "${STRIPPED_BUNDLE}" -translations-dir "${QT_TRANSLATION_DIR}" -add-qt-tr "${QT_LOCALES}" -dmg -fancy "${MACDEPLOY_DIR}/fancy.plist" -volname "${OSX_VOLNAME}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS "${STRIPPED_BUNDLE}" "${BACKGROUND_TIFF_MULTIRES}" ) endif() endif() configure_file( "${CMAKE_SOURCE_DIR}/cmake/utils/translate.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/translate.sh" @ONLY ) add_custom_target(translate COMMENT "Updating the translations..." COMMAND "${CMAKE_CURRENT_BINARY_DIR}/translate.sh" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/translate.sh" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.." ) # Test tests add_subdirectory(test) diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index dbe966b24..9f896ee3b 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -1,168 +1,168 @@ ReceiveRequestDialog 0 0 487 597 0 0 300 320 QR Code Qt::PlainText Qt::AlignCenter true 0 0 0 50 QFrame::NoFrame QFrame::Plain true Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse Copy &URI false Copy &Address false &Save Image... false Qt::Horizontal 40 20 QDialogButtonBox::Close QRImageWidget QLabel -
qt/receiverequestdialog.h
+
qt/qrimagewidget.h
buttonBox rejected() ReceiveRequestDialog reject() 452 573 243 298 buttonBox accepted() ReceiveRequestDialog accept() 452 573 243 298
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 36a53092a..db47a4b00 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -1,57 +1,51 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_GUICONSTANTS_H #define BITCOIN_QT_GUICONSTANTS_H #include /* Milliseconds between model updates */ static const int MODEL_UPDATE_DELAY = 250; /* AskPassphraseDialog -- Maximum passphrase length */ static const int MAX_PASSPHRASE_SIZE = 1024; /* BitcoinGUI -- Size of icons in status bar */ static const int STATUSBAR_ICONSIZE = 16; static const bool DEFAULT_SPLASHSCREEN = true; /* Invalid field background style */ #define STYLE_INVALID "background:#FF8080" /* Transaction list -- unconfirmed transaction */ #define COLOR_UNCONFIRMED QColor(128, 128, 128) /* Transaction list -- negative amount */ #define COLOR_NEGATIVE QColor(255, 0, 0) /* Transaction list -- bare address (without label) */ #define COLOR_BAREADDRESS QColor(140, 140, 140) /* Transaction list -- TX status decoration - open until date */ #define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255) /* Transaction list -- TX status decoration - danger, tx needs attention */ #define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) /* Transaction list -- TX status decoration - default color */ #define COLOR_BLACK QColor(0, 0, 0) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. */ static const int TOOLTIP_WRAP_THRESHOLD = 80; -/* Maximum allowed URI length */ -static const int MAX_URI_LENGTH = 255; - -/* QRCodeDialog -- size of exported QR Code image */ -#define QR_IMAGE_SIZE 350 - /* Number of frames in spinner animation */ #define SPINNER_FRAMES 36 #define QAPP_ORG_NAME "BitcoinABC" #define QAPP_ORG_DOMAIN "bitcoinabc.org" #define QAPP_APP_NAME_DEFAULT "BitcoinABC-Qt" #define QAPP_APP_NAME_TESTNET "BitcoinABC-Qt-testnet" #define QAPP_APP_NAME_REGTEST "BitcoinABC-Qt-regtest" #endif // BITCOIN_QT_GUICONSTANTS_H diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/qrimagewidget.cpp similarity index 59% copy from src/qt/receiverequestdialog.cpp copy to src/qt/qrimagewidget.cpp index eb1978ee4..d39823a72 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/qrimagewidget.cpp @@ -1,247 +1,156 @@ -// Copyright (c) 2011-2016 The Bitcoin Core developers -// Copyright (c) 2017-2019 The Bitcoin developers +// Copyright (c) 2011-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include -#include +#include -#include -#include #include -#include -#include +#include #include #include #include #include #include -#include +#include #if defined(HAVE_CONFIG_H) #include /* for USE_QRCODE */ #endif #ifdef USE_QRCODE #include #endif QRImageWidget::QRImageWidget(QWidget *parent) : QLabel(parent), contextMenu(nullptr) { contextMenu = new QMenu(this); QAction *saveImageAction = new QAction(tr("&Save Image..."), this); connect(saveImageAction, &QAction::triggered, this, &QRImageWidget::saveImage); contextMenu->addAction(saveImageAction); QAction *copyImageAction = new QAction(tr("&Copy Image"), this); connect(copyImageAction, &QAction::triggered, this, &QRImageWidget::copyImage); contextMenu->addAction(copyImageAction); } bool QRImageWidget::hasPixmap() const { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) return !pixmap(Qt::ReturnByValue).isNull(); #else return pixmap() != nullptr; #endif } bool QRImageWidget::setQR(const QString &qrData, const QString &text) { #ifdef USE_QRCODE setText(""); if (qrData.isEmpty()) { return false; } // limit length if (qrData.length() > MAX_URI_LENGTH) { setText(tr("Resulting URI too long, try to reduce the text for label / " "message.")); return false; } QRcode *code = QRcode_encodeString(qrData.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); if (!code) { setText(tr("Error encoding URI into QR Code.")); return false; } QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); qrImage.fill(0xffffff); uint8_t *p = code->data; for (int y = 0; y < code->width; ++y) { for (int x = 0; x < code->width; ++x) { qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); ++p; } } QRcode_free(code); QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 20), QImage::Format_RGB32); qrAddrImage.fill(0xffffff); QPainter painter(&qrAddrImage); painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE)); if (!text.isEmpty()) { QFont font = GUIUtil::fixedPitchFont(); QRect paddedRect = qrAddrImage.rect(); // calculate ideal font size qreal font_size = GUIUtil::calculateIdealFontSize( paddedRect.width() - 20, text, font); font.setPointSizeF(font_size); painter.setFont(font); paddedRect.setHeight(QR_IMAGE_SIZE + 12); painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text); } painter.end(); setPixmap(QPixmap::fromImage(qrAddrImage)); return true; #else setText(tr("QR code support not available.")); return false; #endif } QImage QRImageWidget::exportImage() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) return pixmap(Qt::ReturnByValue).toImage(); #else return hasPixmap() ? pixmap()->toImage() : QImage(); #endif } void QRImageWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && hasPixmap()) { event->accept(); QMimeData *mimeData = new QMimeData; mimeData->setImageData(exportImage()); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->exec(); } else { QLabel::mousePressEvent(event); } } void QRImageWidget::saveImage() { if (!hasPixmap()) { return; } QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr); if (!fn.isEmpty()) { exportImage().save(fn); } } void QRImageWidget::copyImage() { if (!hasPixmap()) { return; } QApplication::clipboard()->setImage(exportImage()); } void QRImageWidget::contextMenuEvent(QContextMenuEvent *event) { if (!hasPixmap()) { return; } contextMenu->exec(event->globalPos()); } - -ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) - : QDialog(parent), ui(new Ui::ReceiveRequestDialog), model(nullptr) { - ui->setupUi(this); - -#ifndef USE_QRCODE - ui->btnSaveAs->setVisible(false); - ui->lblQRCode->setVisible(false); -#endif - - connect(ui->btnSaveAs, &QPushButton::clicked, ui->lblQRCode, - &QRImageWidget::saveImage); -} - -ReceiveRequestDialog::~ReceiveRequestDialog() { - delete ui; -} - -void ReceiveRequestDialog::setModel(WalletModel *_model) { - this->model = _model; - - if (_model) { - connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, - this, &ReceiveRequestDialog::update); - } - - // update the display unit if necessary - update(); -} - -void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info) { - this->info = _info; - update(); -} - -void ReceiveRequestDialog::update() { - if (!model) { - return; - } - QString target = info.label; - if (target.isEmpty()) { - target = info.address; - } - setWindowTitle(tr("Request payment to %1").arg(target)); - - QString uri = GUIUtil::formatBitcoinURI(info); - ui->btnSaveAs->setEnabled(false); - QString html; - html += ""; - html += "" + tr("Payment information") + "
"; - html += "" + tr("URI") + ": "; - html += "" + GUIUtil::HtmlEscape(uri) + "
"; - html += "" + tr("Address") + - ": " + GUIUtil::HtmlEscape(info.address) + "
"; - if (info.amount != Amount::zero()) { - html += "" + tr("Amount") + ": " + - BitcoinUnits::formatHtmlWithUnit( - model->getOptionsModel()->getDisplayUnit(), info.amount) + - "
"; - } - if (!info.label.isEmpty()) { - html += "" + tr("Label") + - ": " + GUIUtil::HtmlEscape(info.label) + "
"; - } - if (!info.message.isEmpty()) { - html += "" + tr("Message") + - ": " + GUIUtil::HtmlEscape(info.message) + "
"; - } - if (model->isMultiwallet()) { - html += "" + tr("Wallet") + - ": " + GUIUtil::HtmlEscape(model->getWalletName()) + "
"; - } - ui->outUri->setText(html); - - if (ui->lblQRCode->setQR(uri, info.address)) { - ui->btnSaveAs->setEnabled(true); - } -} - -void ReceiveRequestDialog::on_btnCopyURI_clicked() { - GUIUtil::setClipboard(GUIUtil::formatBitcoinURI(info)); -} - -void ReceiveRequestDialog::on_btnCopyAddress_clicked() { - GUIUtil::setClipboard(info.address); -} diff --git a/src/qt/qrimagewidget.h b/src/qt/qrimagewidget.h new file mode 100644 index 000000000..5907d0505 --- /dev/null +++ b/src/qt/qrimagewidget.h @@ -0,0 +1,46 @@ +// Copyright (c) 2011-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_QRIMAGEWIDGET_H +#define BITCOIN_QT_QRIMAGEWIDGET_H + +#include +#include + +/* Maximum allowed URI length */ +static const int MAX_URI_LENGTH = 255; + +/* Size of exported QR Code image */ +static const int QR_IMAGE_SIZE = 350; + +QT_BEGIN_NAMESPACE +class QMenu; +QT_END_NAMESPACE + +/** + * Label widget for QR code. This image can be dragged, dropped, copied and + * saved to disk. + */ +class QRImageWidget : public QLabel { + Q_OBJECT + +public: + explicit QRImageWidget(QWidget *parent = nullptr); + bool hasPixmap() const; + bool setQR(const QString &qrData, const QString &text = ""); + QImage exportImage(); + +public Q_SLOTS: + void saveImage(); + void copyImage(); + +protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *event); + +private: + QMenu *contextMenu; +}; + +#endif // BITCOIN_QT_QRIMAGEWIDGET_H diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index eb1978ee4..526e24435 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -1,247 +1,106 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include -#include #include #include +#include #include #include -#include -#include -#include -#include #include #if defined(HAVE_CONFIG_H) #include /* for USE_QRCODE */ #endif -#ifdef USE_QRCODE -#include -#endif - -QRImageWidget::QRImageWidget(QWidget *parent) - : QLabel(parent), contextMenu(nullptr) { - contextMenu = new QMenu(this); - QAction *saveImageAction = new QAction(tr("&Save Image..."), this); - connect(saveImageAction, &QAction::triggered, this, - &QRImageWidget::saveImage); - contextMenu->addAction(saveImageAction); - QAction *copyImageAction = new QAction(tr("&Copy Image"), this); - connect(copyImageAction, &QAction::triggered, this, - &QRImageWidget::copyImage); - contextMenu->addAction(copyImageAction); -} - -bool QRImageWidget::hasPixmap() const { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) - return !pixmap(Qt::ReturnByValue).isNull(); -#else - return pixmap() != nullptr; -#endif -} - -bool QRImageWidget::setQR(const QString &qrData, const QString &text) { -#ifdef USE_QRCODE - setText(""); - if (qrData.isEmpty()) { - return false; - } - - // limit length - if (qrData.length() > MAX_URI_LENGTH) { - setText(tr("Resulting URI too long, try to reduce the text for label / " - "message.")); - return false; - } - - QRcode *code = QRcode_encodeString(qrData.toUtf8().constData(), 0, - QR_ECLEVEL_L, QR_MODE_8, 1); - - if (!code) { - setText(tr("Error encoding URI into QR Code.")); - return false; - } - - QImage qrImage = - QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); - qrImage.fill(0xffffff); - uint8_t *p = code->data; - for (int y = 0; y < code->width; ++y) { - for (int x = 0; x < code->width; ++x) { - qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); - ++p; - } - } - QRcode_free(code); - - QImage qrAddrImage = - QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 20), - QImage::Format_RGB32); - qrAddrImage.fill(0xffffff); - QPainter painter(&qrAddrImage); - painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE)); - - if (!text.isEmpty()) { - QFont font = GUIUtil::fixedPitchFont(); - QRect paddedRect = qrAddrImage.rect(); - - // calculate ideal font size - qreal font_size = GUIUtil::calculateIdealFontSize( - paddedRect.width() - 20, text, font); - font.setPointSizeF(font_size); - - painter.setFont(font); - paddedRect.setHeight(QR_IMAGE_SIZE + 12); - painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text); - } - - painter.end(); - setPixmap(QPixmap::fromImage(qrAddrImage)); - - return true; -#else - setText(tr("QR code support not available.")); - return false; -#endif -} - -QImage QRImageWidget::exportImage() { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) - return pixmap(Qt::ReturnByValue).toImage(); -#else - return hasPixmap() ? pixmap()->toImage() : QImage(); -#endif -} - -void QRImageWidget::mousePressEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton && hasPixmap()) { - event->accept(); - QMimeData *mimeData = new QMimeData; - mimeData->setImageData(exportImage()); - - QDrag *drag = new QDrag(this); - drag->setMimeData(mimeData); - drag->exec(); - } else { - QLabel::mousePressEvent(event); - } -} - -void QRImageWidget::saveImage() { - if (!hasPixmap()) { - return; - } - QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), - tr("PNG Image (*.png)"), nullptr); - if (!fn.isEmpty()) { - exportImage().save(fn); - } -} - -void QRImageWidget::copyImage() { - if (!hasPixmap()) { - return; - } - QApplication::clipboard()->setImage(exportImage()); -} - -void QRImageWidget::contextMenuEvent(QContextMenuEvent *event) { - if (!hasPixmap()) { - return; - } - contextMenu->exec(event->globalPos()); -} - ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ReceiveRequestDialog), model(nullptr) { ui->setupUi(this); #ifndef USE_QRCODE ui->btnSaveAs->setVisible(false); ui->lblQRCode->setVisible(false); #endif connect(ui->btnSaveAs, &QPushButton::clicked, ui->lblQRCode, &QRImageWidget::saveImage); } ReceiveRequestDialog::~ReceiveRequestDialog() { delete ui; } void ReceiveRequestDialog::setModel(WalletModel *_model) { this->model = _model; if (_model) { connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveRequestDialog::update); } // update the display unit if necessary update(); } void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info) { this->info = _info; update(); } void ReceiveRequestDialog::update() { if (!model) { return; } QString target = info.label; if (target.isEmpty()) { target = info.address; } setWindowTitle(tr("Request payment to %1").arg(target)); QString uri = GUIUtil::formatBitcoinURI(info); ui->btnSaveAs->setEnabled(false); QString html; html += ""; html += "" + tr("Payment information") + "
"; html += "" + tr("URI") + ": "; html += "" + GUIUtil::HtmlEscape(uri) + "
"; html += "" + tr("Address") + ": " + GUIUtil::HtmlEscape(info.address) + "
"; if (info.amount != Amount::zero()) { html += "" + tr("Amount") + ": " + BitcoinUnits::formatHtmlWithUnit( model->getOptionsModel()->getDisplayUnit(), info.amount) + "
"; } if (!info.label.isEmpty()) { html += "" + tr("Label") + ": " + GUIUtil::HtmlEscape(info.label) + "
"; } if (!info.message.isEmpty()) { html += "" + tr("Message") + ": " + GUIUtil::HtmlEscape(info.message) + "
"; } if (model->isMultiwallet()) { html += "" + tr("Wallet") + ": " + GUIUtil::HtmlEscape(model->getWalletName()) + "
"; } ui->outUri->setText(html); if (ui->lblQRCode->setQR(uri, info.address)) { ui->btnSaveAs->setEnabled(true); } } void ReceiveRequestDialog::on_btnCopyURI_clicked() { GUIUtil::setClipboard(GUIUtil::formatBitcoinURI(info)); } void ReceiveRequestDialog::on_btnCopyAddress_clicked() { GUIUtil::setClipboard(info.address); } diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index afb4f01b6..7ce089230 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -1,72 +1,40 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H #define BITCOIN_QT_RECEIVEREQUESTDIALOG_H #include #include -#include -#include -#include class WalletModel; namespace Ui { class ReceiveRequestDialog; } -QT_BEGIN_NAMESPACE -class QMenu; -QT_END_NAMESPACE - -/* Label widget for QR code. This image can be dragged, dropped, copied and - * saved - * to disk. - */ -class QRImageWidget : public QLabel { - Q_OBJECT - -public: - explicit QRImageWidget(QWidget *parent = nullptr); - bool hasPixmap() const; - bool setQR(const QString &data, const QString &text = ""); - QImage exportImage(); - -public Q_SLOTS: - void saveImage(); - void copyImage(); - -protected: - virtual void mousePressEvent(QMouseEvent *event) override; - virtual void contextMenuEvent(QContextMenuEvent *event) override; - -private: - QMenu *contextMenu; -}; - class ReceiveRequestDialog : public QDialog { Q_OBJECT public: explicit ReceiveRequestDialog(QWidget *parent = nullptr); ~ReceiveRequestDialog(); void setModel(WalletModel *model); void setInfo(const SendCoinsRecipient &info); private Q_SLOTS: void on_btnCopyURI_clicked(); void on_btnCopyAddress_clicked(); void update(); private: Ui::ReceiveRequestDialog *ui; WalletModel *model; SendCoinsRecipient info; }; #endif // BITCOIN_QT_RECEIVEREQUESTDIALOG_H