diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 07dce4a185..b0229214a2 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,538 +1,540 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/kis_dlg_internal_color_selector.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc kis_base_option.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc kis_config_notifier.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp kis_painting_assistants_manager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp kis_resource_server_provider.cpp kis_selection_decoration.cc kis_selection_manager.cc kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp kis_view_plugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp kis_fps_decoration.cpp recorder/kis_node_query_path_editor.cc recorder/kis_recorded_action_creator.cc recorder/kis_recorded_action_creator_factory.cc recorder/kis_recorded_action_creator_factory_registry.cc recorder/kis_recorded_action_editor_factory.cc recorder/kis_recorded_action_editor_factory_registry.cc recorder/kis_recorded_filter_action_editor.cc recorder/kis_recorded_filter_action_creator.cpp recorder/kis_recorded_paint_action_editor.cc tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_recording_adapter.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/strokes/freehand_stroke.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_gradient_slider_widget.cc widgets/kis_gradient_slider.cpp widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_pattern_chooser.cc widgets/kis_popup_button.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/squeezedcombobox.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_spinbox_color_selector.cpp widgets/kis_screen_color_picker.cpp widgets/KoDualColorButton.cpp widgets/kis_color_input.cpp widgets/kis_color_button.cpp widgets/KisVisualColorSelector.cpp widgets/KisVisualColorSelectorShape.cpp widgets/KisVisualEllipticalSelectorShape.cpp widgets/KisVisualRectangleSelectorShape.cpp widgets/KisVisualTriangleSelectorShape.cpp + utils/kis_document_aware_spin_box_unit_manager.cpp + input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_transaction_based_command.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisNodeDelegate.cpp kis_node_view_visibility_delegate.cpp KisNodeToolTip.cpp KisNodeView.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisFilterEntry.cpp KisImportExportManager.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoStackAction.cpp KisView.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisPaletteModel.cpp kis_palette_delegate.cpp kis_palette_view.cpp KisColorsetChooser.cpp KisSaveGroupVisitor.cpp ) if(WIN32) if (NOT Qt5Gui_PRIVATE_INCLUDE_DIRS) message(FATAL_ERROR "Qt5Gui Private header are missing!") endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp ) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp canvas/kis_animation_player.cpp kis_animation_exporter.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp ) if(UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support.cpp qtlockedfile/qtlockedfile_unix.cpp ) if(NOT APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_x11.cpp input/wintab/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() ki18n_wrap_ui(kritaui_LIB_SRCS forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgpaintactioneditor.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui brushhud/kis_dlg_brush_hud_config.ui forms/wdgdlginternalcolorselector.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} ) if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (HAVE_KIO) target_link_libraries(kritaui KF5::KIOCore) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index 8cb1b6a641..b02d5fe1f8 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,761 +1,768 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2009 Thomas Zander Copyright (C) 2012 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoGlobal.h" #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" +#include "kis_spin_box_unit_manager.h" +#include "kis_document_aware_spin_box_unit_manager.h" #include namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplicationPrivate { public: KisApplicationPrivate() : splashScreen(0) {} QPointer splashScreen; }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash = 0) : m_splash(splash) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (hideSplash) { m_splash->hide(); } else { m_splash->setWindowFlags(Qt::Dialog); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); m_splash->setParent(0); Q_FOREACH (QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); m_splash->activateWindow(); } } } QPointer m_splash; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new KisApplicationPrivate) , m_autosaveDialog(0) , m_mainWindow(0) , m_batchRun(false) { QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } KisOpenGL::initialize(); qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL(); } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void addResourceTypes() { // All Krita's resource types KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); } void KisApplication::loadResources() { setSplashScreenLoadingText(i18n("Loading Gradients...")); processEvents(); KoResourceServerProvider::instance()->gradientServer(true); // Load base resources setSplashScreenLoadingText(i18n("Loading Patterns...")); processEvents(); KoResourceServerProvider::instance()->patternServer(true); setSplashScreenLoadingText(i18n("Loading Palettes...")); processEvents(); KoResourceServerProvider::instance()->paletteServer(false); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(true); // load paintop presets setSplashScreenLoadingText(i18n("Loading Paint Operations...")); processEvents(); KisResourceServerProvider::instance()->paintOpPresetServer(true); setSplashScreenLoadingText(i18n("Loading Resource Bundles...")); processEvents(); KisResourceServerProvider::instance()->resourceBundleServer(); } void KisApplication::loadPlugins() { KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoColorSpaceRegistry::instance(); // Load the krita-specific tools setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"), QString::fromLatin1("[X-Krita-Version] == 28")); // Load dockers setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"), QString::fromLatin1("[X-Krita-Version] == 28")); // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg; #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doTemplate = args.doTemplate(); const bool print = args.print(); const bool exportAs = args.exportAs(); const bool exportAsPdf = args.exportAsPdf(); const QString exportFileName = args.exportFileName(); m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); // print & exportAsPdf do user interaction ATM const bool needsMainWindow = !exportAs; // only show the mainWindow when no command-line mode option is passed // TODO: fix print & exportAsPdf to work without mainwindow shown const bool showmainWindow = !exportAs; // would be !batchRun; const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH");// && qgetenv("XDG_CURRENT_DESKTOP") != "GNOME"; if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); // Initialize all Krita directories etc. KoGlobal::initialize(); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); processEvents(); addResourceTypes(); // Load all resources and tags before the plugins do that loadResources(); // Load the plugins loadPlugins(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); m_mainWindow = KisPart::instance()->createMainWindow(); if (showmainWindow) { m_mainWindow->initializeGeometry(); m_mainWindow->show(); } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf) if (!m_batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); + //configure the unit manager + KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); + connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. + //the new syntax slot syntax allow to connect to a non q_object static method. + // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments short int nPrinted = 0; for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip if (m_batchRun) { continue; } if (createNewDocFromTemplate(fileName, m_mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return 1; } KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); doc->setOutputMimeType(outputMimetype.toLatin1()); if (!doc->exportDocument(QUrl::fromLocalFile(exportFileName))) { dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } nPrinted++; QTimer::singleShot(0, this, SLOT(quit())); } else if (m_mainWindow) { KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); if (m_mainWindow->openDocumentInternal(QUrl::fromLocalFile(fileName), doc)) { if (print) { m_mainWindow->slotFilePrint(); nPrinted++; // TODO: trigger closing of app once printing is done } else if (exportAsPdf) { KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName); if (job) connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument } } } } if (m_batchRun) { return nPrinted > 0; } } // fixes BUG:369308 - Krita crashing on splash screen when loading. // trying to open a file before Krita has loaded can cause it to hang and crash if (d->splashScreen) { d->splashScreen->displayLinks(); d->splashScreen->displayRecentFiles(); } // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { delete d; } void KisApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { if (d->splashScreen) { d->splashScreen->loadingLabel->setText(textToLoad); d->splashScreen->repaint(); } } void KisApplication::hideSplashScreen() { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { return; } KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { createNewDocFromTemplate(filename, mw); } else if (QFile(filename).exists()) { KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); mw->openDocumentInternal(QUrl::fromLocalFile(filename), doc); } } } } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); mainWindow->openDocumentInternal(QUrl::fromLocalFile(url), doc); } } void KisApplication::checkAutosaveFiles() { if (m_batchRun) return; // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList filters; filters << QString(".krita-*-*-autosave.kra"); #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // all autosave files for our application m_autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Allow the user to make their selection if (m_autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } m_autosaveDialog = new KisAutoSaveRecoveryDialog(m_autosaveFiles, activeWindow()); QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec(); if (result == QDialog::Accepted) { QStringList filesToRecover = m_autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, m_autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } m_autosaveFiles = filesToRecover; } else { m_autosaveFiles.clear(); } if (m_autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, m_autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } if (m_mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); m_mainWindow->openDocumentInternal(url, doc); } } } // cleanup delete m_autosaveDialog; m_autosaveDialog = nullptr; } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); if (mainWindow->openDocumentInternal(templateURL, doc)) { doc->resetURL(); doc->setTitleModified(); dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::clearConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); // find user settings file bool createDir = false; QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir); QFile configFile(kritarcPath); if (configFile.exists()) { // clear file if (configFile.open(QFile::WriteOnly)) { configFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); } void KisApplication::askClearConfig() { Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers(); bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier); if (askClearConfig) { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { clearConfig(); } } } diff --git a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp new file mode 100644 index 0000000000..23858a32ae --- /dev/null +++ b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2017 Laurent Valentin Jospin + * + * 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 2 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. + */ + +#include "kis_document_aware_spin_box_unit_manager.h" + +#include "KisPart.h" +#include "KisMainWindow.h" +#include "KisView.h" +#include "KisDocument.h" +#include "kis_types.h" +#include "kis_image.h" +#include "kis_image_animation_interface.h" +#include "kis_time_range.h" + + +KisSpinBoxUnitManager* KisDocumentAwareSpinBoxUnitManagerBuilder::buildUnitManager(QObject* parent) +{ + return new KisDocumentAwareSpinBoxUnitManager(parent); +} + +void KisDocumentAwareSpinBoxUnitManager::setDocumentAwarnessToExistingUnitSpinBox(KisDoubleParseUnitSpinBox* spinBox, bool setUnitFromOutsideToggle) +{ + KisDocumentAwareSpinBoxUnitManager* manager = new KisDocumentAwareSpinBoxUnitManager(spinBox); + spinBox->setUnitManager(manager); + spinBox->setUnitChangeFromOutsideBehavior(setUnitFromOutsideToggle); +} + +KisDoubleParseUnitSpinBox* KisDocumentAwareSpinBoxUnitManager::createUnitSpinBoxWithDocumentAwarness(QWidget* parent) +{ + KisDoubleParseUnitSpinBox* spinBox = new KisDoubleParseUnitSpinBox(parent); + setDocumentAwarnessToExistingUnitSpinBox(spinBox); + + return spinBox; +} + +KisDocumentAwareSpinBoxUnitManager::KisDocumentAwareSpinBoxUnitManager(QObject *parent, int pPixDir): + KisSpinBoxUnitManager(parent) +{ + if (pPixDir == PIX_DIR_Y) { + pixDir = PIX_DIR_Y; + } else { + pixDir = PIX_DIR_X; + } + + grantDocumentRelativeUnits(); //the purpose of this class is to manage document relative units. +} + + +qreal KisDocumentAwareSpinBoxUnitManager::getConversionFactor(int dim, QString symbol) const +{ + qreal factor = KisSpinBoxUnitManager::getConversionFactor(dim, symbol); + + if (factor > 0) { + //no errors occured at a lower level, so the conversion factor has been get. + return factor; + } + + factor = 1; //fall back to something natural in case document is unreachable (1 px = 1 pt = 1vw = 1vh). So a virtual document of 100x100 with a resolution of 1. + + KisView* view = KisPart::instance()->currentMainwindow()->activeView(); + + if (view == nullptr) { + return factor; + } + + KisDocument* doc = view->document(); + + if (doc == nullptr) { + return factor; + } + + KisImage* img = doc->image().data(); + + if (img == nullptr) { + return factor; + } + + qreal resX = img->xRes(); + qreal resY = img->yRes(); + qreal sizeX = img->width(); + qreal sizeY = img->height(); + + switch (dim) { + + case LENGTH: + if (symbol == "px") { + + if (pixDir == PIX_DIR_X) { + factor = resX; + } else { + factor = resY; + } + } else if (symbol == "vw") { + qreal docWidth = sizeX/resX; + + factor = 100.0/docWidth; //1 vw is 1% of document width, 1 vw in point is docWidth/100 so 1 point in vw is the inverse. + } else if (symbol == "vh") { + qreal docHeight = sizeY/resY; + + factor = 100.0/docHeight; + } + break; + + case IMLENGTH: + + if (symbol == "vw") { + factor = 100.0/sizeX; //1 vw is 1% of document width, 1 vw in pixel is sizeX/100 so 1 pixel in vw is the inverse. + + } else if (symbol == "vh") { + factor = 100.0/sizeY; + } + break; + + case TIME: + { + if (symbol == "s") { + qreal fps = img->animationInterface()->framerate(); + + factor = 1/fps; + } else if (symbol == "%") { + const KisTimeRange & time_range = img->animationInterface()->fullClipRange(); + qreal n_frame = time_range.end() - time_range.start(); + + factor = 100/n_frame; + } + } + break; + + default: + break; + + } + + return factor; +} + +qreal KisDocumentAwareSpinBoxUnitManager::getConversionConstant(int dim, QString symbol) const +{ + if (dim == TIME && symbol == "%") { + KisImage* img = KisPart::instance()->currentMainwindow()->activeView()->document()->image().data(); + const KisTimeRange & time_range = img->animationInterface()->fullClipRange(); + qreal n_frame = time_range.end() - time_range.start(); + + return -time_range.start()*100.0/n_frame; + } + + return KisSpinBoxUnitManager::getConversionConstant(dim, symbol); +} diff --git a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h new file mode 100644 index 0000000000..1456ed692a --- /dev/null +++ b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Laurent Valentin Jospin + * + * 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 2 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. + */ + +#ifndef KISDOCUMENTAWARESPINBOXUNITMANAGER_H +#define KISDOCUMENTAWARESPINBOXUNITMANAGER_H + +#include "kis_spin_box_unit_manager.h" +#include "kis_double_parse_unit_spin_box.h" + +#include "kritaui_export.h" +class KisDocumentAwareSpinBoxUnitManagerBuilder : public KisSpinBoxUnitManagerBuilder +{ + +public: + + KisSpinBoxUnitManager* buildUnitManager(QObject* parent); +}; + +/*! + * \brief The KisDocumentAwareSpinBoxUnitManager class is a KisSpinBoxUnitManager that is able to connect to the current document to compute transformation for document relative units (the ones that depend of the resolution, or the size in pixels of the image). + * \see KisSpinBoxUnitManager + */ +class KRITAUI_EXPORT KisDocumentAwareSpinBoxUnitManager : public KisSpinBoxUnitManager +{ + Q_OBJECT + +public: + + enum PixDir { + PIX_DIR_X, + PIX_DIR_Y + }; //in case the image has not the same x and y resolution, indicate on which direction get the resolution. + + //! \brief configure a KisDocumentAwareSpinBoxUnitManager for the given spinbox (make the manager a child of the spinbox and attach it to the spinbox). + static void setDocumentAwarnessToExistingUnitSpinBox(KisDoubleParseUnitSpinBox* spinBox, bool setUnitFromOutsideToggle = false); + + //! \brief create a unitSpinBox that is already document aware. + static KisDoubleParseUnitSpinBox* createUnitSpinBoxWithDocumentAwarness(QWidget* parent = 0); + + KisDocumentAwareSpinBoxUnitManager(QObject *parent = 0, int pPixDir = PIX_DIR_X); + + //! \reimp \see KisSpinBoxUnitManager + virtual qreal getConversionFactor(int dim, QString symbol) const; + //! \reimp \see KisSpinBoxUnitManager + virtual qreal getConversionConstant(int dim, QString symbol) const; + +private: + + PixDir pixDir; +}; + +#endif // KISDOCUMENTAWARESPINBOXUNITMANAGER_H diff --git a/libs/widgets/KoShadowConfigWidget.cpp b/libs/widgets/KoShadowConfigWidget.cpp index 4807d08105..6e7ff4b785 100644 --- a/libs/widgets/KoShadowConfigWidget.cpp +++ b/libs/widgets/KoShadowConfigWidget.cpp @@ -1,231 +1,241 @@ /* This file is part of the KDE project * Copyright (C) 2012 C. Boemann * Copyright (C) 2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoShadowConfigWidget.h" #include "ui_KoShadowConfigWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoShadowConfigWidget::Private { public: Private() { } Ui_KoShadowConfigWidget widget; KoColorPopupAction *actionShadowColor; KoCanvasBase *canvas; }; KoShadowConfigWidget::KoShadowConfigWidget(QWidget *parent) : QWidget(parent) , d(new Private()) { d->widget.setupUi(this); d->widget.shadowOffset->setValue(8.0); d->widget.shadowBlur->setValue(8.0); d->widget.shadowBlur->setMinimum(0.0); d->widget.shadowAngle->setValue(315.0); d->widget.shadowAngle->setMinimum(0.0); d->widget.shadowAngle->setMaximum(360.0); d->widget.shadowVisible->setChecked(false); visibilityChanged(); d->actionShadowColor = new KoColorPopupAction(this); d->actionShadowColor->setCurrentColor(QColor(0, 0, 0, 192)); // some reasonable default for shadow d->actionShadowColor->setIcon(koIcon("format-stroke-color")); d->actionShadowColor->setToolTip(i18n("Change the color of the shadow")); d->widget.shadowColor->setDefaultAction(d->actionShadowColor); connect(d->widget.shadowVisible, SIGNAL(toggled(bool)), this, SLOT(applyChanges())); connect(d->widget.shadowVisible, SIGNAL(toggled(bool)), this, SLOT(visibilityChanged())); connect(d->actionShadowColor, SIGNAL(colorChanged(const KoColor&)), this, SLOT(applyChanges())); connect(d->widget.shadowAngle, SIGNAL(valueChanged(int)), this, SLOT(applyChanges())); connect(d->widget.shadowOffset, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyChanges())); connect(d->widget.shadowBlur, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyChanges())); } KoShadowConfigWidget::~KoShadowConfigWidget() { delete d; } void KoShadowConfigWidget::setShadowColor(const QColor &color) { d->widget.shadowColor->blockSignals(true); d->actionShadowColor->blockSignals(true); d->actionShadowColor->setCurrentColor( color ); d->actionShadowColor->blockSignals(false); d->widget.shadowColor->blockSignals(false); } QColor KoShadowConfigWidget::shadowColor() const { return d->actionShadowColor->currentColor(); } void KoShadowConfigWidget::setShadowOffset(const QPointF &offset) { qreal length = sqrt(offset.x()*offset.x() + offset.y()*offset.y()); qreal angle = atan2(-offset.y(), offset.x()); if (angle < 0.0) { angle += 2*M_PI; } d->widget.shadowAngle->blockSignals(true); d->widget.shadowAngle->setValue(-90 - angle * 180.0 / M_PI); d->widget.shadowAngle->blockSignals(false); d->widget.shadowOffset->blockSignals(true); d->widget.shadowOffset->changeValue(length); d->widget.shadowOffset->blockSignals(false); } QPointF KoShadowConfigWidget::shadowOffset() const { QPointF offset(d->widget.shadowOffset->value(), 0); QTransform m; m.rotate(d->widget.shadowAngle->value() + 90); return m.map(offset); } void KoShadowConfigWidget::setShadowBlur(const qreal &blur) { d->widget.shadowBlur->blockSignals(true); d->widget.shadowBlur->changeValue(blur); d->widget.shadowBlur->blockSignals(false); } qreal KoShadowConfigWidget::shadowBlur() const { return d->widget.shadowBlur->value(); } void KoShadowConfigWidget::setShadowVisible(bool visible) { d->widget.shadowVisible->blockSignals(true); d->widget.shadowVisible->setChecked(visible); d->widget.shadowVisible->blockSignals(false); visibilityChanged(); } bool KoShadowConfigWidget::shadowVisible() const { return d->widget.shadowVisible->isChecked(); } void KoShadowConfigWidget::visibilityChanged() { d->widget.shadowAngle->setEnabled( d->widget.shadowVisible->isChecked() ); d->widget.shadowBlur->setEnabled( d->widget.shadowVisible->isChecked() ); d->widget.shadowColor->setEnabled( d->widget.shadowVisible->isChecked() ); d->widget.shadowOffset->setEnabled( d->widget.shadowVisible->isChecked() ); } void KoShadowConfigWidget::applyChanges() { if (d->canvas) { KoSelection *selection = d->canvas->shapeManager()->selection(); KoShape * shape = selection->firstSelectedShape(KoFlake::TopLevelSelection); if (! shape) { return; } KoShapeShadow *newShadow = new KoShapeShadow(); newShadow->setVisible(shadowVisible()); newShadow->setColor(shadowColor()); newShadow->setOffset(shadowOffset()); newShadow->setBlur(shadowBlur()); d->canvas->addCommand(new KoShapeShadowCommand(selection->selectedShapes(KoFlake::TopLevelSelection), newShadow)); } } void KoShadowConfigWidget::selectionChanged() { if (! d->canvas) { return; } KoSelection *selection = d->canvas->shapeManager()->selection(); KoShape * shape = selection->firstSelectedShape(KoFlake::TopLevelSelection); setEnabled(shape != 0); if (! shape) { setShadowVisible(false); return; } KoShapeShadow * shadow = shape->shadow(); if (! shadow) { setShadowVisible(false); return; } setShadowVisible(shadow->isVisible()); setShadowOffset(shadow->offset()); setShadowColor(shadow->color()); setShadowBlur(shadow->blur()); } void KoShadowConfigWidget::setCanvas(KoCanvasBase *canvas) { d->canvas = canvas; connect(canvas->shapeManager(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); connect(canvas->shapeManager(), SIGNAL(selectionContentChanged()), this, SLOT(selectionChanged())); setUnit(canvas->unit()); connect( d->canvas->resourceManager(), SIGNAL( canvasResourceChanged( int, const QVariant& ) ), this, SLOT( resourceChanged( int, const QVariant& ) ) ); } +void KoShadowConfigWidget::setUnitManagers(KisSpinBoxUnitManager* managerBlur, KisSpinBoxUnitManager *managerOffset) +{ + d->widget.shadowOffset->blockSignals(true); + d->widget.shadowBlur->blockSignals(true); + d->widget.shadowOffset->setUnitManager(managerOffset); + d->widget.shadowBlur->setUnitManager(managerBlur); + d->widget.shadowOffset->blockSignals(false); + d->widget.shadowBlur->blockSignals(false); +} + void KoShadowConfigWidget::setUnit(const KoUnit &unit) { d->widget.shadowOffset->blockSignals(true); d->widget.shadowBlur->blockSignals(true); d->widget.shadowOffset->setUnit(unit); d->widget.shadowBlur->setUnit(unit); d->widget.shadowOffset->blockSignals(false); d->widget.shadowBlur->blockSignals(false); } void KoShadowConfigWidget::resourceChanged( int key, const QVariant & res ) { if( key == KoCanvasResourceManager::Unit ) { setUnit(res.value()); } } diff --git a/libs/widgets/KoShadowConfigWidget.h b/libs/widgets/KoShadowConfigWidget.h index ba6e7a5aa5..3786268ecf 100644 --- a/libs/widgets/KoShadowConfigWidget.h +++ b/libs/widgets/KoShadowConfigWidget.h @@ -1,77 +1,80 @@ /* This file is part of the KDE project * Copyright (C) 2012 C. Boemann * Copyright (C) 2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOSHADOWCONFIGWIDGET_H #define KOSHADOWCONFIGWIDGET_H #include "kritawidgets_export.h" #include class KoUnit; class KoCanvasBase; +class KisSpinBoxUnitManager; /// A widget for configuring the shadow of a shape class KRITAWIDGETS_EXPORT KoShadowConfigWidget : public QWidget { Q_OBJECT public: explicit KoShadowConfigWidget(QWidget *parent); ~KoShadowConfigWidget(); /// Sets the shadow color void setShadowColor(const QColor &color); /// Returns the shadow color QColor shadowColor() const; /// Sets the shadow offset void setShadowOffset(const QPointF &offset); /// Returns the shadow offset QPointF shadowOffset() const; /// Sets the shadow blur radius void setShadowBlur(const qreal &blur); /// Returns the shadow blur radius qreal shadowBlur() const; /// Sets if the shadow is visible void setShadowVisible(bool visible); /// Returns if shadow is visible bool shadowVisible() const; public Q_SLOTS: + + void setUnitManagers(KisSpinBoxUnitManager* managerBlur, KisSpinBoxUnitManager* managerOffset); void setUnit( const KoUnit &unit ); void setCanvas(KoCanvasBase *canvas); private Q_SLOTS: void visibilityChanged(); void applyChanges(); void selectionChanged(); void resourceChanged( int key, const QVariant & res ); private: class Private; Private *const d; }; #endif // KOSHADOWCONFIGWIDGET_H diff --git a/libs/widgets/KoStrokeConfigWidget.cpp b/libs/widgets/KoStrokeConfigWidget.cpp index 8f9088fc54..22c3135849 100644 --- a/libs/widgets/KoStrokeConfigWidget.cpp +++ b/libs/widgets/KoStrokeConfigWidget.cpp @@ -1,552 +1,571 @@ /* This file is part of the KDE project * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) * Copyright (C) 2002 Tomislav Lukman * Copyright (C) 2002-2003 Rob Buis * Copyright (C) 2005-2006 Tim Beaulen * Copyright (C) 2005-2007 Thomas Zander * Copyright (C) 2005-2006, 2011 Inge Wallin * Copyright (C) 2005-2008 Jan Hambrecht * Copyright (C) 2006 C. Boemann * Copyright (C) 2006 Peter Simonsson * Copyright (C) 2006 Laurent Montel * Copyright (C) 2007,2011 Thorsten Zachmann * Copyright (C) 2011 Jean-Nicolas Artaud * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // Own #include "KoStrokeConfigWidget.h" // Qt #include #include #include #include #include #include #include // KDE #include // Calligra #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita #include "kis_double_parse_unit_spin_box.h" class CapNJoinMenu : public QMenu { public: CapNJoinMenu(QWidget *parent = 0); QSize sizeHint() const override; KisDoubleParseUnitSpinBox *miterLimit; QButtonGroup *capGroup; QButtonGroup *joinGroup; }; CapNJoinMenu::CapNJoinMenu(QWidget *parent) : QMenu(parent) { QGridLayout *mainLayout = new QGridLayout(); mainLayout->setMargin(2); - // The cap group + // The cap group capGroup = new QButtonGroup(this); capGroup->setExclusive(true); QToolButton *button = 0; button = new QToolButton(this); button->setIcon(koIcon("stroke-cap-butt")); button->setCheckable(true); button->setToolTip(i18n("Butt cap")); capGroup->addButton(button, Qt::FlatCap); mainLayout->addWidget(button, 2, 0); button = new QToolButton(this); button->setIcon(koIcon("stroke-cap-round")); button->setCheckable(true); button->setToolTip(i18n("Round cap")); capGroup->addButton(button, Qt::RoundCap); mainLayout->addWidget(button, 2, 1); button = new QToolButton(this); button->setIcon(koIcon("stroke-cap-square")); button->setCheckable(true); button->setToolTip(i18n("Square cap")); capGroup->addButton(button, Qt::SquareCap); mainLayout->addWidget(button, 2, 2, Qt::AlignLeft); // The join group joinGroup = new QButtonGroup(this); joinGroup->setExclusive(true); button = new QToolButton(this); button->setIcon(koIcon("stroke-join-miter")); button->setCheckable(true); button->setToolTip(i18n("Miter join")); joinGroup->addButton(button, Qt::MiterJoin); mainLayout->addWidget(button, 3, 0); button = new QToolButton(this); button->setIcon(koIcon("stroke-join-round")); button->setCheckable(true); button->setToolTip(i18n("Round join")); joinGroup->addButton(button, Qt::RoundJoin); mainLayout->addWidget(button, 3, 1); button = new QToolButton(this); button->setIcon(koIcon("stroke-join-bevel")); button->setCheckable(true); button->setToolTip(i18n("Bevel join")); joinGroup->addButton(button, Qt::BevelJoin); mainLayout->addWidget(button, 3, 2, Qt::AlignLeft); // Miter limit // set min/max/step and value in points, then set actual unit miterLimit = new KisDoubleParseUnitSpinBox(this); miterLimit->setMinMaxStep(0.0, 1000.0, 0.5); miterLimit->setDecimals(2); - miterLimit->setUnit(KoUnit(KoUnit::Point)); miterLimit->setToolTip(i18n("Miter limit")); + miterLimit->setUnitChangeFromOutsideBehavior(false); mainLayout->addWidget(miterLimit, 4, 0, 1, 3); mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); setLayout(mainLayout); } QSize CapNJoinMenu::sizeHint() const { return layout()->sizeHint(); } class Q_DECL_HIDDEN KoStrokeConfigWidget::Private { public: Private() : canvas(0), - active(true) + active(true), + allowLocalUnitManagement(true) { } KoLineStyleSelector *lineStyle; KisDoubleParseUnitSpinBox *lineWidth; KoMarkerSelector *startMarkerSelector; KoMarkerSelector *endMarkerSelector; CapNJoinMenu *capNJoinMenu; QToolButton *colorButton; KoColorPopupAction *colorAction; QWidget *spacer; KoCanvasBase *canvas; bool active; + bool allowLocalUnitManagement; }; KoStrokeConfigWidget::KoStrokeConfigWidget(QWidget * parent) : QWidget(parent) , d(new Private()) { setObjectName("Stroke widget"); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setMargin(0); QHBoxLayout *firstLineLayout = new QHBoxLayout(); // Start marker QList markers; d->startMarkerSelector = new KoMarkerSelector(KoMarkerData::MarkerStart, this); d->startMarkerSelector->updateMarkers(markers); d->startMarkerSelector->setMaximumWidth(50); firstLineLayout->addWidget(d->startMarkerSelector); // Line style d->lineStyle = new KoLineStyleSelector(this); d->lineStyle->setMinimumWidth(70); firstLineLayout->addWidget(d->lineStyle); // End marker d->endMarkerSelector = new KoMarkerSelector(KoMarkerData::MarkerEnd, this); d->endMarkerSelector->updateMarkers(markers); d->endMarkerSelector->setMaximumWidth(50); firstLineLayout->addWidget(d->endMarkerSelector); QHBoxLayout *secondLineLayout = new QHBoxLayout(); // Line width QLabel *l = new QLabel(this); l->setText(i18n("Thickness:")); l->setAlignment(Qt::AlignRight | Qt::AlignVCenter); l->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); secondLineLayout->addWidget(l); // set min/max/step and value in points, then set actual unit d->lineWidth = new KisDoubleParseUnitSpinBox(this); d->lineWidth->setMinMaxStep(0.0, 1000.0, 0.5); d->lineWidth->setDecimals(2); - d->lineWidth->setUnit(KoUnit(KoUnit::Point)); + d->lineWidth->setUnitChangeFromOutsideBehavior(false); d->lineWidth->setToolTip(i18n("Set line width of actual selection")); secondLineLayout->addWidget(d->lineWidth); QToolButton *capNJoinButton = new QToolButton(this); capNJoinButton->setMinimumHeight(25); d->capNJoinMenu = new CapNJoinMenu(this); capNJoinButton->setMenu(d->capNJoinMenu); capNJoinButton->setText("..."); capNJoinButton->setPopupMode(QToolButton::InstantPopup); secondLineLayout->addWidget(capNJoinButton); d->colorButton = new QToolButton(this); secondLineLayout->addWidget(d->colorButton); d->colorAction = new KoColorPopupAction(this); d->colorAction->setIcon(koIcon("format-stroke-color")); d->colorAction->setToolTip(i18n("Change the color of the line/border")); d->colorButton->setDefaultAction(d->colorAction); mainLayout->addLayout(firstLineLayout); mainLayout->addLayout(secondLineLayout); // Spacer d->spacer = new QWidget(); d->spacer->setObjectName("SpecialSpacer"); mainLayout->addWidget(d->spacer); // set sensitive defaults d->lineStyle->setLineStyle(Qt::SolidLine); d->lineWidth->changeValue(1); d->colorAction->setCurrentColor(Qt::black); // Make the signals visible on the outside of this widget. connect(d->lineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(applyChanges())); connect(d->lineWidth, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyChanges())); connect(d->colorAction, SIGNAL(colorChanged(const KoColor &)), this, SLOT(applyChanges())); connect(d->capNJoinMenu->capGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyChanges())); connect(d->capNJoinMenu->joinGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyChanges())); connect(d->capNJoinMenu->miterLimit, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyChanges())); connect(d->startMarkerSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(startMarkerChanged())); connect(d->endMarkerSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(endMarkerChanged())); } KoStrokeConfigWidget::~KoStrokeConfigWidget() { delete d; } // ---------------------------------------------------------------- // getters and setters Qt::PenStyle KoStrokeConfigWidget::lineStyle() const { return d->lineStyle->lineStyle(); } QVector KoStrokeConfigWidget::lineDashes() const { return d->lineStyle->lineDashes(); } qreal KoStrokeConfigWidget::lineWidth() const { return d->lineWidth->value(); } QColor KoStrokeConfigWidget::color() const { return d->colorAction->currentColor(); } qreal KoStrokeConfigWidget::miterLimit() const { return d->capNJoinMenu->miterLimit->value(); } KoMarker *KoStrokeConfigWidget::startMarker() const { return d->startMarkerSelector->marker(); } KoMarker *KoStrokeConfigWidget::endMarker() const { return d->endMarkerSelector->marker(); } Qt::PenCapStyle KoStrokeConfigWidget::capStyle() const { return static_cast(d->capNJoinMenu->capGroup->checkedId()); } Qt::PenJoinStyle KoStrokeConfigWidget::joinStyle() const { return static_cast(d->capNJoinMenu->joinGroup->checkedId()); } KoShapeStroke* KoStrokeConfigWidget::createShapeStroke() const { KoShapeStroke *stroke = new KoShapeStroke(); stroke->setColor(color()); stroke->setLineWidth(lineWidth()); stroke->setCapStyle(capStyle()); stroke->setJoinStyle(joinStyle()); stroke->setMiterLimit(miterLimit()); stroke->setLineStyle(lineStyle(), lineDashes()); return stroke; } // ---------------------------------------------------------------- // Other public functions void KoStrokeConfigWidget::updateControls(KoShapeStrokeModel *stroke, KoMarker *startMarker, KoMarker *endMarker) { blockChildSignals(true); const KoShapeStroke *lineStroke = dynamic_cast(stroke); if (lineStroke) { d->lineWidth->changeValue(lineStroke->lineWidth()); QAbstractButton *button = d->capNJoinMenu->capGroup->button(lineStroke->capStyle()); if (button) { button->setChecked(true); } button = d->capNJoinMenu->joinGroup->button(lineStroke->joinStyle()); if (button) { button->setChecked(true); } d->capNJoinMenu->miterLimit->changeValue(lineStroke->miterLimit()); d->capNJoinMenu->miterLimit->setEnabled(lineStroke->joinStyle() == Qt::MiterJoin); d->lineStyle->setLineStyle(lineStroke->lineStyle(), lineStroke->lineDashes()); d->colorAction->setCurrentColor(lineStroke->color()); } else { d->lineWidth->changeValue(0.0); d->capNJoinMenu->capGroup->button(Qt::FlatCap)->setChecked(true); d->capNJoinMenu->joinGroup->button(Qt::MiterJoin)->setChecked(true); d->capNJoinMenu->miterLimit->changeValue(0.0); d->capNJoinMenu->miterLimit->setEnabled(true); d->lineStyle->setLineStyle(Qt::NoPen, QVector()); } d->startMarkerSelector->setMarker(startMarker); d->endMarkerSelector->setMarker(endMarker); blockChildSignals(false); } void KoStrokeConfigWidget::setUnit(const KoUnit &unit) { + if (!d->allowLocalUnitManagement) { + return; //the unit management is completly transfered to the unitManagers. + } + blockChildSignals(true); KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection *selection = canvasController->canvas()->shapeManager()->selection(); KoShape * shape = selection->firstSelectedShape(); /** * KoStrokeShape knows nothing about the transformations applied * to the shape, which doesn't prevent the shape to apply them and * display the stroke differently. So just take that into account * and show the user correct values using the multiplier in KoUnit. */ KoUnit newUnit(unit); if (shape) { newUnit.adjustByPixelTransform(shape->absoluteTransformation(0)); } d->lineWidth->setUnit(newUnit); d->capNJoinMenu->miterLimit->setUnit(newUnit); blockChildSignals(false); } void KoStrokeConfigWidget::updateMarkers(const QList &markers) { d->startMarkerSelector->updateMarkers(markers); d->endMarkerSelector->updateMarkers(markers); } void KoStrokeConfigWidget::blockChildSignals(bool block) { d->colorAction->blockSignals(block); d->lineWidth->blockSignals(block); d->capNJoinMenu->capGroup->blockSignals(block); d->capNJoinMenu->joinGroup->blockSignals(block); d->capNJoinMenu->miterLimit->blockSignals(block); d->lineStyle->blockSignals(block); d->startMarkerSelector->blockSignals(block); d->endMarkerSelector->blockSignals(block); } void KoStrokeConfigWidget::setActive(bool active) { d->active = active; } +void KoStrokeConfigWidget::setUnitManagers(KisSpinBoxUnitManager* managerLineWidth, + KisSpinBoxUnitManager *managerMitterLimit) +{ + blockChildSignals(true); + + d->allowLocalUnitManagement = false; + + d->lineWidth->setUnitManager(managerLineWidth); + d->capNJoinMenu->miterLimit->setUnitManager(managerMitterLimit); + + blockChildSignals(false); +} + //------------------------ void KoStrokeConfigWidget::applyChanges() { KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection *selection = canvasController->canvas()->shapeManager()->selection(); //FIXME d->canvas->resourceManager()->setActiveStroke( d->stroke ); if (!selection || !selection->count()) { return; } KoShapeStroke *newStroke = new KoShapeStroke(); KoShapeStroke *oldStroke = dynamic_cast( selection->firstSelectedShape()->stroke() ); if (oldStroke) { newStroke->setLineBrush(oldStroke->lineBrush()); } newStroke->setColor(color()); newStroke->setLineWidth(lineWidth()); newStroke->setCapStyle(static_cast(d->capNJoinMenu->capGroup->checkedId())); newStroke->setJoinStyle(static_cast(d->capNJoinMenu->joinGroup->checkedId())); newStroke->setMiterLimit(miterLimit()); newStroke->setLineStyle(lineStyle(), lineDashes()); if (d->active) { KoShapeStrokeCommand *cmd = new KoShapeStrokeCommand(selection->selectedShapes(), newStroke); canvasController->canvas()->addCommand(cmd); } } void KoStrokeConfigWidget::applyMarkerChanges(KoMarkerData::MarkerPosition position) { KoMarker *marker = 0; if (position == KoMarkerData::MarkerStart) { marker = startMarker(); } else if (position == KoMarkerData::MarkerEnd) { marker = endMarker(); } KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection *selection = canvasController->canvas()->shapeManager()->selection(); if (! selection || !selection->count()) { return; } QList shapeList = selection->selectedShapes(); QList pathShapeList; for (QList::iterator itShape = shapeList.begin(); itShape != shapeList.end(); ++itShape) { KoPathShape* pathShape = dynamic_cast(*itShape); if (pathShape) { pathShapeList << pathShape; } } if (pathShapeList.size()) { KoPathShapeMarkerCommand* cmdMarker = new KoPathShapeMarkerCommand(pathShapeList, marker, position); canvasController->canvas()->addCommand(cmdMarker); } } void KoStrokeConfigWidget::startMarkerChanged() { applyMarkerChanges(KoMarkerData::MarkerStart); } void KoStrokeConfigWidget::endMarkerChanged() { applyMarkerChanges(KoMarkerData::MarkerEnd); } // ---------------------------------------------------------------- void KoStrokeConfigWidget::selectionChanged() { // see a comment in setUnit() setUnit(d->canvas->unit()); KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection *selection = canvasController->canvas()->shapeManager()->selection(); KoShape * shape = selection->firstSelectedShape(); if (shape && shape->stroke()) { KoPathShape *pathShape = dynamic_cast(shape); if (pathShape) { updateControls(shape->stroke(), pathShape->marker(KoMarkerData::MarkerStart), - pathShape->marker(KoMarkerData::MarkerEnd)); + pathShape->marker(KoMarkerData::MarkerEnd)); } else { updateControls(shape->stroke(), 0 ,0); } } } void KoStrokeConfigWidget::setCanvas( KoCanvasBase *canvas ) { if (canvas) { connect(canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); connect(canvas->shapeManager(), SIGNAL(selectionContentChanged()), this, SLOT(selectionChanged())); connect(canvas->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), this, SLOT(canvasResourceChanged(int, const QVariant &))); setUnit(canvas->unit()); KoDocumentResourceManager *resourceManager = canvas->shapeController()->resourceManager(); if (resourceManager) { KoMarkerCollection *collection = resourceManager->resource(KoDocumentResourceManager::MarkerCollection).value(); if (collection) { updateMarkers(collection->markers()); } } } d->canvas = canvas; } void KoStrokeConfigWidget::canvasResourceChanged(int key, const QVariant &value) { switch (key) { case KoCanvasResourceManager::Unit: setUnit(value.value()); break; } } diff --git a/libs/widgets/KoStrokeConfigWidget.h b/libs/widgets/KoStrokeConfigWidget.h index 129a074c19..18b8b27f08 100644 --- a/libs/widgets/KoStrokeConfigWidget.h +++ b/libs/widgets/KoStrokeConfigWidget.h @@ -1,104 +1,108 @@ /* This file is part of the KDE project * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) * Copyright (C) 2002 Tomislav Lukman * Copyright (C) 2002 Rob Buis * Copyright (C) 2004 Laurent Montel * Copyright (C) 2005-2006 Tim Beaulen * Copyright (C) 2005 Inge Wallin * Copyright (C) 2005, 2011 Thomas Zander * Copyright (C) 2005-2008 Jan Hambrecht * Copyright (C) 2006 C. Boemann * Copyright (C) 2011 Jean-Nicolas Artaud * Copyright (C) 2011 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef STROKECONFIGWIDGET_H #define STROKECONFIGWIDGET_H #include "kritawidgets_export.h" #include #include class KoUnit; class KoShapeStrokeModel; class KoMarker; class KoCanvasBase; class KoShapeStroke; +class KisSpinBoxUnitManager; + /// A widget for configuring the stroke of a shape class KRITAWIDGETS_EXPORT KoStrokeConfigWidget : public QWidget { Q_OBJECT public: explicit KoStrokeConfigWidget(QWidget *parent); ~KoStrokeConfigWidget(); // Getters Qt::PenStyle lineStyle() const; QVector lineDashes() const; qreal lineWidth() const; QColor color() const; qreal miterLimit() const; KoMarker *startMarker() const; KoMarker *endMarker() const; Qt::PenCapStyle capStyle() const; Qt::PenJoinStyle joinStyle() const; /** * Creates KoShapeStroke object filled with the options * configured by the widget. The caller is in charge of * deletion of the returned object */ KoShapeStroke* createShapeStroke() const; void setCanvas(KoCanvasBase *canvas); void setActive(bool active); + void setUnitManagers(KisSpinBoxUnitManager* managerLineWidth, KisSpinBoxUnitManager* managerMitterLimit); + private Q_SLOTS: void updateControls(KoShapeStrokeModel *stroke, KoMarker *startMarker, KoMarker *endMarker); void updateMarkers(const QList &markers); /// start marker has changed void startMarkerChanged(); /// end marker has changed void endMarkerChanged(); void canvasResourceChanged(int key, const QVariant &value); /// selection has changed void selectionChanged(); /// apply line changes to the selected shape void applyChanges(); private: void setUnit(const KoUnit &unit); /// apply marker changes to the selected shape void applyMarkerChanges(KoMarkerData::MarkerPosition position); void blockChildSignals(bool block); private: class Private; Private * const d; }; #endif // SHADOWCONFIGWIDGET_H diff --git a/libs/widgets/kis_double_parse_spin_box.cpp b/libs/widgets/kis_double_parse_spin_box.cpp index 801d9cca2f..5b9551ee86 100644 --- a/libs/widgets/kis_double_parse_spin_box.cpp +++ b/libs/widgets/kis_double_parse_spin_box.cpp @@ -1,235 +1,240 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * 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 2 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. */ #include "kis_double_parse_spin_box.h" #include "kis_num_parser.h" #include #include #include #include #include #include // for qIsNaN KisDoubleParseSpinBox::KisDoubleParseSpinBox(QWidget *parent) : QDoubleSpinBox(parent), boolLastValid(true), lastExprParsed(QStringLiteral("0.0")) { connect(this, SIGNAL(noMoreParsingError()), this, SLOT(clearErrorStyle())); //hack to let the clearError be called, even if the value changed method is the one from QDoubleSpinBox. connect(this, SIGNAL(valueChanged(double)), this, SLOT(clearError())); connect(this, SIGNAL(errorWhileParsing(QString)), this, SLOT(setErrorStyle())); oldValue = value(); warningIcon = new QLabel(this); if (QFile(":/./16_light_warning.svg").exists()) { warningIcon->setPixmap(QIcon(":/./16_light_warning.svg").pixmap(16, 16)); } else { warningIcon->setText("!"); } warningIcon->setStyleSheet("background:transparent;"); warningIcon->move(1, 1); warningIcon->setVisible(false); isOldPaletteSaved = false; areOldMarginsSaved = false; } KisDoubleParseSpinBox::~KisDoubleParseSpinBox() { } double KisDoubleParseSpinBox::valueFromText(const QString & text) const { lastExprParsed = text; bool ok; double ret; if ( (suffix().isEmpty() || !text.endsWith(suffix())) && (prefix().isEmpty() || !text.startsWith(prefix())) ) { ret = KisNumericParser::parseSimpleMathExpr(text, &ok); } else { QString expr = text; if (text.endsWith(suffix())) { expr.remove(text.size()-suffix().size(), suffix().size()); } if(text.startsWith(prefix())){ expr.remove(0, prefix().size()); } lastExprParsed = expr; ret = KisNumericParser::parseSimpleMathExpr(expr, &ok); } if(qIsNaN(ret) || qIsInf(ret)){ ok = false; } if (!ok) { if (boolLastValid) { oldValue = value(); } boolLastValid = false; ret = oldValue; //in case of error set to minimum. } else { if (!boolLastValid) { oldValue = ret; } boolLastValid = true; } return ret; } QString KisDoubleParseSpinBox::textFromValue(double val) const { if (!boolLastValid) { emit errorWhileParsing(lastExprParsed); return lastExprParsed; } emit noMoreParsingError(); - double v = KisNumericParser::parseSimpleMathExpr(cleanText()); + double v = KisNumericParser::parseSimpleMathExpr(veryCleanText()); v = QString("%1").arg(v, 0, 'f', decimals()).toDouble(); - if (hasFocus() && (v == value() || (v >= maximum() && value() == maximum()) || (v <= minimum() && value() == minimum())) ) { //solve a very annoying bug where the formula can collapse while editing. With this trick the formula is not lost until focus is lost. - return cleanText(); + if (hasFocus() && (v == value() || (v > maximum() && value() == maximum()) || (v < minimum() && value() == minimum())) ) { //solve a very annoying bug where the formula can collapse while editing. With this trick the formula is not lost until focus is lost. + return veryCleanText(); } return QDoubleSpinBox::textFromValue(val); } +QString KisDoubleParseSpinBox::veryCleanText() const +{ + return cleanText(); +} + QValidator::State KisDoubleParseSpinBox::validate ( QString & input, int & pos ) const { Q_UNUSED(input); Q_UNUSED(pos); return QValidator::Acceptable; } void KisDoubleParseSpinBox::stepBy(int steps) { boolLastValid = true; //reset to valid state so we can use the up and down buttons. emit noMoreParsingError(); QDoubleSpinBox::stepBy(steps); } void KisDoubleParseSpinBox::setValue(double value) { if(value == oldValue && hasFocus()){ //avoid to reset the button when it set the value of something that will recall this slot. return; } if (!hasFocus()) { clearError(); } QDoubleSpinBox::setValue(value); } void KisDoubleParseSpinBox::setErrorStyle() { if (!boolLastValid) { //setStyleSheet(_oldStyleSheet + "Background: red; color: white; padding-left: 18px;"); if (!isOldPaletteSaved) { oldPalette = palette(); } isOldPaletteSaved = true; QPalette nP = oldPalette; nP.setColor(QPalette::Background, Qt::red); nP.setColor(QPalette::Base, Qt::red); nP.setColor(QPalette::Text, Qt::white); setPalette(nP); if (!areOldMarginsSaved) { oldMargins = lineEdit()->textMargins(); } areOldMarginsSaved = true; if (width() - height() >= 3*height()) { //if we have twice as much place as needed by the warning icon then display it. QMargins newMargins = oldMargins; newMargins.setLeft( newMargins.left() + height() - 4 ); lineEdit()->setTextMargins(newMargins); int h = warningIcon->height(); int hp = height()-2; if (h != hp) { warningIcon->resize(hp, hp); if (QFile(":/./16_light_warning.svg").exists()) { warningIcon->setPixmap(QIcon(":/./16_light_warning.svg").pixmap(hp-7, hp-7)); } } warningIcon->move(oldMargins.left()+4, 1); warningIcon->setVisible(true); } } } void KisDoubleParseSpinBox::clearErrorStyle() { if (boolLastValid) { warningIcon->setVisible(false); //setStyleSheet(QString()); setPalette(oldPalette); isOldPaletteSaved = false; lineEdit()->setTextMargins(oldMargins); areOldMarginsSaved = false; } } void KisDoubleParseSpinBox::clearError() { boolLastValid = true; emit noMoreParsingError(); oldValue = value(); clearErrorStyle(); } diff --git a/libs/widgets/kis_double_parse_spin_box.h b/libs/widgets/kis_double_parse_spin_box.h index 29085af972..bc3310f5e6 100644 --- a/libs/widgets/kis_double_parse_spin_box.h +++ b/libs/widgets/kis_double_parse_spin_box.h @@ -1,82 +1,85 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * 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 2 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. */ #ifndef KISDOUBLEPARSESPINBOX_H #define KISDOUBLEPARSESPINBOX_H #include #include "kritawidgets_export.h" class QLabel; /*! * \brief The KisDoubleParseSpinBox class is a cleverer doubleSpinBox, able to parse arithmetic expressions. * * Use this spinbox instead of the basic one from Qt if you want it to be able to parse arithmetic expressions. */ class KRITAWIDGETS_EXPORT KisDoubleParseSpinBox : public QDoubleSpinBox { Q_OBJECT public: KisDoubleParseSpinBox(QWidget* parent = 0); - ~KisDoubleParseSpinBox(); + virtual ~KisDoubleParseSpinBox(); //KisDoubleParseSpinBox may be used polymorphycally as a QDoubleSpinBox. virtual double valueFromText(const QString & text) const; virtual QString textFromValue(double val) const; virtual QValidator::State validate ( QString & input, int & pos ) const; virtual void stepBy(int steps); void setValue(double value); //polymorphism won't work directly, we use a signal/slot hack to do so but if signals are disabled this function will still be useful. bool isLastValid() const{ return boolLastValid; } + //! \brief this virtual function is similar to cleanText(); for KisDoubleParseSpinBox. But child class may remove additional artifacts. + virtual QString veryCleanText() const; + Q_SIGNALS: //! \brief signal emmitted when the last parsed expression create an error. void errorWhileParsing(QString expr) const; //! \brief signal emmitted when the last parsed expression is valid. void noMoreParsingError() const; public Q_SLOTS: //! \brief useful to let the widget change it's stylesheet when an error occured in the last expression. void setErrorStyle(); //! \brief useful to let the widget reset it's stylesheet when there's no more error. void clearErrorStyle(); //! \brief say the widget to return to an error free state. void clearError(); protected: mutable bool boolLastValid; mutable double oldValue; mutable QString lastExprParsed; QLabel* warningIcon; QPalette oldPalette; bool isOldPaletteSaved; QMargins oldMargins; bool areOldMarginsSaved; }; #endif // KISDOUBLEPARSESPINBOX_H diff --git a/libs/widgets/kis_double_parse_unit_spin_box.cpp b/libs/widgets/kis_double_parse_unit_spin_box.cpp index 47c10515dc..21d05ad526 100644 --- a/libs/widgets/kis_double_parse_unit_spin_box.cpp +++ b/libs/widgets/kis_double_parse_unit_spin_box.cpp @@ -1,172 +1,422 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * 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 2 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. */ #include "kis_double_parse_unit_spin_box.h" +#include "kis_spin_box_unit_manager.h" +#include class Q_DECL_HIDDEN KisDoubleParseUnitSpinBox::Private { public: - Private(double low, double up, double step) + Private(double low, double up, double step, KisSpinBoxUnitManager* unitManager) : lowerInPoints(low), - upperInPoints(up), - stepInPoints(step), - unit(KoUnit(KoUnit::Point)) + upperInPoints(up), + stepInPoints(step), + unit(KoUnit(KoUnit::Point)), + outPutSymbol(""), + unitManager(unitManager), + defaultUnitManager(unitManager), + isDeleting(false), + unitHasBeenChangedFromOutSideOnce(false), + letUnitBeChangedFromOutsideMoreThanOnce(true), + displayUnit(true) { } double lowerInPoints; ///< lowest value in points double upperInPoints; ///< highest value in points double stepInPoints; ///< step in points KoUnit unit; + + double previousValueInPoint; ///< allow to store the previous value in point, usefull in some cases, even if, usually, we prefere to refer to the actual value (in selected unit) and convert it, since this is not alway updated. + QString previousSymbol; + QString outPutSymbol; + + KisSpinBoxUnitManager* unitManager; //manage more units than permitted by KoUnit. + KisSpinBoxUnitManager* defaultUnitManager; //the default unit manager is the one the spinbox rely on and go back to if a connected unit manager is destroyed before the spinbox. + + bool isDeleting; + + bool unitHasBeenChangedFromOutSideOnce; //in some part of the code the unit is reset. We want to prevent this overriding the unit defined by the user. We use this switch to do so. + bool letUnitBeChangedFromOutsideMoreThanOnce; + + bool displayUnit; + }; KisDoubleParseUnitSpinBox::KisDoubleParseUnitSpinBox(QWidget *parent) : KisDoubleParseSpinBox(parent), - d(new Private(-9999, 9999, 1)) + d(new Private(-9999, 9999, 1, KisSpinBoxUnitManagerFactory::buildDefaultUnitManager(this))) { setUnit( KoUnit(KoUnit::Point) ); setAlignment( Qt::AlignRight ); - connect(this, SIGNAL(valueChanged( double )), SLOT(privateValueChanged())); + connect(this, SIGNAL(valueChanged( double )), this, SLOT(privateValueChanged())); + connect(lineEdit(), SIGNAL(textChanged(QString)), + this, SLOT(detectUnitChanges()) ); + + connect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); + connect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); + } KisDoubleParseUnitSpinBox::~KisDoubleParseUnitSpinBox() { + d->isDeleting = true; + delete d->defaultUnitManager; delete d; } +void KisDoubleParseUnitSpinBox::setUnitManager(KisSpinBoxUnitManager* unitManager) +{ + qreal oldVal = d->unitManager->getReferenceValue(KisDoubleParseSpinBox::value()); + QString oldSymbol = d->unitManager->getApparentUnitSymbol(); + + qreal newVal; + + double newMin; + double newMax; + double newStep; + + if (oldSymbol == unitManager->getApparentUnitSymbol() && + d->unitManager->getUnitDimensionType() == unitManager->getUnitDimensionType()) + { + d->unitManager = unitManager; //set the new unitmanager anyway, since it may be a subclass, so change the behavior anyway. + goto connect_signals; + } + + if (d->unitManager->getUnitDimensionType() == unitManager->getUnitDimensionType()) { + //dimension is the same, calculate the new value + newVal = unitManager->getApparentValue(oldVal); + } else { + newVal = unitManager->getApparentValue(d->lowerInPoints); + } + + newMin = unitManager->getApparentValue(d->lowerInPoints); + newMax = unitManager->getApparentValue(d->upperInPoints); + newStep = unitManager->getApparentValue(d->stepInPoints); + + if (unitManager->getApparentUnitSymbol() == KoUnit(KoUnit::Pixel).symbol()) { + // limit the pixel step by 1.0 + newStep = qMax(qreal(1.0), newStep); + } + + KisDoubleParseSpinBox::setMinimum(newMin); + KisDoubleParseSpinBox::setMaximum(newMax); + KisDoubleParseSpinBox::setSingleStep(newStep); + +connect_signals: + + if (d->unitManager != d->defaultUnitManager) { + disconnect(d->unitManager, &QObject::destroyed, + this, &KisDoubleParseUnitSpinBox::disconnectExternalUnitManager); //there's no dependance anymore. + } + disconnect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); + disconnect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); + + d->unitManager = unitManager; + + connect(d->unitManager, &QObject::destroyed, + this, &KisDoubleParseUnitSpinBox::disconnectExternalUnitManager); + + + connect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); + connect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); + + KisDoubleParseSpinBox::setValue(newVal); +} + void KisDoubleParseUnitSpinBox::changeValue( double newValue ) { - if (d->unit.toUserValue(newValue) == oldValue) { + double apparentValue; + double fact; + double cons; + + if (d->outPutSymbol.isEmpty()) { + apparentValue = d->unitManager->getApparentValue(newValue); + } else { + + fact = d->unitManager->getConversionFactor(d->unitManager->getUnitDimensionType(), d->outPutSymbol); + cons = d->unitManager->getConversionConstant(d->unitManager->getUnitDimensionType(), d->outPutSymbol); + + apparentValue = fact*newValue + cons; + } + + if (apparentValue == KisDoubleParseSpinBox::value()) { return; } - KisDoubleParseSpinBox::setValue( d->unit.toUserValue(newValue) ); + if (d->outPutSymbol.isEmpty()) { + KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue(newValue) ); + } else { + + KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue((newValue - cons)/fact) ); + } } void KisDoubleParseUnitSpinBox::setUnit( const KoUnit & unit) { - if( unit == d->unit) return; - double oldValue = d->unit.fromUserValue( KisDoubleParseSpinBox::value() ); + if (d->unitHasBeenChangedFromOutSideOnce && !d->letUnitBeChangedFromOutsideMoreThanOnce) { + return; + } - KisDoubleParseSpinBox::setMinimum( unit.toUserValue( d->lowerInPoints ) ); - KisDoubleParseSpinBox::setMaximum( unit.toUserValue( d->upperInPoints ) ); + if (d->unitManager->getUnitDimensionType() != KisSpinBoxUnitManager::LENGTH) { + d->unitManager->setUnitDimension(KisSpinBoxUnitManager::LENGTH); //setting the unit using a KoUnit mean you want to use a length. + } - qreal step = unit.toUserValue( d->stepInPoints ); + setUnit(unit.symbol()); + d->unit = unit; +} +void KisDoubleParseUnitSpinBox::setUnit(const QString &symbol) +{ + d->unitManager->setApparentUnitFromSymbol(symbol); //via signals and slots, the correct functions should be called. +} +void KisDoubleParseUnitSpinBox::setReturnUnit(const QString & symbol) +{ + d->outPutSymbol = symbol; +} + +void KisDoubleParseUnitSpinBox::prepareUnitChange() { + + d->previousValueInPoint = d->unitManager->getReferenceValue(KisDoubleParseSpinBox::value()); + d->previousSymbol = d->unitManager->getApparentUnitSymbol(); + +} + +void KisDoubleParseUnitSpinBox::internalUnitChange(const QString &symbol) { - if (unit.type() == KoUnit::Pixel) { + //d->unitManager->setApparentUnitFromSymbol(symbol); + + if (d->unitManager->getApparentUnitSymbol() == d->previousSymbol) { //the setApparentUnitFromSymbol is a bit clever, for example in regard of Casesensitivity. So better check like this. + return; + } + + KisDoubleParseSpinBox::setMinimum( d->unitManager->getApparentValue( d->lowerInPoints ) ); + KisDoubleParseSpinBox::setMaximum( d->unitManager->getApparentValue( d->upperInPoints ) ); + + qreal step = d->unitManager->getApparentValue( d->stepInPoints ); + + if (symbol == KoUnit(KoUnit::Pixel).symbol()) { // limit the pixel step by 1.0 step = qMax(qreal(1.0), step); } KisDoubleParseSpinBox::setSingleStep( step ); - d->unit = unit; - KisDoubleParseSpinBox::setValue( KoUnit::ptToUnit( oldValue, unit ) ); - setSuffix( unit.symbol().prepend(QLatin1Char(' ')) ); + KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue( d->previousValueInPoint ) ); + + d->unitHasBeenChangedFromOutSideOnce = true; +} + +void KisDoubleParseUnitSpinBox::setDimensionType(int dim) +{ + if (!KisSpinBoxUnitManager::isUnitId(dim)) { + return; + } + + d->unitManager->setUnitDimension((KisSpinBoxUnitManager::UnitDimension) dim); } double KisDoubleParseUnitSpinBox::value( ) const { - return d->unit.fromUserValue( KisDoubleParseSpinBox::value() ); + if (d->outPutSymbol.isEmpty()) { + return d->unitManager->getReferenceValue( KisDoubleParseSpinBox::value() ); + } + + double ref = d->unitManager->getReferenceValue( KisDoubleParseSpinBox::value() ); + double fact = d->unitManager->getConversionFactor(d->unitManager->getUnitDimensionType(), d->outPutSymbol); + double cons = d->unitManager->getConversionConstant(d->unitManager->getUnitDimensionType(), d->outPutSymbol); + + return fact*ref + cons; } void KisDoubleParseUnitSpinBox::setMinimum(double min) { d->lowerInPoints = min; - KisDoubleParseSpinBox::setMinimum( d->unit.toUserValue( min ) ); + KisDoubleParseSpinBox::setMinimum( d->unitManager->getApparentValue( min ) ); } void KisDoubleParseUnitSpinBox::setMaximum(double max) { d->upperInPoints = max; - KisDoubleParseSpinBox::setMaximum( d->unit.toUserValue( max ) ); + KisDoubleParseSpinBox::setMaximum( d->unitManager->getApparentValue( max ) ); } void KisDoubleParseUnitSpinBox::setLineStep(double step) { - d->stepInPoints = KoUnit(KoUnit::Point).toUserValue(step); + d->stepInPoints = d->unitManager->getReferenceValue(step); KisDoubleParseSpinBox::setSingleStep( step ); } void KisDoubleParseUnitSpinBox::setLineStepPt(double step) { d->stepInPoints = step; - KisDoubleParseSpinBox::setSingleStep( d->unit.toUserValue( step ) ); + KisDoubleParseSpinBox::setSingleStep( d->unitManager->getApparentValue( step ) ); } void KisDoubleParseUnitSpinBox::setMinMaxStep( double min, double max, double step ) { setMinimum( min ); setMaximum( max ); setLineStepPt( step ); } QValidator::State KisDoubleParseUnitSpinBox::validate(QString &input, int &pos) const { Q_UNUSED(pos); QRegExp regexp ("([ a-zA-Z]+)$"); // Letters or spaces at end const int res = input.indexOf( regexp ); - if ( res == -1 ) { + /*if ( res == -1 ) { // Nothing like an unit? The user is probably editing the unit return QValidator::Intermediate; - } + }*/ - QString expr ( input.left( res ) ); - const QString unitName ( regexp.cap( 1 ).trimmed().toLower() ); + QString expr ( (res > 0) ? input.left( res ) : input ); + const QString unitName ( (res > 0) ? regexp.cap( 1 ).trimmed().toLower() : "" ); bool ok = true; bool interm = false; QValidator::State exprState = KisDoubleParseSpinBox::validate(expr, pos); + if (res < 0) { + return exprState; + } + if (exprState == QValidator::Invalid) { return exprState; } else if (exprState == QValidator::Intermediate) { interm = true; } //check if we can parse the unit. - KoUnit::fromSymbol(unitName, &ok); + QStringList listOfSymbol = d->unitManager->getsUnitSymbolList(); + ok = listOfSymbol.contains(unitName); if (!ok || interm) { return QValidator::Intermediate; } return QValidator::Acceptable; } QString KisDoubleParseUnitSpinBox::textFromValue( double value ) const { - return KisDoubleParseSpinBox::textFromValue(value); + QString txt = KisDoubleParseSpinBox::textFromValue(value); + if (d->displayUnit) { + if (!txt.endsWith(d->unitManager->getApparentUnitSymbol())) { + txt += " " + d->unitManager->getApparentUnitSymbol(); + } + } + return txt; +} + +QString KisDoubleParseUnitSpinBox::veryCleanText() const +{ + + return makeTextClean(cleanText()); + } double KisDoubleParseUnitSpinBox::valueFromText( const QString& str ) const { - //KisDoubleParseSpinBox is supposed to remove the suffix and prefix by itself. - return KisDoubleParseSpinBox::valueFromText(str); + + QString txt = makeTextClean(str); + + return KisDoubleParseSpinBox::valueFromText(txt); //this function will take care of prefix (and don't mind if suffix has been removed. +} + +void KisDoubleParseUnitSpinBox::setUnitChangeFromOutsideBehavior(bool toggle) { + d->letUnitBeChangedFromOutsideMoreThanOnce = toggle; +} + +void KisDoubleParseUnitSpinBox::setDisplayUnit(bool toggle) { + + d->displayUnit = toggle; + } void KisDoubleParseUnitSpinBox::privateValueChanged() { emit valueChangedPt( value() ); } + +QString KisDoubleParseUnitSpinBox::detectUnit() +{ + QString str = veryCleanText().trimmed(); //text with the new unit but not the old one. + + QRegExp regexp ("([ ]*[a-zA-Z]+[ ]*)$"); // Letters or spaces at end + int res = str.indexOf( regexp ); + + if (res > -1) { + QString expr ( str.right( str.size() - res ) ); + expr = expr.trimmed(); + return expr; + } + + return ""; +} + +void KisDoubleParseUnitSpinBox::detectUnitChanges() +{ + QString unitSymb = detectUnit(); + + if (unitSymb.isEmpty()) { + return; + } + + QString oldUnitSymb = d->unitManager->getApparentUnitSymbol(); + + setUnit(unitSymb); + setValue(valueFromText(cleanText())); //change value keep the old value, but converted to new unit... which is different from the value the user entered in the new unit. So we need to set the new value. + + if (oldUnitSymb != d->unitManager->getApparentUnitSymbol()) { + // the user has changed the unit, so we block changes from outside. + setUnitChangeFromOutsideBehavior(false); + } +} + +QString KisDoubleParseUnitSpinBox::makeTextClean(QString const& txt) const +{ + QString expr = txt; + QString symbol = d->unitManager->getApparentUnitSymbol(); + + if ( expr.endsWith(suffix()) ) { + expr.remove(expr.size()-suffix().size(), suffix().size()); + } + + expr = expr.trimmed(); + + if ( expr.endsWith(symbol) ) { + expr.remove(expr.size()-symbol.size(), symbol.size()); + } + + return expr.trimmed(); +} + +void KisDoubleParseUnitSpinBox::disconnectExternalUnitManager() +{ + if (!d->isDeleting) + { + setUnitManager(d->defaultUnitManager); //go back to default unit manager. + } +} diff --git a/libs/widgets/kis_double_parse_unit_spin_box.h b/libs/widgets/kis_double_parse_unit_spin_box.h index 83094f8e85..a8efac06df 100644 --- a/libs/widgets/kis_double_parse_unit_spin_box.h +++ b/libs/widgets/kis_double_parse_unit_spin_box.h @@ -1,100 +1,137 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * 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 2 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. */ #ifndef KIS_DOUBLEPARSEUNITSPINBOX_H #define KIS_DOUBLEPARSEUNITSPINBOX_H #include #include "kis_double_parse_spin_box.h" #include "kritawidgets_export.h" +class KisSpinBoxUnitManager; + /*! * \brief The KisDoubleParseUnitSpinBox class is an evolution of the \see KoUnitDoubleSpinBox, but inherit from \see KisDoubleParseSpinBox to be able to parse math expressions. * * This class store the */ class KRITAWIDGETS_EXPORT KisDoubleParseUnitSpinBox : public KisDoubleParseSpinBox { Q_OBJECT public: KisDoubleParseUnitSpinBox(QWidget* parent = 0); - ~KisDoubleParseUnitSpinBox(); + virtual ~KisDoubleParseUnitSpinBox(); + + void setUnitManager(KisSpinBoxUnitManager* unitManager); /** - * Set the new value in points which will then be converted to the current unit for display + * Set the new value in points (or other reference unit) which will then be converted to the current unit for display * @param newValue the new value * @see value() */ virtual void changeValue( double newValue ); + /** * This spinbox shows the internal value after a conversion to the unit set here. */ virtual void setUnit(const KoUnit &unit); + virtual void setUnit(const QString & symbol); + /*! + * \brief setReturnUnit set a unit, such that the spinbox now return values in this unit instead of the reference unit for the current dimension. + * \param symbol the symbol of the new unit. + */ + void setReturnUnit(const QString & symbol); + + /** + * @brief setDimensionType set the dimension (for example length or angle) of the units the spinbox manage + * @param dim the dimension id. (if not an id in KisSpinBoxUnitManager::UnitDimension, then the function does nothing). + */ + virtual void setDimensionType(int dim); /// @return the current value, converted in points double value( ) const; /// Set minimum value in points. void setMinimum(double min); /// Set maximum value in points. void setMaximum(double max); /// Set step size in the current unit. void setLineStep(double step); /// Set step size in points. void setLineStepPt(double step); /// Set minimum, maximum value and the step size (all in points) void setMinMaxStep( double min, double max, double step ); /// reimplemented from superclass, will forward to KoUnitDoubleValidator virtual QValidator::State validate(QString &input, int &pos) const; /** * Transform the double in a nice text, using locale symbols * @param value the number as double * @return the resulting string */ virtual QString textFromValue( double value ) const; + + //! \brief get the text in the spinbox without prefix or suffix, and remove unit symbol if present. + virtual QString veryCleanText() const; + /** * Transfrom a string into a double, while taking care of locale specific symbols. * @param str the string to transform into a number * @return the value as double */ virtual double valueFromText( const QString& str ) const; + void setUnitChangeFromOutsideBehavior(bool toggle); //if set to false, setting the unit using KoUnit won't have any effect. + + //! \brief display the unit symbol in the spinbox or not. For example if the unit is displayed in a combobox connected to the unit manager. + void setDisplayUnit(bool toggle); + Q_SIGNALS: - /// emitted like valueChanged in the parent, but this one emits the point value + /// emitted like valueChanged in the parent, but this one emits the point value, or converted to another reference unit. void valueChangedPt( qreal ); private: class Private; Private * const d; + QString detectUnit(); + QString makeTextClean(QString const& txt) const; + + //thoses functions are usefull to sync the spinbox with it's unitmanager. + //! \brief change the unit, reset the spin box everytime. From the outside it's alway set unit that should be called. + void internalUnitChange(QString const& symbol); + void prepareUnitChange(); + private Q_SLOTS: // exists to do emits for valueChangedPt void privateValueChanged(); + void detectUnitChanges(); + void disconnectExternalUnitManager(); + }; #endif // KIS_DOUBLEPARSEUNITSPINBOX_H diff --git a/libs/widgetutils/CMakeLists.txt b/libs/widgetutils/CMakeLists.txt index ede32a1558..a8577f39f3 100644 --- a/libs/widgetutils/CMakeLists.txt +++ b/libs/widgetutils/CMakeLists.txt @@ -1,128 +1,130 @@ add_subdirectory(tests) configure_file(xmlgui/config-xmlgui.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-xmlgui.h ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/config) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/xmlgui) set(kritawidgetutils_LIB_SRCS WidgetUtilsDebug.cpp kis_icon_utils.cpp kis_action_registry.cpp KisActionsSnapshot.cpp KoGroupButton.cpp KoProgressBar.cpp KoProgressUpdater.cpp KoUpdater.cpp KoUpdaterPrivate_p.cpp KoProperties.cpp KoFileDialog.cpp KoResourcePaths.cpp kis_num_parser.cpp + kis_spin_box_unit_manager.cpp config/kcolorscheme.cpp config/kcolorschememanager.cpp config/khelpclient.cpp config/klanguagebutton.cpp config/krecentfilesaction.cpp config/kstandardaction.cpp xmlgui/KisShortcutsEditorItem.cpp xmlgui/KisShortcutEditWidget.cpp xmlgui/KisShortcutsEditorDelegate.cpp xmlgui/KisShortcutsDialog.cpp xmlgui/KisShortcutsDialog_p.cpp xmlgui/KisShortcutsEditor.cpp xmlgui/KisShortcutsEditor_p.cpp xmlgui/kshortcutschemeseditor.cpp xmlgui/kshortcutschemeshelper.cpp xmlgui/kaboutkdedialog_p.cpp xmlgui/kactioncategory.cpp xmlgui/kactioncollection.cpp xmlgui/kactionconflictdetector.cpp xmlgui/kbugreport.cpp xmlgui/kcheckaccelerators.cpp xmlgui/kedittoolbar.cpp xmlgui/kgesture.cpp xmlgui/kgesturemap.cpp xmlgui/khelpmenu.cpp xmlgui/kkeysequencewidget.cpp xmlgui/kmainwindow.cpp xmlgui/kmenumenuhandler_p.cpp xmlgui/kshortcutwidget.cpp xmlgui/kswitchlanguagedialog_p.cpp xmlgui/ktoggletoolbaraction.cpp xmlgui/ktoolbar.cpp xmlgui/ktoolbarhandler.cpp xmlgui/kundoactions.cpp xmlgui/kxmlguibuilder.cpp xmlgui/kxmlguiclient.cpp xmlgui/kxmlguifactory.cpp xmlgui/kxmlguifactory_p.cpp xmlgui/kxmlguiversionhandler.cpp xmlgui/kxmlguiwindow.cpp ) if (HAVE_DBUS) set(kritawidgetutils_LIB_SRCS ${kritawidgetutils_LIB_SRCS} xmlgui/kmainwindowiface.cpp ) endif() ki18n_wrap_ui(kritawidgetutils_LIB_SRCS xmlgui/KisShortcutsDialog.ui xmlgui/kshortcutwidget.ui ) qt5_add_resources(kritawidgetutils_LIB_SRCS xmlgui/kxmlgui.qrc) add_library(kritawidgetutils SHARED ${kritawidgetutils_LIB_SRCS}) target_include_directories(kritawidgetutils PUBLIC $ $ ) generate_export_header(kritawidgetutils BASE_NAME kritawidgetutils) if (HAVE_DBUS) set (KRITA_WIDGET_UTILS_EXTRA_LIBS ${KRITA_WIDGET_UTILS_EXTRA_LIBS} Qt5::DBus) endif () if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) set(KRITA_WIDGET_UTILS_EXTRA_LIBS ${KRITA_WIDGET_UTILS_EXTRA_LIBS} ${FOUNDATION_LIBRARY}) endif () target_link_libraries(kritawidgetutils PUBLIC Qt5::Widgets Qt5::Gui Qt5::Xml Qt5::Core KF5::ItemViews kritaglobal PRIVATE Qt5::PrintSupport KF5::I18n KF5::ConfigCore KF5::CoreAddons KF5::ConfigGui KF5::GuiAddons KF5::WidgetsAddons KF5::WindowSystem kritaplugin + kritaodf ${KRITA_WIDGET_UTILS_EXTRA_LIBS} ) set_target_properties(kritawidgetutils PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritawidgetutils ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/widgetutils/kis_num_parser.cpp b/libs/widgetutils/kis_num_parser.cpp index 262c4cf404..99576c9c4c 100644 --- a/libs/widgetutils/kis_num_parser.cpp +++ b/libs/widgetutils/kis_num_parser.cpp @@ -1,567 +1,574 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * 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 2 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. */ #include "kis_num_parser.h" #include // for qIsNaN #include #include #include #include #include +#include #include using namespace std; const QVector opLevel1 = {'+', '-'}; const QVector opLevel2 = {'*', '/'}; const QStringList supportedFuncs = {"", "cos", "sin", "tan", "acos", "asin", "atan", "exp", "ln", "log10", "abs"}; const QRegExp funcExpr("(-)?([a-zA-Z]*[0-9]*)?\\((.+)\\)"); const QRegExp numberExpr("(-)?([0-9]+\\.?[0-9]*(e[0-9]*)?)"); const QRegExp funcExprInteger("(-)?\\((.+)\\)"); const QRegExp integerExpr("(-)?([0-9]+)"); //double functions double treatFuncs(QString const& expr, bool & noProblem); double treatLevel1(QString const& expr, bool & noProblem); double treatLevel2(QString const& expr, bool & noProblem); double treatLevel3(QString const& expr, bool & noProblem); //int functions double treatLevel1Int(QString const& expr, bool & noProblem); double treatLevel2Int(QString const& expr, bool & noProblem); double treatFuncsInt(QString const& expr, bool & noProblem); namespace KisNumericParser { /*! * \param expr the expression to parse * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise. * \return the numerical value the expression eval to (or 0 in case of error). */ double parseSimpleMathExpr(const QString &expr, bool *noProblem) { bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided). //then go down each 3 levels of operation priority. if (noProblem != nullptr) { return treatLevel1(expr, *noProblem); } return treatLevel1(expr, ok); } /*! * \param expr the expression to parse * \param noProblem if provided, the value pointed to will be se to true is no problem appeared, false otherwise. * \return the numerical value the expression eval to (or 0 in case of error). */ int parseIntegerMathExpr(QString const& expr, bool* noProblem) { bool ok = true; //intermediate variable to pass by reference to the sublevel parser (if no pointer is provided). if (noProblem != nullptr) { return qRound(treatLevel1Int(expr, *noProblem)); } return qRound(treatLevel1Int(expr, ok)); } } //namespace KisNumericParser. //intermediate functions /*! * \brief extractSubExprLevel1 extract from an expression the part of an expression that need to be treated recursivly before computing level 1 operations (+, -). * \param expr The expression to treat, the part returned will be removed. * \param nextOp This reference, in case of sucess, will hold the first level operation identified as separator ('+' or '-') * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise. * \return The first part of the expression that doesn't contain first level operations not nested within parenthesis. */ inline QString extractSubExprLevel1(QString & expr, char & nextOp, bool & noProblem){ QString ret; int subCount = 0; bool lastMetIsNumber = false; for(int i = 0; i < expr.size(); i++){ if (expr.at(i) == '(') { subCount++; } if (expr.at(i) == ')') { subCount--; } if (subCount < 0) { noProblem = false; return ret; } if(i == expr.size()-1 && subCount == 0){ ret = expr; expr.clear(); break; } if( (expr.at(i) == '+' || expr.at(i) == '-') && subCount == 0) { if (expr.at(i) == '-' && i < expr.size()-1) { bool cond = !expr.at(i+1).isSpace(); if (cond && !lastMetIsNumber) { continue; } } ret = expr.mid(0, i).trimmed(); nextOp = expr.at(i).toLatin1(); expr = expr.mid(i+1); break; } if (expr.at(i).isDigit()) { lastMetIsNumber = true; } else if (expr.at(i) != '.' && !expr.at(i).isSpace()) { lastMetIsNumber = false; } } noProblem = true; return ret; } /*! * \brief extractSubExprLevel2 extract from an expression the part of an expression that need to be treated recursivly before computing level 2 operations (*, /). * \param expr The expression to treat, the part returned will be removed. * \param nextOp This reference, in case of sucess, will hold the first level operation identified as separator ('*' or '/') * \param noProblem A reference to a bool, set to true if there was no problem, false otherwise. * \return The first part of the expression that doesn't contain second level operations not nested within parenthesis. */ inline QString extractSubExprLevel2(QString & expr, char & nextOp, bool & noProblem){ QString ret; int subCount = 0; for(int i = 0; i < expr.size(); i++){ if (expr.at(i) == '(') { subCount++; } if (expr.at(i) == ')') { subCount--; } if (subCount < 0) { noProblem = false; return ret; } if(i == expr.size()-1 && subCount == 0){ ret = expr; expr.clear(); break; } if( (expr.at(i) == '*' || expr.at(i) == '/') && subCount == 0) { ret = expr.mid(0, i).trimmed(); nextOp = expr.at(i).toLatin1(); expr = expr.mid(i+1); break; } } noProblem = true; return ret; } /*! * \brief treatLevel1 treat an expression at the first level of recursion. * \param expr The expression to treat. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed expression or subexpression or 0 in case of error. */ double treatLevel1(const QString &expr, bool & noProblem) { noProblem = true; QString exprDestructable = expr; char nextOp = '+'; double result = 0.0; while (!exprDestructable.isEmpty()) { double sign = (nextOp == '-') ? -1 : 1; QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem); if (!noProblem) { return 0.0; } if (sign > 0) { result += treatLevel2(part, noProblem); } else { result -= treatLevel2(part, noProblem); } if(!noProblem){ return 0.0; } } return result; } /*! * \brief treatLevel2 treat a subexpression at the second level of recursion. * \param expr The subexpression to treat. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed subexpression or 0 in case of error. * * The expression should not contain first level operations not nested in parenthesis. */ double treatLevel2(QString const& expr, bool & noProblem) { noProblem = true; QString exprDestructable = expr; char nextOp = '*'; QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); double result = treatLevel3(part, noProblem); while (!exprDestructable.isEmpty()) { if (!noProblem) { return 0.0; } bool needToMultiply = (nextOp == '*'); part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); if (!noProblem) { return 0.0; } if (needToMultiply) { result *= treatLevel3(part, noProblem); } else { result /= treatLevel3(part, noProblem); } } return result; } /*! * \brief treatLevel3 treat a subexpression at the third level of recursion. * \param expr The subexpression to treat. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed subexpression or 0 in case of error. * * The expression should not contain first or second level operations not nested in parenthesis. */ double treatLevel3(const QString &expr, bool & noProblem) { noProblem = true; int indexPower = -1; int indexCount = 0; int subLevels = 0; for (int i = 0; i < expr.size(); i++) { if (expr.at(i) == '(') { subLevels++; } else if(expr.at(i) == ')') { subLevels--; if (subLevels < 0) { noProblem = false; return 0.0; } } else if (expr.at(i) == '^') { if (subLevels == 0) { indexPower = i; indexCount++; } } } if (indexCount > 1 || indexPower + 1 >= expr.size()) { noProblem = false; return 0.0; } if (indexPower > -1) { QStringList subExprs; subExprs << expr.mid(0,indexPower); subExprs << expr.mid(indexPower+1); bool noProb1 = true; bool noProb2 = true; double base = treatFuncs(subExprs[0], noProb1); double power = treatFuncs(subExprs[1], noProb2); return qPow(base, power); } else { return treatFuncs(expr, noProblem); } noProblem = false; return 0.0; } /*! * \brief treatFuncs treat the last level of recursion: parenthesis and functions. * \param expr The expression to parse. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed subexpression or 0 in case of error. * * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function. */ double treatFuncs(QString const& expr, bool & noProblem) { noProblem = true; QRegExp funcExp = funcExpr; //copy the expression in the current execution stack, to avoid errors for example when multiple thread call this function. QRegExp numExp = numberExpr; if (funcExp.exactMatch(expr.trimmed())) { int sign = funcExp.capturedTexts()[1].isEmpty() ? 1 : -1; QString func = funcExp.capturedTexts()[2].toLower(); QString subExpr = funcExp.capturedTexts()[3]; double val = treatLevel1(subExpr, noProblem); if (!noProblem) { return 0.0; } if (func.isEmpty()) { return sign*val; } if (!supportedFuncs.contains(func)) { noProblem = false; return 0.0; } //trigonometry is done in degree if (func == "cos") { val = qCos(val/180*qAcos(-1)); } else if (func == "sin") { val = qSin(val/180*qAcos(-1)); } else if (func == "tan") { val = qTan(val/180*qAcos(-1)); } else if(func == "acos") { val = qAcos(val)*180/qAcos(-1); } else if (func == "asin") { val = qAsin(val)*180/qAcos(-1); } else if (func == "atan") { val = qAtan(val)*180/qAcos(-1); } else if (func == "exp") { val = qExp(val); } else if (func == "ln") { val = qLn(val); } else if (func == "log10") { val = qLn(val)/qLn(10.0); } else if (func == "abs") { val = qAbs(val); } return sign*val; } else if(numExp.exactMatch(expr.trimmed())) { return expr.toDouble(&noProblem); } + double val = QLocale().toDouble(expr, &noProblem); + + if(noProblem) { + return val; + } + noProblem = false; return 0.0; } //int functions /*! * \brief treatLevel1 treat an expression at the first level of recursion. * \param expr The expression to treat. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed expression or subexpression or 0 in case of error. */ double treatLevel1Int(QString const& expr, bool & noProblem) { noProblem = true; QString exprDestructable = expr; char nextOp = '+'; double result = 0.0; while (!exprDestructable.isEmpty()) { double sign = (nextOp == '-') ? -1 : 1; QString part = extractSubExprLevel1(exprDestructable, nextOp, noProblem); if( !noProblem) { return 0.0; } if (sign > 0) { result += treatLevel2Int(part, noProblem); } else { result -= treatLevel2Int(part, noProblem); } if(!noProblem){ return 0.0; } } return result; } /*! * \brief treatLevel2 treat a subexpression at the second level of recursion. * \param expr The subexpression to treat. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed subexpression or 0 in case of error. * * The expression should not contain first level operations not nested in parenthesis. */ double treatLevel2Int(const QString &expr, bool &noProblem) { noProblem = true; QString exprDestructable = expr; char nextOp = '*'; QString part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); double result = treatFuncsInt(part, noProblem); while (!exprDestructable.isEmpty()) { if (!noProblem) { return 0.0; } bool needToMultiply = (nextOp == '*'); part = extractSubExprLevel2(exprDestructable, nextOp, noProblem); if (!noProblem) { return 0.0; } if (needToMultiply) { result *= treatFuncsInt(part, noProblem); } else { double val = treatFuncsInt(part, noProblem); if(std::isinf(result/val) || qIsNaN(result/val)){ noProblem = false; return 0.0; } result /= val; } } return result; } /*! * \brief treatFuncs treat the last level of recursion: parenthesis * \param expr The expression to parse. * \param noProblem A reference to a bool set to true if no problem happened, false otherwise. * \return The value of the parsed subexpression or 0 in case of error. * * The expression should not contain operators not nested anymore. The subexpressions within parenthesis will be treated by recalling the level 1 function. */ double treatFuncsInt(QString const& expr, bool & noProblem) { noProblem = true; QRegExp funcExpInteger = funcExprInteger; QRegExp integerExp = integerExpr; QRegExp numberExp = numberExpr; if (funcExpInteger.exactMatch(expr.trimmed())) { int sign = funcExpInteger.capturedTexts()[1].isEmpty() ? 1 : -1; QString subExpr = funcExpInteger.capturedTexts()[2]; double val = treatLevel1Int(subExpr, noProblem); if (!noProblem) { return 0; } return sign*val; } else if(numberExp.exactMatch(expr.trimmed())) { double value = QVariant(expr).toDouble(&noProblem); return value; } noProblem = false; return 0; } diff --git a/libs/widgetutils/kis_spin_box_unit_manager.cpp b/libs/widgetutils/kis_spin_box_unit_manager.cpp new file mode 100644 index 0000000000..acae8ac490 --- /dev/null +++ b/libs/widgetutils/kis_spin_box_unit_manager.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2017 Laurent Valentin Jospin + * + * 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 2 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. + */ + +#include "kis_spin_box_unit_manager.h" + +#include "KoUnit.h" +#include + +#include + + +KisSpinBoxUnitManagerBuilder* KisSpinBoxUnitManagerFactory::builder = nullptr; + +KisSpinBoxUnitManager* KisSpinBoxUnitManagerFactory::buildDefaultUnitManager(QObject* parent) +{ + if (builder == nullptr) { + return new KisSpinBoxUnitManager(parent); + } + + return builder->buildUnitManager(parent); +} + +void KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(KisSpinBoxUnitManagerBuilder* pBuilder) +{ + if (builder != nullptr) { + delete builder; //The factory took over the lifecycle of the builder, so it delete it when replaced. + } + + builder = pBuilder; +} + +void KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder() +{ + if (builder != nullptr) { + delete builder; //The factory took over the lifecycle of the builder, so it delete it when replaced. + } + + builder = nullptr; +} + +const QStringList KisSpinBoxUnitManager::referenceUnitSymbols = {"pt", "px", "°", "frame"}; + +const QStringList KisSpinBoxUnitManager::documentRelativeLengthUnitSymbols = {"px", "vw", "vh"}; //px are relative to the resolution, vw and vh to the width and height. +const QStringList KisSpinBoxUnitManager::documentRelativeTimeUnitSymbols = {"s", "%"}; //secondes are relative to the framerate, % to the sequence length. + +class Q_DECL_HIDDEN KisSpinBoxUnitManager::Private +{ +public: + Private(KisSpinBoxUnitManager::UnitDimension pDim = KisSpinBoxUnitManager::LENGTH, + QString pUnitSymbol = "pt", + double pConv = 1.0): + dim(pDim), + unitSymbol(pUnitSymbol), + conversionFactor(pConv), + conversionFactorIsFixed(true), + conversionConstant(0), + conversionConstantIsFixed(true), + constrains(0), + unitListCached(false), + hasHundredPercent(false), + canAccessDocument(false) + { + + } + + KisSpinBoxUnitManager::UnitDimension dim; + + QString unitSymbol; + mutable double conversionFactor; + bool conversionFactorIsFixed; //tell if it's possible to trust the conversion factor stored or if it's needed to recompute it. + mutable double conversionConstant; + bool conversionConstantIsFixed; //tell if it's possible to trust the conversion constant stored or if it's needed to recompute it. + + KisSpinBoxUnitManager::Constrains constrains; + + mutable QStringList unitList; + mutable bool unitListCached; + + mutable QStringList unitListWithName; + mutable bool unitListWithNameCached; + + //it's possible to store a reference for the % unit, for lenght. + bool hasHundredPercent; + qreal hundredPercent; + + bool canAccessDocument; +}; + +KisSpinBoxUnitManager::KisSpinBoxUnitManager(QObject *parent) : QAbstractListModel(parent) +{ + d = new Private(); + + connect(this, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, &KisSpinBoxUnitManager::newUnitSymbolToUnitIndex); +} +KisSpinBoxUnitManager::~KisSpinBoxUnitManager() +{ + delete d; +} + +int KisSpinBoxUnitManager::getUnitDimensionType() const +{ + return d->dim; +} + +QString KisSpinBoxUnitManager::getReferenceUnitSymbol() const +{ + return referenceUnitSymbols[d->dim]; +} + +QString KisSpinBoxUnitManager::getApparentUnitSymbol() const +{ + return d->unitSymbol; +} + +int KisSpinBoxUnitManager::getApparentUnitId() const +{ + QStringList list = getsUnitSymbolList(); + return list.indexOf(d->unitSymbol); +} + +QStringList KisSpinBoxUnitManager::getsUnitSymbolList(bool withName) const{ + + QStringList list; + + if (withName) { + if (d->unitListWithNameCached) { + return d->unitListWithName; + } + } else { + if (d->unitListCached) { + return d->unitList; + } + } + + switch (d->dim) { + + case LENGTH: + + for (int i = 0; i < KoUnit::TypeCount; i++) { + + if (KoUnit::Type(i) == KoUnit::Pixel) { + continue; //skip pixel, which is a document relative unit, in the base classe. + } + + if (withName) { + list << KoUnit::unitDescription(KoUnit::Type(i)); + } else { + list << KoUnit(KoUnit::Type(i)).symbol(); + } + } + + if (d->canAccessDocument) { + // ad document relative units + if (withName) { + list << KoUnit::unitDescription(KoUnit::Pixel) << i18n("view width (vw)") << i18n("view height (vh)"); + } else { + list << documentRelativeLengthUnitSymbols; + } + } + + break; + + case IMLENGTH: + + if (withName) { + list << KoUnit::unitDescription(KoUnit::Pixel); + } else { + list << "px"; + } + + if (d->canAccessDocument) { + // ad document relative units + if (withName) { + list << i18n("view width (vw)") << i18n("view height (vh)"); + } else { + list << "vw" << "vh"; + } + } + break; + + case ANGLE: + + if (withName) { + list << i18n("degrees (°)") << i18n("radians (rad)") << i18n("gons (gon)") << i18n("percent of circle (%)"); + } else { + list << "°" << "rad" << "gon" << "%"; + } + break; + + case TIME: + + if (withName) { + list << i18n("frames (f)"); + } else { + list << "f"; + } + + if (d->canAccessDocument) { + if (withName) { + list << i18n("seconds (s)") << i18n("percent of animation (%)"); + } else { + list << documentRelativeTimeUnitSymbols; + } + } + + break; + + } + + if (withName) { + d->unitListWithName = list; + d->unitListWithNameCached = true; + } else { + d->unitList = list; + d->unitListCached = true; + } + + return list; + +} + +qreal KisSpinBoxUnitManager::getConversionConstant(int dim, QString symbol) const +{ + Q_UNUSED(dim); + Q_UNUSED(symbol); + + return 0; // all units managed here are transform via a linear function, so this wll alway be 0 in this class. +} + +qreal KisSpinBoxUnitManager::getReferenceValue(double apparentValue) const +{ + if (!d->conversionFactorIsFixed) { + recomputeConversionFactor(); + } + + if(!d->conversionConstantIsFixed) { + recomputeConvesrionConstant(); + } + + qreal v = (apparentValue - d->conversionConstant)/d->conversionFactor; + + if (d->constrains &= REFISINT) { + v = qFloor(v); + } + + return v; + +} + +int KisSpinBoxUnitManager::rowCount(const QModelIndex &parent) const { + if (parent == QModelIndex()) { + return getsUnitSymbolList().size(); + } + return 0; +} + +QVariant KisSpinBoxUnitManager::data(const QModelIndex &index, int role) const { + + if (role == Qt::DisplayRole) { + return getsUnitSymbolList(false).at(index.row()); + } + + return QVariant(); +} + +qreal KisSpinBoxUnitManager::getApparentValue(double refValue) const +{ + if (!d->conversionFactorIsFixed) { + recomputeConversionFactor(); + } + + if(!d->conversionConstantIsFixed) { + recomputeConvesrionConstant(); + } + + qreal v = refValue*d->conversionFactor + d->conversionConstant; + + if (d->constrains &= VALISINT) { + v = qFloor(v); + } + + return v; +} + +qreal KisSpinBoxUnitManager::getConversionFactor(int dim, QString symbol) const +{ + + qreal factor = -1; + + switch (dim) { + + case LENGTH: + do { + if (symbol == "px") { + break; + } + + bool ok; + KoUnit unit = KoUnit::fromSymbol(symbol, &ok); + if (! ok) { + break; + } + factor = unit.toUserValue(1.0); + } while (0) ; + break; + + case IMLENGTH: + if (symbol == "px") { + factor = 1; + } + break; + + case ANGLE: + if (symbol == "°") { + factor = 1.0; + break; + } + if (symbol == "rad") { + factor = acos(-1)/90.0; + break; + } + if (symbol == "gon") { + factor = 10.0/9.0; + break; + } + if (symbol == "%") { + factor = 2.5/9.0; //(25% of circle is 90°) + break; + } + break; + + case TIME: + + if (symbol != "f") { //we have only frames for the moment. + break; + } + factor = 1.0; + break; + + default: + break; + } + + return factor; +} + + +void KisSpinBoxUnitManager::setUnitDimension(UnitDimension dimension) +{ + if (dimension == d->dim) { + return; + } + + d->dim = dimension; + d->unitSymbol = referenceUnitSymbols[d->dim]; //Active dim is reference dim when just changed. + d->conversionFactor = 1.0; + + emit unitDimensionChanged(d->dim); + +} + +void KisSpinBoxUnitManager::setApparentUnitFromSymbol(QString pSymbol) +{ + + QString symbol = pSymbol.trimmed(); + + if (symbol == d->unitSymbol) { + return; + } + + emit unitAboutToChange(); + + QString newSymb = ""; + + switch (d->dim) { + + case ANGLE: + if (symbol.toLower() == "deg") { + newSymb = "°"; + break; + } + goto default_indentifier; //alway do default after handling possible special cases. + +default_indentifier: + default: + QStringList list = getsUnitSymbolList(); + if (list.contains(symbol, Qt::CaseInsensitive)) { + for (QString str : list) { + if (str.toLower() == symbol.toLower()) { + newSymb = str; //official symbol may contain capitals letters, so better take the official version. + break; + } + } + break; + } + + } + + if(newSymb.isEmpty()) { + return; //abort if it was impossible to locate the correct symbol. + } + + if (d->canAccessDocument) { + //manage document relative units. + + QStringList speUnits; + + switch (d->dim) { + + case LENGTH: + speUnits = documentRelativeLengthUnitSymbols; + goto default_identifier_conv_fact; + + case IMLENGTH: + speUnits << "vw" << "vh"; + goto default_identifier_conv_fact; + + case TIME: + speUnits = documentRelativeTimeUnitSymbols; + goto default_identifier_conv_fact; + +default_identifier_conv_fact: + default: + + if (speUnits.isEmpty()) { + d->conversionFactorIsFixed = true; + break; + } + + if (speUnits.contains(newSymb)) { + d->conversionFactorIsFixed = false; + break; + } + + d->conversionFactorIsFixed = true; + break; + } + + if (d->dim == TIME) { + if (newSymb == "%") { + d->conversionConstantIsFixed = false; + } + } else { + d->conversionConstantIsFixed = true; + } + + } + + qreal conversFact = getConversionFactor(d->dim, newSymb); + qreal oldConversFact = d->conversionFactor; + + d->conversionFactor = conversFact; + emit conversionFactorChanged(d->conversionFactor, oldConversFact); + + d->unitSymbol = newSymb; + emit unitChanged(newSymb); + +} + +void KisSpinBoxUnitManager::selectApparentUnitFromIndex(int index) { + + if (index >= 0 && index < rowCount()) { + setApparentUnitFromSymbol(getsUnitSymbolList().at(index)); + } + +} + +void KisSpinBoxUnitManager::newUnitSymbolToUnitIndex(QString symbol) { + int id = getsUnitSymbolList().indexOf(symbol); + + if (id >= 0) { + emit unitChanged(id); + } +} + +void KisSpinBoxUnitManager::recomputeConversionFactor() const +{ + if (d->conversionFactorIsFixed) { + return; + } + + qreal oldConversionFactor = d->conversionFactor; + + d->conversionFactor = getConversionFactor(d->dim, d->unitSymbol); + + if (oldConversionFactor != d->conversionFactor) { + emit conversionFactorChanged(d->conversionFactor, oldConversionFactor); + } +} + +void KisSpinBoxUnitManager::recomputeConvesrionConstant() const +{ + if (d->conversionConstantIsFixed) { + return; + } + + qreal oldConversionConstant = d->conversionConstant; + + d->conversionConstant = getConversionConstant(d->dim, d->unitSymbol); + + if (oldConversionConstant != d->conversionConstant) { + emit conversionConstantChanged(d->conversionConstant, oldConversionConstant); + } +} + +void KisSpinBoxUnitManager::grantDocumentRelativeUnits() +{ + d->canAccessDocument = true; +} diff --git a/libs/widgetutils/kis_spin_box_unit_manager.h b/libs/widgetutils/kis_spin_box_unit_manager.h new file mode 100644 index 0000000000..c0d44ffeb1 --- /dev/null +++ b/libs/widgetutils/kis_spin_box_unit_manager.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017 Laurent Valentin Jospin + * + * 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 2 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. + */ + +#ifndef KISSPINBOXUNITMANAGER_H +#define KISSPINBOXUNITMANAGER_H + +#include +#include +#include + +#include "kritawidgetutils_export.h" + +class KisSpinBoxUnitManager; +class KisSpinBoxUnitManagerBuilder; +class KisSpinBoxUnitManagerFactory; + +/*! + * \brief The KisSpinBoxUnitManagerFactory class is a factory that is used to build a default KisSpinBoxUnitManager. + * \see KisSpinBoxUnitManagerBuilder + */ +class KRITAWIDGETUTILS_EXPORT KisSpinBoxUnitManagerFactory +{ +public: + + static KisSpinBoxUnitManager* buildDefaultUnitManager(QObject* parent); + //! \brief set a builder the factory can use. The factory should take on the lifecycle of the builder, so to delete it call clearUnitManagerBuilder(); + static void setDefaultUnitManagerBuilder(KisSpinBoxUnitManagerBuilder* pBuilder); + static void clearUnitManagerBuilder(); + +private: + + static KisSpinBoxUnitManagerBuilder* builder; + +}; + +/*! + * \brief The KisSpinBoxUnitManagerBuilder class is the base class, used in the strategy pattern of KisSpinBoxUnitManagerFactory. + * \see KisSpinBoxUnitManagerFactory. + */ +class KRITAWIDGETUTILS_EXPORT KisSpinBoxUnitManagerBuilder +{ + +public: + + virtual ~KisSpinBoxUnitManagerBuilder() {} + + virtual KisSpinBoxUnitManager* buildUnitManager(QObject* parent) = 0; //this pure virtual function is used to build a unitmanager, it will be used by the unitManagerFactory. +}; + +/** + * @brief The KisSpinBoxUnitManager class is an abstract interface for the unitspinboxes classes to manage different type of units. + * + * The class make a difference between unit dimension (distance, angle, time). + * + * The class allow to convert values between reference unit and apparent unit, but also to get other informations like possible units symbols. + * + * This class don't allow to use relative units (units which conversion factor is dependant of the context), even if its private data are prepared to manage it. + * The reason for this is that from the library of this class it is very hard to acess easily the informations needed. So all will be managed by subclasses in other libs. + * + * The class is a subclass of QAbstractListModel, so that available list of units is easily acessed by other Qt standard components, like QComboBoxes. + * + */ +class KRITAWIDGETUTILS_EXPORT KisSpinBoxUnitManager : public QAbstractListModel +{ + Q_OBJECT + +public: + + enum UnitDimension{ + LENGTH = 0, //length, print size, reference is point + IMLENGTH = 1, //length, image size, reference is pixel. This dimension is used when the printing units must be avoided + ANGLE = 2, + TIME = 3 + }; + + static inline bool isUnitId(int code) { return (code == LENGTH || code == ANGLE || code == TIME); } + + //! \brief this list hold the symbols of the referenc unit per dimension. The index is equal to the value in UnitDimension so that the dimension name can be used to index the list. + static const QStringList referenceUnitSymbols; + + enum Constrain{ + NOCONSTR = 0, + REFISINT = 1, + VALISINT = 2 + + }; + + Q_DECLARE_FLAGS(Constrains, Constrain) + + explicit KisSpinBoxUnitManager(QObject *parent = 0); + virtual ~KisSpinBoxUnitManager(); + + int getUnitDimensionType() const; + QString getReferenceUnitSymbol() const; + QString getApparentUnitSymbol() const; + + //! \brief get the position of the apparent unit in the list of units. It is usefull if we want to build a model for combo-box based unit management. + int getApparentUnitId() const; + + virtual QStringList getsUnitSymbolList(bool withName = false) const; + + qreal getReferenceValue(double apparentValue) const; + qreal getApparentValue(double refValue) const; + + //! \brief gets the conversion factor of a managed unit, or -1 in case of error. This method is the one that need to be overridden to extend the ability of the KisSpinBoxUnitManager. + virtual qreal getConversionFactor(int dim, QString symbol) const; + //! \brief some units conversions are done via an affine transform, not just a linear transform. This function gives the constant of this affine transform (usually 0). + virtual qreal getConversionConstant(int dim, QString symbol) const; + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +Q_SIGNALS: + + void unitDimensionChanged(int dimCode); + void unitAboutToChange(); + void unitChanged(QString symbol); + void unitChanged(int index); + void conversionFactorChanged(qreal newConversionFactor, qreal oldConversionFactor) const; + void conversionConstantChanged(qreal newConversionFactor, qreal oldConversionFactor) const; + void unitListChanged(); + +public Q_SLOTS: + + void setUnitDimension(UnitDimension dimension); + void setApparentUnitFromSymbol(QString pSymbol); + void selectApparentUnitFromIndex(int index); + +protected: + + class Private; + Private * d; + + //! \brief convert a unitChanged signal with a QString to one with an index. + void newUnitSymbolToUnitIndex(QString symbol); + + //unit's that may be used only if acess to the document informations exists. + static const QStringList documentRelativeLengthUnitSymbols; + static const QStringList documentRelativeTimeUnitSymbols; + + void recomputeConversionFactor() const; + void recomputeConvesrionConstant() const; + + //! \brief calling this method give acess to document relative units. Only subclasses that manage thoses units should call it. + void grantDocumentRelativeUnits(); + +}; + +#endif // KISSPINBOXUNITMANAGER_H diff --git a/plugins/extensions/imagesize/dlg_canvassize.cc b/plugins/extensions/imagesize/dlg_canvassize.cc index af146bd26b..b394a041d9 100644 --- a/plugins/extensions/imagesize/dlg_canvassize.cc +++ b/plugins/extensions/imagesize/dlg_canvassize.cc @@ -1,705 +1,469 @@ /* * * Copyright (c) 2009 Edward Apap * Copyright (c) 2013 Juan Palacios * * 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 2 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. */ #include "dlg_canvassize.h" #include "kcanvaspreview.h" #include #include #include #include #include +#include + +#include + // used to extend KoUnit in comboboxes static const QString percentStr(i18n("Percent (%)")); DlgCanvasSize::DlgCanvasSize(QWidget *parent, int width, int height, double resolution) - : KoDialog(parent) - , m_keepAspect(true) - , m_aspectRatio((double)width / height) - , m_resolution(resolution) - , m_originalWidth(width) - , m_originalHeight(height) - , m_newWidth(width) - , m_newHeight(height) - , m_xOffset(0) - , m_yOffset(0) + : KoDialog(parent) + , m_keepAspect(true) + , m_aspectRatio((double)width / height) + , m_resolution(resolution) + , m_originalWidth(width) + , m_originalHeight(height) + , m_newWidth(width) + , m_newHeight(height) + , m_xOffset(0) + , m_yOffset(0) { setCaption(i18n("Resize Canvas")); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgCanvasSize(this); Q_CHECK_PTR(m_page); m_page->layout()->setMargin(0); m_page->setObjectName("canvas_size"); - m_page->newWidth->setValue(width); - m_page->newWidth->setFocus(); - m_page->newHeight->setValue(height); + _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); + _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); + + _widthUnitManager->setApparentUnitFromSymbol("px"); + _heightUnitManager->setApparentUnitFromSymbol("px"); + + m_page->newWidthDouble->setUnitManager(_widthUnitManager); + m_page->newHeightDouble->setUnitManager(_heightUnitManager); + m_page->newWidthDouble->setDecimals(2); + m_page->newHeightDouble->setDecimals(2); + m_page->newWidthDouble->setDisplayUnit(false); + m_page->newHeightDouble->setDisplayUnit(false); - m_page->newWidthDouble->setVisible(false); - m_page->newHeightDouble->setVisible(false); + m_page->newWidthDouble->setValue(width); + m_page->newWidthDouble->setFocus(); + m_page->newHeightDouble->setValue(height); - m_page->widthUnit->addItems(KoUnit::listOfUnitNameForUi()); - m_page->widthUnit->addItem(percentStr); - m_page->heightUnit->addItems(KoUnit::listOfUnitNameForUi()); - m_page->heightUnit->addItem(percentStr); + m_page->widthUnit->setModel(_widthUnitManager); + m_page->heightUnit->setModel(_heightUnitManager); - const int pixelUnitIndex = KoUnit(KoUnit::Pixel).indexInListForUi(); + const int pixelUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf("px"); //TODO: have a better way to identify units. m_page->widthUnit->setCurrentIndex(pixelUnitIndex); m_page->heightUnit->setCurrentIndex(pixelUnitIndex); - m_page->xOffsetDouble->setVisible(false); - m_page->yOffsetDouble->setVisible(false); + _xOffsetUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); + _yOffsetUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); - m_page->xOffUnit->addItems(KoUnit::listOfUnitNameForUi()); - m_page->xOffUnit->addItem(percentStr); - m_page->yOffUnit->addItems(KoUnit::listOfUnitNameForUi()); - m_page->yOffUnit->addItem(percentStr); + _xOffsetUnitManager->setApparentUnitFromSymbol("px"); + _yOffsetUnitManager->setApparentUnitFromSymbol("px"); + + m_page->xOffsetDouble->setUnitManager(_xOffsetUnitManager); + m_page->yOffsetDouble->setUnitManager(_yOffsetUnitManager); + m_page->xOffsetDouble->setDecimals(2); + m_page->yOffsetDouble->setDecimals(2); + m_page->xOffsetDouble->setDisplayUnit(false); + m_page->yOffsetDouble->setDisplayUnit(false); + + m_page->xOffUnit->setModel(_xOffsetUnitManager); + m_page->yOffUnit->setModel(_yOffsetUnitManager); m_page->xOffUnit->setCurrentIndex(pixelUnitIndex); m_page->yOffUnit->setCurrentIndex(pixelUnitIndex); m_page->canvasPreview->setImageSize(m_originalWidth, m_originalHeight); m_page->canvasPreview->setCanvasSize(m_originalWidth, m_originalHeight); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); + m_page->xOffsetDouble->changeValue(m_xOffset); + m_page->yOffsetDouble->changeValue(m_yOffset); + KisConfig cfg; m_page->aspectRatioBtn->setKeepAspectRatio(cfg.readEntry("CanvasSize/KeepAspectRatio", false)); m_page->constrainProportionsCkb->setChecked(cfg.readEntry("CanvasSize/ConstrainProportions", false)); m_keepAspect = cfg.readEntry("CanvasSize/KeepAspectRatio", false); m_group = new QButtonGroup(m_page); m_group->addButton(m_page->topLeft, NORTH_WEST); m_group->addButton(m_page->topCenter, NORTH); m_group->addButton(m_page->topRight, NORTH_EAST); m_group->addButton(m_page->middleLeft, WEST); m_group->addButton(m_page->middleCenter, CENTER); m_group->addButton(m_page->middleRight, EAST); m_group->addButton(m_page->bottomLeft, SOUTH_WEST); m_group->addButton(m_page->bottomCenter, SOUTH); m_group->addButton(m_page->bottomRight, SOUTH_EAST); loadAnchorIcons(); m_group->button(CENTER)->setChecked(true); updateAnchorIcons(CENTER); KisSizeGroup *labelsGroup = new KisSizeGroup(this); labelsGroup->addWidget(m_page->lblNewWidth); labelsGroup->addWidget(m_page->lblNewHeight); labelsGroup->addWidget(m_page->lblXOff); labelsGroup->addWidget(m_page->lblYOff); labelsGroup->addWidget(m_page->lblAnchor); KisSizeGroup *spinboxesGroup = new KisSizeGroup(this); - spinboxesGroup->addWidget(m_page->newWidth); spinboxesGroup->addWidget(m_page->newWidthDouble); - spinboxesGroup->addWidget(m_page->newHeight); spinboxesGroup->addWidget(m_page->newHeightDouble); - spinboxesGroup->addWidget(m_page->xOffset); spinboxesGroup->addWidget(m_page->xOffsetDouble); - spinboxesGroup->addWidget(m_page->yOffset); spinboxesGroup->addWidget(m_page->yOffsetDouble); KisSizeGroup *comboboxesGroup = new KisSizeGroup(this); comboboxesGroup->addWidget(m_page->widthUnit); comboboxesGroup->addWidget(m_page->heightUnit); comboboxesGroup->addWidget(m_page->xOffUnit); comboboxesGroup->addWidget(m_page->yOffUnit); setMainWidget(m_page); connect(this, SIGNAL(okClicked()), this, SLOT(accept())); - connect(m_page->newWidth, SIGNAL(valueChanged(int)), this, SLOT(slotWidthChanged(int))); - connect(m_page->newHeight, SIGNAL(valueChanged(int)), this, SLOT(slotHeightChanged(int))); - connect(m_page->newWidthDouble, SIGNAL(valueChanged(double)), this, SLOT(slotWidthChanged(double))); - connect(m_page->newHeightDouble, SIGNAL(valueChanged(double)), this, SLOT(slotHeightChanged(double))); - connect(m_page->widthUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWidthUnitChanged(int))); - connect(m_page->heightUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotHeightUnitChanged(int))); + connect(m_page->newWidthDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotWidthChanged(double))); + connect(m_page->newHeightDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotHeightChanged(double))); + connect(m_page->widthUnit, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(m_page->heightUnit, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->widthUnit, SLOT(setCurrentIndex(int))); + connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->heightUnit, SLOT(setCurrentIndex(int))); - connect(m_page->xOffset, SIGNAL(valueChanged(int)), this, SLOT(slotXOffsetChanged(int))); - connect(m_page->yOffset, SIGNAL(valueChanged(int)), this, SLOT(slotYOffsetChanged(int))); - connect(m_page->xOffsetDouble, SIGNAL(valueChanged(double)), this, SLOT(slotXOffsetChanged(double))); - connect(m_page->yOffsetDouble, SIGNAL(valueChanged(double)), this, SLOT(slotYOffsetChanged(double))); - connect(m_page->xOffUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotXOffsetUnitChanged(int))); - connect(m_page->yOffUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotYOffsetUnitChanged(int))); + connect(m_page->xOffsetDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotXOffsetChanged(double))); + connect(m_page->yOffsetDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotYOffsetChanged(double))); + connect(m_page->xOffUnit, SIGNAL(currentIndexChanged(int)), _xOffsetUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(m_page->yOffUnit, SIGNAL(currentIndexChanged(int)), _yOffsetUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(_xOffsetUnitManager, SIGNAL(unitChanged(int)), m_page->xOffUnit, SLOT(setCurrentIndex(int))); + connect(_yOffsetUnitManager, SIGNAL(unitChanged(int)), m_page->yOffUnit, SLOT(setCurrentIndex(int))); connect(m_page->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->aspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); - connect(m_page->aspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_group, SIGNAL(buttonClicked(int)), SLOT(slotAnchorButtonClicked(int))); connect(m_page->canvasPreview, SIGNAL(sigModifiedXOffset(int)), this, SLOT(slotCanvasPreviewXOffsetChanged(int))); connect(m_page->canvasPreview, SIGNAL(sigModifiedYOffset(int)), this, SLOT(slotCanvasPreviewYOffsetChanged(int))); } DlgCanvasSize::~DlgCanvasSize() { KisConfig cfg; cfg.writeEntry("CanvasSize/KeepAspectRatio", m_page->aspectRatioBtn->keepAspectRatio()); cfg.writeEntry("CanvasSize/ConstrainProportions", m_page->constrainProportionsCkb->isChecked()); delete m_page; } qint32 DlgCanvasSize::width() { - return (qint32)m_newWidth; + return (qint32) m_newWidth; } qint32 DlgCanvasSize::height() { - return (qint32)m_newHeight; + return (qint32) m_newHeight; } qint32 DlgCanvasSize::xOffset() { - return (qint32)m_page->xOffset->value(); + return (qint32) m_page->xOffsetDouble->value(); } qint32 DlgCanvasSize::yOffset() { - return (qint32)m_page->yOffset->value(); + return (qint32) m_page->yOffsetDouble->value(); } void DlgCanvasSize::slotAspectChanged(bool keep) { m_page->aspectRatioBtn->blockSignals(true); m_page->constrainProportionsCkb->blockSignals(true); m_page->aspectRatioBtn->setKeepAspectRatio(keep); m_page->constrainProportionsCkb->setChecked(keep); m_page->aspectRatioBtn->blockSignals(false); m_page->constrainProportionsCkb->blockSignals(false); m_keepAspect = keep; if (keep) { // size values may be out of sync, so we need to reset it to defaults m_newWidth = m_originalWidth; m_newHeight = m_originalHeight; m_xOffset = 0; m_yOffset = 0; - updateWidthUIValue(m_newWidth); - updateHeightUIValue(m_newHeight); - updateXOffsetUIValue(m_xOffset); - updateYOffsetUIValue(m_yOffset); - m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setCanvasSize(m_newWidth, m_newHeight); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); m_page->canvasPreview->blockSignals(false); updateOffset(CENTER); updateButtons(CENTER); } } void DlgCanvasSize::slotAnchorButtonClicked(int id) { updateOffset(id); updateButtons(id); } -void DlgCanvasSize::slotWidthChanged(int v) -{ - slotWidthChanged((double) v); -} - -void DlgCanvasSize::slotHeightChanged(int v) -{ - slotHeightChanged((double) v); -} - void DlgCanvasSize::slotWidthChanged(double v) { - if (m_page->widthUnit->currentText() == percentStr) { - m_newWidth = qRound((v * m_originalWidth) / 100.0); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->widthUnit->currentIndex()); - const double resValue = (selectedUnit == KoUnit(KoUnit::Pixel)) ? v : (v * m_resolution); - m_newWidth = qRound(selectedUnit.fromUserValue(resValue)); - } + //this slot receiv values in pt from the unitspinbox, so just use the resolution. + const double resValue = v*_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_newWidth = qRound(resValue); if (m_keepAspect) { m_newHeight = qRound(m_newWidth / m_aspectRatio); - updateHeightUIValue(m_newHeight); + m_page->newHeightDouble->blockSignals(true); + m_page->newHeightDouble->changeValue(v / m_aspectRatio); + m_page->newHeightDouble->blockSignals(false); } int savedId = m_group->checkedId(); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setCanvasSize(m_newWidth, m_newHeight); m_page->canvasPreview->blockSignals(false); updateOffset(savedId); updateButtons(savedId); } void DlgCanvasSize::slotHeightChanged(double v) { - if (m_page->heightUnit->currentText() == percentStr) { - m_newHeight = qRound((v * m_originalHeight) / 100.0); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->heightUnit->currentIndex()); - const double resValue = (selectedUnit == KoUnit(KoUnit::Pixel)) ? v : (v * m_resolution); - m_newHeight = qRound(selectedUnit.fromUserValue(resValue)); - } + //this slot receiv values in pt from the unitspinbox, so just use the resolution. + const double resValue = v*_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_newHeight = qRound(resValue); if (m_keepAspect) { m_newWidth = qRound(m_newHeight * m_aspectRatio); - updateWidthUIValue(m_newWidth); + m_page->newWidthDouble->blockSignals(true); + m_page->newWidthDouble->changeValue(v * m_aspectRatio); + m_page->newWidthDouble->blockSignals(false); } int savedId = m_group->checkedId(); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setCanvasSize(m_newWidth, m_newHeight); m_page->canvasPreview->blockSignals(false); updateOffset(savedId); updateButtons(savedId); } -void DlgCanvasSize::slotWidthUnitChanged(int index) -{ - updateWidthUIValue(m_newWidth); - - if (m_page->widthUnit->currentText() == percentStr) { - m_page->newWidth->setVisible(false); - m_page->newWidthDouble->setVisible(true); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(index); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newWidth->setVisible(false); - m_page->newWidthDouble->setVisible(true); - } else { - m_page->newWidth->setVisible(true); - m_page->newWidthDouble->setVisible(false); - } - } -} - -void DlgCanvasSize::slotHeightUnitChanged(int index) -{ - updateHeightUIValue(m_newHeight); - - if (m_page->heightUnit->currentText() == percentStr) { - m_page->newHeight->setVisible(false); - m_page->newHeightDouble->setVisible(true); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(index); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newHeight->setVisible(false); - m_page->newHeightDouble->setVisible(true); - } else { - m_page->newHeight->setVisible(true); - m_page->newHeightDouble->setVisible(false); - } - } -} - -void DlgCanvasSize::slotXOffsetChanged(int v) -{ - slotXOffsetChanged((double) v); -} - -void DlgCanvasSize::slotYOffsetChanged(int v) -{ - slotYOffsetChanged((double) v); -} - void DlgCanvasSize::slotXOffsetChanged(double v) { - if (m_page->xOffUnit->currentText() == percentStr) { - m_xOffset = qRound((v * m_newWidth) / 100.0); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); - const double resValue = (selectedUnit == KoUnit(KoUnit::Pixel)) ? v : (v * m_resolution); - m_xOffset = qRound(selectedUnit.fromUserValue(resValue)); - } + //this slot receiv values in pt from the unitspinbox, so just use the resolution. + const double resValue = v*_xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_xOffset = qRound(resValue); m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); m_page->canvasPreview->blockSignals(false); updateButtons(-1); } void DlgCanvasSize::slotYOffsetChanged(double v) { - if (m_page->yOffUnit->currentText() == percentStr) { - m_yOffset = qRound((v * m_newHeight) / 100.0); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); - const double resValue = (selectedUnit == KoUnit(KoUnit::Pixel)) ? v : (v * m_resolution); - m_yOffset = qRound(selectedUnit.fromUserValue(resValue)); - } + //this slot receiv values in pt from the unitspinbox, so just use the resolution. + const double resValue = v*_xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_yOffset = qRound(resValue); + m_page->canvasPreview->blockSignals(true); m_page->canvasPreview->setImageOffset(m_xOffset, m_yOffset); m_page->canvasPreview->blockSignals(false); updateButtons(-1); } -void DlgCanvasSize::slotXOffsetUnitChanged(int index) -{ - updateXOffsetUIValue(m_xOffset); - - if (m_page->xOffUnit->currentText() == percentStr) { - m_page->xOffset->setVisible(false); - m_page->xOffsetDouble->setVisible(true); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(index); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->xOffset->setVisible(false); - m_page->xOffsetDouble->setVisible(true); - } else { - m_page->xOffset->setVisible(true); - m_page->xOffsetDouble->setVisible(false); - } - } -} - -void DlgCanvasSize::slotYOffsetUnitChanged(int index) -{ - updateYOffsetUIValue(m_yOffset); - - if (m_page->yOffUnit->currentText() == percentStr) { - m_page->yOffset->setVisible(false); - m_page->yOffsetDouble->setVisible(true); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(index); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->yOffset->setVisible(false); - m_page->yOffsetDouble->setVisible(true); - } else { - m_page->yOffset->setVisible(true); - m_page->yOffsetDouble->setVisible(false); - } - } -} - void DlgCanvasSize::slotCanvasPreviewXOffsetChanged(int v) { - // Convert input value to selected x offset unit. - // This will be undone later in slotXOffsetChanged (through spinboxes valueChanged signal). - if (m_page->xOffUnit->currentText() == percentStr) { - m_page->xOffsetDouble->setValue((v * 100.0) / m_newWidth); - } else { - const KoUnit pixelUnit(KoUnit::Pixel); - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); - //const double convertedValue = xOffsetUnit.convertFromUnitToUnit(v, pixelUnit, xOffsetUnit); - - if (selectedUnit != pixelUnit) { - m_page->xOffsetDouble->setValue(selectedUnit.toUserValue(v / m_resolution)); - } else { - m_page->xOffset->setValue(v); - } - } + double newVal = v / _xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_page->xOffsetDouble->changeValue(newVal); } void DlgCanvasSize::slotCanvasPreviewYOffsetChanged(int v) { - // Convert input value to selected y offset unit. - // This will be undone later in slotYOffsetChanged (through spinboxes valueChanged signal). - if (m_page->yOffUnit->currentText() == percentStr) { - m_page->yOffsetDouble->setValue((v * 100.0) / m_newHeight); - } else { - const KoUnit pixelUnit(KoUnit::Pixel); - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); - //const double convertedValue = yOffsetUnit.convertFromUnitToUnit(v, pixelUnit, yOffsetUnit); - if (selectedUnit != pixelUnit) { - m_page->yOffsetDouble->setValue(selectedUnit.toUserValue(v / m_resolution)); - } else { - m_page->yOffset->setValue(v); - } - } -} - -void DlgCanvasSize::updateWidthUIValue(double value) -{ - if (m_page->widthUnit->currentText() == percentStr) { - m_page->newWidthDouble->blockSignals(true); - m_page->newWidthDouble->setValue((value * 100.0) / m_originalWidth); - m_page->newWidthDouble->blockSignals(false); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->widthUnit->currentIndex()); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newWidthDouble->blockSignals(true); - m_page->newWidthDouble->setValue(selectedUnit.toUserValue(value / m_resolution)); - m_page->newWidthDouble->blockSignals(false); - } else { - m_page->newWidth->blockSignals(true); - m_page->newWidth->setValue(selectedUnit.toUserValue(value)); - m_page->newWidth->blockSignals(false); - } - } -} - -void DlgCanvasSize::updateHeightUIValue(double value) -{ - if (m_page->heightUnit->currentText() == percentStr) { - m_page->newHeightDouble->blockSignals(true); - m_page->newHeightDouble->setValue((value * 100.0) / m_originalHeight); - m_page->newHeightDouble->blockSignals(false); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->heightUnit->currentIndex()); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newHeightDouble->blockSignals(true); - m_page->newHeightDouble->setValue(selectedUnit.toUserValue(value / m_resolution)); - m_page->newHeightDouble->blockSignals(false); - } else { - m_page->newHeight->blockSignals(true); - m_page->newHeight->setValue(selectedUnit.toUserValue(value)); - m_page->newHeight->blockSignals(false); - } - } -} - -void DlgCanvasSize::updateXOffsetUIValue(double value) -{ - if (m_page->xOffUnit->currentText() == percentStr) { - m_page->xOffsetDouble->blockSignals(true); - m_page->xOffsetDouble->setValue((value * 100.0) / m_newWidth); - m_page->xOffsetDouble->blockSignals(false); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->xOffsetDouble->blockSignals(true); - m_page->xOffsetDouble->setValue(selectedUnit.toUserValue(value / m_resolution)); - m_page->xOffsetDouble->blockSignals(false); - } else { - m_page->xOffset->blockSignals(true); - m_page->xOffset->setValue(qRound(selectedUnit.toUserValue(value))); - m_page->xOffset->blockSignals(false); - } - } -} - -void DlgCanvasSize::updateYOffsetUIValue(double value) -{ - if (m_page->yOffUnit->currentText() == percentStr) { - m_page->yOffsetDouble->blockSignals(true); - m_page->yOffsetDouble->setValue((value * 100.0) / m_newHeight); - m_page->yOffsetDouble->blockSignals(false); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->yOffsetDouble->blockSignals(true); - m_page->yOffsetDouble->setValue(selectedUnit.toUserValue(value / m_resolution)); - m_page->yOffsetDouble->blockSignals(false); - } else { - m_page->yOffset->blockSignals(true); - m_page->yOffset->setValue(qRound(selectedUnit.toUserValue(value))); - m_page->yOffset->blockSignals(false); - } - } + double newVal = v / _yOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_page->yOffsetDouble->changeValue(newVal); } void DlgCanvasSize::loadAnchorIcons() { m_anchorIcons[NORTH_WEST] = KisIconUtils::loadIcon("arrow-topleft"); m_anchorIcons[NORTH] = KisIconUtils::loadIcon("arrow-up"); m_anchorIcons[NORTH_EAST] = KisIconUtils::loadIcon("arrow-topright"); m_anchorIcons[EAST] = KisIconUtils::loadIcon("arrow-right"); m_anchorIcons[CENTER] = KisIconUtils::loadIcon("arrow_center"); m_anchorIcons[WEST] = KisIconUtils::loadIcon("arrow-left"); m_anchorIcons[SOUTH_WEST] = KisIconUtils::loadIcon("arrow-downleft"); m_anchorIcons[SOUTH] = KisIconUtils::loadIcon("arrow-down"); m_anchorIcons[SOUTH_EAST] = KisIconUtils::loadIcon("arrow-downright"); } void DlgCanvasSize::updateAnchorIcons(int id) { anchor iconLayout[10][9] = { {NONE, EAST, NONE, SOUTH, SOUTH_EAST, NONE, NONE, NONE, NONE}, {WEST, NONE, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST, NONE, NONE, NONE}, {NONE, WEST, NONE, NONE, SOUTH_WEST, SOUTH, NONE, NONE, NONE}, {NORTH, NORTH_EAST, NONE, NONE, EAST, NONE, SOUTH, SOUTH_EAST, NONE}, {NORTH_WEST, NORTH, NORTH_EAST, WEST, NONE, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST}, {NONE, NORTH_WEST, NORTH, NONE, WEST, NONE, NONE, SOUTH_WEST, SOUTH}, {NONE, NONE, NONE, NORTH, NORTH_EAST, NONE, NONE, EAST, NONE}, {NONE, NONE, NONE, NORTH_WEST, NORTH, NORTH_EAST, WEST, NONE, EAST}, {NONE, NONE, NONE, NONE, NORTH_WEST, NORTH, NONE, WEST, NONE}, {NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE} }; if (id == -1) { id = SOUTH_EAST + 1; } // we are going to swap arrows direction based on width and height shrinking bool shrinkWidth = (m_newWidth < m_originalWidth) ? true : false; bool shrinkHeight = (m_newHeight < m_originalHeight) ? true : false; for (int i = NORTH_WEST; i <= SOUTH_EAST; i++) { anchor iconId = iconLayout[id][i]; // all corner arrows represents shrinking in some direction if (shrinkWidth || shrinkHeight) { switch (iconId) { case NORTH_WEST: iconId = SOUTH_EAST; break; case NORTH_EAST: iconId = SOUTH_WEST; break; case SOUTH_WEST: iconId = NORTH_EAST; break; case SOUTH_EAST: iconId = NORTH_WEST; break; default: break; } } if (shrinkWidth) { switch (iconId) { case WEST: iconId = EAST; break; case EAST: iconId = WEST; break; default: break; } } if (shrinkHeight) { switch (iconId) { case NORTH: iconId = SOUTH; break; case SOUTH: iconId = NORTH; break; default: break; } } QAbstractButton *button = m_group->button(i); if (iconId == NONE) { button->setIcon(QIcon()); } else { button->setIcon(m_anchorIcons[iconId]); } } } void DlgCanvasSize::updateButtons(int forceId) { int id = m_group->checkedId(); if (forceId != -1) { m_group->setExclusive(true); m_group->button(forceId)->setChecked(true); updateAnchorIcons(forceId); } else if (id != -1) { double xOffset, yOffset; expectedOffset(id, xOffset, yOffset); // convert values to internal unit int internalXOffset = 0; int internalYOffset = 0; if (m_page->xOffUnit->currentText() == percentStr) { internalXOffset = qRound((xOffset * m_newWidth) / 100.0); internalYOffset = qRound((yOffset * m_newHeight) / 100.0); } else { const KoUnit xOffsetUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); internalXOffset = qRound(xOffsetUnit.fromUserValue(xOffset)); const KoUnit yOffsetUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); internalYOffset = qRound(yOffsetUnit.fromUserValue(yOffset)); } bool offsetAsExpected = - internalXOffset == m_xOffset && - internalYOffset == m_yOffset; + internalXOffset == m_xOffset && + internalYOffset == m_yOffset; if (offsetAsExpected) { m_group->setExclusive(true); } else { m_group->setExclusive(false); m_group->button(id)->setChecked(false); id = -1; } updateAnchorIcons(id); } else { updateAnchorIcons(id); } } void DlgCanvasSize::updateOffset(int id) { if (id == -1) return; double xOffset; double yOffset; expectedOffset(id, xOffset, yOffset); - const KoUnit pixelUnit(KoUnit::Pixel); - - // update spinbox value (other widgets will be autoupdated later through valueChanged signal) - if (m_page->xOffUnit->currentText() == percentStr) { - m_page->xOffsetDouble->setValue(xOffset); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); - if (pixelUnit != selectedUnit) { - m_page->xOffsetDouble->setValue(xOffset); - } else { - m_page->xOffset->setValue(qRound(xOffset)); - } - } - - // update spinbox value (other widgets will be autoupdated later through valueChanged signal) - if (m_page->yOffUnit->currentText() == percentStr) { - m_page->yOffsetDouble->setValue(yOffset); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); - if (pixelUnit != selectedUnit) { - m_page->yOffsetDouble->setValue(yOffset); - } else { - m_page->yOffset->setValue(qRound(yOffset)); - } - } + m_page->xOffsetDouble->changeValue(xOffset); + m_page->yOffsetDouble->changeValue(yOffset); } void DlgCanvasSize::expectedOffset(int id, double &xOffset, double &yOffset) { const double xCoeff = (id % 3) * 0.5; const double yCoeff = (id / 3) * 0.5; const int xDiff = m_newWidth - m_originalWidth; const int yDiff = m_newHeight - m_originalHeight; - // use selected unit to convert expected values (the inverse will be do later) - // so output values are now considered as if they were regular user input - if (m_page->xOffUnit->currentText() == percentStr) { - xOffset = (xDiff * xCoeff * 100.0) / m_newWidth; - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->xOffUnit->currentIndex()); - const double resXDiff = (selectedUnit != KoUnit(KoUnit::Pixel)) ? xDiff / m_resolution : xDiff; - xOffset = selectedUnit.toUserValue(resXDiff * xCoeff); - } - - if (m_page->yOffUnit->currentText() == percentStr) { - yOffset = (yDiff * yCoeff * 100.0) / m_newHeight; - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->yOffUnit->currentIndex()); - const double resYDiff = (selectedUnit != KoUnit(KoUnit::Pixel)) ? yDiff / m_resolution : yDiff; - yOffset = selectedUnit.toUserValue(resYDiff * yCoeff); - } + //convert to unitmanager default unit. + xOffset = xDiff * xCoeff / _xOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + yOffset = yDiff * yCoeff / _yOffsetUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); } diff --git a/plugins/extensions/imagesize/dlg_canvassize.h b/plugins/extensions/imagesize/dlg_canvassize.h index 9b2ea69641..91ea435bed 100644 --- a/plugins/extensions/imagesize/dlg_canvassize.h +++ b/plugins/extensions/imagesize/dlg_canvassize.h @@ -1,106 +1,100 @@ /* * * Copyright (c) 2009 Edward Apap * Copyright (c) 2013 Juan Palacios * * 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 2 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. */ #ifndef DLG_CANVASSIZE #define DLG_CANVASSIZE #include #include #include "ui_wdg_canvassize.h" +class KisDocumentAwareSpinBoxUnitManager; + class WdgCanvasSize : public QWidget, public Ui::WdgCanvasSize { Q_OBJECT public: WdgCanvasSize(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgCanvasSize: public KoDialog { Q_OBJECT public: enum anchor { NORTH_WEST = 0, NORTH, NORTH_EAST, WEST, CENTER, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST, NONE}; DlgCanvasSize(QWidget * parent, int width, int height, double resolution); ~DlgCanvasSize(); qint32 width(); qint32 height(); qint32 xOffset(); qint32 yOffset(); private Q_SLOTS: void slotAspectChanged(bool keep); void slotAnchorButtonClicked(int id); - void slotWidthChanged(int v); - void slotHeightChanged(int v); void slotWidthChanged(double v); void slotHeightChanged(double v); - void slotWidthUnitChanged(int index); - void slotHeightUnitChanged(int index); - - void slotXOffsetChanged(int v); - void slotYOffsetChanged(int v); void slotXOffsetChanged(double v); void slotYOffsetChanged(double v); - void slotXOffsetUnitChanged(int index); - void slotYOffsetUnitChanged(int index); - void slotCanvasPreviewXOffsetChanged(int v); void slotCanvasPreviewYOffsetChanged(int v); private: - void updateWidthUIValue(double value); - void updateHeightUIValue(double value); - void updateXOffsetUIValue(double value); - void updateYOffsetUIValue(double value); void loadAnchorIcons(); void updateAnchorIcons(int id); void updateButtons(int forceId); void updateOffset(int id); void expectedOffset(int id, double &xOffset, double &yOffset); bool m_keepAspect; const double m_aspectRatio; const double m_resolution; const int m_originalWidth, m_originalHeight; int m_newWidth, m_newHeight; int m_xOffset, m_yOffset; WdgCanvasSize * m_page; QIcon m_anchorIcons[9]; QButtonGroup *m_group; + + KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; + KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; + + KisDocumentAwareSpinBoxUnitManager* _xOffsetUnitManager; + KisDocumentAwareSpinBoxUnitManager* _yOffsetUnitManager; }; #endif // DLG_CANVASSIZE diff --git a/plugins/extensions/imagesize/dlg_imagesize.cc b/plugins/extensions/imagesize/dlg_imagesize.cc index 1d38c57bdc..846c7fa66b 100644 --- a/plugins/extensions/imagesize/dlg_imagesize.cc +++ b/plugins/extensions/imagesize/dlg_imagesize.cc @@ -1,493 +1,422 @@ /* * dlg_imagesize.cc - part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2009 C. Boemann * Copyright (c) 2013 Juan Palacios * * 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 2 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. */ #include "dlg_imagesize.h" #include #include #include #include #include +#include "kis_double_parse_unit_spin_box.h" +#include "kis_document_aware_spin_box_unit_manager.h" + static const QString pixelStr(KoUnit::unitDescription(KoUnit::Pixel)); static const QString percentStr(i18n("Percent (%)")); static const QString pixelsInchStr(i18n("Pixels/Inch")); static const QString pixelsCentimeterStr(i18n("Pixels/Centimeter")); DlgImageSize::DlgImageSize(QWidget *parent, int width, int height, double resolution) : KoDialog(parent) , m_aspectRatio(((double) width) / height) , m_originalWidth(width) , m_originalHeight(height) , m_width(width) , m_height(height) , m_printWidth(width / resolution) , m_printHeight(height / resolution) , m_originalResolution(resolution) , m_resolution(resolution) , m_keepAspect(true) { setCaption(i18n("Scale To New Size")); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgImageSize(this); Q_CHECK_PTR(m_page); m_page->layout()->setMargin(0); m_page->setObjectName("image_size"); - m_page->pixelWidth->setValue(width); - m_page->pixelWidth->setFocus(); - m_page->pixelHeight->setValue(height); + _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); + _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); + + //configure the unit to image length, default unit is pixel and printing units are forbiden. + _widthUnitManager->setUnitDimension(KisSpinBoxUnitManager::IMLENGTH); + _heightUnitManager->setUnitDimension(KisSpinBoxUnitManager::IMLENGTH); + + m_page->pixelWidthDouble->setUnitManager(_widthUnitManager); + m_page->pixelHeightDouble->setUnitManager(_heightUnitManager); + + m_page->pixelWidthDouble->changeValue(width); + m_page->pixelHeightDouble->changeValue(height); + m_page->pixelWidthDouble->setDecimals(2); + m_page->pixelHeightDouble->setDecimals(2); + m_page->pixelWidthDouble->setDisplayUnit(false); + m_page->pixelHeightDouble->setDisplayUnit(false); - m_page->pixelWidthDouble->setVisible(false); - m_page->pixelHeightDouble->setVisible(false); + m_page->pixelWidthUnit->setModel(_widthUnitManager); + m_page->pixelHeightUnit->setModel(_widthUnitManager); + m_page->pixelWidthUnit->setCurrentText("px"); + m_page->pixelHeightUnit->setCurrentText("px"); m_page->pixelFilterCmb->setIDList(KisFilterStrategyRegistry::instance()->listKeys()); m_page->pixelFilterCmb->setToolTip(KisFilterStrategyRegistry::instance()->formatedDescriptions()); m_page->pixelFilterCmb->setCurrent("Bicubic"); - m_page->pixelWidthUnit->addItem(pixelStr); - m_page->pixelWidthUnit->addItem(percentStr); - m_page->pixelWidthUnit->setCurrentIndex(0); + _printWidthUnitManager = new KisSpinBoxUnitManager(this); + _printHeightUnitManager = new KisSpinBoxUnitManager(this); - m_page->pixelHeightUnit->addItem(pixelStr); - m_page->pixelHeightUnit->addItem(percentStr); - m_page->pixelHeightUnit->setCurrentIndex(0); + m_page->printWidth->setUnitManager(_printWidthUnitManager); + m_page->printHeight->setUnitManager(_printHeightUnitManager); + m_page->printWidth->setDecimals(2); + m_page->printHeight->setDecimals(2); + m_page->printWidth->setDisplayUnit(false); + m_page->printHeight->setDisplayUnit(false); + m_page->printResolution->setDecimals(2); + m_page->printResolution->setAlignment(Qt::AlignRight); - m_page->printWidthUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::HidePixel)); - m_page->printWidthUnit->addItem(percentStr); - m_page->printHeightUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::HidePixel)); - m_page->printHeightUnit->addItem(percentStr); + m_page->printWidthUnit->setModel(_printWidthUnitManager); + m_page->printHeightUnit->setModel(_printHeightUnitManager); + m_page->printWidth->changeValue(m_printWidth); + m_page->printHeight->changeValue(m_printHeight); + + //TODO: create a resolution dimension in the unit manager. m_page->printResolutionUnit->addItem(pixelsInchStr); m_page->printResolutionUnit->addItem(pixelsCentimeterStr); - // pick selected print units from user locale - if (QLocale().measurementSystem() == QLocale::MetricSystem) { - const int unitIndex = KoUnit(KoUnit::Centimeter).indexInListForUi(KoUnit::HidePixel); - m_page->printWidthUnit->setCurrentIndex(unitIndex); - m_page->printHeightUnit->setCurrentIndex(unitIndex); - m_page->printResolutionUnit->setCurrentIndex(0); // Pixels/Centimeter - } else { // Imperial - const int unitIndex = KoUnit(KoUnit::Inch).indexInListForUi(KoUnit::HidePixel); - m_page->printWidthUnit->setCurrentIndex(unitIndex); - m_page->printHeightUnit->setCurrentIndex(unitIndex); - m_page->printResolutionUnit->setCurrentIndex(1); // Pixels/Inch - } - updatePrintWidthUIValue(m_printWidth); - updatePrintHeightUIValue(m_printHeight); - updatePrintResolutionUIValue(m_resolution); - m_page->pixelAspectRatioBtn->setKeepAspectRatio(true); m_page->printAspectRatioBtn->setKeepAspectRatio(true); m_page->constrainProportionsCkb->setChecked(true); KisSizeGroup *labelsGroup = new KisSizeGroup(this); labelsGroup->addWidget(m_page->lblPixelWidth); labelsGroup->addWidget(m_page->lblPixelHeight); labelsGroup->addWidget(m_page->lblPixelFilter); labelsGroup->addWidget(m_page->lblPrintWidth); labelsGroup->addWidget(m_page->lblPrintHeight); labelsGroup->addWidget(m_page->lblResolution); KisSizeGroup *spinboxesGroup = new KisSizeGroup(this); - spinboxesGroup->addWidget(m_page->pixelWidth); spinboxesGroup->addWidget(m_page->pixelWidthDouble); - spinboxesGroup->addWidget(m_page->pixelHeight); spinboxesGroup->addWidget(m_page->pixelHeightDouble); spinboxesGroup->addWidget(m_page->printWidth); spinboxesGroup->addWidget(m_page->printHeight); spinboxesGroup->addWidget(m_page->printResolution); KisSizeGroup *comboboxesGroup = new KisSizeGroup(this); comboboxesGroup->addWidget(m_page->pixelWidthUnit); comboboxesGroup->addWidget(m_page->pixelHeightUnit); comboboxesGroup->addWidget(m_page->printWidthUnit); comboboxesGroup->addWidget(m_page->printHeightUnit); comboboxesGroup->addWidget(m_page->printResolutionUnit); connect(this, SIGNAL(okClicked()), this, SLOT(accept())); connect(m_page->pixelAspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->printAspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotAspectChanged(bool))); - connect(m_page->pixelWidth, SIGNAL(valueChanged(int)), this, SLOT(slotPixelWidthChanged(int))); - connect(m_page->pixelHeight, SIGNAL(valueChanged(int)), this, SLOT(slotPixelHeightChanged(int))); - connect(m_page->pixelWidthDouble, SIGNAL(valueChanged(double)), this, SLOT(slotPixelWidthChanged(double))); - connect(m_page->pixelHeightDouble, SIGNAL(valueChanged(double)), this, SLOT(slotPixelHeightChanged(double))); - connect(m_page->pixelWidthUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPixelWidthUnitChanged())); - connect(m_page->pixelHeightUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPixelHeightUnitChanged())); + connect(m_page->pixelWidthDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotPixelWidthChanged(double))); + connect(m_page->pixelHeightDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotPixelHeightChanged(double))); + connect(m_page->pixelWidthUnit, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(m_page->pixelHeightUnit, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->pixelWidthUnit, SLOT(setCurrentIndex(int))); + connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->pixelHeightUnit, SLOT(setCurrentIndex(int))); - connect(m_page->printWidth, SIGNAL(valueChanged(double)), this, SLOT(slotPrintWidthChanged(double))); - connect(m_page->printHeight, SIGNAL(valueChanged(double)), this, SLOT(slotPrintHeightChanged(double))); - connect(m_page->printWidthUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPrintWidthUnitChanged())); - connect(m_page->printHeightUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPrintHeightUnitChanged())); + connect(m_page->printWidth, SIGNAL(valueChangedPt(double)), this, SLOT(slotPrintWidthChanged(double))); + connect(m_page->printHeight, SIGNAL(valueChangedPt(double)), this, SLOT(slotPrintHeightChanged(double))); + connect(m_page->printWidthUnit, SIGNAL(currentIndexChanged(int)), _printWidthUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(m_page->printHeightUnit, SIGNAL(currentIndexChanged(int)), _printHeightUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(_printWidthUnitManager, SIGNAL(unitChanged(int)), m_page->printWidthUnit, SLOT(setCurrentIndex(int))); + connect(_printHeightUnitManager, SIGNAL(unitChanged(int)), m_page->printHeightUnit, SLOT(setCurrentIndex(int))); connect(m_page->printResolution, SIGNAL(valueChanged(double)), this, SLOT(slotPrintResolutionChanged(double))); connect(m_page->printResolution, SIGNAL(editingFinished()), this, SLOT(slotPrintResolutionEditFinished())); connect(m_page->printResolutionUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPrintResolutionUnitChanged())); + // pick selected print units from user locale (after slots connection, so the spinbox will be updated too). + if (QLocale().measurementSystem() == QLocale::MetricSystem) { + m_page->printWidthUnit->setCurrentText("cm"); + m_page->printHeightUnit->setCurrentText("cm"); + m_page->printResolutionUnit->setCurrentIndex(0); // Pixels/Centimeter + slotPrintResolutionUnitChanged(); //ensure the resolution is updated, even if the index didn't changed. + } else { // Imperial + m_page->printWidthUnit->setCurrentText("in"); + m_page->printHeightUnit->setCurrentText("in"); + m_page->printResolutionUnit->setCurrentIndex(1); // Pixels/Inch + slotPrintResolutionUnitChanged(); //ensure the resolution is updated, even if the index didn't changed. + } + setMainWidget(m_page); } DlgImageSize::~DlgImageSize() { delete m_page; } qint32 DlgImageSize::width() { return (qint32)m_width; } qint32 DlgImageSize::height() { return (qint32)m_height; } double DlgImageSize::resolution() { return m_resolution; } KisFilterStrategy *DlgImageSize::filterType() { KoID filterID = m_page->pixelFilterCmb->currentItem(); KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value(filterID.id()); return filter; } // SLOTS -void DlgImageSize::slotPixelWidthChanged(int w) -{ - slotPixelWidthChanged((double) w); -} - -void DlgImageSize::slotPixelHeightChanged(int h) -{ - slotPixelHeightChanged((double) h); -} - void DlgImageSize::slotPixelWidthChanged(double w) { - if (m_page->pixelWidthUnit->currentText() == percentStr) { - m_width = qRound((w * m_originalWidth) / 100.0); - } else { - m_width = w; - } + m_width = w; m_printWidth = m_width / m_resolution; updatePrintWidthUIValue(m_printWidth); if (m_keepAspect) { m_height = qRound(m_width / m_aspectRatio); updatePixelHeightUIValue(m_height); m_printHeight = m_height / m_resolution; updatePrintHeightUIValue(m_printHeight); } } void DlgImageSize::slotPixelHeightChanged(double h) { - if (m_page->pixelHeightUnit->currentText() == percentStr) { - m_height = qRound((h * m_originalHeight) / 100.0); - } else { - m_height = h; - } + m_height = h; m_printHeight = m_height / m_resolution; updatePrintHeightUIValue(m_printHeight); if (m_keepAspect) { m_width = qRound(m_height * m_aspectRatio); updatePixelWidthUIValue(m_width); m_printWidth = m_width / m_resolution; updatePrintWidthUIValue(m_printWidth); } } -void DlgImageSize::slotPixelWidthUnitChanged() -{ - updatePixelWidthUIValue(m_width); - - m_page->pixelWidth->setVisible(m_page->pixelWidthUnit->currentText() == pixelStr); - m_page->pixelWidthDouble->setVisible(m_page->pixelWidthUnit->currentText() == percentStr); -} - -void DlgImageSize::slotPixelHeightUnitChanged() -{ - updatePixelHeightUIValue(m_height); - - m_page->pixelHeight->setVisible(m_page->pixelHeightUnit->currentText() == pixelStr); - m_page->pixelHeightDouble->setVisible(m_page->pixelHeightUnit->currentText() == percentStr); -} - void DlgImageSize::slotPrintWidthChanged(double w) { - if (m_page->printWidthUnit->currentText() == percentStr) { - const double originalWidthPoint = m_originalWidth / m_originalResolution; - m_printWidth = (w * originalWidthPoint) / 100.0; - } else { - KoUnit selectedUnit = KoUnit::fromListForUi(m_page->printWidthUnit->currentIndex(), KoUnit::HidePixel); - m_printWidth = selectedUnit.fromUserValue(w); - } + m_printWidth = w; if (m_keepAspect) { m_printHeight = m_printWidth / m_aspectRatio; updatePrintHeightUIValue(m_printHeight); } if (m_page->adjustPrintSizeSeparatelyCkb->isChecked()) { m_resolution = m_width / m_printWidth; updatePrintResolutionUIValue(m_resolution); if (!m_keepAspect) { // compute and update a new image height value from the print size values const double printHeightInch = KoUnit::convertFromUnitToUnit(m_printHeight, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); m_height = qRound(printHeightInch * 72 * m_resolution); updatePixelHeightUIValue(m_height); } } else { const double printWidthInch = KoUnit::convertFromUnitToUnit(m_printWidth, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); m_width = qRound(printWidthInch * 72 * m_resolution); updatePixelWidthUIValue(m_width); if (m_keepAspect) { m_height = qRound(m_width / m_aspectRatio); updatePixelHeightUIValue(m_height); } } } void DlgImageSize::slotPrintHeightChanged(double h) { - if (m_page->printHeightUnit->currentText() == percentStr) { - const double originalHeightPoint = m_originalHeight / m_originalResolution; - m_printHeight = (h * originalHeightPoint) / 100.0; - } else { - KoUnit selectedUnit = KoUnit::fromListForUi(m_page->printHeightUnit->currentIndex(), KoUnit::HidePixel); - m_printHeight = selectedUnit.fromUserValue(h); - } + m_printHeight = h; if (m_keepAspect) { m_printWidth = m_printHeight * m_aspectRatio; updatePrintWidthUIValue(m_printWidth); } if (m_page->adjustPrintSizeSeparatelyCkb->isChecked()) { m_resolution = m_height / m_printHeight; updatePrintResolutionUIValue(m_resolution); if (!m_keepAspect) { // compute and update a new image width value from the print size values const double printWidthInch = KoUnit::convertFromUnitToUnit(m_printWidth, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); m_width = qRound(printWidthInch * 72 * m_resolution); updatePixelWidthUIValue(m_width); } } else { const double printHeightInch = KoUnit::convertFromUnitToUnit(m_printHeight, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); m_height = qRound(printHeightInch * 72 * m_resolution); updatePixelHeightUIValue(m_height); if (m_keepAspect) { m_width = qRound(m_height * m_aspectRatio); updatePixelWidthUIValue(m_width); } } } -void DlgImageSize::slotPrintWidthUnitChanged() -{ - updatePrintWidthUIValue(m_printWidth); -} - -void DlgImageSize::slotPrintHeightUnitChanged() -{ - updatePrintHeightUIValue(m_printHeight); -} - void DlgImageSize::slotAspectChanged(bool keep) { m_page->pixelAspectRatioBtn->blockSignals(true); m_page->printAspectRatioBtn->blockSignals(true); m_page->constrainProportionsCkb->blockSignals(true); m_page->pixelAspectRatioBtn->setKeepAspectRatio(keep); m_page->printAspectRatioBtn->setKeepAspectRatio(keep); m_page->constrainProportionsCkb->setChecked(keep); m_page->pixelAspectRatioBtn->blockSignals(false); m_page->printAspectRatioBtn->blockSignals(false); m_page->constrainProportionsCkb->blockSignals(false); m_keepAspect = keep; if (keep) { // values may be out of sync, so we need to reset it to defaults m_width = m_originalWidth; m_height = m_originalHeight; m_printWidth = m_originalWidth / m_originalResolution; m_printHeight = m_originalHeight / m_originalResolution; m_resolution = m_originalResolution; updatePixelWidthUIValue(m_width); updatePixelHeightUIValue(m_height); updatePrintWidthUIValue(m_printWidth); updatePrintHeightUIValue(m_printHeight); updatePrintResolutionUIValue(m_resolution); } } void DlgImageSize::slotPrintResolutionChanged(double r) { if (m_page->printResolutionUnit->currentText() == pixelsInchStr) m_resolution = KoUnit::convertFromUnitToUnit(r, KoUnit(KoUnit::Pixel), KoUnit(KoUnit::Inch)); else m_resolution = KoUnit::convertFromUnitToUnit(r, KoUnit(KoUnit::Pixel), KoUnit(KoUnit::Centimeter)); if (m_page->adjustPrintSizeSeparatelyCkb->isChecked()) { m_printWidth = m_width / m_resolution; m_printHeight = m_height / m_resolution; updatePrintWidthUIValue(m_printWidth); updatePrintHeightUIValue(m_printHeight); } else { // Do not commit m_width and m_height values yet. This is done to avoid // nasty results in image size values while the user is typing a resolution value const double printWidthInch = KoUnit::convertFromUnitToUnit(m_printWidth, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); const int width = qRound(printWidthInch * 72 * m_resolution); const double printHeightInch = KoUnit::convertFromUnitToUnit(m_printHeight, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); const int height = qRound(printHeightInch * 72 * m_resolution); updatePixelWidthUIValue(width); updatePixelHeightUIValue(height); } } void DlgImageSize::slotPrintResolutionEditFinished() { if (!m_page->adjustPrintSizeSeparatelyCkb->isChecked()) { const double printWidthInch = KoUnit::convertFromUnitToUnit(m_printWidth, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); const double printHeightInch = KoUnit::convertFromUnitToUnit(m_printHeight, KoUnit(KoUnit::Point), KoUnit(KoUnit::Inch)); // Commit width and height values m_width = qRound(printWidthInch * 72 * m_resolution); m_height = qRound(printHeightInch * 72 * m_resolution); // Note that spinbox values should be up to date // (updated through slotResolutionChanged()) } } void DlgImageSize::slotPrintResolutionUnitChanged() { updatePrintResolutionUIValue(m_resolution); } void DlgImageSize::updatePixelWidthUIValue(double value) { - if (m_page->pixelWidthUnit->currentText() == percentStr) { - m_page->pixelWidthDouble->blockSignals(true); - m_page->pixelWidthDouble->setValue((value * 100.0) / m_originalWidth); - m_page->pixelWidthDouble->blockSignals(false); - } else { - m_page->pixelWidth->blockSignals(true); - m_page->pixelWidth->setValue(value); - m_page->pixelWidth->blockSignals(false); - } + m_page->pixelWidthDouble->blockSignals(true); + m_page->pixelWidthDouble->changeValue(value); + m_page->pixelWidthDouble->blockSignals(false); } void DlgImageSize::updatePixelHeightUIValue(double value) { - if (m_page->pixelHeightUnit->currentText() == percentStr) { - m_page->pixelHeightDouble->blockSignals(true); - m_page->pixelHeightDouble->setValue((value * 100.0) / m_originalHeight); - m_page->pixelHeightDouble->blockSignals(false); - } else { - m_page->pixelHeight->blockSignals(true); - m_page->pixelHeight->setValue(value); - m_page->pixelHeight->blockSignals(false); - } + m_page->pixelHeightDouble->blockSignals(true); + m_page->pixelHeightDouble->changeValue(value); + m_page->pixelHeightDouble->blockSignals(false); } void DlgImageSize::updatePrintWidthUIValue(double value) { - double uiValue = 0.0; - if (m_page->printWidthUnit->currentText() == percentStr) { - // We need to compute percent in point unit because: - // - originalWith is a value expressed in px (original resolution) - // - value is expressed in point unit (current resolution) - // - the percentage value should be based on the original print size - const double originalWidthPoint = m_originalWidth / m_originalResolution; - uiValue = (value * 100.0) / originalWidthPoint; - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->printWidthUnit->currentIndex()); - uiValue = selectedUnit.toUserValue(value); - } m_page->printWidth->blockSignals(true); - m_page->printWidth->setValue(uiValue); + m_page->printWidth->changeValue(value); m_page->printWidth->blockSignals(false); } void DlgImageSize::updatePrintHeightUIValue(double value) { - double uiValue = 0.0; - if (m_page->printHeightUnit->currentText() == percentStr) { - // We need to compute percent in point unit because: - // - originalHeight is a value expressed in px (original resolution) - // - value is expressed in point unit (current resolution) - // - the percentage value should be based on the original print size - const double originalHeightPoint = m_originalHeight / m_originalResolution; - uiValue = (value * 100.0) / originalHeightPoint; - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->printHeightUnit->currentIndex()); - uiValue = selectedUnit.toUserValue(value); - } m_page->printHeight->blockSignals(true); - m_page->printHeight->setValue(uiValue); + m_page->printHeight->changeValue(value); m_page->printHeight->blockSignals(false); } void DlgImageSize::updatePrintResolutionUIValue(double value) { double uiValue = 0.0; if (m_page->printResolutionUnit->currentText() == pixelsInchStr) { // show the value in pixel/inch unit uiValue = KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Inch), KoUnit(KoUnit::Pixel)); } else { // show the value in pixel/centimeter unit uiValue = KoUnit::convertFromUnitToUnit(value, KoUnit(KoUnit::Centimeter), KoUnit(KoUnit::Pixel)); } m_page->printResolution->blockSignals(true); m_page->printResolution->setValue(uiValue); m_page->printResolution->blockSignals(false); } diff --git a/plugins/extensions/imagesize/dlg_imagesize.h b/plugins/extensions/imagesize/dlg_imagesize.h index ba2d8c7667..77e4fbf527 100644 --- a/plugins/extensions/imagesize/dlg_imagesize.h +++ b/plugins/extensions/imagesize/dlg_imagesize.h @@ -1,89 +1,91 @@ /* * dlg_imagesize.h -- part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2013 Juan Palacios * * 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 2 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. */ #ifndef DLG_IMAGESIZE #define DLG_IMAGESIZE #include class KisFilterStrategy; class WdgImageSize; +class KisDocumentAwareSpinBoxUnitManager; +class KisSpinBoxUnitManager; #include "ui_wdg_imagesize.h" class WdgImageSize : public QWidget, public Ui::WdgImageSize { Q_OBJECT public: WdgImageSize(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgImageSize: public KoDialog { Q_OBJECT public: DlgImageSize(QWidget * parent, int width, int height, double resolution); ~DlgImageSize(); qint32 width(); qint32 height(); double resolution(); KisFilterStrategy *filterType(); private Q_SLOTS: - void slotPixelWidthChanged(int w); - void slotPixelHeightChanged(int h); void slotPixelWidthChanged(double w); void slotPixelHeightChanged(double h); - void slotPixelWidthUnitChanged(); - void slotPixelHeightUnitChanged(); void slotPrintWidthChanged(double w); void slotPrintHeightChanged(double h); - void slotPrintWidthUnitChanged(); - void slotPrintHeightUnitChanged(); void slotAspectChanged(bool keep); void slotPrintResolutionChanged(double r); void slotPrintResolutionEditFinished(); void slotPrintResolutionUnitChanged(); private: void updatePixelWidthUIValue(double value); void updatePixelHeightUIValue(double value); void updatePrintWidthUIValue(double value); void updatePrintHeightUIValue(double value); void updatePrintResolutionUIValue(double value); WdgImageSize *m_page; const double m_aspectRatio; const int m_originalWidth, m_originalHeight; int m_width, m_height; double m_printWidth, m_printHeight; // in points const double m_originalResolution; double m_resolution; bool m_keepAspect; + + KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; + KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; + + KisSpinBoxUnitManager* _printWidthUnitManager; + KisSpinBoxUnitManager* _printHeightUnitManager; }; #endif // DLG_IMAGESIZE diff --git a/plugins/extensions/imagesize/dlg_layersize.cc b/plugins/extensions/imagesize/dlg_layersize.cc index 429dcdd72c..621eaf24e3 100644 --- a/plugins/extensions/imagesize/dlg_layersize.cc +++ b/plugins/extensions/imagesize/dlg_layersize.cc @@ -1,260 +1,197 @@ /* * dlg_layersize.cc - part of Krita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Sven Langkamp * Copyright (c) 2013 Juan Palacios * * 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 2 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. */ #include "dlg_layersize.h" #include #include +#include + #include // XXX: I'm really real bad at arithmetic, let alone math. Here // be rounding errors. (Boudewijn) static const QString pixelStr(KoUnit::unitDescription(KoUnit::Pixel)); static const QString percentStr(i18n("Percent (%)")); DlgLayerSize::DlgLayerSize(QWidget * parent, const char * name, int width, int height, double resolution) - : KoDialog(parent) - , m_aspectRatio(((double) width) / height) - , m_originalWidth(width) - , m_originalHeight(height) - , m_width(width) - , m_height(height) - , m_resolution(resolution) - , m_keepAspect(true) + : KoDialog(parent) + , m_aspectRatio(((double) width) / height) + , m_originalWidth(width) + , m_originalHeight(height) + , m_width(width) + , m_height(height) + , m_resolution(resolution) + , m_keepAspect(true) { setCaption(i18n("Layer Size")); setObjectName(name); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgLayerSize(this); Q_CHECK_PTR(m_page); m_page->layout()->setMargin(0); m_page->setObjectName(name); - m_page->newWidth->setValue(width); - m_page->newWidth->setFocus(); - m_page->newHeight->setValue(height); + _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); + _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); + + _widthUnitManager->setApparentUnitFromSymbol("px"); + _heightUnitManager->setApparentUnitFromSymbol("px"); + + m_page->newWidthDouble->setUnitManager(_widthUnitManager); + m_page->newHeightDouble->setUnitManager(_heightUnitManager); + m_page->newWidthDouble->setDecimals(2); + m_page->newHeightDouble->setDecimals(2); + m_page->newWidthDouble->setDisplayUnit(false); + m_page->newHeightDouble->setDisplayUnit(false); - m_page->newWidthDouble->setVisible(false); - m_page->newHeightDouble->setVisible(false); + m_page->newWidthDouble->setValue(width); + m_page->newWidthDouble->setFocus(); + m_page->newHeightDouble->setValue(height); m_page->filterCmb->setIDList(KisFilterStrategyRegistry::instance()->listKeys()); m_page->filterCmb->setToolTip(KisFilterStrategyRegistry::instance()->formatedDescriptions()); m_page->filterCmb->setCurrent("Bicubic"); - m_page->newWidthUnit->addItems(KoUnit::listOfUnitNameForUi()); - m_page->newWidthUnit->addItem(percentStr); + m_page->newWidthUnit->setModel(_widthUnitManager); + m_page->newHeightUnit->setModel(_heightUnitManager); - m_page->newHeightUnit->addItems(KoUnit::listOfUnitNameForUi()); - m_page->newHeightUnit->addItem(percentStr); - - const int pixelUnitIndex = KoUnit(KoUnit::Pixel).indexInListForUi(); + const int pixelUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf("px"); //TODO: have a better way to identify units. m_page->newWidthUnit->setCurrentIndex(pixelUnitIndex); m_page->newHeightUnit->setCurrentIndex(pixelUnitIndex); m_page->aspectRatioBtn->setKeepAspectRatio(true); m_page->constrainProportionsCkb->setChecked(true); setMainWidget(m_page); connect(this, SIGNAL(okClicked()), this, SLOT(accept())); connect(m_page->aspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool))); connect(m_page->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotAspectChanged(bool))); - connect(m_page->newWidth, SIGNAL(valueChanged(int)), this, SLOT(slotWidthChanged(int))); - connect(m_page->newHeight, SIGNAL(valueChanged(int)), this, SLOT(slotHeightChanged(int))); - connect(m_page->newWidthDouble, SIGNAL(valueChanged(double)), this, SLOT(slotWidthChanged(double))); - connect(m_page->newHeightDouble, SIGNAL(valueChanged(double)), this, SLOT(slotHeightChanged(double))); - connect(m_page->newWidthUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWidthUnitChanged(int))); - connect(m_page->newHeightUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotHeightUnitChanged(int))); + connect(m_page->newWidthDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotWidthChanged(double))); + connect(m_page->newHeightDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotHeightChanged(double))); + + connect(m_page->newWidthUnit, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(m_page->newHeightUnit, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->newWidthUnit, SLOT(setCurrentIndex(int))); + connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->newHeightUnit, SLOT(setCurrentIndex(int))); } DlgLayerSize::~DlgLayerSize() { delete m_page; } qint32 DlgLayerSize::width() { return (qint32)m_width; } qint32 DlgLayerSize::height() { return (qint32)m_height; } KisFilterStrategy *DlgLayerSize::filterType() { KoID filterID = m_page->filterCmb->currentItem(); KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value(filterID.id()); return filter; } // SLOTS -void DlgLayerSize::slotWidthChanged(int w) -{ - slotWidthChanged((double) w); -} - -void DlgLayerSize::slotHeightChanged(int h) -{ - slotHeightChanged((double) h); -} - void DlgLayerSize::slotWidthChanged(double w) { - if (m_page->newWidthUnit->currentText() == percentStr) { - m_width = qRound((w * m_originalWidth) / 100.0); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->newWidthUnit->currentIndex()); - const double resValue = (selectedUnit == KoUnit(KoUnit::Pixel)) ? w : (w * m_resolution); - m_width = qRound(selectedUnit.fromUserValue(resValue)); - } + + //this slot receiv values in pt from the unitspinbox, so just use the resolution. + const double resValue = w*_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_width = qRound(resValue); if (m_keepAspect) { m_height = qRound(m_width / m_aspectRatio); - updateHeightUIValue(m_height); + m_page->newHeightDouble->blockSignals(true); + m_page->newHeightDouble->changeValue(w / m_aspectRatio); + m_page->newHeightDouble->blockSignals(false); } + } void DlgLayerSize::slotHeightChanged(double h) { - if (m_page->newHeightUnit->currentText() == percentStr) { - m_height = qRound((h * m_originalHeight) / 100.0); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->newHeightUnit->currentIndex()); - const double resValue = (selectedUnit == KoUnit(KoUnit::Pixel)) ? h : (h * m_resolution); - m_height = qRound(selectedUnit.fromUserValue(resValue)); - } + + //this slot receiv values in pt from the unitspinbox, so just use the resolution. + const double resValue = h*_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_height = qRound(resValue); if (m_keepAspect) { m_width = qRound(m_height * m_aspectRatio); - updateWidthUIValue(m_width); - } -} - -void DlgLayerSize::slotWidthUnitChanged(int index) -{ - updateWidthUIValue(m_width); - - if (m_page->newWidthUnit->currentText() == percentStr) { - m_page->newWidth->setVisible(false); - m_page->newWidthDouble->setVisible(true); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(index); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newWidth->setVisible(false); - m_page->newWidthDouble->setVisible(true); - } else { - m_page->newWidth->setVisible(true); - m_page->newWidthDouble->setVisible(false); - } - } -} - -void DlgLayerSize::slotHeightUnitChanged(int index) -{ - updateHeightUIValue(m_height); - - if (m_page->newHeightUnit->currentText() == percentStr) { - m_page->newHeight->setVisible(false); - m_page->newHeightDouble->setVisible(true); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(index); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newHeight->setVisible(false); - m_page->newHeightDouble->setVisible(true); - } else { - m_page->newHeight->setVisible(true); - m_page->newHeightDouble->setVisible(false); - } + m_page->newWidthDouble->blockSignals(true); + m_page->newWidthDouble->changeValue(h * m_aspectRatio); + m_page->newWidthDouble->blockSignals(false); } } void DlgLayerSize::slotAspectChanged(bool keep) { m_page->aspectRatioBtn->blockSignals(true); m_page->constrainProportionsCkb->blockSignals(true); m_page->aspectRatioBtn->setKeepAspectRatio(keep); m_page->constrainProportionsCkb->setChecked(keep); m_page->aspectRatioBtn->blockSignals(false); m_page->constrainProportionsCkb->blockSignals(false); m_keepAspect = keep; if (keep) { // values may be out of sync, so we need to reset it to defaults m_width = m_originalWidth; m_height = m_originalHeight; updateWidthUIValue(m_width); updateHeightUIValue(m_height); } } void DlgLayerSize::updateWidthUIValue(double value) { - if (m_page->newWidthUnit->currentText() == percentStr) { - m_page->newWidthDouble->blockSignals(true); - m_page->newWidthDouble->setValue((value * 100.0) / m_originalWidth); - m_page->newWidthDouble->blockSignals(false); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->newWidthUnit->currentIndex()); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newWidthDouble->blockSignals(true); - m_page->newWidthDouble->setValue(selectedUnit.toUserValue(value / m_resolution)); - m_page->newWidthDouble->blockSignals(false); - } else { - m_page->newWidth->blockSignals(true); - m_page->newWidth->setValue(selectedUnit.toUserValue(value)); - m_page->newWidth->blockSignals(false); - } - } + m_page->newWidthDouble->blockSignals(true); + const double resValue = value/_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_page->newWidthDouble->changeValue(resValue); + m_page->newWidthDouble->blockSignals(false); } void DlgLayerSize::updateHeightUIValue(double value) { - if (m_page->newHeightUnit->currentText() == percentStr) { - m_page->newHeightDouble->blockSignals(true); - m_page->newHeightDouble->setValue((value * 100.0) / m_originalHeight); - m_page->newHeightDouble->blockSignals(false); - } else { - const KoUnit selectedUnit = KoUnit::fromListForUi(m_page->newHeightUnit->currentIndex()); - if (selectedUnit != KoUnit(KoUnit::Pixel)) { - m_page->newHeightDouble->blockSignals(true); - m_page->newHeightDouble->setValue(selectedUnit.toUserValue(value / m_resolution)); - m_page->newHeightDouble->blockSignals(false); - } else { - m_page->newHeight->blockSignals(true); - m_page->newHeight->setValue(selectedUnit.toUserValue(value)); - m_page->newHeight->blockSignals(false); - } - } + m_page->newHeightDouble->blockSignals(true); + const double resValue = value/_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px"); + m_page->newHeightDouble->changeValue(resValue); + m_page->newHeightDouble->blockSignals(false); } diff --git a/plugins/extensions/imagesize/dlg_layersize.h b/plugins/extensions/imagesize/dlg_layersize.h index 73c943b10a..53fccdf309 100644 --- a/plugins/extensions/imagesize/dlg_layersize.h +++ b/plugins/extensions/imagesize/dlg_layersize.h @@ -1,78 +1,79 @@ /* * dlg_layersize.h -- part of Krita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Sven Langkamp * Copyright (c) 2013 Juan Palacios * * 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 2 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. */ #ifndef DLG_LAYERSIZE #define DLG_LAYERSIZE #include #include "ui_wdg_layersize.h" +class KisDocumentAwareSpinBoxUnitManager; + class WdgLayerSize : public QWidget, public Ui::WdgLayerSize { Q_OBJECT public: WdgLayerSize(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KisFilterStrategy; class DlgLayerSize: public KoDialog { Q_OBJECT public: DlgLayerSize(QWidget * parent, const char* name, int width, int height, double resolution); ~DlgLayerSize(); qint32 width(); qint32 height(); KisFilterStrategy *filterType(); private Q_SLOTS: - void slotWidthChanged(int w); - void slotHeightChanged(int h); void slotWidthChanged(double w); void slotHeightChanged(double h); - void slotWidthUnitChanged(int index); - void slotHeightUnitChanged(int index); void slotAspectChanged(bool keep); private: void updateWidthUIValue(double value); void updateHeightUIValue(double value); WdgLayerSize * m_page; const double m_aspectRatio; const int m_originalWidth, m_originalHeight; int m_width, m_height; const double m_resolution; bool m_keepAspect; + + KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; + KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; }; #endif // DLG_IMAGESIZE diff --git a/plugins/extensions/imagesize/imagesize.cc b/plugins/extensions/imagesize/imagesize.cc index ca21339e1a..83601304cf 100644 --- a/plugins/extensions/imagesize/imagesize.cc +++ b/plugins/extensions/imagesize/imagesize.cc @@ -1,166 +1,166 @@ /* * imagesize.cc -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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. */ #include "imagesize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dlg_imagesize.h" #include "dlg_canvassize.h" #include "dlg_layersize.h" #include "kis_filter_strategy.h" #include "kis_action.h" #include "kis_action_manager.h" K_PLUGIN_FACTORY_WITH_JSON(ImageSizeFactory, "kritaimagesize.json", registerPlugin();) ImageSize::ImageSize(QObject *parent, const QVariantList &) - : KisViewPlugin(parent) + : KisViewPlugin(parent) { KisAction *action = createAction("imagesize"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageSize())); action = createAction("canvassize"); connect(action, SIGNAL(triggered()), this, SLOT(slotCanvasSize())); action = createAction("layersize"); connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSize())); action = createAction("selectionscale"); connect(action, SIGNAL(triggered()), this, SLOT(slotSelectionScale())); } ImageSize::~ImageSize() { } void ImageSize::slotImageSize() { KisImageSP image = m_view->image().toStrongRef(); if (!image) return; DlgImageSize * dlgImageSize = new DlgImageSize(m_view->mainWindow(), image->width(), image->height(), image->yRes()); Q_CHECK_PTR(dlgImageSize); dlgImageSize->setObjectName("ImageSize"); if (dlgImageSize->exec() == QDialog::Accepted) { qint32 w = dlgImageSize->width(); qint32 h = dlgImageSize->height(); double res = dlgImageSize->resolution(); m_view->imageManager()->scaleCurrentImage(QSize(w, h), res, res, dlgImageSize->filterType()); } delete dlgImageSize; } void ImageSize::slotCanvasSize() { KisImageWSP image = m_view->image(); if (!image) return; DlgCanvasSize * dlgCanvasSize = new DlgCanvasSize(m_view->mainWindow(), image->width(), image->height(), image->yRes()); Q_CHECK_PTR(dlgCanvasSize); if (dlgCanvasSize->exec() == QDialog::Accepted) { qint32 width = dlgCanvasSize->width(); qint32 height = dlgCanvasSize->height(); qint32 xOffset = dlgCanvasSize->xOffset(); qint32 yOffset = dlgCanvasSize->yOffset(); m_view->imageManager()->resizeCurrentImage(width, height, xOffset, yOffset); } delete dlgCanvasSize; } void ImageSize::slotLayerSize() { KisImageWSP image = m_view->image(); if (!image) return; KisPaintDeviceSP dev = m_view->activeLayer()->projection(); Q_ASSERT(dev); QRect rc = dev->exactBounds(); DlgLayerSize * dlgLayerSize = new DlgLayerSize(m_view->mainWindow(), "LayerSize", rc.width(), rc.height(), image->yRes()); Q_CHECK_PTR(dlgLayerSize); dlgLayerSize->setCaption(i18n("Resize Layer")); if (dlgLayerSize->exec() == QDialog::Accepted) { qint32 w = dlgLayerSize->width(); qint32 h = dlgLayerSize->height(); m_view->nodeManager()->scale((double)w / ((double)(rc.width())), (double)h / ((double)(rc.height())), dlgLayerSize->filterType()); } delete dlgLayerSize; } void ImageSize::slotSelectionScale() { KisImageSP image = m_view->image(); if (!image) { return; } KisLayerSP layer = m_view->activeLayer(); KIS_ASSERT_RECOVER_RETURN(image && layer); KisSelectionMaskSP selectionMask = layer->selectionMask(); if (!selectionMask) { selectionMask = image->rootLayer()->selectionMask(); } KIS_ASSERT_RECOVER_RETURN(selectionMask); QRect rc = selectionMask->selection()->selectedExactRect(); DlgLayerSize * dlgSize = new DlgLayerSize(m_view->mainWindow(), "SelectionScale", rc.width(), rc.height(), image->yRes()); dlgSize->setCaption(i18n("Scale Selection")); if (dlgSize->exec() == QDialog::Accepted) { qint32 w = dlgSize->width(); qint32 h = dlgSize->height(); image->scaleNode(selectionMask, qreal(w) / rc.width(), qreal(h) / rc.height(), dlgSize->filterType()); } delete dlgSize; } #include "imagesize.moc" diff --git a/plugins/extensions/imagesize/wdg_canvassize.ui b/plugins/extensions/imagesize/wdg_canvassize.ui index 858140b589..466f45e393 100644 --- a/plugins/extensions/imagesize/wdg_canvassize.ui +++ b/plugins/extensions/imagesize/wdg_canvassize.ui @@ -1,692 +1,643 @@ WdgCanvasSize 0 0 395 387 Canvas Size New Size true - - - - 1 - - - 100000 - - - Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - 1 - - - 100000 - - - Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + 4 0.000100000000000 10000.000000000000000 0.100000000000000 - + 4 0.000100000000000 10000.000000000000000 0.100000000000000 Qt::Horizontal QSizePolicy::Fixed 25 20 Qt::Horizontal QSizePolicy::MinimumExpanding 40 20 Constrain aspect ratio Constrain proportions true Offset true Qt::Vertical QSizePolicy::MinimumExpanding 20 30 Qt::Vertical QSizePolicy::Fixed 20 16 X: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - -100000 - - - 100000 - - - - - + 4 -10000.000000000000000 10000.000000000000000 0.100000000000000 Qt::Horizontal QSizePolicy::Fixed 25 20 Anchor: Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing Y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - -100000 - - - 100000 - - - - - + 4 -10000.000000000000000 10000.000000000000000 0.100000000000000 0 0 0 0 QFrame::StyledPanel QFrame::Sunken 2 2 2 2 0 0 0 25 25 15 15 true 0 0 25 25 15 15 true 0 0 25 25 15 15 true 0 0 25 25 15 15 true 0 0 25 25 15 15 true 0 0 25 25 15 15 true 0 0 25 25 15 15 true 0 0 25 25 15 15 true false 0 0 25 25 15 15 true Qt::Horizontal QSizePolicy::Maximum 16 20 Qt::Horizontal QSizePolicy::MinimumExpanding 40 20 KoAspectButton QWidget
KoAspectButton.h
1
KCanvasPreview QWidget
kcanvaspreview.h
1
- KisDoubleParseSpinBox + KisDoubleParseUnitSpinBox QDoubleSpinBox -
kis_double_parse_spin_box.h
-
- - KisIntParseSpinBox - QSpinBox -
kis_int_parse_spin_box.h
+
kis_double_parse_unit_spin_box.h
- newWidth newWidthDouble widthUnit - newHeight newHeightDouble heightUnit constrainProportionsCkb - xOffset xOffsetDouble xOffUnit - yOffset yOffsetDouble yOffUnit topLeft topCenter topRight middleLeft middleCenter middleRight bottomLeft bottomCenter bottomRight
diff --git a/plugins/extensions/imagesize/wdg_imagesize.ui b/plugins/extensions/imagesize/wdg_imagesize.ui index 8a45fdf251..6edf3cafaf 100644 --- a/plugins/extensions/imagesize/wdg_imagesize.ui +++ b/plugins/extensions/imagesize/wdg_imagesize.ui @@ -1,447 +1,413 @@ WdgImageSize 0 0 391 386 Scale To New Size Pixel Dimensions true - + 80 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 - - - - - 80 - 0 - - - - 1 - - - 100000 - - - Qt::Horizontal QSizePolicy::Fixed 25 20 - W&idth: + Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - pixelWidth - &Filter: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter printWidth - &Height: + Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - pixelHeight - - + 80 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 - - - - 1 - - - 100000 - - - Qt::Horizontal QSizePolicy::MinimumExpanding 40 20 Print Size true Hei&ght: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter printHeight - + 80 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 Wid&th: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter printWidth - + 80 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 Resolution: 0 0 70 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 Qt::Horizontal QSizePolicy::Fixed 25 20 Qt::Horizontal QSizePolicy::MinimumExpanding 40 20 Qt::Vertical QSizePolicy::Fixed 20 16 Constrain aspect ratio Constrain proportions true Adjust print size separately Qt::Vertical QSizePolicy::MinimumExpanding 20 30 KisCmbIDList
widgets/kis_cmb_idlist.h
KoAspectButton QWidget
KoAspectButton.h
1
- - KisIntParseSpinBox - QSpinBox -
kis_int_parse_spin_box.h
-
KisDoubleParseSpinBox QDoubleSpinBox
kis_double_parse_spin_box.h
+ + KisDoubleParseUnitSpinBox + QDoubleSpinBox +
kis_double_parse_unit_spin_box.h
+
- pixelWidth pixelWidthDouble pixelWidthUnit - pixelHeight pixelHeightDouble pixelHeightUnit pixelFilterCmb printWidth printWidthUnit printHeight printHeightUnit printResolution printResolutionUnit constrainProportionsCkb adjustPrintSizeSeparatelyCkb
diff --git a/plugins/extensions/imagesize/wdg_layersize.ui b/plugins/extensions/imagesize/wdg_layersize.ui index 58170c58ad..6a07d00756 100644 --- a/plugins/extensions/imagesize/wdg_layersize.ui +++ b/plugins/extensions/imagesize/wdg_layersize.ui @@ -1,248 +1,209 @@ WdgLayerSize 0 0 - 366 + 374 201 Layer Size New Size true Qt::Horizontal QSizePolicy::MinimumExpanding 40 20 - W&idth: + Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - newWidth - - + 80 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 - + 80 0 4 0.000100000000000 10000.000000000000000 0.100000000000000 - - - - - 80 - 0 - - - - 1 - - - 100000 - - - - - - - 1 - - - 100000 - - - Filter: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - &Height: + Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - newHeight - Constrain aspect ratio Constrain proportions true Qt::Horizontal QSizePolicy::Fixed 25 20 Qt::Vertical QSizePolicy::MinimumExpanding 20 30 - - KisCmbIDList - -
widgets/kis_cmb_idlist.h
-
KoAspectButton QWidget
KoAspectButton.h
1
- KisIntParseSpinBox - QSpinBox -
kis_int_parse_spin_box.h
+ KisDoubleParseUnitSpinBox + QDoubleSpinBox +
kis_double_parse_unit_spin_box.h
- KisDoubleParseSpinBox - QDoubleSpinBox -
kis_double_parse_spin_box.h
+ KisCmbIDList + +
widgets/kis_cmb_idlist.h
- newWidth newWidthDouble newWidthUnit - newHeight newHeightDouble newHeightUnit filterCmb
diff --git a/plugins/extensions/offsetimage/dlg_offsetimage.cpp b/plugins/extensions/offsetimage/dlg_offsetimage.cpp index 5c4094921c..9df512c883 100644 --- a/plugins/extensions/offsetimage/dlg_offsetimage.cpp +++ b/plugins/extensions/offsetimage/dlg_offsetimage.cpp @@ -1,79 +1,109 @@ /* * Copyright (c) 2013 Lukáš Tvrdý * * 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 2 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. */ #include "dlg_offsetimage.h" #include #include +#include "kis_document_aware_spin_box_unit_manager.h" + DlgOffsetImage::DlgOffsetImage(QWidget * parent, const char * name, QSize imageSize) - : KoDialog(parent), - m_offsetSize(imageSize) + : KoDialog(parent), + m_offsetSize(imageSize) { setCaption("BUG: No sane caption is set"); setButtons(Ok | Cancel); setDefaultButton(Ok); setObjectName(name); m_lock = false; m_page = new WdgOffsetImage(this); Q_CHECK_PTR(m_page); m_page->setObjectName("offset_image"); setMainWidget(m_page); resize(m_page->sizeHint()); + _widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this); + _heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y); + + _widthUnitManager->setApparentUnitFromSymbol("px"); + _heightUnitManager->setApparentUnitFromSymbol("px"); + + m_page->offsetXdoubleSpinBox->setUnitManager(_widthUnitManager); + m_page->offsetYdoubleSpinBox->setUnitManager(_heightUnitManager); + m_page->offsetXdoubleSpinBox->setDecimals(2); + m_page->offsetYdoubleSpinBox->setDecimals(2); + m_page->offsetXdoubleSpinBox->setDisplayUnit(false); + m_page->offsetYdoubleSpinBox->setDisplayUnit(false); + + m_page->offsetXdoubleSpinBox->setReturnUnit("px"); + m_page->offsetYdoubleSpinBox->setReturnUnit("px"); + + m_page->unitXComboBox->setModel(_widthUnitManager); + m_page->unitYComboBox->setModel(_heightUnitManager); + + const int pixelUnitIndex = _widthUnitManager->getsUnitSymbolList().indexOf("px"); //TODO: have a better way to identify units. + m_page->unitXComboBox->setCurrentIndex(pixelUnitIndex); + m_page->unitYComboBox->setCurrentIndex(pixelUnitIndex); + connect(this, SIGNAL(okClicked()),this, SLOT(okClicked())); connect(m_page->middleOffsetBtn, SIGNAL(clicked()), this, SLOT(slotMiddleOffset())); - connect(m_page->offsetXspinBox, SIGNAL(valueChanged(int)), this, SLOT(slotOffsetXChanged(int))); - connect(m_page->offsetYspinBox, SIGNAL(valueChanged(int)), this, SLOT(slotOffsetYChanged(int))); + connect(m_page->offsetXdoubleSpinBox, SIGNAL(valueChangedPt(double)), this, SLOT(slotOffsetXChanged(double))); + connect(m_page->offsetYdoubleSpinBox, SIGNAL(valueChangedPt(double)), this, SLOT(slotOffsetYChanged(double))); + + connect(m_page->unitXComboBox, SIGNAL(currentIndexChanged(int)), _widthUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(m_page->unitYComboBox, SIGNAL(currentIndexChanged(int)), _heightUnitManager, SLOT(selectApparentUnitFromIndex(int))); + connect(_widthUnitManager, SIGNAL(unitChanged(int)), m_page->unitXComboBox, SLOT(setCurrentIndex(int))); + connect(_heightUnitManager, SIGNAL(unitChanged(int)), m_page->unitYComboBox, SLOT(setCurrentIndex(int))); slotMiddleOffset(); } DlgOffsetImage::~DlgOffsetImage() { delete m_page; } -void DlgOffsetImage::slotOffsetXChanged(int newOffsetX) +void DlgOffsetImage::slotOffsetXChanged(double newOffsetX) { m_offsetX = newOffsetX; } -void DlgOffsetImage::slotOffsetYChanged(int newOffsetY) +void DlgOffsetImage::slotOffsetYChanged(double newOffsetY) { m_offsetY = newOffsetY; } void DlgOffsetImage::slotMiddleOffset() { int offsetX = m_offsetSize.width() / 2; int offsetY = m_offsetSize.height() / 2; - m_page->offsetXspinBox->setValue(offsetX); - m_page->offsetYspinBox->setValue(offsetY); + m_page->offsetXdoubleSpinBox->setValue(offsetX); + m_page->offsetYdoubleSpinBox->setValue(offsetY); } void DlgOffsetImage::okClicked() { accept(); } diff --git a/plugins/extensions/offsetimage/dlg_offsetimage.h b/plugins/extensions/offsetimage/dlg_offsetimage.h index 785d8bd2a3..5488fa6517 100644 --- a/plugins/extensions/offsetimage/dlg_offsetimage.h +++ b/plugins/extensions/offsetimage/dlg_offsetimage.h @@ -1,66 +1,70 @@ /* * Copyright (c) 2013 Lukáš Tvrdý * * 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 2 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. */ #ifndef DLG_OFFSETIMAGE #define DLG_OFFSETIMAGE #include #include #include "ui_wdg_offsetimage.h" +class KisDocumentAwareSpinBoxUnitManager; class WdgOffsetImage : public QWidget, public Ui::WdgOffsetImage { Q_OBJECT public: WdgOffsetImage(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgOffsetImage: public KoDialog { Q_OBJECT public: DlgOffsetImage(QWidget * parent = 0, const char* name = 0, QSize imageSize = QSize()); ~DlgOffsetImage(); int offsetX() const { return m_offsetX;} int offsetY() const { return m_offsetY;} private Q_SLOTS: void okClicked(); - void slotOffsetXChanged(int); - void slotOffsetYChanged(int); + void slotOffsetXChanged(double); + void slotOffsetYChanged(double); void slotMiddleOffset(); private: WdgOffsetImage * m_page; int m_offsetX; int m_offsetY; bool m_lock; QSize m_offsetSize; + KisDocumentAwareSpinBoxUnitManager* _widthUnitManager; + KisDocumentAwareSpinBoxUnitManager* _heightUnitManager; + }; #endif // DLG_OFFSETIMAGE diff --git a/plugins/extensions/offsetimage/wdg_offsetimage.ui b/plugins/extensions/offsetimage/wdg_offsetimage.ui index dbcf84b6bd..8a1eedac33 100644 --- a/plugins/extensions/offsetimage/wdg_offsetimage.ui +++ b/plugins/extensions/offsetimage/wdg_offsetimage.ui @@ -1,126 +1,132 @@ WdgOffsetImage 0 0 - 203 + 214 157 Rotate Image Offset - + 0 0 0 0 X: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - px - - - 999999999 + + + + 0 + 0 + + + + - + Y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - px - - - 999999999 + + + + 0 + 0 + + + + 0 0 Offset by x/2, y/2 Qt::Vertical QSizePolicy::Minimum 0 0 - KisIntParseSpinBox - QSpinBox -
kis_int_parse_spin_box.h
+ KisDoubleParseUnitSpinBox + QDoubleSpinBox +
kis_double_parse_unit_spin_box.h
diff --git a/plugins/flake/pathshapes/rectangle/RectangleShapeConfigWidget.cpp b/plugins/flake/pathshapes/rectangle/RectangleShapeConfigWidget.cpp index 927cd9421d..bc6b809651 100644 --- a/plugins/flake/pathshapes/rectangle/RectangleShapeConfigWidget.cpp +++ b/plugins/flake/pathshapes/rectangle/RectangleShapeConfigWidget.cpp @@ -1,82 +1,84 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "RectangleShapeConfigWidget.h" #include "RectangleShape.h" #include "RectangleShapeConfigCommand.h" +#include "kis_document_aware_spin_box_unit_manager.h" + RectangleShapeConfigWidget::RectangleShapeConfigWidget() { widget.setupUi(this); connect(widget.cornerRadiusX, SIGNAL(editingFinished()), this, SIGNAL(propertyChanged())); connect(widget.cornerRadiusY, SIGNAL(editingFinished()), this, SIGNAL(propertyChanged())); } void RectangleShapeConfigWidget::setUnit(const KoUnit &unit) { widget.cornerRadiusX->setUnit(unit); widget.cornerRadiusY->setUnit(unit); } void RectangleShapeConfigWidget::open(KoShape *shape) { m_rectangle = dynamic_cast(shape); if (!m_rectangle) { return; } widget.cornerRadiusX->blockSignals(true); widget.cornerRadiusY->blockSignals(true); QSizeF size = m_rectangle->size(); widget.cornerRadiusX->setMaximum(0.5 * size.width()); widget.cornerRadiusX->changeValue(0.01 * m_rectangle->cornerRadiusX() * 0.5 * size.width()); widget.cornerRadiusY->setMaximum(0.5 * size.height()); widget.cornerRadiusY->changeValue(0.01 * m_rectangle->cornerRadiusY() * 0.5 * size.height()); widget.cornerRadiusX->blockSignals(false); widget.cornerRadiusY->blockSignals(false); } void RectangleShapeConfigWidget::save() { if (!m_rectangle) { return; } QSizeF size = m_rectangle->size(); m_rectangle->setCornerRadiusX(100.0 * widget.cornerRadiusX->value() / (0.5 * size.width())); m_rectangle->setCornerRadiusY(100.0 * widget.cornerRadiusY->value() / (0.5 * size.height())); } KUndo2Command *RectangleShapeConfigWidget::createCommand() { if (!m_rectangle) { return 0; } QSizeF size = m_rectangle->size(); qreal cornerRadiusX = 100.0 * widget.cornerRadiusX->value() / (0.5 * size.width()); qreal cornerRadiusY = 100.0 * widget.cornerRadiusY->value() / (0.5 * size.height()); return new RectangleShapeConfigCommand(m_rectangle, cornerRadiusX, cornerRadiusY); } diff --git a/plugins/flake/pathshapes/star/StarShapeConfigWidget.cpp b/plugins/flake/pathshapes/star/StarShapeConfigWidget.cpp index 502f1511cd..0d203d231b 100644 --- a/plugins/flake/pathshapes/star/StarShapeConfigWidget.cpp +++ b/plugins/flake/pathshapes/star/StarShapeConfigWidget.cpp @@ -1,93 +1,95 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "StarShapeConfigWidget.h" #include "StarShape.h" #include "StarShapeConfigCommand.h" +#include "kis_document_aware_spin_box_unit_manager.h" + StarShapeConfigWidget::StarShapeConfigWidget() { widget.setupUi(this); connect(widget.corners, SIGNAL(valueChanged(int)), this, SIGNAL(propertyChanged())); connect(widget.innerRadius, SIGNAL(editingFinished()), this, SIGNAL(propertyChanged())); connect(widget.outerRadius, SIGNAL(editingFinished()), this, SIGNAL(propertyChanged())); connect(widget.convex, SIGNAL(stateChanged(int)), this, SIGNAL(propertyChanged())); connect(widget.convex, SIGNAL(clicked()), this, SLOT(typeChanged())); } void StarShapeConfigWidget::setUnit(const KoUnit &unit) { widget.innerRadius->setUnit(unit); widget.outerRadius->setUnit(unit); } void StarShapeConfigWidget::open(KoShape *shape) { m_star = dynamic_cast(shape); if (!m_star) { return; } widget.corners->blockSignals(true); widget.innerRadius->blockSignals(true); widget.outerRadius->blockSignals(true); widget.convex->blockSignals(true); widget.corners->setValue(m_star->cornerCount()); widget.innerRadius->changeValue(m_star->baseRadius()); widget.outerRadius->changeValue(m_star->tipRadius()); widget.convex->setCheckState(m_star->convex() ? Qt::Checked : Qt::Unchecked); typeChanged(); widget.corners->blockSignals(false); widget.innerRadius->blockSignals(false); widget.outerRadius->blockSignals(false); widget.convex->blockSignals(false); } void StarShapeConfigWidget::save() { if (!m_star) { return; } m_star->setCornerCount(widget.corners->value()); m_star->setBaseRadius(widget.innerRadius->value()); m_star->setTipRadius(widget.outerRadius->value()); m_star->setConvex(widget.convex->checkState() == Qt::Checked); } KUndo2Command *StarShapeConfigWidget::createCommand() { if (!m_star) { return 0; } else return new StarShapeConfigCommand(m_star, widget.corners->value(), widget.innerRadius->value(), widget.outerRadius->value(), widget.convex->checkState() == Qt::Checked); } void StarShapeConfigWidget::typeChanged() { if (widget.convex->checkState() == Qt::Checked) { widget.innerRadius->setEnabled(false); } else { widget.innerRadius->setEnabled(true); } } diff --git a/plugins/tools/basictools/kis_tool_pencil.cc b/plugins/tools/basictools/kis_tool_pencil.cc index 7b7516af63..50b8858024 100644 --- a/plugins/tools/basictools/kis_tool_pencil.cc +++ b/plugins/tools/basictools/kis_tool_pencil.cc @@ -1,68 +1,82 @@ /* * Copyright (c) 2012 Sven Langkamp * Copyright (c) 2010 Cyrille Berger * * 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 2 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. */ #include "kis_tool_pencil.h" #include #include #include #include #include KisToolPencil::KisToolPencil(KoCanvasBase * canvas) : DelegatedPencilTool(canvas, Qt::ArrowCursor, new __KisToolPencilLocalTool(canvas, this)) { } void KisToolPencil::resetCursorStyle() { DelegatedPencilTool::resetCursorStyle(); overrideCursorIfNotEditable(); } void KisToolPencil::mousePressEvent(KoPointerEvent *event) { if (!nodeEditable()) return; DelegatedPencilTool::mousePressEvent(event); } +QList > KisToolPencil::createOptionWidgets() +{ + QList > widgetsList = + DelegatedPencilTool::createOptionWidgets(); + + QList > filteredWidgets; + Q_FOREACH (QWidget* widget, widgetsList) { + if (widget->objectName() != "Stroke widget") { + filteredWidgets.push_back(widget); + } + } + return filteredWidgets; +} __KisToolPencilLocalTool::__KisToolPencilLocalTool(KoCanvasBase * canvas, KisToolPencil* parentTool) : KoPencilTool(canvas), m_parentTool(parentTool) {} void __KisToolPencilLocalTool::paintPath(KoPathShape &pathShape, QPainter &painter, const KoViewConverter &converter) { Q_UNUSED(converter); QTransform matrix; matrix.scale(m_parentTool->image()->xRes(), m_parentTool->image()->yRes()); matrix.translate(pathShape.position().x(), pathShape.position().y()); m_parentTool->paintToolOutline(&painter, m_parentTool->pixelToView(matrix.map(pathShape.outline()))); } void __KisToolPencilLocalTool::addPathShape(KoPathShape* pathShape, bool closePath) { if (closePath) { pathShape->close(); pathShape->normalize(); } m_parentTool->addPathShape(pathShape, kundo2_i18n("Draw Freehand Path")); } + diff --git a/plugins/tools/basictools/kis_tool_pencil.h b/plugins/tools/basictools/kis_tool_pencil.h index 3b3aed4a42..3781e64a66 100644 --- a/plugins/tools/basictools/kis_tool_pencil.h +++ b/plugins/tools/basictools/kis_tool_pencil.h @@ -1,88 +1,90 @@ /* * Copyright (c) 2012 Sven Langkamp * * 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 2 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. */ #ifndef KIS_TOOL_PENCIL_H_ #define KIS_TOOL_PENCIL_H_ #include #include #include "flake/kis_node_shape.h" #include "kis_tool_shape.h" #include "kis_delegated_tool.h" #include class KoCanvasBase; class KisToolPencil; class __KisToolPencilLocalTool : public KoPencilTool { public: __KisToolPencilLocalTool(KoCanvasBase * canvas, KisToolPencil* parentTool); virtual void paintPath(KoPathShape &path, QPainter &painter, const KoViewConverter &converter); virtual void addPathShape(KoPathShape* pathShape, bool closePath); using KoPencilTool::createOptionWidgets; private: KisToolPencil* const m_parentTool; }; typedef KisDelegatedTool DelegatedPencilTool; +__KisToolPencilLocalTool, +DeselectShapesActivationPolicy> DelegatedPencilTool; class KisToolPencil : public DelegatedPencilTool { Q_OBJECT public: KisToolPencil(KoCanvasBase * canvas); void mousePressEvent(KoPointerEvent *event); + virtual QList > createOptionWidgets(); + protected Q_SLOTS: virtual void resetCursorStyle(); private: friend class __KisToolPencilLocalTool; }; class KisToolPencilFactory : public KoToolFactoryBase { public: KisToolPencilFactory() - : KoToolFactoryBase("KisToolPencil") { + : KoToolFactoryBase("KisToolPencil") { setToolTip(i18n("Freehand Path Tool")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_freehandvector")); setPriority(9); } virtual ~KisToolPencilFactory() {} virtual KoToolBase * createTool(KoCanvasBase *canvas) { return new KisToolPencil(canvas); } }; #endif // KIS_TOOL_PENCIL_H_ diff --git a/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp b/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp index 5260c43996..23098b2673 100644 --- a/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp +++ b/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp @@ -1,979 +1,986 @@ /* This file is part of the KDE project * * Copyright (C) 2009 Thorsten Zachmann * Copyright (C) 2009 Jean-Nicolas Artaud * Copyright (C) 2011 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ConnectionTool.h" #include #include #include #include "AddConnectionPointCommand.h" #include "RemoveConnectionPointCommand.h" #include "ChangeConnectionPointCommand.h" #include "MoveConnectionPointStrategy.h" #include "ConnectionPointWidget.h" #define TextShape_SHAPEID "TextShapeID" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "kis_document_aware_spin_box_unit_manager.h" + #include #include "kis_action_registry.h" #include #include #include #include #include #include ConnectionTool::ConnectionTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_editMode(Idle) , m_connectionType(KoConnectionShape::Standard) , m_currentShape(0) , m_activeHandle(-1) , m_currentStrategy(0) , m_oldSnapStrategies(0) , m_resetPaint(true) { QPixmap connectPixmap; connectPixmap.load(":/cursor_connect.png"); m_connectCursor = QCursor(connectPixmap, 4, 1); KisActionRegistry *actionRegistry = KisActionRegistry::instance(); m_editConnectionPoint = actionRegistry->makeQAction("toggle-edit-mode", this); m_editConnectionPoint->setCheckable(true); addAction("toggle-edit-mode", m_editConnectionPoint); m_alignPercent = actionRegistry->makeQAction("align-relative", this); m_alignPercent->setCheckable(true); addAction("align-relative", m_alignPercent); m_alignLeft = actionRegistry->makeQAction("align-left", this); m_alignLeft->setCheckable(true); addAction("align-left", m_alignLeft); m_alignCenterH = actionRegistry->makeQAction("align-centerh", this); m_alignCenterH->setCheckable(true); addAction("align-centerh", m_alignCenterH); m_alignRight = actionRegistry->makeQAction("align-right", this); m_alignRight->setCheckable(true); addAction("align-right", m_alignRight); m_alignTop = actionRegistry->makeQAction("align-top", this); m_alignTop->setCheckable(true); addAction("align-top", m_alignTop); m_alignCenterV = actionRegistry->makeQAction("align-centerv", this); m_alignCenterV->setCheckable(true); addAction("align-centerv", m_alignCenterV); m_alignBottom = actionRegistry->makeQAction("align-bottom", this); m_alignBottom->setCheckable(true); addAction("align-bottom", m_alignBottom); m_escapeAll = actionRegistry->makeQAction("escape-all", this); m_escapeAll->setCheckable(true); addAction("escape-all", m_escapeAll); m_escapeHorizontal = actionRegistry->makeQAction("escape-horizontal", this); m_escapeHorizontal->setCheckable(true); addAction("escape-horizontal", m_escapeHorizontal); m_escapeVertical = actionRegistry->makeQAction("escape-vertical", this); m_escapeVertical->setCheckable(true); addAction("escape-vertical", m_escapeVertical); m_escapeLeft = actionRegistry->makeQAction("escape-left", this); m_escapeLeft->setCheckable(true); addAction("escape-left", m_escapeLeft); m_escapeRight = actionRegistry->makeQAction("escape-right", this); m_escapeRight->setCheckable(true); addAction("escape-right", m_escapeRight); m_escapeUp = actionRegistry->makeQAction("escape-up", this); m_escapeUp->setCheckable(true); addAction("escape-up", m_escapeUp); m_escapeDown = actionRegistry->makeQAction("escape-down", this); m_escapeDown->setCheckable(true); addAction("escape-down", m_escapeDown); m_alignHorizontal = new QActionGroup(this); m_alignHorizontal->setExclusive(true); m_alignHorizontal->addAction(m_alignLeft); m_alignHorizontal->addAction(m_alignCenterH); m_alignHorizontal->addAction(m_alignRight); connect(m_alignHorizontal, SIGNAL(triggered(QAction*)), this, SLOT(horizontalAlignChanged())); m_alignVertical = new QActionGroup(this); m_alignVertical->setExclusive(true); m_alignVertical->addAction(m_alignTop); m_alignVertical->addAction(m_alignCenterV); m_alignVertical->addAction(m_alignBottom); connect(m_alignVertical, SIGNAL(triggered(QAction*)), this, SLOT(verticalAlignChanged())); m_alignRelative = new QActionGroup(this); m_alignRelative->setExclusive(true); m_alignRelative->addAction(m_alignPercent); connect(m_alignRelative, SIGNAL(triggered(QAction*)), this, SLOT(relativeAlignChanged())); m_escapeDirections = new QActionGroup(this); m_escapeDirections->setExclusive(true); m_escapeDirections->addAction(m_escapeAll); m_escapeDirections->addAction(m_escapeHorizontal); m_escapeDirections->addAction(m_escapeVertical); m_escapeDirections->addAction(m_escapeLeft); m_escapeDirections->addAction(m_escapeRight); m_escapeDirections->addAction(m_escapeUp); m_escapeDirections->addAction(m_escapeDown); connect(m_escapeDirections, SIGNAL(triggered(QAction*)), this, SLOT(escapeDirectionChanged())); connect(this, SIGNAL(connectionPointEnabled(bool)), m_alignHorizontal, SLOT(setEnabled(bool))); connect(this, SIGNAL(connectionPointEnabled(bool)), m_alignVertical, SLOT(setEnabled(bool))); connect(this, SIGNAL(connectionPointEnabled(bool)), m_alignRelative, SLOT(setEnabled(bool))); connect(this, SIGNAL(connectionPointEnabled(bool)), m_escapeDirections, SLOT(setEnabled(bool))); resetEditMode(); } ConnectionTool::~ConnectionTool() { } void ConnectionTool::paint(QPainter &painter, const KoViewConverter &converter) { // get the correctly sized rect for painting handles QRectF handleRect = handlePaintRect(QPointF()); painter.setRenderHint(QPainter::Antialiasing, true); if (m_currentStrategy) { painter.save(); m_currentStrategy->paint(painter, converter); painter.restore(); } QList shapes = canvas()->shapeManager()->shapes(); for (QList::const_iterator end = shapes.constBegin(); end != shapes.constEnd(); ++end) { KoShape *shape = *end; if (!dynamic_cast(shape)) { // only paint connection points of textShapes not inside a tos container and other shapes if (shape->shapeId() == TextShape_SHAPEID && dynamic_cast(shape->parent())) { continue; } painter.save(); painter.setPen(Qt::black); QTransform transform = shape->absoluteTransformation(0); KoShape::applyConversion(painter, converter); // Draw all the connection points of the shape KoConnectionPoints connectionPoints = shape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { if (shape == findNonConnectionShapeAtPosition(transform.map(cp.value().position))) { handleRect.moveCenter(transform.map(cp.value().position)); painter.setBrush(cp.key() == m_activeHandle && shape == m_currentShape ? - Qt::red : Qt::white); + Qt::red : Qt::white); painter.drawRect(handleRect); } } painter.restore(); } } // paint connection points or connection handles depending // on the shape the mouse is currently if (m_currentShape && m_editMode == EditConnection) { KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); if (connectionShape) { int radius = handleRadius() + 1; int handleCount = connectionShape->handleCount(); for (int i = 0; i < handleCount; ++i) { painter.save(); painter.setPen(Qt::blue); painter.setBrush(i == m_activeHandle ? Qt::red : Qt::white); painter.setTransform(connectionShape->absoluteTransformation(&converter) * painter.transform()); connectionShape->paintHandle(painter, converter, i, radius); painter.restore(); } } } } void ConnectionTool::repaintDecorations() { const qreal radius = handleRadius(); QRectF repaintRect; if (m_currentShape) { repaintRect = m_currentShape->boundingRect(); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); if (!m_resetPaint && m_currentShape->isVisible(true) && !connectionShape) { // only paint connection points of textShapes not inside a tos container and other shapes if (!(m_currentShape->shapeId() == TextShape_SHAPEID && - dynamic_cast(m_currentShape->parent()))) { + dynamic_cast(m_currentShape->parent()))) { KoConnectionPoints connectionPoints = m_currentShape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { repaintRect = handleGrabRect(m_currentShape->shapeToDocument(cp.value().position)); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } } } if (m_editMode == EditConnection) { if (connectionShape) { QPointF handlePos = connectionShape->handlePosition(m_activeHandle); handlePos = connectionShape->shapeToDocument(handlePos); repaintRect = handlePaintRect(handlePos); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } } } if (m_resetPaint) { QList shapes = canvas()->shapeManager()->shapes(); for (QList::const_iterator end = shapes.constBegin(); end != shapes.constEnd(); ++end) { KoShape *shape = *end; if (!dynamic_cast(shape)) { // only paint connection points of textShapes not inside a tos container and other shapes if (shape->shapeId() == TextShape_SHAPEID && dynamic_cast(shape->parent())) { continue; } KoConnectionPoints connectionPoints = shape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { repaintRect = handleGrabRect(shape->shapeToDocument(cp.value().position)); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } } } } m_resetPaint = false; } void ConnectionTool::mousePressEvent(KoPointerEvent *event) { if (!m_currentShape) { return; } KoShape *hitShape = findShapeAtPosition(event->point); int hitHandle = handleAtPoint(m_currentShape, event->point); if (m_editMode == EditConnection && hitHandle >= 0) { // create connection handle change strategy m_currentStrategy = new KoPathConnectionPointStrategy(this, dynamic_cast(m_currentShape), hitHandle); } else if (m_editMode == EditConnectionPoint) { if (hitHandle >= KoConnectionPoint::FirstCustomConnectionPoint) { // start moving custom connection point m_currentStrategy = new MoveConnectionPointStrategy(m_currentShape, hitHandle, this); } } else if (m_editMode == CreateConnection) { // create new connection shape, connect it to the active connection point // and start editing the new connection // create the new connection shape KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value("KoConnectionShape"); KoShape *shape = factory->createDefaultShape(canvas()->shapeController()->resourceManager()); KoConnectionShape *connectionShape = dynamic_cast(shape); if (!connectionShape) { delete shape; resetEditMode(); return; } //set connection type connectionShape->setType(m_connectionType); // get the position of the connection point we start our connection from QPointF cp = m_currentShape->shapeToDocument(m_currentShape->connectionPoint(m_activeHandle).position); // move both handles to that point connectionShape->moveHandle(0, cp); connectionShape->moveHandle(1, cp); // connect the first handle of the connection shape to our connection point if (!connectionShape->connectFirst(m_currentShape, m_activeHandle)) { delete shape; resetEditMode(); return; } //add connector label connectionShape->createTextShape(canvas()->shapeController()->resourceManager()); connectionShape->setPlainText(QString()); // create the connection edit strategy from the path tool m_currentStrategy = new KoPathConnectionPointStrategy(this, connectionShape, 1); if (!m_currentStrategy) { delete shape; resetEditMode(); return; } // update our handle data setEditMode(m_editMode, shape, 1); // add connection shape to the shape manager so it gets painted canvas()->shapeManager()->addShape(connectionShape); } else { // pressing on a shape in idle mode switches to corresponding edit mode if (hitShape) { if (dynamic_cast(hitShape)) { int hitHandle = handleAtPoint(hitShape, event->point); setEditMode(EditConnection, hitShape, hitHandle); if (hitHandle >= 0) { // start editing connection shape m_currentStrategy = new KoPathConnectionPointStrategy(this, dynamic_cast(m_currentShape), m_activeHandle); } } } else { resetEditMode(); } } } void ConnectionTool::mouseMoveEvent(KoPointerEvent *event) { if (m_currentStrategy) { repaintDecorations(); if (m_editMode != EditConnection && m_editMode != CreateConnection) { QPointF snappedPos = canvas()->snapGuide()->snap(event->point, event->modifiers()); m_currentStrategy->handleMouseMove(snappedPos, event->modifiers()); } else { m_currentStrategy->handleMouseMove(event->point, event->modifiers()); } repaintDecorations(); } else if (m_editMode == EditConnectionPoint) { KoShape *hoverShape = findNonConnectionShapeAtPosition(event->point);//TODO exclude connectors, need snap guide maybe? if (hoverShape) { m_currentShape = hoverShape; Q_ASSERT(m_currentShape); // check if we should highlight another connection point int handle = handleAtPoint(m_currentShape, event->point); if (handle >= 0) { setEditMode(m_editMode, m_currentShape, handle); useCursor(handle >= KoConnectionPoint::FirstCustomConnectionPoint ? Qt::SizeAllCursor : Qt::ArrowCursor); } else { updateStatusText(); useCursor(Qt::CrossCursor); } } else { m_currentShape = 0; useCursor(Qt::ArrowCursor); } } else if (m_editMode == EditConnection) { Q_ASSERT(m_currentShape); KoShape *hoverShape = findShapeAtPosition(event->point); // check if we should highlight another connection handle int handle = handleAtPoint(m_currentShape, event->point); setEditMode(m_editMode, m_currentShape, handle); if (m_activeHandle == KoConnectionShape::StartHandle || m_activeHandle == KoConnectionShape::EndHandle) { useCursor(Qt::SizeAllCursor); } else if (m_activeHandle >= KoConnectionShape::ControlHandle_1) { } else if (hoverShape && hoverShape != m_currentShape) { useCursor(Qt::PointingHandCursor); } else { useCursor(Qt::ArrowCursor); } } else {// Idle and no current strategy KoShape *hoverShape = findShapeAtPosition(event->point); int hoverHandle = -1; if (hoverShape) { KoConnectionShape *connectionShape = dynamic_cast(hoverShape); if (!connectionShape) { QPointF snappedPos = canvas()->snapGuide()->snap(event->point, event->modifiers()); hoverHandle = handleAtPoint(hoverShape, snappedPos); setEditMode(hoverHandle >= 0 ? CreateConnection : Idle, hoverShape, hoverHandle); } useCursor(hoverHandle >= 0 ? m_connectCursor : Qt::PointingHandCursor); } else { useCursor(Qt::ArrowCursor); } } } void ConnectionTool::mouseReleaseEvent(KoPointerEvent *event) { if (m_currentStrategy) { if (m_editMode == CreateConnection) { // check if connection handles have a minimal distance KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); Q_ASSERT(connectionShape); // get both handle positions in document coordinates QPointF p1 = connectionShape->shapeToDocument(connectionShape->handlePosition(0)); QPointF p2 = connectionShape->shapeToDocument(connectionShape->handlePosition(1)); int grabDistance = grabSensitivity(); // use grabbing sensitivity as minimal distance threshold if (squareDistance(p1, p2) < grabDistance * grabDistance) { // minimal distance was not reached, so we have to undo the started work: // - cleanup and delete the strategy // - remove connection shape from shape manager and delete it // - reset edit mode to last state delete m_currentStrategy; m_currentStrategy = 0; repaintDecorations(); canvas()->shapeManager()->remove(m_currentShape); setEditMode(m_editMode, connectionShape->firstShape(), connectionShape->firstConnectionId()); repaintDecorations(); delete connectionShape; return; } else { // finalize adding the new connection shape with an undo command KUndo2Command *cmd = canvas()->shapeController()->addShape(m_currentShape); canvas()->addCommand(cmd); setEditMode(EditConnection, m_currentShape, KoConnectionShape::StartHandle); } } m_currentStrategy->finishInteraction(event->modifiers()); // TODO: Add parent command to KoInteractionStrategy::createCommand // so that we can have a single command to undo for the user KUndo2Command *command = m_currentStrategy->createCommand(); if (command) { canvas()->addCommand(command); } delete m_currentStrategy; m_currentStrategy = 0; } updateStatusText(); } void ConnectionTool::mouseDoubleClickEvent(KoPointerEvent *event) { if (m_editMode == EditConnectionPoint) { repaintDecorations(); //quit EditConnectionPoint mode when double click blank region on canvas if (!m_currentShape) { resetEditMode(); return; } //add connection point when double click a shape //remove connection point when double click a existed connection point int handleId = handleAtPoint(m_currentShape, event->point); if (handleId < 0) { QPointF mousePos = canvas()->snapGuide()->snap(event->point, event->modifiers()); QPointF point = m_currentShape->documentToShape(mousePos); canvas()->addCommand(new AddConnectionPointCommand(m_currentShape, point)); } else { canvas()->addCommand(new RemoveConnectionPointCommand(m_currentShape, handleId)); } setEditMode(m_editMode, m_currentShape, -1); } else { //deactivate connection tool when double click blank region on canvas KoShape *hitShape = findShapeAtPosition(event->point); if (!hitShape) { deactivate(); emit done(); } else if (dynamic_cast(hitShape)) { repaintDecorations(); setEditMode(EditConnection, m_currentShape, -1); //TODO: temporarily activate text tool to edit connection path } } } void ConnectionTool::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { deactivate(); emit done(); } else if (event->key() == Qt::Key_Backspace) { deleteSelection(); event->accept(); } } void ConnectionTool::activate(ToolActivation, const QSet &) { // save old enabled snap strategies, set bounding box snap strategy m_oldSnapStrategies = canvas()->snapGuide()->enabledSnapStrategies(); canvas()->snapGuide()->enableSnapStrategies(KoSnapGuide::BoundingBoxSnapping); canvas()->snapGuide()->reset(); m_resetPaint = true; repaintDecorations(); } void ConnectionTool::deactivate() { // Put everything to 0 to be able to begin a new shape properly delete m_currentStrategy; m_currentStrategy = 0; resetEditMode(); m_resetPaint = true; repaintDecorations(); // restore previously set snap strategies canvas()->snapGuide()->enableSnapStrategies(m_oldSnapStrategies); canvas()->snapGuide()->reset(); } qreal ConnectionTool::squareDistance(const QPointF &p1, const QPointF &p2) const { // Square of the distance const qreal dx = p2.x() - p1.x(); const qreal dy = p2.y() - p1.y(); return dx * dx + dy * dy; } KoShape *ConnectionTool::findShapeAtPosition(const QPointF &position) const { QList shapes = canvas()->shapeManager()->shapesAt(handleGrabRect(position)); if (!shapes.isEmpty()) { qSort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); // we want to priorize connection shape handles, even if the connection shape // is not at the top of the shape stack at the mouse position KoConnectionShape *connectionShape = nearestConnectionShape(shapes, position); // use best connection shape or first shape from stack (last in the list) if not found if (connectionShape) { return connectionShape; } else { for (QList::const_iterator end = shapes.constEnd() - 1; end >= shapes.constBegin(); --end) { KoShape *shape = *end; if (!dynamic_cast(shape) && shape->shapeId() != TextShape_SHAPEID) { return shape; } } } } return 0; } KoShape *ConnectionTool::findNonConnectionShapeAtPosition(const QPointF &position) const { QList shapes = canvas()->shapeManager()->shapesAt(handleGrabRect(position)); if (!shapes.isEmpty()) { qSort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); for (QList::const_iterator end = shapes.constEnd() - 1; end >= shapes.constBegin(); --end) { KoShape *shape = *end; if (!dynamic_cast(shape) && shape->shapeId() != TextShape_SHAPEID) { return shape; } } } return 0; } int ConnectionTool::handleAtPoint(KoShape *shape, const QPointF &mousePoint) const { if (!shape) { return -1; } const QPointF shapePoint = shape->documentToShape(mousePoint); KoConnectionShape *connectionShape = dynamic_cast(shape); if (connectionShape) { // check connection shape handles return connectionShape->handleIdAt(handleGrabRect(shapePoint)); } else { // check connection points int grabDistance = grabSensitivity(); qreal minDistance = HUGE_VAL; int handleId = -1; KoConnectionPoints connectionPoints = shape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { qreal d = squareDistance(shapePoint, cp.value().position); if (d <= grabDistance && d < minDistance) { handleId = cp.key(); minDistance = d; } } return handleId; } } KoConnectionShape *ConnectionTool::nearestConnectionShape(const QList &shapes, const QPointF &mousePos) const { int grabDistance = grabSensitivity(); KoConnectionShape *nearestConnectionShape = 0; qreal minSquaredDistance = HUGE_VAL; const qreal maxSquaredDistance = grabDistance * grabDistance; Q_FOREACH (KoShape *shape, shapes) { KoConnectionShape *connectionShape = dynamic_cast(shape); if (!connectionShape || !connectionShape->isParametricShape()) { continue; } // convert document point to shape coordinates QPointF p = connectionShape->documentToShape(mousePos); // our region of interest, i.e. a region around our mouse position QRectF roi = handleGrabRect(p); // check all segments of this shape which intersect the region of interest QList segments = connectionShape->segmentsAt(roi); foreach (const KoPathSegment &s, segments) { qreal nearestPointParam = s.nearestPoint(p); QPointF nearestPoint = s.pointAt(nearestPointParam); QPointF diff = p - nearestPoint; qreal squaredDistance = diff.x() * diff.x() + diff.y() * diff.y(); // are we within the allowed distance ? if (squaredDistance > maxSquaredDistance) { continue; } // are we closer to the last closest point ? if (squaredDistance < minSquaredDistance) { nearestConnectionShape = connectionShape; minSquaredDistance = squaredDistance; } } } return nearestConnectionShape; } void ConnectionTool::setEditMode(EditMode mode, KoShape *currentShape, int handle) { repaintDecorations(); m_editMode = mode; if (m_currentShape != currentShape) { KoConnectionShape *connectionShape = dynamic_cast(currentShape); foreach (KoShapeConfigWidgetBase *cw, m_connectionShapeWidgets) { if (connectionShape) { cw->open(currentShape); } } } if (mode == Idle) { emit sendConnectionType(m_connectionType); } m_currentShape = currentShape; m_activeHandle = handle; repaintDecorations(); updateActions(); updateStatusText(); } void ConnectionTool::resetEditMode() { m_connectionType = KoConnectionShape::Standard; setEditMode(Idle, 0, -1); emit sendConnectionPointEditState(false); } void ConnectionTool::updateActions() { const bool connectionPointSelected = m_editMode == EditConnectionPoint && m_activeHandle >= 0; if (connectionPointSelected) { KoConnectionPoint cp = m_currentShape->connectionPoint(m_activeHandle); m_alignPercent->setChecked(false); Q_FOREACH (QAction *action, m_alignHorizontal->actions()) { action->setChecked(false); } Q_FOREACH (QAction *action, m_alignVertical->actions()) { action->setChecked(false); } switch (cp.alignment) { case KoConnectionPoint::AlignNone: m_alignPercent->setChecked(true); break; case KoConnectionPoint::AlignTopLeft: m_alignLeft->setChecked(true); m_alignTop->setChecked(true); break; case KoConnectionPoint::AlignTop: m_alignCenterH->setChecked(true); m_alignTop->setChecked(true); break; case KoConnectionPoint::AlignTopRight: m_alignRight->setChecked(true); m_alignTop->setChecked(true); break; case KoConnectionPoint::AlignLeft: m_alignLeft->setChecked(true); m_alignCenterV->setChecked(true); break; case KoConnectionPoint::AlignCenter: m_alignCenterH->setChecked(true); m_alignCenterV->setChecked(true); break; case KoConnectionPoint::AlignRight: m_alignRight->setChecked(true); m_alignCenterV->setChecked(true); break; case KoConnectionPoint::AlignBottomLeft: m_alignLeft->setChecked(true); m_alignBottom->setChecked(true); break; case KoConnectionPoint::AlignBottom: m_alignCenterH->setChecked(true); m_alignBottom->setChecked(true); break; case KoConnectionPoint::AlignBottomRight: m_alignRight->setChecked(true); m_alignBottom->setChecked(true); break; } Q_FOREACH (QAction *action, m_escapeDirections->actions()) { action->setChecked(false); } switch (cp.escapeDirection) { case KoConnectionPoint::AllDirections: m_escapeAll->setChecked(true); break; case KoConnectionPoint::HorizontalDirections: m_escapeHorizontal->setChecked(true); break; case KoConnectionPoint::VerticalDirections: m_escapeVertical->setChecked(true); break; case KoConnectionPoint::LeftDirection: m_escapeLeft->setChecked(true); break; case KoConnectionPoint::RightDirection: m_escapeRight->setChecked(true); break; case KoConnectionPoint::UpDirection: m_escapeUp->setChecked(true); break; case KoConnectionPoint::DownDirection: m_escapeDown->setChecked(true); break; } } emit connectionPointEnabled(connectionPointSelected); } void ConnectionTool::updateStatusText() { switch (m_editMode) { case Idle: if (m_currentShape) { if (dynamic_cast(m_currentShape)) { if (m_activeHandle >= 0) { emit statusTextChanged(i18n("Drag to edit connection.")); } else { emit statusTextChanged(i18n("Double click connection or press delete to remove it.")); } } else if (m_activeHandle < 0) { emit statusTextChanged(i18n("Click to edit connection points.")); } } else { emit statusTextChanged(QString()); } break; case EditConnection: if (m_activeHandle >= 0) { emit statusTextChanged(i18n("Drag to edit connection.")); } else { emit statusTextChanged(i18n("Double click connection or press delete to remove it.")); } break; case EditConnectionPoint: if (m_activeHandle >= KoConnectionPoint::FirstCustomConnectionPoint) { emit statusTextChanged(i18n("Drag to move connection point. Double click connection or press delete to remove it.")); } else if (m_activeHandle >= 0) { emit statusTextChanged(i18n("Double click connection point or press delete to remove it.")); } else { emit statusTextChanged(i18n("Double click to add connection point.")); } break; case CreateConnection: emit statusTextChanged(i18n("Drag to create new connection.")); break; default: emit statusTextChanged(QString()); } } QList > ConnectionTool::createOptionWidgets() { QList > list; m_connectionShapeWidgets.clear(); KoShapeFactoryBase *factory = KoShapeRegistry::instance()->get(KOCONNECTIONSHAPEID); if (factory) { QList widgets = factory->createShapeOptionPanels(); Q_FOREACH (KoShapeConfigWidgetBase *cw, widgets) { if (cw->showOnShapeCreate() || !cw->showOnShapeSelect()) { delete cw; continue; } connect(cw, SIGNAL(propertyChanged()), this, SLOT(connectionChanged())); KoConnectionShapeConfigWidget *cw2 = (KoConnectionShapeConfigWidget *)cw; if (cw2) { connect(cw2, SIGNAL(connectionTypeChanged(int)), this, SLOT(getConnectionType(int))); connect(this, SIGNAL(sendConnectionType(int)), cw2, SLOT(setConnectionType(int))); } m_connectionShapeWidgets.append(cw); cw->setWindowTitle(i18n("Connection")); list.append(cw); } } KoStrokeConfigWidget *strokeWidget = new KoStrokeConfigWidget(0); + KisDocumentAwareSpinBoxUnitManager* managerLineWidth = new KisDocumentAwareSpinBoxUnitManager(strokeWidget); + KisDocumentAwareSpinBoxUnitManager* managerMitterLimit = new KisDocumentAwareSpinBoxUnitManager(strokeWidget); + managerLineWidth->setApparentUnitFromSymbol("px"); + managerMitterLimit->setApparentUnitFromSymbol("px"); + strokeWidget->setUnitManagers(managerLineWidth, managerMitterLimit); strokeWidget->setWindowTitle(i18n("Line")); strokeWidget->setCanvas(canvas()); list.append(strokeWidget); ConnectionPointWidget *connectPoint = new ConnectionPointWidget(this); connectPoint->setWindowTitle(i18n("Connection Point")); list.append(connectPoint); return list; } void ConnectionTool::horizontalAlignChanged() { if (m_alignPercent->isChecked()) { m_alignPercent->setChecked(false); m_alignTop->setChecked(true); } updateConnectionPoint(); } void ConnectionTool::verticalAlignChanged() { if (m_alignPercent->isChecked()) { m_alignPercent->setChecked(false); m_alignLeft->setChecked(true); } updateConnectionPoint(); } void ConnectionTool::relativeAlignChanged() { Q_FOREACH (QAction *action, m_alignHorizontal->actions()) { action->setChecked(false); } Q_FOREACH (QAction *action, m_alignVertical->actions()) { action->setChecked(false); } m_alignPercent->setChecked(true); updateConnectionPoint(); } void ConnectionTool::updateConnectionPoint() { if (m_editMode == EditConnectionPoint && m_currentShape && m_activeHandle >= 0) { KoConnectionPoint oldPoint = m_currentShape->connectionPoint(m_activeHandle); KoConnectionPoint newPoint = oldPoint; if (m_alignPercent->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignNone; } else if (m_alignLeft->isChecked() && m_alignTop->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignTopLeft; } else if (m_alignCenterH->isChecked() && m_alignTop->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignTop; } else if (m_alignRight->isChecked() && m_alignTop->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignTopRight; } else if (m_alignLeft->isChecked() && m_alignCenterV->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignLeft; } else if (m_alignCenterH->isChecked() && m_alignCenterV->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignCenter; } else if (m_alignRight->isChecked() && m_alignCenterV->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignRight; } else if (m_alignLeft->isChecked() && m_alignBottom->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignBottomLeft; } else if (m_alignCenterH->isChecked() && m_alignBottom->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignBottom; } else if (m_alignRight->isChecked() && m_alignBottom->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignBottomRight; } canvas()->addCommand(new ChangeConnectionPointCommand(m_currentShape, m_activeHandle, oldPoint, newPoint)); } } void ConnectionTool::escapeDirectionChanged() { if (m_editMode == EditConnectionPoint && m_currentShape && m_activeHandle >= 0) { KoConnectionPoint oldPoint = m_currentShape->connectionPoint(m_activeHandle); KoConnectionPoint newPoint = oldPoint; QAction *checkedAction = m_escapeDirections->checkedAction(); if (checkedAction == m_escapeAll) { newPoint.escapeDirection = KoConnectionPoint::AllDirections; } else if (checkedAction == m_escapeHorizontal) { newPoint.escapeDirection = KoConnectionPoint::HorizontalDirections; } else if (checkedAction == m_escapeVertical) { newPoint.escapeDirection = KoConnectionPoint::VerticalDirections; } else if (checkedAction == m_escapeLeft) { newPoint.escapeDirection = KoConnectionPoint::LeftDirection; } else if (checkedAction == m_escapeRight) { newPoint.escapeDirection = KoConnectionPoint::RightDirection; } else if (checkedAction == m_escapeUp) { newPoint.escapeDirection = KoConnectionPoint::UpDirection; } else if (checkedAction == m_escapeDown) { newPoint.escapeDirection = KoConnectionPoint::DownDirection; } canvas()->addCommand(new ChangeConnectionPointCommand(m_currentShape, m_activeHandle, oldPoint, newPoint)); } } void ConnectionTool::connectionChanged() { if (m_editMode != EditConnection) { return; } KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); if (!connectionShape) { return; } Q_FOREACH (KoShapeConfigWidgetBase *cw, m_connectionShapeWidgets) { canvas()->addCommand(cw->createCommand()); } } void ConnectionTool::deleteSelection() { if (m_editMode == EditConnectionPoint && m_currentShape && m_activeHandle >= 0) { repaintDecorations(); canvas()->addCommand(new RemoveConnectionPointCommand(m_currentShape, m_activeHandle)); setEditMode(m_editMode, m_currentShape, -1); } else if (m_editMode == EditConnection && m_currentShape) { repaintDecorations(); canvas()->addCommand(canvas()->shapeController()->removeShape(m_currentShape)); resetEditMode(); } } void ConnectionTool::getConnectionType(int type) { if (m_editMode == Idle) { m_connectionType = (KoConnectionShape::Type)type; } } void ConnectionTool::toggleConnectionPointEditMode(int state) { if (state == Qt::Checked) { setEditMode(EditConnectionPoint, 0, -1); } else if (state == Qt::Unchecked) { setEditMode(Idle, 0, -1); } else { return; } } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp index 02e5e2f2cf..97681020b8 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp @@ -1,1242 +1,1253 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2008-2009 Jan Hambrecht Copyright (C) 2008 C. Boemann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "DefaultTool.h" #include "DefaultToolWidget.h" #include "DefaultToolArrangeWidget.h" #include "SelectionDecorator.h" #include "ShapeMoveStrategy.h" #include "ShapeRotateStrategy.h" #include "ShapeShearStrategy.h" #include "ShapeResizeStrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_action_registry.h" +#include "kis_document_aware_spin_box_unit_manager.h" + #include #include #include #include #include #include #include #include #define HANDLE_DISTANCE 10 class NopInteractionStrategy : public KoInteractionStrategy { public: explicit NopInteractionStrategy(KoToolBase *parent) : KoInteractionStrategy(parent) { } KUndo2Command *createCommand() override { return 0; } void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {} void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {} }; class SelectionHandler : public KoToolSelection { public: SelectionHandler(DefaultTool *parent) : KoToolSelection(parent) , m_selection(parent->koSelection()) { } bool hasSelection() override { if (m_selection) { return m_selection->count(); } return false; } private: QPointer m_selection; }; DefaultTool::DefaultTool(KoCanvasBase *canvas) : KoInteractionTool(canvas) , m_lastHandle(KoFlake::NoHandle) , m_hotPosition(KoFlake::TopLeftCorner) , m_mouseWasInsideHandles(false) , m_moveCommand(0) , m_selectionHandler(new SelectionHandler(this)) , m_customEventStrategy(0) { setupActions(); QPixmap rotatePixmap, shearPixmap; rotatePixmap.load(":/cursor_rotate.png"); Q_ASSERT(!rotatePixmap.isNull()); shearPixmap.load(":/cursor_shear.png"); Q_ASSERT(!shearPixmap.isNull()); m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45))); m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90))); m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135))); m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180))); m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225))); m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270))); m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315))); m_rotateCursors[7] = QCursor(rotatePixmap); /* m_rotateCursors[0] = QCursor(Qt::RotateNCursor); m_rotateCursors[1] = QCursor(Qt::RotateNECursor); m_rotateCursors[2] = QCursor(Qt::RotateECursor); m_rotateCursors[3] = QCursor(Qt::RotateSECursor); m_rotateCursors[4] = QCursor(Qt::RotateSCursor); m_rotateCursors[5] = QCursor(Qt::RotateSWCursor); m_rotateCursors[6] = QCursor(Qt::RotateWCursor); m_rotateCursors[7] = QCursor(Qt::RotateNWCursor); */ m_shearCursors[0] = QCursor(shearPixmap); m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45))); m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90))); m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135))); m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180))); m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225))); m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270))); m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315))); m_sizeCursors[0] = Qt::SizeVerCursor; m_sizeCursors[1] = Qt::SizeBDiagCursor; m_sizeCursors[2] = Qt::SizeHorCursor; m_sizeCursors[3] = Qt::SizeFDiagCursor; m_sizeCursors[4] = Qt::SizeVerCursor; m_sizeCursors[5] = Qt::SizeBDiagCursor; m_sizeCursors[6] = Qt::SizeHorCursor; m_sizeCursors[7] = Qt::SizeFDiagCursor; KoShapeManager *manager = canvas->shapeManager(); connect(manager, SIGNAL(selectionChanged()), this, SLOT(updateActions())); } DefaultTool::~DefaultTool() { } bool DefaultTool::wantsAutoScroll() const { return true; } void DefaultTool::setupActions() { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QAction *actionBringToFront = actionRegistry->makeQAction("object_order_front", this); addAction("object_order_front", actionBringToFront); connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront())); QAction *actionRaise = actionRegistry->makeQAction("object_order_raise", this); addAction("object_order_raise", actionRaise); connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp())); QAction *actionLower = actionRegistry->makeQAction("object_order_lower", this); addAction("object_order_lower", actionLower); connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown())); QAction *actionSendToBack = actionRegistry->makeQAction("object_order_back", this); addAction("object_order_back", actionSendToBack); connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack())); QAction *actionAlignLeft = actionRegistry->makeQAction("object_align_horizontal_left", this); addAction("object_align_horizontal_left", actionAlignLeft); connect(actionAlignLeft, SIGNAL(triggered()), this, SLOT(selectionAlignHorizontalLeft())); QAction *actionAlignCenter = actionRegistry->makeQAction("object_align_horizontal_center", this); addAction("object_align_horizontal_center", actionAlignCenter); connect(actionAlignCenter, SIGNAL(triggered()), this, SLOT(selectionAlignHorizontalCenter())); QAction *actionAlignRight = actionRegistry->makeQAction("object_align_horizontal_right", this); addAction("object_align_horizontal_right", actionAlignRight); connect(actionAlignRight, SIGNAL(triggered()), this, SLOT(selectionAlignHorizontalRight())); QAction *actionAlignTop = actionRegistry->makeQAction("object_align_vertical_top", this); addAction("object_align_vertical_top", actionAlignTop); connect(actionAlignTop, SIGNAL(triggered()), this, SLOT(selectionAlignVerticalTop())); QAction *actionAlignMiddle = actionRegistry->makeQAction("object_align_vertical_center", this); addAction("object_align_vertical_center", actionAlignMiddle); connect(actionAlignMiddle, SIGNAL(triggered()), this, SLOT(selectionAlignVerticalCenter())); QAction *actionAlignBottom = actionRegistry->makeQAction("object_align_vertical_bottom", this); addAction("object_align_vertical_bottom", actionAlignBottom); connect(actionAlignBottom, SIGNAL(triggered()), this, SLOT(selectionAlignVerticalBottom())); QAction *actionGroupBottom = actionRegistry->makeQAction("object_group", this); addAction("object_group", actionGroupBottom); connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup())); QAction *actionUngroupBottom = actionRegistry->makeQAction("object_ungroup", this); addAction("object_ungroup", actionUngroupBottom); connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup())); } qreal DefaultTool::rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation) { QPointF selectionCenter = koSelection()->absolutePosition(); QPointF direction; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopRightCorner) - - koSelection()->absolutePosition(KoFlake::TopLeftCorner); + - koSelection()->absolutePosition(KoFlake::TopLeftCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeftCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRightCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRightCorner) - koSelection()->absolutePosition(KoFlake::TopLeftCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRightCorner) - koSelection()->absolutePosition(KoFlake::BottomRightCorner)).normalized()).toPointF(); break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomRightCorner) - - koSelection()->absolutePosition(KoFlake::TopRightCorner); + - koSelection()->absolutePosition(KoFlake::TopRightCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRightCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRightCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRightCorner) - koSelection()->absolutePosition(KoFlake::BottomLeftCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRightCorner) - koSelection()->absolutePosition(KoFlake::TopRightCorner)).normalized()).toPointF(); break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - - koSelection()->absolutePosition(KoFlake::BottomRightCorner); + - koSelection()->absolutePosition(KoFlake::BottomRightCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeftCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRightCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomLeftHandle: direction = koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - koSelection()->absolutePosition(KoFlake::BottomRightCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - koSelection()->absolutePosition(KoFlake::TopLeftCorner)).normalized()).toPointF(); break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopLeftCorner) - - koSelection()->absolutePosition(KoFlake::BottomLeftCorner); + - koSelection()->absolutePosition(KoFlake::BottomLeftCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeftCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopLeftHandle: direction = koSelection()->absolutePosition(KoFlake::TopLeftCorner) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeftCorner) - koSelection()->absolutePosition(KoFlake::TopRightCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeftCorner) - koSelection()->absolutePosition(KoFlake::BottomLeftCorner)).normalized()).toPointF(); break; case KoFlake::NoHandle: return 0.0; break; } qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { rotation -= 0.0; } else { rotation -= 270.0; } break; case KoFlake::TopRightHandle: rotation -= 315.0; break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { rotation -= 90.0; } else { rotation -= 0.0; } break; case KoFlake::BottomRightHandle: rotation -= 45.0; break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { rotation -= 180.0; } else { rotation -= 90.0; } break; case KoFlake::BottomLeftHandle: rotation -= 135.0; break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { rotation -= 270.0; } else { rotation -= 180.0; } break; case KoFlake::TopLeftHandle: rotation -= 225.0; break; case KoFlake::NoHandle: break; } if (rotation < 0.0) { rotation += 360.0; } return rotation; } void DefaultTool::updateCursor() { QCursor cursor = Qt::ArrowCursor; QString statusText; KoSelection * selection = koSelection(); if (selection && selection->count() > 0) { // has a selection bool editable = editableShapesCount(koSelection()->selectedShapes(KoFlake::StrippedSelection)); if (!m_mouseWasInsideHandles) { m_angle = rotationOfHandle(m_lastHandle, true); int rotOctant = 8 + int(8.5 + m_angle / 45); bool rotateHandle = false; bool shearHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_shearCursors[(0 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopRightHandle: cursor = m_rotateCursors[(1 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_shearCursors[(2 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomRightHandle: cursor = m_rotateCursors[(3 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_shearCursors[(4 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomLeftHandle: cursor = m_rotateCursors[(5 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_shearCursors[(6 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopLeftHandle: cursor = m_rotateCursors[(7 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::NoHandle: cursor = Qt::ArrowCursor; break; } if (rotateHandle) { statusText = i18n("Left click rotates around center, right click around highlighted position."); } if (shearHandle) { statusText = i18n("Click and drag to shear selection."); } } else { statusText = i18n("Click and drag to resize selection."); m_angle = rotationOfHandle(m_lastHandle, false); int rotOctant = 8 + int(8.5 + m_angle / 45); bool cornerHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_sizeCursors[(0 + rotOctant) % 8]; break; case KoFlake::TopRightHandle: cursor = m_sizeCursors[(1 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_sizeCursors[(2 + rotOctant) % 8]; break; case KoFlake::BottomRightHandle: cursor = m_sizeCursors[(3 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_sizeCursors[(4 + rotOctant) % 8]; break; case KoFlake::BottomLeftHandle: cursor = m_sizeCursors[(5 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_sizeCursors[(6 + rotOctant) % 8]; break; case KoFlake::TopLeftHandle: cursor = m_sizeCursors[(7 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::NoHandle: cursor = Qt::SizeAllCursor; statusText = i18n("Click and drag to move selection."); break; } if (cornerHandle) { statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position."); } } if (!editable) { cursor = Qt::ArrowCursor; } } else { // there used to be guides... :'''( } useCursor(cursor); if (currentStrategy() == 0) { emit statusTextChanged(statusText); } } void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter) { KoInteractionTool::paint(painter, converter); if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) { SelectionDecorator decorator(m_mouseWasInsideHandles ? m_lastHandle : KoFlake::NoHandle, true, true); decorator.setSelection(koSelection()); decorator.setHandleRadius(handleRadius()); decorator.setHotPosition(m_hotPosition); decorator.paint(painter, converter); } painter.save(); KoShape::applyConversion(painter, converter); canvas()->snapGuide()->paint(painter, converter); painter.restore(); } void DefaultTool::mousePressEvent(KoPointerEvent *event) { KoInteractionTool::mousePressEvent(event); updateCursor(); } void DefaultTool::mouseMoveEvent(KoPointerEvent *event) { KoInteractionTool::mouseMoveEvent(event); if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) { QRectF bound = handlesSize(); if (bound.contains(event->point)) { bool inside; KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside); if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) { m_lastHandle = newDirection; m_mouseWasInsideHandles = inside; //repaintDecorations(); } } else { /*if (m_lastHandle != KoFlake::NoHandle) repaintDecorations(); */ m_lastHandle = KoFlake::NoHandle; m_mouseWasInsideHandles = false; // there used to be guides... :'''( } } else { // there used to be guides... :'''( } updateCursor(); } QRectF DefaultTool::handlesSize() { QRectF bound = koSelection()->boundingRect(); // expansion Border if (!canvas() || !canvas()->viewConverter()) { return bound; } QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE)); bound.adjust(-border.x(), -border.y(), border.x(), border.y()); return bound; } void DefaultTool::mouseReleaseEvent(KoPointerEvent *event) { KoInteractionTool::mouseReleaseEvent(event); updateCursor(); } void DefaultTool::mouseDoubleClickEvent(KoPointerEvent *event) { QList shapes; Q_FOREACH (KoShape *shape, koSelection()->selectedShapes()) { if (shape->boundingRect().contains(event->point) && // first 'cheap' check shape->outline().contains(event->point)) { // this is more expensive but weeds out the almost hits shapes.append(shape); } } if (shapes.count() == 0) { // nothing in the selection was clicked on. KoShape *shape = canvas()->shapeManager()->shapeAt(event->point, KoFlake::ShapeOnTop); if (shape) { shapes.append(shape); } // there used to be guides... :'''( } QList shapes2; foreach (KoShape *shape, shapes) { QSet delegates = shape->toolDelegates(); if (delegates.isEmpty()) { shapes2.append(shape); } else { foreach (KoShape *delegatedShape, delegates) { shapes2.append(delegatedShape); } } } KoToolManager::instance()->switchToolRequested( - KoToolManager::instance()->preferredToolForSelection(shapes2)); + KoToolManager::instance()->preferredToolForSelection(shapes2)); } bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers) { qreal x = 0.0, y = 0.0; if (direction == Qt::Key_Left) { x = -5; } else if (direction == Qt::Key_Right) { x = 5; } else if (direction == Qt::Key_Up) { y = -5; } else if (direction == Qt::Key_Down) { y = 5; } if (x != 0.0 || y != 0.0) { // actually move if ((modifiers & Qt::ShiftModifier) != 0) { x *= 10; y *= 10; } else if ((modifiers & Qt::AltModifier) != 0) { // more precise x /= 5; y /= 5; } QList prevPos; QList newPos; QList shapes; Q_FOREACH (KoShape *shape, koSelection()->selectedShapes(KoFlake::TopLevelSelection)) { if (shape->isGeometryProtected()) { continue; } shapes.append(shape); QPointF p = shape->position(); prevPos.append(p); p.setX(p.x() + x); p.setY(p.y() + y); newPos.append(p); } if (shapes.count() > 0) { // use a timeout to make sure we don't reuse a command possibly deleted by the commandHistory if (m_lastUsedMoveCommand.msecsTo(QTime::currentTime()) > 5000) { m_moveCommand = 0; } if (m_moveCommand) { // alter previous instead of creating new one. m_moveCommand->setNewPositions(newPos); m_moveCommand->redo(); } else { m_moveCommand = new KoShapeMoveCommand(shapes, prevPos, newPos); canvas()->addCommand(m_moveCommand); } m_lastUsedMoveCommand = QTime::currentTime(); return true; } } return false; } void DefaultTool::keyPressEvent(QKeyEvent *event) { KoInteractionTool::keyPressEvent(event); if (currentStrategy() == 0) { switch (event->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: if (moveSelection(event->key(), event->modifiers())) { event->accept(); } break; case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: canvas()->resourceManager()->setResource(HotPosition, event->key() - Qt::Key_1); event->accept(); break; default: return; } } } void DefaultTool::customMoveEvent(KoPointerEvent *event) { if (koSelection() && koSelection()->count() <= 0) { event->ignore(); return; } int move = qMax(qAbs(event->x()), qAbs(event->y())); int zoom = qAbs(event->z()); int rotate = qAbs(event->rotationZ()); const int threshold = 2; if (move < threshold && zoom < threshold && rotate < threshold) { if (m_customEventStrategy) { m_customEventStrategy->finishInteraction(event->modifiers()); KUndo2Command *command = m_customEventStrategy->createCommand(); if (command) { canvas()->addCommand(command); } delete m_customEventStrategy; m_customEventStrategy = 0; repaintDecorations(); } event->accept(); return; } // check if the z-movement is dominant if (zoom > move && zoom > rotate) { // zoom if (!m_customEventStrategy) { m_customEventStrategy = new ShapeResizeStrategy(this, event->point, KoFlake::TopLeftHandle); } } else if (move > zoom && move > rotate) { // check if x-/y-movement is dominant // move if (!m_customEventStrategy) { m_customEventStrategy = new ShapeMoveStrategy(this, event->point); } } else if (rotate > zoom && rotate > move) { // rotation is dominant // rotate if (!m_customEventStrategy) { m_customEventStrategy = new ShapeRotateStrategy(this, event->point, event->buttons()); } } if (m_customEventStrategy) { m_customEventStrategy->handleCustomEvent(event); } event->accept(); } void DefaultTool::repaintDecorations() { if (koSelection() && koSelection()->count() > 0) { canvas()->updateCanvas(handlesSize()); } } void DefaultTool::copy() const { QList shapes = canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection); if (!shapes.empty()) { KoShapeOdfSaveHelper saveHelper(shapes); KoDrag drag; drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); drag.addToClipboard(); } } void DefaultTool::deleteSelection() { QList shapes; foreach (KoShape *s, canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection)) { if (s->isGeometryProtected()) { continue; } shapes << s; } if (!shapes.empty()) { canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes)); } } bool DefaultTool::paste() { // we no longer have to do anything as tool Proxy will do it for us return false; } QStringList DefaultTool::supportedPasteMimeTypes() const { QStringList list; list << KoOdf::mimeType(KoOdf::Text); return list; } KoSelection *DefaultTool::koSelection() { Q_ASSERT(canvas()); Q_ASSERT(canvas()->shapeManager()); return canvas()->shapeManager()->selection(); } KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning) { // check for handles in this order; meaning that when handles overlap the one on top is chosen static const KoFlake::SelectionHandle handleOrder[] = { KoFlake::BottomRightHandle, KoFlake::TopLeftHandle, KoFlake::BottomLeftHandle, KoFlake::TopRightHandle, KoFlake::BottomMiddleHandle, KoFlake::RightMiddleHandle, KoFlake::LeftMiddleHandle, KoFlake::TopMiddleHandle, KoFlake::NoHandle }; if (koSelection() && koSelection()->count() == 0) { return KoFlake::NoHandle; } recalcSelectionBox(); const KoViewConverter *converter = canvas()->viewConverter(); if (!converter) { return KoFlake::NoHandle; } if (innerHandleMeaning != 0) { QPainterPath path; path.addPolygon(m_selectionOutline); *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point)); } for (int i = 0; i < KoFlake::NoHandle; ++i) { KoFlake::SelectionHandle handle = handleOrder[i]; QPointF pt = converter->documentToView(point) - converter->documentToView(m_selectionBox[handle]); // if just inside the outline if (qAbs(pt.x()) < HANDLE_DISTANCE && qAbs(pt.y()) < HANDLE_DISTANCE) { if (innerHandleMeaning != 0) { if (qAbs(pt.x()) < 4 && qAbs(pt.y()) < 4) { *innerHandleMeaning = true; } } return handle; } } return KoFlake::NoHandle; } void DefaultTool::recalcSelectionBox() { if (!koSelection()) { return; } if (koSelection()->count() == 0) { return; } if (koSelection()->count() > 1) { QTransform matrix = koSelection()->absoluteTransformation(0); m_selectionOutline = matrix.map(QPolygonF(QRectF(QPointF(0, 0), koSelection()->size()))); m_angle = 0.0; //koSelection()->rotation(); } else { QTransform matrix = koSelection()->firstSelectedShape()->absoluteTransformation(0); m_selectionOutline = matrix.map(QPolygonF(QRectF(QPointF(0, 0), koSelection()->firstSelectedShape()->size()))); m_angle = 0.0; //koSelection()->firstSelectedShape()->rotation(); } QPolygonF outline = m_selectionOutline; //shorter name in the following :) m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2; m_selectionBox[KoFlake::TopRightHandle] = outline.value(1); m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2; m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2); m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2; m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3); m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2; m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0); if (koSelection()->count() == 1) { #if 0 // TODO detect mirroring KoShape *s = koSelection()->firstSelectedShape(); if (s->scaleX() < 0) { // vertically mirrored: swap left / right qSwap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::TopRightHandle]); qSwap(m_selectionBox[KoFlake::LeftMiddleHandle], m_selectionBox[KoFlake::RightMiddleHandle]); qSwap(m_selectionBox[KoFlake::BottomLeftHandle], m_selectionBox[KoFlake::BottomRightHandle]); } if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom qSwap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::BottomLeftHandle]); qSwap(m_selectionBox[KoFlake::TopMiddleHandle], m_selectionBox[KoFlake::BottomMiddleHandle]); qSwap(m_selectionBox[KoFlake::TopRightHandle], m_selectionBox[KoFlake::BottomRightHandle]); } #endif } } void DefaultTool::activate(ToolActivation, const QSet &) { m_mouseWasInsideHandles = false; m_lastHandle = KoFlake::NoHandle; useCursor(Qt::ArrowCursor); repaintDecorations(); updateActions(); } void DefaultTool::selectionAlignHorizontalLeft() { selectionAlign(KoShapeAlignCommand::HorizontalLeftAlignment); } void DefaultTool::selectionAlignHorizontalCenter() { selectionAlign(KoShapeAlignCommand::HorizontalCenterAlignment); } void DefaultTool::selectionAlignHorizontalRight() { selectionAlign(KoShapeAlignCommand::HorizontalRightAlignment); } void DefaultTool::selectionAlignVerticalTop() { selectionAlign(KoShapeAlignCommand::VerticalTopAlignment); } void DefaultTool::selectionAlignVerticalCenter() { selectionAlign(KoShapeAlignCommand::VerticalCenterAlignment); } void DefaultTool::selectionAlignVerticalBottom() { selectionAlign(KoShapeAlignCommand::VerticalBottomAlignment); } void DefaultTool::selectionGroup() { KoSelection *selection = koSelection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QList groupedShapes; // only group shapes with an unselected parent foreach (KoShape *shape, selectedShapes) { if (!selectedShapes.contains(shape->parent()) && shape->isEditable()) { groupedShapes << shape; } } KoShapeGroup *group = new KoShapeGroup(); // TODO what if only one shape is left? KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes")); canvas()->shapeController()->addShapeDirect(group, cmd); KoShapeGroupCommand::createCommand(group, groupedShapes, cmd); canvas()->addCommand(cmd); // update selection so we can ungroup immediately again selection->deselectAll(); selection->select(group); } void DefaultTool::selectionUngroup() { KoSelection *selection = canvas()->shapeManager()->selection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QList containerSet; // only ungroup shape groups with an unselected parent foreach (KoShape *shape, selectedShapes) { if (!selectedShapes.contains(shape->parent()) && shape->isEditable()) { containerSet << shape; } } KUndo2Command *cmd = 0; // add a ungroup command for each found shape container to the macro command Q_FOREACH (KoShape *shape, containerSet) { KoShapeGroup *group = dynamic_cast(shape); if (group) { cmd = cmd ? cmd : new KUndo2Command(kundo2_i18n("Ungroup shapes")); new KoShapeUngroupCommand(group, group->shapes(), group->parent() ? QList() : canvas()->shapeManager()->topLevelShapes(), cmd); canvas()->shapeController()->removeShape(group, cmd); } } if (cmd) { canvas()->addCommand(cmd); } } void DefaultTool::selectionAlign(KoShapeAlignCommand::Align align) { KoSelection *selection = canvas()->shapeManager()->selection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); if (selectedShapes.count() < 1) { return; } QList editableShapes = filterEditableShapes(selectedShapes); // TODO add an option to the widget so that one can align to the page // with multiple selected shapes too QRectF bb; // single selected shape is automatically aligned to document rect if (editableShapes.count() == 1) { if (!canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize)) { return; } bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResourceManager::PageSize)); } else { Q_FOREACH (KoShape *shape, editableShapes) { bb |= shape->boundingRect(); } } KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb); canvas()->addCommand(cmd); selection->updateSizeAndPosition(); } void DefaultTool::selectionBringToFront() { selectionReorder(KoShapeReorderCommand::BringToFront); } void DefaultTool::selectionMoveUp() { selectionReorder(KoShapeReorderCommand::RaiseShape); } void DefaultTool::selectionMoveDown() { selectionReorder(KoShapeReorderCommand::LowerShape); } void DefaultTool::selectionSendToBack() { selectionReorder(KoShapeReorderCommand::SendToBack); } void DefaultTool::selectionReorder(KoShapeReorderCommand::MoveShapeType order) { KoSelection *selection = canvas()->shapeManager()->selection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); if (selectedShapes.count() < 1) { return; } QList editableShapes = filterEditableShapes(selectedShapes); if (editableShapes.count() < 1) { return; } KUndo2Command *cmd = KoShapeReorderCommand::createCommand(editableShapes, canvas()->shapeManager(), order); if (cmd) { canvas()->addCommand(cmd); } } QList > DefaultTool::createOptionWidgets() { QList > widgets; DefaultToolArrangeWidget *defaultArrange = new DefaultToolArrangeWidget(this); defaultArrange->setWindowTitle(i18n("Arrange")); widgets.append(defaultArrange); DefaultToolWidget *defaultTool = new DefaultToolWidget(this); defaultTool->setWindowTitle(i18n("Geometry")); widgets.append(defaultTool); + KoStrokeConfigWidget *strokeWidget = new KoStrokeConfigWidget(0); + KisDocumentAwareSpinBoxUnitManager* managerLineWidth = new KisDocumentAwareSpinBoxUnitManager(strokeWidget); + KisDocumentAwareSpinBoxUnitManager* managerMitterLimit = new KisDocumentAwareSpinBoxUnitManager(strokeWidget); + managerLineWidth->setApparentUnitFromSymbol("px"); + managerMitterLimit->setApparentUnitFromSymbol("px"); //set unit to px by default + strokeWidget->setUnitManagers(managerLineWidth, managerMitterLimit); strokeWidget->setWindowTitle(i18n("Line")); strokeWidget->setCanvas(canvas()); widgets.append(strokeWidget); KoFillConfigWidget *fillWidget = new KoFillConfigWidget(0); fillWidget->setWindowTitle(i18n("Fill")); fillWidget->setCanvas(canvas()); widgets.append(fillWidget); KoShadowConfigWidget *shadowWidget = new KoShadowConfigWidget(0); + KisDocumentAwareSpinBoxUnitManager* managerBlur = new KisDocumentAwareSpinBoxUnitManager(shadowWidget); //let the shadow widget be aware of document relative units. + KisDocumentAwareSpinBoxUnitManager* managerOffset = new KisDocumentAwareSpinBoxUnitManager(shadowWidget); //let the shadow widget be aware of document relative units. + shadowWidget->setUnitManagers(managerBlur, managerOffset); shadowWidget->setWindowTitle(i18n("Shadow")); shadowWidget->setCanvas(canvas()); widgets.append(shadowWidget); return widgets; } void DefaultTool::canvasResourceChanged(int key, const QVariant &res) { if (key == HotPosition) { m_hotPosition = static_cast(res.toInt()); repaintDecorations(); } } KoInteractionStrategy *DefaultTool::createStrategy(KoPointerEvent *event) { // reset the move by keys when a new strategy is created otherwise we might change the // command after a new command was added. This happend when you where faster than the timer. m_moveCommand = 0; KoShapeManager *shapeManager = canvas()->shapeManager(); KoSelection *select = shapeManager->selection(); bool insideSelection; KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection); bool editableShape = editableShapesCount(select->selectedShapes()); if (event->buttons() & Qt::MidButton) { // change the hot selection position when middle clicking on a handle KoFlake::Position newHotPosition = m_hotPosition; switch (handle) { case KoFlake::TopLeftHandle: newHotPosition = KoFlake::TopLeftCorner; break; case KoFlake::TopRightHandle: newHotPosition = KoFlake::TopRightCorner; break; case KoFlake::BottomLeftHandle: newHotPosition = KoFlake::BottomLeftCorner; break; case KoFlake::BottomRightHandle: newHotPosition = KoFlake::BottomRightCorner; break; default: { // check if we had hit the center point const KoViewConverter *converter = canvas()->viewConverter(); QPointF pt = converter->documentToView(event->point - select->absolutePosition()); if (qAbs(pt.x()) < HANDLE_DISTANCE && qAbs(pt.y()) < HANDLE_DISTANCE) { newHotPosition = KoFlake::CenteredPosition; } break; } } if (m_hotPosition != newHotPosition) { canvas()->resourceManager()->setResource(HotPosition, newHotPosition); } return 0; } bool selectMultiple = event->modifiers() & Qt::ControlModifier; bool selectNextInStack = event->modifiers() & Qt::ShiftModifier; if (editableShape) { // manipulation of selected shapes goes first if (handle != KoFlake::NoHandle) { if (event->buttons() == Qt::LeftButton) { // resizing or shearing only with left mouse button if (insideSelection) { return new ShapeResizeStrategy(this, event->point, handle); } if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle || handle == KoFlake::BottomMiddleHandle || handle == KoFlake::LeftMiddleHandle) { return new ShapeShearStrategy(this, event->point, handle); } } // rotating is allowed for rigth mouse button too if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle || handle == KoFlake::BottomLeftHandle || handle == KoFlake::BottomRightHandle) { return new ShapeRotateStrategy(this, event->point, event->buttons()); } } if (!(selectMultiple || selectNextInStack) && event->buttons() == Qt::LeftButton) { const QPainterPath outlinePath = select->transformation().map(select->outline()); if (outlinePath.contains(event->point) || outlinePath.intersects(handlePaintRect(event->point))) { return new ShapeMoveStrategy(this, event->point); } } } if ((event->buttons() & Qt::LeftButton) == 0) { return 0; // Nothing to do for middle/right mouse button } KoShape *shape = shapeManager->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop); if (!shape && handle == KoFlake::NoHandle) { if (!selectMultiple) { repaintDecorations(); select->deselectAll(); } return new KoShapeRubberSelectStrategy(this, event->point); } if (select->isSelected(shape)) { if (selectMultiple) { repaintDecorations(); select->deselect(shape); } } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected repaintDecorations(); if (!selectMultiple) { shapeManager->selection()->deselectAll(); } select->select(shape, selectNextInStack ? false : true); repaintDecorations(); // tablet selection isn't precise and may lead to a move, preventing that if (event->isTabletEvent()) { return new NopInteractionStrategy(this); } return new ShapeMoveStrategy(this, event->point); } return 0; } void DefaultTool::updateActions() { KoSelection *selection(koSelection()); if (!selection) { action("object_order_front")->setEnabled(false); action("object_order_raise")->setEnabled(false); action("object_order_lower")->setEnabled(false); action("object_order_back")->setEnabled(false); action("object_align_horizontal_left")->setEnabled(false); action("object_align_horizontal_center")->setEnabled(false); action("object_align_horizontal_right")->setEnabled(false); action("object_align_vertical_top")->setEnabled(false); action("object_align_vertical_center")->setEnabled(false); action("object_align_vertical_bottom")->setEnabled(false); action("object_group")->setEnabled(false); action("object_ungroup")->setEnabled(false); return; } QList editableShapes = filterEditableShapes(selection->selectedShapes(KoFlake::TopLevelSelection)); bool enable = editableShapes.count() > 0; action("object_order_front")->setEnabled(enable); action("object_order_raise")->setEnabled(enable); action("object_order_lower")->setEnabled(enable); action("object_order_back")->setEnabled(enable); enable = (editableShapes.count() > 1) || (enable && canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize)); action("object_align_horizontal_left")->setEnabled(enable); action("object_align_horizontal_center")->setEnabled(enable); action("object_align_horizontal_right")->setEnabled(enable); action("object_align_vertical_top")->setEnabled(enable); action("object_align_vertical_center")->setEnabled(enable); action("object_align_vertical_bottom")->setEnabled(enable); action("object_group")->setEnabled(editableShapes.count() > 1); bool groupShape = false; foreach (KoShape *shape, editableShapes) { if (dynamic_cast(shape)) { groupShape = true; break; } } action("object_ungroup")->setEnabled(groupShape); emit selectionChanged(selection->count()); } KoToolSelection *DefaultTool::selection() { return m_selectionHandler; } QList DefaultTool::filterEditableShapes(const QList &shapes) { QList editableShapes; Q_FOREACH (KoShape *shape, shapes) { if (shape->isEditable()) { editableShapes.append(shape); } } return editableShapes; } uint DefaultTool::editableShapesCount(const QList &shapes) { uint count = 0; Q_FOREACH (KoShape *shape, shapes) { if (shape->isEditable()) { count++; } } return count; } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolTransformWidget.cpp b/plugins/tools/defaulttool/defaulttool/DefaultToolTransformWidget.cpp index 93b5c7db6f..37461e944e 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultToolTransformWidget.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultToolTransformWidget.cpp @@ -1,284 +1,286 @@ /* This file is part of the KDE project * Copyright (C) 2007 Martin Pfeiffer * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 C. Boemann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "DefaultToolTransformWidget.h" #include #include #include #include #include #include #include #include #include #include "SelectionDecorator.h" #include #include #include #include #include #include #include +#include "kis_document_aware_spin_box_unit_manager.h" + DefaultToolTransformWidget::DefaultToolTransformWidget(KoInteractionTool *tool, QWidget *parent) : QMenu(parent) { m_tool = tool; setupUi(this); setUnit(m_tool->canvas()->unit()); connect(m_tool->canvas()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(resourceChanged(int,QVariant))); connect(rotateButton, SIGNAL(clicked()), this, SLOT(rotationChanged())); connect(shearXButton, SIGNAL(clicked()), this, SLOT(shearXChanged())); connect(shearYButton, SIGNAL(clicked()), this, SLOT(shearYChanged())); connect(scaleXButton, SIGNAL(clicked()), this, SLOT(scaleXChanged())); connect(scaleYButton, SIGNAL(clicked()), this, SLOT(scaleYChanged())); connect(scaleAspectCheckBox, SIGNAL(toggled(bool)), scaleYSpinBox, SLOT(setDisabled(bool))); connect(scaleAspectCheckBox, SIGNAL(toggled(bool)), scaleYButton, SLOT(setDisabled(bool))); connect(resetButton, SIGNAL(clicked()), this, SLOT(resetTransformations())); } void DefaultToolTransformWidget::setUnit(const KoUnit &unit) { shearXSpinBox->setUnit(unit); shearYSpinBox->setUnit(unit); } void DefaultToolTransformWidget::resourceChanged(int key, const QVariant &res) { if (key == KoCanvasResourceManager::Unit) { setUnit(res.value()); } } void DefaultToolTransformWidget::rotationChanged() { QList selectedShapes = m_tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection); QList oldTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { oldTransforms << shape->transformation(); } qreal angle = rotateSpinBox->value(); QPointF rotationCenter = m_tool->canvas()->shapeManager()->selection()->absolutePosition(SelectionDecorator::hotPosition()); QTransform matrix; matrix.translate(rotationCenter.x(), rotationCenter.y()); matrix.rotate(angle); matrix.translate(-rotationCenter.x(), -rotationCenter.y()); Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); shape->applyAbsoluteTransformation(matrix); shape->update(); } m_tool->canvas()->shapeManager()->selection()->applyAbsoluteTransformation(matrix); QList newTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(selectedShapes, oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Rotate")); m_tool->canvas()->addCommand(cmd); } void DefaultToolTransformWidget::shearXChanged() { KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QList oldTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { oldTransforms << shape->transformation(); } qreal shearX = shearXSpinBox->value() / selection->size().height(); QPointF basePoint = selection->absolutePosition(SelectionDecorator::hotPosition()); QTransform matrix; matrix.translate(basePoint.x(), basePoint.y()); matrix.shear(shearX, 0.0); matrix.translate(-basePoint.x(), -basePoint.y()); Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); shape->applyAbsoluteTransformation(matrix); shape->update(); } selection->applyAbsoluteTransformation(matrix); QList newTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(selectedShapes, oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Shear X")); m_tool->canvas()->addCommand(cmd); } void DefaultToolTransformWidget::shearYChanged() { KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QList oldTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { oldTransforms << shape->transformation(); } qreal shearY = shearYSpinBox->value() / selection->size().width(); QPointF basePoint = selection->absolutePosition(SelectionDecorator::hotPosition()); QTransform matrix; matrix.translate(basePoint.x(), basePoint.y()); matrix.shear(0.0, shearY); matrix.translate(-basePoint.x(), -basePoint.y()); Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); shape->applyAbsoluteTransformation(matrix); shape->update(); } selection->applyAbsoluteTransformation(matrix); QList newTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(selectedShapes, oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Shear Y")); m_tool->canvas()->addCommand(cmd); } void DefaultToolTransformWidget::scaleXChanged() { QList selectedShapes = m_tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection); QList oldTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { oldTransforms << shape->transformation(); } qreal scale = scaleXSpinBox->value() * 0.01; // Input is in per cent QPointF basePoint = m_tool->canvas()->shapeManager()->selection()->absolutePosition(SelectionDecorator::hotPosition()); QTransform matrix; matrix.translate(basePoint.x(), basePoint.y()); if (scaleAspectCheckBox->isChecked()) { matrix.scale(scale, scale); } else { matrix.scale(scale, 1.0); } matrix.translate(-basePoint.x(), -basePoint.y()); Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); shape->applyAbsoluteTransformation(matrix); shape->update(); } m_tool->canvas()->shapeManager()->selection()->applyAbsoluteTransformation(matrix); QList newTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(selectedShapes, oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Scale")); m_tool->canvas()->addCommand(cmd); } void DefaultToolTransformWidget::scaleYChanged() { QList selectedShapes = m_tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection); QList oldTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { oldTransforms << shape->transformation(); } qreal scale = scaleYSpinBox->value() * 0.01; // Input is in per cent QPointF basePoint = m_tool->canvas()->shapeManager()->selection()->absolutePosition(SelectionDecorator::hotPosition()); QTransform matrix; matrix.translate(basePoint.x(), basePoint.y()); matrix.scale(1.0, scale); matrix.translate(-basePoint.x(), -basePoint.y()); Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); shape->applyAbsoluteTransformation(matrix); shape->update(); } m_tool->canvas()->shapeManager()->selection()->applyAbsoluteTransformation(matrix); QList newTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(selectedShapes, oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Scale")); m_tool->canvas()->addCommand(cmd); } void DefaultToolTransformWidget::resetTransformations() { QList selectedShapes = m_tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection); QList oldTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { oldTransforms << shape->transformation(); } QTransform matrix; Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); shape->setTransformation(matrix); shape->update(); } m_tool->canvas()->shapeManager()->selection()->applyAbsoluteTransformation(matrix); QList newTransforms; Q_FOREACH (KoShape *shape, selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(selectedShapes, oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Reset Transformations")); m_tool->canvas()->addCommand(cmd); } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp b/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp index 5c5b0e7b91..408daa2358 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp @@ -1,280 +1,282 @@ /* This file is part of the KDE project * Copyright (C) 2007 Martin Pfeiffer * Copyright (C) 2007 Jan Hambrecht Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2010 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "DefaultToolWidget.h" #include "DefaultTool.h" #include #include #include #include #include #include #include #include #include #include #include "SelectionDecorator.h" #include "DefaultToolTransformWidget.h" #include #include #include #include #include #include #include #include +#include "kis_document_aware_spin_box_unit_manager.h" + DefaultToolWidget::DefaultToolWidget(KoInteractionTool *tool, QWidget *parent) : QWidget(parent) , m_tool(tool) , m_blockSignals(false) { setupUi(this); setUnit(m_tool->canvas()->unit()); aspectButton->setKeepAspectRatio(false); updatePosition(); updateSize(); connect(positionSelector, SIGNAL(positionSelected(KoFlake::Position)), this, SLOT(positionSelected(KoFlake::Position))); connect(positionXSpinBox, SIGNAL(editingFinished()), this, SLOT(positionHasChanged())); connect(positionYSpinBox, SIGNAL(editingFinished()), this, SLOT(positionHasChanged())); connect(widthSpinBox, SIGNAL(editingFinished()), this, SLOT(sizeHasChanged())); connect(heightSpinBox, SIGNAL(editingFinished()), this, SLOT(sizeHasChanged())); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); connect(selection, SIGNAL(selectionChanged()), this, SLOT(updatePosition())); connect(selection, SIGNAL(selectionChanged()), this, SLOT(updateSize())); KoShapeManager *manager = m_tool->canvas()->shapeManager(); connect(manager, SIGNAL(selectionContentChanged()), this, SLOT(updatePosition())); connect(manager, SIGNAL(selectionContentChanged()), this, SLOT(updateSize())); connect(m_tool->canvas()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(resourceChanged(int,QVariant))); connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectButtonToggled(bool))); } void DefaultToolWidget::positionSelected(KoFlake::Position position) { m_tool->canvas()->resourceManager()->setResource(DefaultTool::HotPosition, QVariant(position)); updatePosition(); } void DefaultToolWidget::updatePosition() { QPointF selPosition(0, 0); KoFlake::Position position = positionSelector->position(); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); if (selection && selection->count()) { selPosition = selection->absolutePosition(position); } positionXSpinBox->setEnabled(selection && selection->count()); positionYSpinBox->setEnabled(selection && selection->count()); if (m_blockSignals) { return; } m_blockSignals = true; positionXSpinBox->changeValue(selPosition.x()); positionYSpinBox->changeValue(selPosition.y()); QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); bool aspectLocked = false; foreach (KoShape *shape, selectedShapes) { aspectLocked = aspectLocked | shape->keepAspectRatio(); } aspectButton->setKeepAspectRatio(aspectLocked); m_blockSignals = false; } void DefaultToolWidget::positionHasChanged() { KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); if (!selection || selection->count() <= 0) { return; } KoFlake::Position position = positionSelector->position(); QPointF newPos(positionXSpinBox->value(), positionYSpinBox->value()); QPointF oldPos = selection->absolutePosition(position); if (oldPos == newPos) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QPointF moveBy = newPos - oldPos; QList oldPositions; QList newPositions; Q_FOREACH (KoShape *shape, selectedShapes) { oldPositions.append(shape->position()); newPositions.append(shape->position() + moveBy); } selection->setPosition(selection->position() + moveBy); m_tool->canvas()->addCommand(new KoShapeMoveCommand(selectedShapes, oldPositions, newPositions)); updatePosition(); } void DefaultToolWidget::updateSize() { QSizeF selSize(0, 0); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); uint selectionCount = 0; if (selection && selection->count()) { selectionCount = selection->count(); } if (selectionCount) { selSize = selection->boundingRect().size(); } widthSpinBox->setEnabled(selectionCount); heightSpinBox->setEnabled(selectionCount); if (m_blockSignals) { return; } m_blockSignals = true; widthSpinBox->changeValue(selSize.width()); heightSpinBox->changeValue(selSize.height()); m_blockSignals = false; } void DefaultToolWidget::sizeHasChanged() { if (aspectButton->hasFocus()) { return; } if (m_blockSignals) { return; } QSizeF newSize(widthSpinBox->value(), heightSpinBox->value()); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); QRectF rect = selection->boundingRect(); if (aspectButton->keepAspectRatio()) { qreal aspect = rect.width() / rect.height(); if (rect.width() != newSize.width()) { newSize.setHeight(newSize.width() / aspect); } else if (rect.height() != newSize.height()) { newSize.setWidth(newSize.height() * aspect); } } if (rect.width() != newSize.width() || rect.height() != newSize.height()) { // get the scale/resize center from the position selector QPointF scaleCenter = selection->absolutePosition(positionSelector->position()); QTransform resizeMatrix; resizeMatrix.translate(scaleCenter.x(), scaleCenter.y()); // make sure not to divide by 0 in case the selection is a line and has no width. In this case just scale by 1. resizeMatrix.scale(rect.width() ? newSize.width() / rect.width() : 1, rect.height() ? newSize.height() / rect.height() : 1); resizeMatrix.translate(-scaleCenter.x(), -scaleCenter.y()); QList selectedShapes = selection->selectedShapes(KoFlake::StrippedSelection); QList oldSizes, newSizes; QList oldState; QList newState; Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); QSizeF oldSize = shape->size(); oldState << shape->transformation(); QTransform shapeMatrix = shape->absoluteTransformation(0); // calculate the matrix we would apply to the local shape matrix // that tells us the effective scale values we have to use for the resizing QTransform localMatrix = shapeMatrix * resizeMatrix * shapeMatrix.inverted(); // save the effective scale values, without any mirroring portion const qreal scaleX = qAbs(localMatrix.m11()); const qreal scaleY = qAbs(localMatrix.m22()); // calculate the scale matrix which is equivalent to our resizing above QTransform scaleMatrix = (QTransform().scale(scaleX, scaleY)); scaleMatrix = shapeMatrix.inverted() * scaleMatrix * shapeMatrix; // calculate the new size of the shape, using the effective scale values oldSizes << oldSize; QSizeF newSize = QSizeF(scaleX * oldSize.width(), scaleY * oldSize.height()); newSizes << newSize; shape->setSize(newSize); // apply the rest of the transformation without the resizing part shape->applyAbsoluteTransformation(scaleMatrix.inverted() * resizeMatrix); newState << shape->transformation(); } m_tool->repaintDecorations(); selection->applyAbsoluteTransformation(resizeMatrix); KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Resize")); new KoShapeSizeCommand(selectedShapes, oldSizes, newSizes, cmd); new KoShapeTransformCommand(selectedShapes, oldState, newState, cmd); m_tool->canvas()->addCommand(cmd); updateSize(); updatePosition(); } } void DefaultToolWidget::setUnit(const KoUnit &unit) { m_blockSignals = true; positionXSpinBox->setUnit(unit); positionYSpinBox->setUnit(unit); widthSpinBox->setUnit(unit); heightSpinBox->setUnit(unit); m_blockSignals = false; updatePosition(); updateSize(); } void DefaultToolWidget::resourceChanged(int key, const QVariant &res) { if (key == KoCanvasResourceManager::Unit) { setUnit(res.value()); } else if (key == DefaultTool::HotPosition) { if (res.toInt() != positionSelector->position()) { positionSelector->setPosition(static_cast(res.toInt())); updatePosition(); } } } void DefaultToolWidget::aspectButtonToggled(bool keepAspect) { if (m_blockSignals) { return; } KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); foreach (KoShape *shape, selection->selectedShapes(KoFlake::TopLevelSelection)) { shape->setKeepAspectRatio(keepAspect); } } diff --git a/plugins/tools/selectiontools/kis_tool_select_path.cc b/plugins/tools/selectiontools/kis_tool_select_path.cc index 23ecc10b51..fa0dd2d5af 100644 --- a/plugins/tools/selectiontools/kis_tool_select_path.cc +++ b/plugins/tools/selectiontools/kis_tool_select_path.cc @@ -1,167 +1,173 @@ /* * Copyright (c) 2007 Sven Langkamp * * 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 2 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. */ #include "kis_tool_select_path.h" #include #include "kis_cursor.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_selection_options.h" #include "kis_canvas_resource_provider.h" #include "kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include KisToolSelectPath::KisToolSelectPath(KoCanvasBase * canvas) : KisToolSelectBase(canvas, KisCursor::load("tool_polygonal_selection_cursor.png", 6, 6), i18n("Select path"), (KisTool*) (new __KisToolSelectPathLocalTool(canvas, this))) { } void KisToolSelectPath::requestStrokeEnd() { localTool()->endPathWithoutLastPoint(); } void KisToolSelectPath::requestStrokeCancellation() { localTool()->cancelPath(); } void KisToolSelectPath::mousePressEvent(KoPointerEvent* event) { if (!selectionEditable()) return; DelegatedSelectPathTool::mousePressEvent(event); } // Install an event filter to catch right-click events. // This code is duplicated in kis_tool_path.cc bool KisToolSelectPath::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); if (event->type() == QEvent::MouseButtonPress || - event->type() == QEvent::MouseButtonDblClick) { + event->type() == QEvent::MouseButtonDblClick) { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::RightButton) { localTool()->removeLastPoint(); return true; } } else if (event->type() == QEvent::TabletPress) { QTabletEvent *tabletEvent = static_cast(event); if (tabletEvent->button() == Qt::RightButton) { localTool()->removeLastPoint(); return true; } } return false; } QList > KisToolSelectPath::createOptionWidgets() { QList > widgetsList = DelegatedSelectPathTool::createOptionWidgets(); - return widgetsList; + QList > filteredWidgets; + Q_FOREACH (QWidget* widget, widgetsList) { + if (widget->objectName() != "Stroke widget") { + filteredWidgets.push_back(widget); + } + } + return filteredWidgets; } void KisToolSelectPath::setAlternateSelectionAction(SelectionAction action) { // We will turn off the ability to change the selection in the middle of drawing a path. if (!m_localTool->listeningToModifiers()) { KisToolSelectBase::setAlternateSelectionAction(action); } } bool KisDelegatedSelectPathWrapper::listeningToModifiers() { return m_localTool->listeningToModifiers(); } void KisDelegatedSelectPathWrapper::beginPrimaryAction(KoPointerEvent *event) { mousePressEvent(event); } void KisDelegatedSelectPathWrapper::continuePrimaryAction(KoPointerEvent *event){ mouseMoveEvent(event); } void KisDelegatedSelectPathWrapper::endPrimaryAction(KoPointerEvent *event) { mouseReleaseEvent(event); } __KisToolSelectPathLocalTool::__KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool) : KoCreatePathTool(canvas), m_selectionTool(parentTool) { } void __KisToolSelectPathLocalTool::paintPath(KoPathShape &pathShape, QPainter &painter, const KoViewConverter &converter) { Q_UNUSED(converter); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (!kisCanvas) return; QTransform matrix; matrix.scale(kisCanvas->image()->xRes(), kisCanvas->image()->yRes()); matrix.translate(pathShape.position().x(), pathShape.position().y()); m_selectionTool->paintToolOutline(&painter, m_selectionTool->pixelToView(matrix.map(pathShape.outline()))); } void __KisToolSelectPathLocalTool::addPathShape(KoPathShape* pathShape) { pathShape->normalize(); pathShape->close(); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (!kisCanvas) return; KisImageWSP image = kisCanvas->image(); KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select by Bezier Curve")); if (m_selectionTool->selectionMode() == PIXEL_SELECTION) { KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); KisPainter painter(tmpSel); painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace())); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setAntiAliasPolygonFill(m_selectionTool->antiAliasSelection()); painter.setStrokeStyle(KisPainter::StrokeStyleNone); QTransform matrix; matrix.scale(image->xRes(), image->yRes()); matrix.translate(pathShape->position().x(), pathShape->position().y()); QPainterPath path = matrix.map(pathShape->outline()); painter.fillPainterPath(path); tmpSel->setOutlineCache(path); helper.selectPixelSelection(tmpSel, m_selectionTool->selectionAction()); delete pathShape; } else { helper.addSelectionShape(pathShape); } } diff --git a/plugins/tools/selectiontools/kis_tool_select_path.h b/plugins/tools/selectiontools/kis_tool_select_path.h index f321c52cf1..261fbed4ba 100644 --- a/plugins/tools/selectiontools/kis_tool_select_path.h +++ b/plugins/tools/selectiontools/kis_tool_select_path.h @@ -1,107 +1,107 @@ /* * Copyright (c) 2007 Sven Langkamp * Copyright (c) 2015 Michael Abrahams * * 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 2 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. */ #ifndef KIS_TOOL_SELECT_PATH_H_ #define KIS_TOOL_SELECT_PATH_H_ #include #include #include "kis_tool_select_base.h" #include "kis_delegated_tool.h" #include class KoCanvasBase; class KisToolSelectPath; class __KisToolSelectPathLocalTool : public KoCreatePathTool { public: __KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool); virtual void paintPath(KoPathShape &path, QPainter &painter, const KoViewConverter &converter); virtual void addPathShape(KoPathShape* pathShape); using KoCreatePathTool::createOptionWidgets; using KoCreatePathTool::endPathWithoutLastPoint; using KoCreatePathTool::endPath; using KoCreatePathTool::cancelPath; using KoCreatePathTool::removeLastPoint; private: KisToolSelectPath* const m_selectionTool; }; typedef KisDelegatedTool DelegatedSelectPathTool; +DeselectShapesActivationPolicy> DelegatedSelectPathTool; struct KisDelegatedSelectPathWrapper : public DelegatedSelectPathTool { KisDelegatedSelectPathWrapper(KoCanvasBase *canvas, const QCursor &cursor, KisTool* delegateTool) : DelegatedSelectPathTool(canvas, cursor, (__KisToolSelectPathLocalTool*) delegateTool) { } bool listeningToModifiers(); // If an event is explicitly forwarded only as an action (e.g. shift-click is captured by "change size") // we will receive a primary action but no mousePressEvent. Thus these events must be explicitly forwarded. void beginPrimaryAction(KoPointerEvent *event); void continuePrimaryAction(KoPointerEvent *event); void endPrimaryAction(KoPointerEvent *event); }; class KisToolSelectPath : public KisToolSelectBase { Q_OBJECT public: KisToolSelectPath(KoCanvasBase * canvas); void mousePressEvent(KoPointerEvent* event); bool eventFilter(QObject *obj, QEvent *event); protected: void requestStrokeCancellation(); void requestStrokeEnd(); void setAlternateSelectionAction(SelectionAction action); friend class __KisToolSelectPathLocalTool; QList > createOptionWidgets(); }; class KisToolSelectPathFactory : public KoToolFactoryBase { public: KisToolSelectPathFactory() : KoToolFactoryBase("KisToolSelectPath") { setToolTip(i18n("Bezier Curve Selection Tool")); setSection(TOOL_TYPE_SELECTION); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("tool_path_selection")); setPriority(6); } virtual ~KisToolSelectPathFactory() {} virtual KoToolBase * createTool(KoCanvasBase *canvas) { return new KisToolSelectPath(canvas); } }; #endif // KIS_TOOL_SELECT_PATH_H_