diff --git a/krita/data/input/kritadefault.profile b/krita/data/input/kritadefault.profile index 9f09feca8f..a1ce565a5d 100644 --- a/krita/data/input/kritadefault.profile +++ b/krita/data/input/kritadefault.profile @@ -1,70 +1,67 @@ [Alternate Invocation] 0={1;2;[1000023,1000020];1;0;0} 1={0;2;[1000021,1000020];1;0;0} 2={5;2;[1000021];2;0;0} 3={3;2;[1000021,1000023];2;0;0} 4={2;2;[1000023,1000021];1;0;0} 5={4;2;[1000021];1;0;0} [Change Primary Setting] 0={0;2;[1000020];1;0;0} [Exposure or Gamma] 0={0;2;[59];1;0;0} [General] name=Krita Default version=3 [Pan Canvas] 0={0;4;[];0;0;2} 1={0;2;[20];1;0;0} 2={0;2;[];4;0;0} 3={1;1;[];0;0;0} 4={2;1;[];0;0;0} 5={3;1;[];0;0;0} 6={4;1;[];0;0;0} -7={0;3;[];0;5;0} [Rotate Canvas] 0={0;2;[1000020,20];1;0;0} 1={1;2;[1000020,1000023,20];1;0;0} 2={0;2;[1000020];4;0;0} 3={2;1;[34];0;0;0} 4={4;1;[35];0;0;0} 5={3;1;[36];0;0;0} -6={0;4;[];0;0;3} [Select Layer] 0={1;2;[1000020,52];1;0;0} 1={0;2;[52];1;0;0} [Show Popup Palette] 0={0;2;[];2;0;0} [Switch Time] 0={0;1;[1000014];0;0;0} 1={1;1;[1000012];0;0;0} [Tool Invocation] 0={3;2;[56];1;0;0} 1={1;1;[1000005];0;0;0} 2={0;2;[];1;0;0} 3={1;1;[1000004];0;0;0} 4={2;1;[1000000];0;0;0} [Zoom Canvas] 0={2;1;[2b];0;0;0} 1={4;1;[31];0;0;0} 10={5;1;[32];0;0;0} 11={0;4;[];0;0;1} 12={8;2;[1000021,1000023];4;0;0} -13={0;4;[];0;0;4} 2={3;1;[2d];0;0;0} 3={2;1;[3d];0;0;0} 4={3;3;[];0;2;0} 5={2;3;[];0;1;0} 6={7;2;[1000021];4;0;0} 7={7;2;[1000021,20];1;0;0} 8={6;1;[33];0;0;0} 9={8;2;[1000021,1000023,20];1;0;0} diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index a73e5355eb..eb6a4dc66a 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,573 +1,572 @@ 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) find_library(APPKIT_LIBRARY AppKit) 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 canvas/KisSnapPointStrategy.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 KisImageBarrierLockerWithFeedback.cpp 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_script_manager.cpp kis_resource_server_provider.cpp KisSelectedShapesProxy.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_paintop_presets_save.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/kis_preset_live_preview_view.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 widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp widgets/KoShapeFillWrapper.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_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.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 actions/KisPasteActionFactory.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 KisImportExportUtils.cpp kis_async_action_feedback.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 input/wintab/kis_tablet_support_win8.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 KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.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() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui 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/wdgsavebrushpreset.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}) target_link_libraries(kritaui ${APPKIT_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/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp index 635e445089..b034521aa4 100644 --- a/libs/ui/canvas/kis_canvas_controller.cpp +++ b/libs/ui/canvas/kis_canvas_controller.cpp @@ -1,319 +1,314 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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_canvas_controller.h" #include #include #include #include "kis_canvas_decoration.h" #include "kis_paintop_transformation_connector.h" #include "kis_coordinates_converter.h" #include "kis_canvas2.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisView.h" #include "krita_utils.h" #include "kis_signal_compressor_with_param.h" static const int gRulersUpdateDelay = 80 /* ms */; struct KisCanvasController::Private { Private(KisCanvasController *qq) : q(qq), paintOpTransformationConnector(0) { using namespace std::placeholders; std::function callback( std::bind(&KisCanvasController::Private::emitPointerPositionChangedSignals, this, _1)); mousePositionCompressor.reset( new KisSignalCompressorWithParam( gRulersUpdateDelay, callback, KisSignalCompressor::FIRST_ACTIVE)); } QPointer view; KisCoordinatesConverter *coordinatesConverter; KisCanvasController *q; KisPaintopTransformationConnector *paintOpTransformationConnector; QScopedPointer > mousePositionCompressor; void emitPointerPositionChangedSignals(QPoint pointerPos); void updateDocumentSizeAfterTransform(); void showRotationValueOnCanvas(); void showMirrorStateOnCanvas(); }; void KisCanvasController::Private::emitPointerPositionChangedSignals(QPoint pointerPos) { if (!coordinatesConverter) return; QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos); q->proxyObject->emitDocumentMousePositionChanged(documentPos); q->proxyObject->emitCanvasMousePositionChanged(pointerPos); } void KisCanvasController::Private::updateDocumentSizeAfterTransform() { // round the size of the area to the nearest integer instead of getting aligned rect QSize widgetSize = coordinatesConverter->imageRectInWidgetPixels().toRect().size(); q->updateDocumentSize(widgetSize, true); KisCanvas2 *kritaCanvas = dynamic_cast(q->canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->notifyZoomChanged(); } KisCanvasController::KisCanvasController(QPointerparent, KActionCollection * actionCollection) : KoCanvasControllerWidget(actionCollection, parent), m_d(new Private(this)) { m_d->view = parent; } KisCanvasController::~KisCanvasController() { delete m_d; } void KisCanvasController::setCanvas(KoCanvasBase *canvas) { if (canvas) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas); KIS_SAFE_ASSERT_RECOVER_RETURN(kritaCanvas); m_d->coordinatesConverter = const_cast(kritaCanvas->coordinatesConverter()); m_d->paintOpTransformationConnector = new KisPaintopTransformationConnector(kritaCanvas, this); } else { m_d->coordinatesConverter = 0; delete m_d->paintOpTransformationConnector; m_d->paintOpTransformationConnector = 0; } KoCanvasControllerWidget::setCanvas(canvas); } void KisCanvasController::changeCanvasWidget(QWidget *widget) { KoCanvasControllerWidget::changeCanvasWidget(widget); } void KisCanvasController::activate() { KoCanvasControllerWidget::activate(); } QPointF KisCanvasController::currentCursorPosition() const { KoCanvasBase *canvas = m_d->view->canvasBase(); QWidget *canvasWidget = canvas->canvasWidget(); const QPointF cursorPosWidget = canvasWidget->mapFromGlobal(QCursor::pos()); return m_d->coordinatesConverter->widgetToDocument(cursorPosWidget); } void KisCanvasController::keyPressEvent(QKeyEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::keyPressEvent() * to avoid activation of Pan and Default tool activation shortcuts */ Q_UNUSED(event); } void KisCanvasController::wheelEvent(QWheelEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::wheelEvent() * to disable the default behavior of KoCanvasControllerWidget and QAbstractScrollArea */ Q_UNUSED(event); } bool KisCanvasController::eventFilter(QObject *watched, QEvent *event) { KoCanvasBase *canvas = this->canvas(); if (!canvas || !canvas->canvasWidget() || canvas->canvasWidget() != watched) return false; if (event->type() == QEvent::MouseMove) { QMouseEvent *mevent = static_cast(event); m_d->mousePositionCompressor->start(mevent->pos()); } else if (event->type() == QEvent::TabletMove) { QTabletEvent *tevent = static_cast(event); m_d->mousePositionCompressor->start(tevent->pos()); } else if (event->type() == QEvent::FocusIn) { m_d->view->syncLastActiveNodeToDocument(); } return false; } void KisCanvasController::updateDocumentSize(const QSize &sz, bool recalculateCenter) { KoCanvasControllerWidget::updateDocumentSize(sz, recalculateCenter); emit documentSizeChanged(); } void KisCanvasController::Private::showMirrorStateOnCanvas() { bool isXMirrored = coordinatesConverter->xAxisMirrored(); view->viewManager()-> showFloatingMessage( i18nc("floating message about mirroring", "Horizontal mirroring: %1 ", isXMirrored ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } void KisCanvasController::mirrorCanvas(bool enable) { QPoint newOffset = m_d->coordinatesConverter->mirror(m_d->coordinatesConverter->widgetCenterPoint(), enable, false); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showMirrorStateOnCanvas(); } void KisCanvasController::Private::showRotationValueOnCanvas() { qreal rotationAngle = coordinatesConverter->rotationAngle(); view->viewManager()-> showFloatingMessage( i18nc("floating message about rotation", "Rotation: %1° ", KritaUtils::prettyFormatReal(rotationAngle)), QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter); } -void KisCanvasController::rotateCanvas(qreal angle, const QPointF ¢er) +void KisCanvasController::rotateCanvas(qreal angle) { - QPoint newOffset = m_d->coordinatesConverter->rotate(center, angle); + QPoint newOffset = m_d->coordinatesConverter->rotate(m_d->coordinatesConverter->widgetCenterPoint(), angle); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } -void KisCanvasController::rotateCanvas(qreal angle) -{ - rotateCanvas(angle, m_d->coordinatesConverter->widgetCenterPoint()); -} - void KisCanvasController::rotateCanvasRight15() { rotateCanvas(15.0); } void KisCanvasController::rotateCanvasLeft15() { rotateCanvas(-15.0); } qreal KisCanvasController::rotation() const { return m_d->coordinatesConverter->rotationAngle(); } void KisCanvasController::resetCanvasRotation() { QPoint newOffset = m_d->coordinatesConverter->resetRotation(m_d->coordinatesConverter->widgetCenterPoint()); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::slotToggleWrapAroundMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); if (!canvas()->canvasIsOpenGL() && value) { m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n" "To visualize wrap-around mode, enable OpenGL."), QIcon()); } kritaCanvas->setWrapAroundViewingMode(value); kritaCanvas->image()->setWrapAroundModePermitted(value); } bool KisCanvasController::wrapAroundMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->wrapAroundViewingMode(); } void KisCanvasController::slotToggleLevelOfDetailMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->setLodAllowedInCanvas(value); bool result = levelOfDetailMode(); if (!value || result) { m_d->view->viewManager()->showFloatingMessage( i18n("Instant Preview Mode: %1", result ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } else { QString reason; if (!kritaCanvas->canvasIsOpenGL()) { reason = i18n("Instant Preview is only supported with OpenGL activated"); } else if (kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode || kritaCanvas->openGLFilterMode() == KisOpenGL::NearestFilterMode) { QString filteringMode = kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode ? i18n("Bilinear") : i18n("Nearest Neighbour"); reason = i18n("Instant Preview is supported\n in Trilinear or High Quality filtering modes.\nCurrent mode is %1", filteringMode); } m_d->view->viewManager()->showFloatingMessage( i18n("Failed activating Instant Preview mode!\n\n%1", reason), QIcon(), 5000, KisFloatingMessage::Low); } } bool KisCanvasController::levelOfDetailMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->lodAllowedInCanvas(); } diff --git a/libs/ui/canvas/kis_canvas_controller.h b/libs/ui/canvas/kis_canvas_controller.h index 98945cd06d..22e980e532 100644 --- a/libs/ui/canvas/kis_canvas_controller.h +++ b/libs/ui/canvas/kis_canvas_controller.h @@ -1,71 +1,70 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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_CANVAS_CONTROLLER_H #define KIS_CANVAS_CONTROLLER_H #include #include "kritaui_export.h" #include "kis_types.h" class KisView; class KRITAUI_EXPORT KisCanvasController : public KoCanvasControllerWidget { Q_OBJECT public: KisCanvasController(QPointerparent, KActionCollection * actionCollection); ~KisCanvasController() override; void setCanvas(KoCanvasBase *canvas) override; void changeCanvasWidget(QWidget *widget) override; void keyPressEvent(QKeyEvent *event) override; void wheelEvent(QWheelEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override; void updateDocumentSize(const QSize &sz, bool recalculateCenter) override; void activate() override; QPointF currentCursorPosition() const override; public: using KoCanvasController::documentSize; bool wrapAroundMode() const; bool levelOfDetailMode() const; public Q_SLOTS: void mirrorCanvas(bool enable); - void rotateCanvas(qreal angle, const QPointF ¢er); void rotateCanvas(qreal angle); void rotateCanvasRight15(); void rotateCanvasLeft15(); qreal rotation() const; void resetCanvasRotation(); void slotToggleWrapAroundMode(bool value); void slotToggleLevelOfDetailMode(bool value); Q_SIGNALS: void documentSizeChanged(); private: struct Private; Private * const m_d; }; #endif /* KIS_CANVAS_CONTROLLER_H */ diff --git a/libs/ui/input/kis_abstract_input_action.cpp b/libs/ui/input/kis_abstract_input_action.cpp index 18e205c22b..fa2ba54cfa 100644 --- a/libs/ui/input/kis_abstract_input_action.cpp +++ b/libs/ui/input/kis_abstract_input_action.cpp @@ -1,217 +1,211 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_abstract_input_action.h" #include #include #include #include class Q_DECL_HIDDEN KisAbstractInputAction::Private { public: QString id; QString name; QString description; QHash indexes; QPointF lastCursorPosition; static KisInputManager* inputManager; }; KisInputManager *KisAbstractInputAction::Private::inputManager = 0; KisAbstractInputAction::KisAbstractInputAction(const QString & id) : d(new Private) { d->id = id; d->indexes.insert(i18n("Activate"), 0); } KisAbstractInputAction::~KisAbstractInputAction() { delete d; } void KisAbstractInputAction::activate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::deactivate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::begin(int shortcut, QEvent *event) { Q_UNUSED(shortcut); if (event) { d->lastCursorPosition = eventPosF(event); } } void KisAbstractInputAction::inputEvent(QEvent* event) { if (event) { QPointF newPosition = eventPosF(event); cursorMoved(d->lastCursorPosition, newPosition); d->lastCursorPosition = newPosition; } } void KisAbstractInputAction::end(QEvent *event) { Q_UNUSED(event); } void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { Q_UNUSED(lastPos); Q_UNUSED(pos); } bool KisAbstractInputAction::supportsHiResInputEvents() const { return false; } KisInputManager* KisAbstractInputAction::inputManager() const { return Private::inputManager; } QString KisAbstractInputAction::name() const { return d->name; } QString KisAbstractInputAction::description() const { return d->description; } int KisAbstractInputAction::priority() const { return 0; } bool KisAbstractInputAction::canIgnoreModifiers() const { return false; } QHash< QString, int > KisAbstractInputAction::shortcutIndexes() const { return d->indexes; } QString KisAbstractInputAction::id() const { return d->id; } void KisAbstractInputAction::setName(const QString& name) { d->name = name; } void KisAbstractInputAction::setDescription(const QString& description) { d->description = description; } void KisAbstractInputAction::setShortcutIndexes(const QHash< QString, int >& indexes) { d->indexes = indexes; } void KisAbstractInputAction::setInputManager(KisInputManager *manager) { Private::inputManager = manager; } bool KisAbstractInputAction::isShortcutRequired(int shortcut) const { Q_UNUSED(shortcut); return false; } QPoint KisAbstractInputAction::eventPos(const QEvent *event) { if(!event) { return QPoint(); } switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: return static_cast(event)->pos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->pos(); case QEvent::Wheel: return static_cast(event)->pos(); - case QEvent::NativeGesture: - return static_cast(event)->pos(); - default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPoint(); } } QPointF KisAbstractInputAction::eventPosF(const QEvent *event) { switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: return static_cast(event)->posF(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->posF(); case QEvent::Wheel: return static_cast(event)->posF(); - case QEvent::NativeGesture: - return QPointF(static_cast(event)->pos()); - default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPointF(); } } bool KisAbstractInputAction::isAvailable() const { return true; } diff --git a/libs/ui/input/kis_input_manager.cpp b/libs/ui/input/kis_input_manager.cpp index 989b476b10..74dc0ebc3a 100644 --- a/libs/ui/input/kis_input_manager.cpp +++ b/libs/ui/input/kis_input_manager.cpp @@ -1,678 +1,614 @@ /* This file is part of the KDE project * * Copyright (C) 2012 Arjen Hiemstra * 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. */ #include "kis_input_manager.h" #include #include #include #include #include #include #include #include "kis_tool_proxy.h" #include #include #include #include #include #include #include "kis_abstract_input_action.h" #include "kis_tool_invocation_action.h" #include "kis_pan_action.h" #include "kis_alternate_invocation_action.h" #include "kis_rotate_canvas_action.h" #include "kis_zoom_action.h" #include "kis_show_palette_action.h" #include "kis_change_primary_setting_action.h" #include "kis_shortcut_matcher.h" #include "kis_stroke_shortcut.h" #include "kis_single_action_shortcut.h" #include "kis_touch_shortcut.h" #include "kis_input_profile.h" #include "kis_input_profile_manager.h" #include "kis_shortcut_configuration.h" #include #include #include "kis_extended_modifiers_mapper.h" #include "kis_input_manager_p.h" template uint qHash(QPointer value) { return reinterpret_cast(value.data()); } KisInputManager::KisInputManager(QObject *parent) : QObject(parent), d(new Private(this)) { d->setupActions(); connect(KoToolManager::instance(), SIGNAL(aboutToChangeTool(KoCanvasController*)), SLOT(slotAboutToChangeTool())); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), SLOT(slotToolChanged())); connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent())); QApplication::instance()-> installEventFilter(new Private::ProximityNotifier(d, this)); } KisInputManager::~KisInputManager() { delete d; } void KisInputManager::addTrackedCanvas(KisCanvas2 *canvas) { d->canvasSwitcher.addCanvas(canvas); } void KisInputManager::removeTrackedCanvas(KisCanvas2 *canvas) { d->canvasSwitcher.removeCanvas(canvas); } void KisInputManager::toggleTabletLogger() { KisTabletDebugger::instance()->toggleDebugging(); } void KisInputManager::attachPriorityEventFilter(QObject *filter, int priority) { Private::PriorityList::iterator begin = d->priorityEventFilter.begin(); Private::PriorityList::iterator it = begin; Private::PriorityList::iterator end = d->priorityEventFilter.end(); it = std::find_if(begin, end, [filter] (const Private::PriorityPair &a) { return a.second == filter; }); if (it != end) return; it = std::find_if(begin, end, [priority] (const Private::PriorityPair &a) { return a.first > priority; }); d->priorityEventFilter.insert(it, qMakePair(priority, filter)); d->priorityEventFilterSeqNo++; } void KisInputManager::detachPriorityEventFilter(QObject *filter) { Private::PriorityList::iterator it = d->priorityEventFilter.begin(); Private::PriorityList::iterator end = d->priorityEventFilter.end(); it = std::find_if(it, end, [filter] (const Private::PriorityPair &a) { return a.second == filter; }); if (it != end) { d->priorityEventFilter.erase(it); } } void KisInputManager::setupAsEventFilter(QObject *receiver) { if (d->eventsReceiver) { d->eventsReceiver->removeEventFilter(this); } d->eventsReceiver = receiver; if (d->eventsReceiver) { d->eventsReceiver->installEventFilter(this); } } void KisInputManager::stopIgnoringEvents() { d->allowMouseEvents(); } #if defined (__clang__) #pragma GCC diagnostic ignored "-Wswitch" #endif bool KisInputManager::eventFilter(QObject* object, QEvent* event) { if (object != d->eventsReceiver) return false; if (d->eventEater.eventFilter(object, event)) return false; if (!d->matcher.hasRunningShortcut()) { int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo; for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) { const QPointer &filter = it->second; if (filter.isNull()) { it = d->priorityEventFilter.erase(it); d->priorityEventFilterSeqNo++; savedPriorityEventFilterSeqNo++; continue; } if (filter->eventFilter(object, event)) return true; /** * If the filter removed itself from the filters list or * added something there, just exit the loop */ if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) { return true; } ++it; } // KoToolProxy needs to pre-process some events to ensure the // global shortcuts (not the input manager's ones) are not // executed, in particular, this line will accept events when the // tool is in text editing, preventing shortcut triggering if (d->toolProxy) { d->toolProxy->processEvent(event); } } // Continue with the actual switch statement... return eventFilterImpl(event); } template bool KisInputManager::compressMoveEventCommon(Event *event) { /** * We construct a copy of this event object, so we must ensure it * has a correct type. */ static_assert(std::is_same::value || std::is_same::value, "event should be a mouse or a tablet event"); bool retval = false; /** * Compress the events if the tool doesn't need high resolution input */ if ((event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) && (!d->matcher.supportsHiResInputEvents() || d->testingCompressBrushEvents)) { d->compressedMoveEvent.reset(new Event(*event)); d->moveEventCompressor.start(); /** * On Linux Qt eats the rest of unneeded events if we * ignore the first of the chunk of tablet events. So * generally we should never activate this feature. Only * for testing purposes! */ if (d->testingAcceptCompressedTabletEvents) { event->setAccepted(true); } retval = true; } else { slotCompressedMoveEvent(); retval = d->handleCompressedTabletEvent(event); } return retval; } bool KisInputManager::eventFilterImpl(QEvent * event) { bool retval = false; if (event->type() != QEvent::Wheel) { d->accumulatedScrollDelta = 0; } switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: { d->debugEvent(event); //Block mouse press events on Genius tablets if (d->tabletActive) break; if (d->ignoringQtCursorEvents()) break; if (d->touchHasBlockedPressEvents) break; QMouseEvent *mouseEvent = static_cast(event); if (d->tryHidePopupPalette()) { retval = true; } else { //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent); } //Reset signal compressor to prevent processing events before press late d->resetCompressor(); event->setAccepted(retval); break; } case QEvent::MouseButtonRelease: { d->debugEvent(event); if (d->ignoringQtCursorEvents()) break; if (d->touchHasBlockedPressEvents) break; QMouseEvent *mouseEvent = static_cast(event); retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent); event->setAccepted(retval); break; } case QEvent::ShortcutOverride: { d->debugEvent(event); QKeyEvent *keyEvent = static_cast(event); Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent); if (!keyEvent->isAutoRepeat()) { retval = d->matcher.keyPressed(key); } else { retval = d->matcher.autoRepeatedKeyPressed(key); } /** * Workaround for temporary switching of tools by * KoCanvasControllerWidget. We don't need this switch because * we handle it ourselves. */ retval |= !d->forwardAllEventsToTool && (keyEvent->key() == Qt::Key_Space || keyEvent->key() == Qt::Key_Escape); break; } case QEvent::KeyRelease: { d->debugEvent(event); QKeyEvent *keyEvent = static_cast(event); if (!keyEvent->isAutoRepeat()) { Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent); retval = d->matcher.keyReleased(key); } break; } case QEvent::MouseMove: { d->debugEvent(event); if (d->ignoringQtCursorEvents()) break; QMouseEvent *mouseEvent = static_cast(event); retval = compressMoveEventCommon(mouseEvent); break; } case QEvent::Wheel: { d->debugEvent(event); QWheelEvent *wheelEvent = static_cast(event); - -#ifdef Q_OS_OSX - // Some QT wheel events are actually touch pad pan events. From the QT docs: - // "Wheel events are generated for both mouse wheels and trackpad scroll gestures." - - // We differentiate between touchpad events and real mouse wheels by inspecting the - // event source. - - if (wheelEvent->source() == Qt::MouseEventSource::MouseEventSynthesizedBySystem) { - KisAbstractInputAction::setInputManager(this); - retval = d->matcher.wheelEvent(KisSingleActionShortcut::WheelTrackpad, wheelEvent); - break; - } -#endif - d->accumulatedScrollDelta += wheelEvent->delta(); KisSingleActionShortcut::WheelAction action; /** * Ignore delta 0 events on OSX, since they are triggered by tablet * proximity when using Wacom devices. */ #ifdef Q_OS_OSX if(wheelEvent->delta() == 0) { retval = true; break; } #endif if (wheelEvent->orientation() == Qt::Horizontal) { if(wheelEvent->delta() < 0) { action = KisSingleActionShortcut::WheelRight; } else { action = KisSingleActionShortcut::WheelLeft; } } else { if(wheelEvent->delta() > 0) { action = KisSingleActionShortcut::WheelUp; } else { action = KisSingleActionShortcut::WheelDown; } } if (qAbs(d->accumulatedScrollDelta) >= QWheelEvent::DefaultDeltasPerStep) { //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); retval = d->matcher.wheelEvent(action, wheelEvent); d->accumulatedScrollDelta = 0; } else { retval = true; } break; } case QEvent::Enter: d->debugEvent(event); d->containsPointer = true; //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); d->allowMouseEvents(); d->touchHasBlockedPressEvents = false; d->matcher.enterEvent(); break; case QEvent::Leave: d->debugEvent(event); d->containsPointer = false; /** * We won't get a TabletProximityLeave event when the tablet * is hovering above some other widget, so restore cursor * events processing right now. */ d->allowMouseEvents(); d->touchHasBlockedPressEvents = false; d->matcher.leaveEvent(); break; case QEvent::FocusIn: d->debugEvent(event); KisAbstractInputAction::setInputManager(this); //Clear all state so we don't have half-matched shortcuts dangling around. d->matcher.reinitialize(); { // Emulate pressing of the key that are already pressed KisExtendedModifiersMapper mapper; Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers(); Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) { QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers); eventFilterImpl(&kevent); } } d->allowMouseEvents(); break; case QEvent::FocusOut: { d->debugEvent(event); KisAbstractInputAction::setInputManager(this); QPointF currentLocalPos = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos()); d->matcher.lostFocusEvent(currentLocalPos); break; } case QEvent::TabletPress: { d->debugEvent(event); QTabletEvent *tabletEvent = static_cast(event); if (d->tryHidePopupPalette()) { retval = true; } else { //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent); } event->setAccepted(true); retval = true; d->blockMouseEvents(); //Reset signal compressor to prevent processing events before press late d->resetCompressor(); d->eatOneMousePress(); break; } case QEvent::TabletMove: { d->debugEvent(event); QTabletEvent *tabletEvent = static_cast(event); retval = compressMoveEventCommon(tabletEvent); /** * The flow of tablet events means the tablet is in the * proximity area, so activate it even when the * TabletEnterProximity event was missed (may happen when * changing focus of the window with tablet in the proximity * area) */ d->blockMouseEvents(); break; } case QEvent::TabletRelease: { #ifdef Q_OS_MAC d->allowMouseEvents(); #endif if (d->touchHasBlockedPressEvents) break; d->debugEvent(event); QTabletEvent *tabletEvent = static_cast(event); retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent); retval = true; event->setAccepted(true); break; } case QEvent::TouchBegin: { - if (startTouch(retval)) { - QTouchEvent *tevent = static_cast(event); + QTouchEvent *tevent = static_cast(event); + d->touchHasBlockedPressEvents = KisConfig().disableTouchOnCanvas(); + // Touch rejection: if touch is disabled on canvas, no need to block mouse press events + if (KisConfig().disableTouchOnCanvas()) d->eatOneMousePress(); + if (d->tryHidePopupPalette()) { + retval = true; + } else { KisAbstractInputAction::setInputManager(this); retval = d->matcher.touchBeginEvent(tevent); event->accept(); } break; } case QEvent::TouchUpdate: { QTouchEvent *tevent = static_cast(event); #ifdef Q_OS_MAC int count = 0; Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) { if (point.state() != Qt::TouchPointReleased) { count++; } } if (count < 2 && tevent->touchPoints().length() > count) { d->touchHasBlockedPressEvents = false; retval = d->matcher.touchEndEvent(tevent); } else { #endif d->touchHasBlockedPressEvents = KisConfig().disableTouchOnCanvas(); KisAbstractInputAction::setInputManager(this); retval = d->matcher.touchUpdateEvent(tevent); #ifdef Q_OS_OSX } #endif event->accept(); break; } case QEvent::TouchEnd: { - endTouch(); QTouchEvent *tevent = static_cast(event); + d->touchHasBlockedPressEvents = false; retval = d->matcher.touchEndEvent(tevent); event->accept(); break; } - - case QEvent::NativeGesture: - { - QNativeGestureEvent *gevent = static_cast(event); - switch (gevent->gestureType()) { - case Qt::BeginNativeGesture: - { - if (startTouch(retval)) { - KisAbstractInputAction::setInputManager(this); - retval = d->matcher.nativeGestureBeginEvent(gevent); - event->accept(); - } - break; - } - case Qt::EndNativeGesture: - { - endTouch(); - retval = d->matcher.nativeGestureEndEvent(gevent); - event->accept(); - break; - } - default: - { - KisAbstractInputAction::setInputManager(this); - retval = d->matcher.nativeGestureEvent(gevent); - event->accept(); - break; - } - } - break; - } - default: break; } return !retval ? d->processUnhandledEvent(event) : true; } -bool KisInputManager::startTouch(bool &retval) -{ - d->touchHasBlockedPressEvents = KisConfig().disableTouchOnCanvas(); - // Touch rejection: if touch is disabled on canvas, no need to block mouse press events - if (KisConfig().disableTouchOnCanvas()) { - d->eatOneMousePress(); - } - if (d->tryHidePopupPalette()) { - retval = true; - return false; - } else { - return true; - } -} - -void KisInputManager::endTouch() -{ - d->touchHasBlockedPressEvents = false; -} - void KisInputManager::slotCompressedMoveEvent() { if (d->compressedMoveEvent) { // d->touchHasBlockedPressEvents = false; (void) d->handleCompressedTabletEvent(d->compressedMoveEvent.data()); d->compressedMoveEvent.reset(); dbgKrita << "Compressed move event received."; } else { dbgKrita << "Unexpected empty move event"; } } KisCanvas2* KisInputManager::canvas() const { return d->canvas; } QPointer KisInputManager::toolProxy() const { return d->toolProxy; } void KisInputManager::slotAboutToChangeTool() { QPointF currentLocalPos; if (canvas() && canvas()->canvasWidget()) { currentLocalPos = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos()); } d->matcher.lostFocusEvent(currentLocalPos); } void KisInputManager::slotToolChanged() { KoToolManager *toolManager = KoToolManager::instance(); KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId()); if (tool) { d->setMaskSyntheticEvents(tool->maskSyntheticEvents()); if (tool->isInTextMode()) { d->forwardAllEventsToTool = true; d->matcher.suppressAllActions(true); } else { d->forwardAllEventsToTool = false; d->matcher.suppressAllActions(false); } } } void KisInputManager::profileChanged() { d->matcher.clearShortcuts(); KisInputProfile *profile = KisInputProfileManager::instance()->currentProfile(); if (profile) { const QList shortcuts = profile->allShortcuts(); for (KisShortcutConfiguration * const shortcut : shortcuts) { dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name(); switch(shortcut->type()) { case KisShortcutConfiguration::KeyCombinationType: d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys()); break; case KisShortcutConfiguration::MouseButtonType: d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons()); break; case KisShortcutConfiguration::MouseWheelType: d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel()); break; case KisShortcutConfiguration::GestureType: - if (!d->addNativeGestureShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture())) { - d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture()); - } + d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture()); break; default: break; } } } else { dbgKrita << "No Input Profile Found: canvas interaction will be impossible"; } } diff --git a/libs/ui/input/kis_input_manager.h b/libs/ui/input/kis_input_manager.h index 56d76272fb..f503bf959a 100644 --- a/libs/ui/input/kis_input_manager.h +++ b/libs/ui/input/kis_input_manager.h @@ -1,129 +1,126 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_INPUTMANAGER_H #define KIS_INPUTMANAGER_H #include #include #include #include class QPointF; class QTouchEvent; class KisCanvas2; /** * \brief Central object to manage canvas input. * * The Input Manager class manages all canvas input. It is created * by KisCanvas2 and processes all events related to input sent to the * canvas. * * The Input Manager keeps track of a set of actions and a set of * shortcuts. The actions are pre-defined while the shortcuts are * set from configuration. * * For each event, it will try to determine if there is a shortcut that * matches the input. It will then activate this action and pass all * consecutive events on to this action. * * \sa KisAbstractInputAction */ class KRITAUI_EXPORT KisInputManager : public QObject { Q_OBJECT public: /** * Constructor. */ KisInputManager(QObject *parent); /** * Destructor. */ ~KisInputManager() override; void addTrackedCanvas(KisCanvas2 *canvas); void removeTrackedCanvas(KisCanvas2 *canvas); void toggleTabletLogger(); /** * Installs the input manager as an event filter for \p receiver. * Please note that KisInputManager is supposed to handle events * for a single receiver only. This is defined by the fact that it * resends some of the events back through the Qt's queue to the * reciever. That is why the input manager will assert when it gets * an event with wrong destination. */ void setupAsEventFilter(QObject *receiver); /** * Event filter method. Overridden from QObject. */ bool eventFilter(QObject* object, QEvent* event ) override; /** * @brief attachPriorityEventFilter * @param filter * @param priority */ void attachPriorityEventFilter(QObject *filter, int priority = 0); /** * @brief detachPriorityEventFilter * @param filter */ void detachPriorityEventFilter(QObject *filter); /** * Return the canvas this input manager is associated with. */ KisCanvas2 *canvas() const; /** * The tool proxy of the current application. */ QPointer toolProxy() const; public Q_SLOTS: void stopIgnoringEvents(); private Q_SLOTS: void slotAboutToChangeTool(); void slotToolChanged(); void profileChanged(); void slotCompressedMoveEvent(); private: - bool startTouch(bool &retval); - void endTouch(); - bool eventFilterImpl(QEvent * event); template bool compressMoveEventCommon(Event *event); private: class Private; Private* const d; }; #endif // KIS_INPUTMANAGER_H diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp index d7f25b0cfb..72bb748493 100644 --- a/libs/ui/input/kis_input_manager_p.cpp +++ b/libs/ui/input/kis_input_manager_p.cpp @@ -1,570 +1,538 @@ /* * 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. */ #include "kis_input_manager_p.h" #include #include #include #include #include "kis_input_manager.h" #include "kis_config.h" #include "kis_abstract_input_action.h" #include "kis_tool_invocation_action.h" #include "kis_stroke_shortcut.h" #include "kis_touch_shortcut.h" -#include "kis_native_gesture_shortcut.h" #include "kis_input_profile_manager.h" /** * This hungry class EventEater encapsulates event masking logic. * * Its basic role is to kill synthetic mouseMove events sent by Xorg or Qt after * tablet events. Those events are sent in order to allow widgets that haven't * implemented tablet specific functionality to seamlessly behave as if one were * using a mouse. These synthetic events are *supposed* to be optional, or at * least come with a flag saying "This is a fake event!!" but neither of those * methods is trustworthy. (This is correct as of Qt 5.4 + Xorg.) * * Qt 5.4 provides no reliable way to see if a user's tablet is being hovered * over the pad, since it converts all tablethover events into mousemove, with * no option to turn this off. Moreover, sometimes the MouseButtonPress event * from the tapping their tablet happens BEFORE the TabletPress event. This * means we have to resort to a somewhat complicated logic. What makes this * truly a joke is that we are not guaranteed to observe TabletProximityEnter * events when we're using a tablet, either, you may only see an Enter event. * * Once we see tablet events heading our way, we can say pretty confidently that * every mouse event is fake. There are two painful cases to consider - a * mousePress event could arrive before the tabletPress event, or it could * arrive much later, e.g. after tabletRelease. The first was only seen on Linux * with Qt's XInput2 code, the solution was to hold onto mousePress events * temporarily and wait for tabletPress later, this is contained in git history * but is now removed. The second case is currently handled by the * eatOneMousePress function, which waits as long as necessary to detect and * block a single mouse press event. */ static bool isMouseEventType(QEvent::Type t) { return (t == QEvent::MouseMove || t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease || t == QEvent::MouseButtonDblClick); } bool KisInputManager::Private::EventEater::eventFilter(QObject* target, QEvent* event ) { Q_UNUSED(target) auto debugEvent = [&](int i) { if (KisTabletDebugger::instance()->debugEnabled()) { QString pre = QString("[BLOCKED %1:]").arg(i); QMouseEvent *ev = static_cast(event); dbgTablet << KisTabletDebugger::instance()->eventToString(*ev, pre); } }; if (peckish && event->type() == QEvent::MouseButtonPress // Drop one mouse press following tabletPress or touchBegin && (static_cast(event)->button() == Qt::LeftButton)) { peckish = false; debugEvent(1); return true; } else if (isMouseEventType(event->type()) && (hungry // On Mac, we need mouse events when the tablet is in proximity, but not pressed down // since tablet move events are not generated until after tablet press. #ifndef Q_OS_MAC || (eatSyntheticEvents && static_cast(event)->source() != Qt::MouseEventNotSynthesized) #endif )) { // Drop mouse events if enabled or event was synthetic & synthetic events are disabled debugEvent(2); return true; } return false; // All clear - let this one through! } void KisInputManager::Private::EventEater::activate() { if (!hungry && (KisTabletDebugger::instance()->debugEnabled())) { dbgTablet << "Start blocking mouse events"; } hungry = true; } void KisInputManager::Private::EventEater::deactivate() { if (hungry && (KisTabletDebugger::instance()->debugEnabled())) { dbgTablet << "Stop blocking mouse events"; } hungry = false; } void KisInputManager::Private::EventEater::eatOneMousePress() { // Enable on other platforms if getting full-pressure splotches peckish = true; } bool KisInputManager::Private::ignoringQtCursorEvents() { return eventEater.hungry; } void KisInputManager::Private::setMaskSyntheticEvents(bool value) { eventEater.eatSyntheticEvents = value; } void KisInputManager::Private::setTabletActive(bool value) { tabletActive = value; } KisInputManager::Private::Private(KisInputManager *qq) : q(qq) , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE) , priorityEventFilterSeqNo(0) , canvasSwitcher(this, qq) { KisConfig cfg; moveEventCompressor.setDelay(cfg.tabletEventsDelay()); testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents(); testingCompressBrushEvents = cfg.testingCompressBrushEvents(); } static const int InputWidgetsThreshold = 2000; static const int OtherWidgetsThreshold = 400; KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p) : QObject(p), d(_d), eatOneMouseStroke(false), focusSwitchThreshold(InputWidgetsThreshold) { } void KisInputManager::Private::CanvasSwitcher::setupFocusThreshold(QObject* object) { QWidget *widget = qobject_cast(object); KIS_SAFE_ASSERT_RECOVER_RETURN(widget); thresholdConnections.clear(); thresholdConnections.addConnection(&focusSwitchThreshold, SIGNAL(timeout()), widget, SLOT(setFocus())); } void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas) { if (!canvas) return; QObject *canvasWidget = canvas->canvasWidget(); if (!canvasResolver.contains(canvasWidget)) { canvasResolver.insert(canvasWidget, canvas); d->q->setupAsEventFilter(canvasWidget); canvasWidget->installEventFilter(this); setupFocusThreshold(canvasWidget); focusSwitchThreshold.setEnabled(false); d->canvas = canvas; d->toolProxy = qobject_cast(canvas->toolProxy()); } else { KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas); } } void KisInputManager::Private::CanvasSwitcher::removeCanvas(KisCanvas2 *canvas) { QObject *widget = canvas->canvasWidget(); canvasResolver.remove(widget); if (d->eventsReceiver == widget) { d->q->setupAsEventFilter(0); } widget->removeEventFilter(this); } bool isInputWidget(QWidget *w) { if (!w) return false; QList types; types << QLatin1String("QAbstractSlider"); types << QLatin1String("QAbstractSpinBox"); types << QLatin1String("QLineEdit"); types << QLatin1String("QTextEdit"); types << QLatin1String("QPlainTextEdit"); types << QLatin1String("QComboBox"); types << QLatin1String("QKeySequenceEdit"); Q_FOREACH (const QLatin1String &type, types) { if (w->inherits(type.data())) { return true; } } return false; } bool KisInputManager::Private::CanvasSwitcher::eventFilter(QObject* object, QEvent* event ) { if (canvasResolver.contains(object)) { switch (event->type()) { case QEvent::FocusIn: { QFocusEvent *fevent = static_cast(event); KisCanvas2 *canvas = canvasResolver.value(object); // only relevant canvases from the same main window should be // registered in the switcher KIS_SAFE_ASSERT_RECOVER_BREAK(canvas); if (canvas != d->canvas) { eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason); } d->canvas = canvas; d->toolProxy = qobject_cast(canvas->toolProxy()); d->q->setupAsEventFilter(object); object->removeEventFilter(this); object->installEventFilter(this); setupFocusThreshold(object); focusSwitchThreshold.setEnabled(false); QEvent event(QEvent::Enter); d->q->eventFilter(object, &event); break; } case QEvent::FocusOut: { focusSwitchThreshold.setEnabled(true); break; } case QEvent::Enter: { break; } case QEvent::Leave: { focusSwitchThreshold.stop(); break; } case QEvent::Wheel: { QWidget *widget = static_cast(object); widget->setFocus(); break; } case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::TabletPress: case QEvent::TabletRelease: focusSwitchThreshold.forceDone(); if (eatOneMouseStroke) { eatOneMouseStroke--; return true; } break; case QEvent::MouseButtonDblClick: focusSwitchThreshold.forceDone(); if (eatOneMouseStroke) { return true; } break; case QEvent::MouseMove: case QEvent::TabletMove: { QWidget *widget = static_cast(object); if (!widget->hasFocus()) { const int delay = isInputWidget(QApplication::focusWidget()) ? InputWidgetsThreshold : OtherWidgetsThreshold; focusSwitchThreshold.setDelayThreshold(delay); focusSwitchThreshold.start(); } } break; default: break; } } return QObject::eventFilter(object, event); } KisInputManager::Private::ProximityNotifier::ProximityNotifier(KisInputManager::Private *_d, QObject *p) : QObject(p), d(_d) {} bool KisInputManager::Private::ProximityNotifier::eventFilter(QObject* object, QEvent* event ) { switch (event->type()) { case QEvent::TabletEnterProximity: d->debugEvent(event); // Tablet proximity events are unreliable AND fake mouse events do not // necessarily come after tablet events, so this is insufficient. // d->eventEater.eatOneMousePress(); // Qt sends fake mouse events instead of hover events, so not very useful. // Don't block mouse events on tablet since tablet move events are not generated until // after tablet press. #ifndef Q_OS_OSX d->blockMouseEvents(); #else // Notify input manager that tablet proximity is entered for Genius tablets. d->setTabletActive(true); #endif break; case QEvent::TabletLeaveProximity: d->debugEvent(event); d->allowMouseEvents(); #ifdef Q_OS_OSX d->setTabletActive(false); #endif break; default: break; } return QObject::eventFilter(object, event); } void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index, const QList &modifiers, Qt::MouseButtons buttons) { KisStrokeShortcut *strokeShortcut = new KisStrokeShortcut(action, index); QList buttonList; if(buttons & Qt::LeftButton) { buttonList << Qt::LeftButton; } if(buttons & Qt::RightButton) { buttonList << Qt::RightButton; } if(buttons & Qt::MidButton) { buttonList << Qt::MidButton; } if(buttons & Qt::XButton1) { buttonList << Qt::XButton1; } if(buttons & Qt::XButton2) { buttonList << Qt::XButton2; } if (buttonList.size() > 0) { strokeShortcut->setButtons(QSet::fromList(modifiers), QSet::fromList(buttonList)); matcher.addShortcut(strokeShortcut); } else { delete strokeShortcut; } } void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index, const QList &keys) { if (keys.size() == 0) return; KisSingleActionShortcut *keyShortcut = new KisSingleActionShortcut(action, index); //Note: Ordering is important here, Shift + V is different from V + Shift, //which is the reason we use the last key here since most users will enter //shortcuts as "Shift + V". Ideally this should not happen, but this is //the way the shortcut matcher is currently implemented. QList allKeys = keys; Qt::Key key = allKeys.takeLast(); QSet modifiers = QSet::fromList(allKeys); keyShortcut->setKey(modifiers, key); matcher.addShortcut(keyShortcut); } void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index, const QList &modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction) { KisSingleActionShortcut *keyShortcut = new KisSingleActionShortcut(action, index); KisSingleActionShortcut::WheelAction a; switch(wheelAction) { case KisShortcutConfiguration::WheelUp: a = KisSingleActionShortcut::WheelUp; break; case KisShortcutConfiguration::WheelDown: a = KisSingleActionShortcut::WheelDown; break; case KisShortcutConfiguration::WheelLeft: a = KisSingleActionShortcut::WheelLeft; break; case KisShortcutConfiguration::WheelRight: a = KisSingleActionShortcut::WheelRight; break; - case KisShortcutConfiguration::WheelTrackpad: - a = KisSingleActionShortcut::WheelTrackpad; - break; default: return; } keyShortcut->setWheel(QSet::fromList(modifiers), a); matcher.addShortcut(keyShortcut); } void KisInputManager::Private::addTouchShortcut(KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture) { KisTouchShortcut *shortcut = new KisTouchShortcut(action, index); switch(gesture) { case KisShortcutConfiguration::PinchGesture: shortcut->setMinimumTouchPoints(2); shortcut->setMaximumTouchPoints(2); break; case KisShortcutConfiguration::PanGesture: shortcut->setMinimumTouchPoints(3); shortcut->setMaximumTouchPoints(10); break; default: break; } matcher.addShortcut(shortcut); } -bool KisInputManager::Private::addNativeGestureShortcut(KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture) -{ - // each platform should decide here which gestures are handled via QtNativeGestureEvent. - Qt::NativeGestureType type; - switch (gesture) { -#ifdef Q_OS_OSX - case KisShortcutConfiguration::PinchGesture: - type = Qt::ZoomNativeGesture; - break; - case KisShortcutConfiguration::PanGesture: - type = Qt::PanNativeGesture; - break; - case KisShortcutConfiguration::RotateGesture: - type = Qt::RotateNativeGesture; - break; - case KisShortcutConfiguration::SmartZoomGesture: - type = Qt::SmartZoomNativeGesture; - break; -#endif - default: - return false; - } - - KisNativeGestureShortcut *shortcut = new KisNativeGestureShortcut(action, index, type); - matcher.addShortcut(shortcut); - return true; -} - void KisInputManager::Private::setupActions() { QList actions = KisInputProfileManager::instance()->actions(); Q_FOREACH (KisAbstractInputAction *action, actions) { KisToolInvocationAction *toolAction = dynamic_cast(action); if(toolAction) { defaultInputAction = toolAction; } } connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged())); if(KisInputProfileManager::instance()->currentProfile()) { q->profileChanged(); } } bool KisInputManager::Private::processUnhandledEvent(QEvent *event) { bool retval = false; if (forwardAllEventsToTool || event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { defaultInputAction->processUnhandledEvent(event); retval = true; } return retval && !forwardAllEventsToTool; } bool KisInputManager::Private::tryHidePopupPalette() { if (canvas->isPopupPaletteVisible()) { canvas->slotShowPopupPalette(); return true; } return false; } #ifdef HAVE_X11 inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) { return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y()); } inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) { return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y()); } #endif void KisInputManager::Private::blockMouseEvents() { eventEater.activate(); } void KisInputManager::Private::allowMouseEvents() { eventEater.deactivate(); } void KisInputManager::Private::eatOneMousePress() { eventEater.eatOneMousePress(); } void KisInputManager::Private::resetCompressor() { compressedMoveEvent.reset(); moveEventCompressor.stop(); } bool KisInputManager::Private::handleCompressedTabletEvent(QEvent *event) { bool retval = false; if (!matcher.pointerMoved(event) && toolProxy) { toolProxy->forwardHoverEvent(event); } retval = true; event->setAccepted(true); return retval; } diff --git a/libs/ui/input/kis_input_manager_p.h b/libs/ui/input/kis_input_manager_p.h index ce93448b89..c2d5d88875 100644 --- a/libs/ui/input/kis_input_manager_p.h +++ b/libs/ui/input/kis_input_manager_p.h @@ -1,149 +1,148 @@ /* * 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. */ #include #include #include #include #include #include #include #include "kis_input_manager.h" #include "kis_shortcut_matcher.h" #include "kis_shortcut_configuration.h" #include "kis_canvas2.h" #include "kis_tool_proxy.h" #include "kis_signal_compressor.h" #include "input/kis_tablet_debugger.h" #include "kis_timed_signal_threshold.h" #include "kis_signal_auto_connection.h" class KisToolInvocationAction; class KisInputManager::Private { public: Private(KisInputManager *qq); bool tryHidePopupPalette(); void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons); void addKeyShortcut(KisAbstractInputAction* action, int index,const QList &keys); void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture ); - bool addNativeGestureShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture ); void addWheelShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction); bool processUnhandledEvent(QEvent *event); void setupActions(); bool handleCompressedTabletEvent(QEvent *event); KisInputManager *q; QPointer canvas; QPointer toolProxy; bool forwardAllEventsToTool = false; bool ignoringQtCursorEvents(); bool touchHasBlockedPressEvents = false; KisShortcutMatcher matcher; KisToolInvocationAction *defaultInputAction = 0; QObject *eventsReceiver = 0; KisSignalCompressor moveEventCompressor; QScopedPointer compressedMoveEvent; bool testingAcceptCompressedTabletEvents = false; bool testingCompressBrushEvents = false; bool tabletActive = false; // Indicates whether or not tablet is in proximity typedef QPair > PriorityPair; typedef QList PriorityList; PriorityList priorityEventFilter; int priorityEventFilterSeqNo; void blockMouseEvents(); void allowMouseEvents(); void eatOneMousePress(); void setMaskSyntheticEvents(bool value); void setTabletActive(bool value); void resetCompressor(); template void debugEvent(QEvent *event) { if (!KisTabletDebugger::instance()->debugEnabled()) return; QString msg1 = useBlocking && ignoringQtCursorEvents() ? "[BLOCKED] " : "[ ]"; Event *specificEvent = static_cast(event); dbgTablet << KisTabletDebugger::instance()->eventToString(*specificEvent, msg1); } class ProximityNotifier : public QObject { public: ProximityNotifier(Private *_d, QObject *p); bool eventFilter(QObject* object, QEvent* event ) override; private: KisInputManager::Private *d; }; class CanvasSwitcher : public QObject { public: CanvasSwitcher(Private *_d, QObject *p); void addCanvas(KisCanvas2 *canvas); void removeCanvas(KisCanvas2 *canvas); bool eventFilter(QObject* object, QEvent* event ) override; private: void setupFocusThreshold(QObject *object); private: KisInputManager::Private *d; QMap> canvasResolver; int eatOneMouseStroke; KisTimedSignalThreshold focusSwitchThreshold; KisSignalAutoConnectionsStore thresholdConnections; }; CanvasSwitcher canvasSwitcher; struct EventEater { bool eventFilter(QObject* target, QEvent* event); // This should be called after we're certain a tablet stroke has started. void activate(); // This should be called after a tablet stroke has ended. void deactivate(); // On Windows, we sometimes receive mouse events very late, so watch & wait. void eatOneMousePress(); bool hungry{false}; // Continue eating mouse strokes bool peckish{false}; // Eat a single mouse press event bool eatSyntheticEvents{false}; // Mask all synthetic events }; EventEater eventEater; bool containsPointer = true; int accumulatedScrollDelta = 0; }; diff --git a/libs/ui/input/kis_input_profile_manager.cpp b/libs/ui/input/kis_input_profile_manager.cpp index a8da62c9b4..27c18582f3 100644 --- a/libs/ui/input/kis_input_profile_manager.cpp +++ b/libs/ui/input/kis_input_profile_manager.cpp @@ -1,377 +1,367 @@ /* * This file is part of the KDE project * Copyright (C) 2013 Arjen Hiemstra * * 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_input_profile_manager.h" #include "kis_input_profile.h" #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_alternate_invocation_action.h" #include "kis_change_primary_setting_action.h" #include "kis_pan_action.h" #include "kis_rotate_canvas_action.h" #include "kis_show_palette_action.h" #include "kis_tool_invocation_action.h" #include "kis_zoom_action.h" #include "kis_shortcut_configuration.h" #include "kis_select_layer_action.h" #include "kis_gamma_exposure_action.h" #include "kis_change_frame_action.h" #define PROFILE_VERSION 3 class Q_DECL_HIDDEN KisInputProfileManager::Private { public: Private() : currentProfile(0) { } void createActions(); QString profileFileName(const QString &profileName); KisInputProfile *currentProfile; QMap profiles; QList actions; }; Q_GLOBAL_STATIC(KisInputProfileManager, inputProfileManager) KisInputProfileManager *KisInputProfileManager::instance() { return inputProfileManager; } QList< KisInputProfile * > KisInputProfileManager::profiles() const { return d->profiles.values(); } QStringList KisInputProfileManager::profileNames() const { return d->profiles.keys(); } KisInputProfile *KisInputProfileManager::profile(const QString &name) const { if (d->profiles.contains(name)) { return d->profiles.value(name); } return 0; } KisInputProfile *KisInputProfileManager::currentProfile() const { return d->currentProfile; } void KisInputProfileManager::setCurrentProfile(KisInputProfile *profile) { if (profile && profile != d->currentProfile) { d->currentProfile = profile; emit currentProfileChanged(); } } KisInputProfile *KisInputProfileManager::addProfile(const QString &name) { if (d->profiles.contains(name)) { return d->profiles.value(name); } KisInputProfile *profile = new KisInputProfile(this); profile->setName(name); d->profiles.insert(name, profile); emit profilesChanged(); return profile; } void KisInputProfileManager::removeProfile(const QString &name) { if (d->profiles.contains(name)) { QString currentProfileName = d->currentProfile->name(); delete d->profiles.value(name); d->profiles.remove(name); //Delete the settings file for the removed profile, if it exists QDir userDir(KoResourcePaths::saveLocation("data", "input/")); if (userDir.exists(d->profileFileName(name))) { userDir.remove(d->profileFileName(name)); } if (currentProfileName == name) { d->currentProfile = d->profiles.begin().value(); emit currentProfileChanged(); } emit profilesChanged(); } } bool KisInputProfileManager::renameProfile(const QString &oldName, const QString &newName) { if (!d->profiles.contains(oldName)) { return false; } KisInputProfile *profile = d->profiles.value(oldName); d->profiles.remove(oldName); profile->setName(newName); d->profiles.insert(newName, profile); emit profilesChanged(); return true; } void KisInputProfileManager::duplicateProfile(const QString &name, const QString &newName) { if (!d->profiles.contains(name) || d->profiles.contains(newName)) { return; } KisInputProfile *newProfile = new KisInputProfile(this); newProfile->setName(newName); d->profiles.insert(newName, newProfile); KisInputProfile *profile = d->profiles.value(name); QList shortcuts = profile->allShortcuts(); Q_FOREACH(KisShortcutConfiguration * shortcut, shortcuts) { newProfile->addShortcut(new KisShortcutConfiguration(*shortcut)); } emit profilesChanged(); } QList< KisAbstractInputAction * > KisInputProfileManager::actions() { return d->actions; } struct ProfileEntry { QString name; QString fullpath; int version; }; void KisInputProfileManager::loadProfiles() { //Remove any profiles that already exist d->currentProfile = 0; qDeleteAll(d->profiles); d->profiles.clear(); //Look up all profiles (this includes those installed to $prefix as well as the user's local data dir) QStringList profiles = KoResourcePaths::findAllResources("data", "input/*", KoResourcePaths::Recursive); dbgKrita << "profiles" << profiles; QMap > profileEntries; // Get only valid entries... Q_FOREACH(const QString & p, profiles) { ProfileEntry entry; entry.fullpath = p; KConfig config(p, KConfig::SimpleConfig); if (!config.hasGroup("General") || !config.group("General").hasKey("name") || !config.group("General").hasKey("version")) { //Skip if we don't have the proper settings. continue; } // Only entries of exactly the right version can be considered entry.version = config.group("General").readEntry("version", 0); if (entry.version != PROFILE_VERSION) { continue; } entry.name = config.group("General").readEntry("name"); if (!profileEntries.contains(entry.name)) { profileEntries[entry.name] = QList(); } if (p.contains(".kde") || p.contains(".krita")) { // It's the user define one, drop the others profileEntries[entry.name].clear(); profileEntries[entry.name].append(entry); break; } else { profileEntries[entry.name].append(entry); } } - QStringList profilePaths; - Q_FOREACH(const QString & profileName, profileEntries.keys()) { if (profileEntries[profileName].isEmpty()) { continue; } // we have one or more entries for this profile name. We'll take the first, // because that's the most local one. ProfileEntry entry = profileEntries[profileName].first(); - QString path(QFileInfo(entry.fullpath).dir().absolutePath()); - if (!profilePaths.contains(path)) { - profilePaths.append(path); - } - KConfig config(entry.fullpath, KConfig::SimpleConfig); KisInputProfile *newProfile = addProfile(entry.name); Q_FOREACH(KisAbstractInputAction * action, d->actions) { if (!config.hasGroup(action->id())) { continue; } KConfigGroup grp = config.group(action->id()); //Read the settings for the action and create the appropriate shortcuts. Q_FOREACH(const QString & entry, grp.entryMap()) { KisShortcutConfiguration *shortcut = new KisShortcutConfiguration; shortcut->setAction(action); if (shortcut->unserialize(entry)) { newProfile->addShortcut(shortcut); } else { delete shortcut; } } } } - QString profilePathsStr(profilePaths.join("' AND '")); - qDebug() << "input profiles were read from '" << qUtf8Printable(profilePathsStr) << "'."; - KisConfig cfg; QString currentProfile = cfg.currentInputProfile(); if (d->profiles.size() > 0) { if (currentProfile.isEmpty() || !d->profiles.contains(currentProfile)) { d->currentProfile = d->profiles.begin().value(); } else { d->currentProfile = d->profiles.value(currentProfile); } } if (d->currentProfile) { emit currentProfileChanged(); } } void KisInputProfileManager::saveProfiles() { QString storagePath = KoResourcePaths::saveLocation("data", "input/", true); Q_FOREACH(KisInputProfile * p, d->profiles) { QString fileName = d->profileFileName(p->name()); KConfig config(storagePath + fileName, KConfig::SimpleConfig); config.group("General").writeEntry("name", p->name()); config.group("General").writeEntry("version", PROFILE_VERSION); Q_FOREACH(KisAbstractInputAction * action, d->actions) { KConfigGroup grp = config.group(action->id()); grp.deleteGroup(); //Clear the group of any existing shortcuts. int index = 0; QList shortcuts = p->shortcutsForAction(action); Q_FOREACH(KisShortcutConfiguration * shortcut, shortcuts) { grp.writeEntry(QString("%1").arg(index++), shortcut->serialize()); } } config.sync(); } KisConfig config; config.setCurrentInputProfile(d->currentProfile->name()); //Force a reload of the current profile in input manager and whatever else uses the profile. emit currentProfileChanged(); } void KisInputProfileManager::resetAll() { QString kdeHome = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QStringList profiles = KoResourcePaths::findAllResources("data", "input/*", KoResourcePaths::Recursive); Q_FOREACH (const QString &profile, profiles) { if(profile.contains(kdeHome)) { //This is a local file, remove it. QFile::remove(profile); } } //Load the profiles again, this should now only load those shipped with Krita. loadProfiles(); emit profilesChanged(); } KisInputProfileManager::KisInputProfileManager(QObject *parent) : QObject(parent), d(new Private()) { d->createActions(); } KisInputProfileManager::~KisInputProfileManager() { qDeleteAll(d->profiles); qDeleteAll(d->actions); delete d; } void KisInputProfileManager::Private::createActions() { //TODO: Make this plugin based //Note that the ordering here determines how things show up in the UI actions.append(new KisToolInvocationAction()); actions.append(new KisAlternateInvocationAction()); actions.append(new KisChangePrimarySettingAction()); actions.append(new KisPanAction()); actions.append(new KisRotateCanvasAction()); actions.append(new KisZoomAction()); actions.append(new KisShowPaletteAction()); actions.append(new KisSelectLayerAction()); actions.append(new KisGammaExposureAction()); actions.append(new KisChangeFrameAction()); } QString KisInputProfileManager::Private::profileFileName(const QString &profileName) { return profileName.toLower().remove(QRegExp("[^a-z0-9]")).append(".profile"); } diff --git a/libs/ui/input/kis_pan_action.cpp b/libs/ui/input/kis_pan_action.cpp index 6bfc8fa088..5f52268c99 100644 --- a/libs/ui/input/kis_pan_action.cpp +++ b/libs/ui/input/kis_pan_action.cpp @@ -1,192 +1,175 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_pan_action.h" #include #include #include #include #include #include #include #include "kis_input_manager.h" class KisPanAction::Private { public: Private() : panDistance(10) { } QPointF averagePoint( QTouchEvent* event ); const int panDistance; QPointF lastPosition; }; KisPanAction::KisPanAction() : KisAbstractInputAction("Pan Canvas") , d(new Private) { setName(i18n("Pan Canvas")); setDescription(i18n("The Pan Canvas action pans the canvas.")); QHash shortcuts; shortcuts.insert(i18n("Pan Mode"), PanModeShortcut); shortcuts.insert(i18n("Pan Left"), PanLeftShortcut); shortcuts.insert(i18n("Pan Right"), PanRightShortcut); shortcuts.insert(i18n("Pan Up"), PanUpShortcut); shortcuts.insert(i18n("Pan Down"), PanDownShortcut); setShortcutIndexes(shortcuts); } KisPanAction::~KisPanAction() { delete d; } int KisPanAction::priority() const { return 5; } void KisPanAction::activate(int shortcut) { Q_UNUSED(shortcut); QApplication::setOverrideCursor(Qt::OpenHandCursor); } void KisPanAction::deactivate(int shortcut) { Q_UNUSED(shortcut); QApplication::restoreOverrideCursor(); } void KisPanAction::begin(int shortcut, QEvent *event) { KisAbstractInputAction::begin(shortcut, event); - bool overrideCursor = true; - switch (shortcut) { case PanModeShortcut: { QTouchEvent *tevent = dynamic_cast(event); - if (tevent) { + if(tevent) d->lastPosition = d->averagePoint(tevent); - break; - } - - // Some QT wheel events are actually be touch pad pan events. From the QT docs: - // "Wheel events are generated for both mouse wheels and trackpad scroll gestures." - QWheelEvent *wheelEvent = dynamic_cast(event); - if (wheelEvent) { - inputManager()->canvas()->canvasController()->pan(-wheelEvent->pixelDelta()); - overrideCursor = false; - break; - } - break; } case PanLeftShortcut: inputManager()->canvas()->canvasController()->pan(QPoint(d->panDistance, 0)); break; case PanRightShortcut: inputManager()->canvas()->canvasController()->pan(QPoint(-d->panDistance, 0)); break; case PanUpShortcut: inputManager()->canvas()->canvasController()->pan(QPoint(0, d->panDistance)); break; case PanDownShortcut: inputManager()->canvas()->canvasController()->pan(QPoint(0, -d->panDistance)); break; } - - if (overrideCursor) { - QApplication::setOverrideCursor(Qt::ClosedHandCursor); - } + QApplication::setOverrideCursor(Qt::ClosedHandCursor); } void KisPanAction::end(QEvent *event) { QApplication::restoreOverrideCursor(); KisAbstractInputAction::end(event); } void KisPanAction::inputEvent(QEvent *event) { switch (event->type()) { case QEvent::Gesture: { QGestureEvent *gevent = static_cast(event); if (gevent->activeGestures().at(0)->gestureType() == Qt::PanGesture) { QPanGesture *pan = static_cast(gevent->activeGestures().at(0)); inputManager()->canvas()->canvasController()->pan(-pan->delta().toPoint() * 0.2); } return; } case QEvent::TouchUpdate: { QTouchEvent *tevent = static_cast(event); QPointF newPos = d->averagePoint(tevent); QPointF delta = newPos - d->lastPosition; // If this is enormously large, then we are likely in the process of ending the gesture, // with fingers being lifted one by one from the perspective of our very speedy operations, // and as such, ignore those big jumps. if(delta.manhattanLength() < 50) { inputManager()->canvas()->canvasController()->pan(-delta.toPoint()); d->lastPosition = newPos; } return; } default: break; } KisAbstractInputAction::inputEvent(event); } void KisPanAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { QPointF relMovement = -(pos - lastPos); inputManager()->canvas()->canvasController()->pan(relMovement.toPoint()); } QPointF KisPanAction::Private::averagePoint( QTouchEvent* event ) { QPointF result; int count = 0; Q_FOREACH ( QTouchEvent::TouchPoint point, event->touchPoints() ) { if( point.state() != Qt::TouchPointReleased ) { result += point.screenPos(); count++; } } if( count > 0 ) { return result / count; } else { return QPointF(); } } bool KisPanAction::isShortcutRequired(int shortcut) const { return shortcut == PanModeShortcut; } diff --git a/libs/ui/input/kis_rotate_canvas_action.cpp b/libs/ui/input/kis_rotate_canvas_action.cpp index a350b49913..21611b4847 100644 --- a/libs/ui/input/kis_rotate_canvas_action.cpp +++ b/libs/ui/input/kis_rotate_canvas_action.cpp @@ -1,152 +1,132 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_rotate_canvas_action.h" #include -#include #include #include "kis_cursor.h" #include "kis_canvas_controller.h" #include #include "kis_input_manager.h" #include class KisRotateCanvasAction::Private { public: Private() : angleDrift(0) {} Shortcut mode; qreal angleDrift; }; KisRotateCanvasAction::KisRotateCanvasAction() : KisAbstractInputAction("Rotate Canvas") , d(new Private()) { setName(i18n("Rotate Canvas")); setDescription(i18n("The Rotate Canvas action rotates the canvas.")); QHash shortcuts; shortcuts.insert(i18n("Rotate Mode"), RotateModeShortcut); shortcuts.insert(i18n("Discrete Rotate Mode"), DiscreteRotateModeShortcut); shortcuts.insert(i18n("Rotate Left"), RotateLeftShortcut); shortcuts.insert(i18n("Rotate Right"), RotateRightShortcut); shortcuts.insert(i18n("Reset Rotation"), RotateResetShortcut); setShortcutIndexes(shortcuts); } KisRotateCanvasAction::~KisRotateCanvasAction() { delete d; } int KisRotateCanvasAction::priority() const { return 3; } void KisRotateCanvasAction::activate(int shortcut) { if (shortcut == DiscreteRotateModeShortcut) { QApplication::setOverrideCursor(KisCursor::rotateCanvasDiscreteCursor()); } else /* if (shortcut == SmoothRotateModeShortcut) */ { QApplication::setOverrideCursor(KisCursor::rotateCanvasSmoothCursor()); } } void KisRotateCanvasAction::deactivate(int shortcut) { Q_UNUSED(shortcut); QApplication::restoreOverrideCursor(); } void KisRotateCanvasAction::begin(int shortcut, QEvent *event) { KisAbstractInputAction::begin(shortcut, event); KisCanvasController *canvasController = dynamic_cast(inputManager()->canvas()->canvasController()); switch(shortcut) { case RotateModeShortcut: d->mode = (Shortcut)shortcut; break; case DiscreteRotateModeShortcut: d->mode = (Shortcut)shortcut; d->angleDrift = 0; break; case RotateLeftShortcut: canvasController->rotateCanvasLeft15(); break; case RotateRightShortcut: canvasController->rotateCanvasRight15(); break; case RotateResetShortcut: canvasController->resetCanvasRotation(); break; } } void KisRotateCanvasAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { const KisCoordinatesConverter *converter = inputManager()->canvas()->coordinatesConverter(); QPointF centerPoint = converter->flakeToWidget(converter->flakeCenterPoint()); QPointF oldPoint = lastPos - centerPoint; QPointF newPoint = pos - centerPoint; qreal oldAngle = atan2(oldPoint.y(), oldPoint.x()); qreal newAngle = atan2(newPoint.y(), newPoint.x()); qreal angle = (180 / M_PI) * (newAngle - oldAngle); if (d->mode == DiscreteRotateModeShortcut) { const qreal angleStep = 15; qreal initialAngle = inputManager()->canvas()->rotationAngle(); qreal roundedAngle = qRound((initialAngle + angle + d->angleDrift) / angleStep) * angleStep - initialAngle; d->angleDrift += angle - roundedAngle; angle = roundedAngle; } KisCanvasController *canvasController = dynamic_cast(inputManager()->canvas()->canvasController()); canvasController->rotateCanvas(angle); } - -void KisRotateCanvasAction::inputEvent(QEvent* event) -{ - switch (event->type()) { - case QEvent::NativeGesture: { - QNativeGestureEvent *gevent = static_cast(event); - KisCanvas2 *canvas = inputManager()->canvas(); - KisCanvasController *controller = static_cast(canvas->canvasController()); - - const float angle = gevent->value(); - QPoint widgetPos = canvas->canvasWidget()->mapFromGlobal(gevent->globalPos()); - controller->rotateCanvas(angle, widgetPos); - return; - } - default: - break; - } - KisAbstractInputAction::inputEvent(event); -} diff --git a/libs/ui/input/kis_rotate_canvas_action.h b/libs/ui/input/kis_rotate_canvas_action.h index e3da584009..29381f791f 100644 --- a/libs/ui/input/kis_rotate_canvas_action.h +++ b/libs/ui/input/kis_rotate_canvas_action.h @@ -1,60 +1,59 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_ROTATE_CANVAS_ACTION_H #define KIS_ROTATE_CANVAS_ACTION_H #include "kis_abstract_input_action.h" /** * \brief Rotate Canvas implementation of KisAbstractInputAction. * * The Rotate Canvas action rotates the canvas. */ class KisRotateCanvasAction : public KisAbstractInputAction { public: /** * The different behaviours for this action. */ enum Shortcut { RotateModeShortcut, ///< Toggle Rotate mode. DiscreteRotateModeShortcut, ///< Toggle Discrete Rotate mode. RotateLeftShortcut, ///< Rotate left by a fixed amount. RotateRightShortcut, ///< Rotate right by a fixed amount. RotateResetShortcut ///< Reset the rotation to 0. }; explicit KisRotateCanvasAction(); ~KisRotateCanvasAction() override; int priority() const override; void activate(int shortcut) override; void deactivate(int shortcut) override; void begin(int shortcut, QEvent *event) override; void cursorMoved(const QPointF &lastPos, const QPointF &pos) override; - void inputEvent(QEvent* event) override; private: class Private; Private * const d; }; #endif // KIS_ROTATE_CANVAS_ACTION_H diff --git a/libs/ui/input/kis_shortcut_configuration.cpp b/libs/ui/input/kis_shortcut_configuration.cpp index 180e5cac99..745bead306 100644 --- a/libs/ui/input/kis_shortcut_configuration.cpp +++ b/libs/ui/input/kis_shortcut_configuration.cpp @@ -1,388 +1,384 @@ /* * This file is part of the KDE project * Copyright (C) 2013 Arjen Hiemstra * * 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_shortcut_configuration.h" #include #include #include class KisShortcutConfiguration::Private { public: Private() : action(0), type(UnknownType), mode(0), wheel(NoMovement), gesture(NoGesture) { } KisAbstractInputAction *action; ShortcutType type; uint mode; QList keys; Qt::MouseButtons buttons; MouseWheelMovement wheel; GestureAction gesture; }; KisShortcutConfiguration::KisShortcutConfiguration() : d(new Private) { } KisShortcutConfiguration::KisShortcutConfiguration(const KisShortcutConfiguration &other) : d(new Private) { d->action = other.action(); d->type = other.type(); d->mode = other.mode(); d->keys = other.keys(); d->buttons = other.buttons(); d->wheel = other.wheel(); d->gesture = other.gesture(); } KisShortcutConfiguration::~KisShortcutConfiguration() { delete d; } QString KisShortcutConfiguration::serialize() { QString serialized("{"); serialized.append(QString::number(d->mode, 16)); serialized.append(';'); serialized.append(QString::number(d->type, 16)); serialized.append(";["); for (QList::iterator itr = d->keys.begin(); itr != d->keys.end(); ++itr) { serialized.append(QString::number(*itr, 16)); if (itr + 1 != d->keys.end()) { serialized.append(','); } } serialized.append("];"); serialized.append(QString::number(d->buttons, 16)); serialized.append(';'); serialized.append(QString::number(d->wheel, 16)); serialized.append(';'); serialized.append(QString::number(d->gesture, 16)); serialized.append('}'); return serialized; } bool KisShortcutConfiguration::unserialize(const QString &serialized) { if (!serialized.startsWith('{')) return false; //Parse the serialized data and apply it to the current shortcut QString remainder = serialized; //Remove brackets remainder.remove('{').remove('}'); //Split the remainder by ; QStringList parts = remainder.split(';'); if (parts.size() < 6) return false; //Invalid input, abort //First entry in the list is the mode d->mode = parts.at(0).toUInt(); //Second entry is the shortcut type d->type = static_cast(parts.at(1).toInt()); if (d->type == UnknownType) { //Reject input that would set this shortcut to "Unknown" return false; } //Third entry is the list of keys QString serializedKeys = parts.at(2); //Remove brackets serializedKeys.remove('[').remove(']'); //Split by , and add each entry as a key QStringList keylist = serializedKeys.split(','); Q_FOREACH(QString key, keylist) { if (!key.isEmpty()) { d->keys.append(static_cast(key.toUInt(0, 16))); } } //Fourth entry is the button mask d->buttons = static_cast(parts.at(3).toInt()); d->wheel = static_cast(parts.at(4).toUInt()); d->gesture = static_cast(parts.at(5).toUInt()); return true; } KisAbstractInputAction *KisShortcutConfiguration::action() const { return d->action; } void KisShortcutConfiguration::setAction(KisAbstractInputAction *newAction) { if (d->action != newAction) { d->action = newAction; } } KisShortcutConfiguration::ShortcutType KisShortcutConfiguration::type() const { return d->type; } void KisShortcutConfiguration::setType(KisShortcutConfiguration::ShortcutType newType) { if (d->type != newType) { d->type = newType; } } uint KisShortcutConfiguration::mode() const { return d->mode; } void KisShortcutConfiguration::setMode(uint newMode) { if (d->mode != newMode) { d->mode = newMode; } } QList< Qt::Key > KisShortcutConfiguration::keys() const { return d->keys; } void KisShortcutConfiguration::setKeys(const QList< Qt::Key > &newKeys) { if (d->keys != newKeys) { d->keys = newKeys; } } Qt::MouseButtons KisShortcutConfiguration::buttons() const { return d->buttons; } void KisShortcutConfiguration::setButtons(Qt::MouseButtons newButtons) { if (d->buttons != newButtons) { d->buttons = newButtons; } } KisShortcutConfiguration::MouseWheelMovement KisShortcutConfiguration::wheel() const { return d->wheel; } void KisShortcutConfiguration::setWheel(KisShortcutConfiguration::MouseWheelMovement type) { if (d->wheel != type) { d->wheel = type; } } KisShortcutConfiguration::GestureAction KisShortcutConfiguration::gesture() const { return d->gesture; } void KisShortcutConfiguration::setGesture(KisShortcutConfiguration::GestureAction type) { if (d->gesture != type) { d->gesture = type; } } QString KisShortcutConfiguration::buttonsToText(Qt::MouseButtons buttons) { QString text; QString sep = i18nc("Separator in the list of mouse buttons for shortcut", " + "); int buttonCount = 0; if (buttons & Qt::LeftButton) { text.append(i18nc("Left Mouse Button", "Left")); buttonCount++; } if (buttons & Qt::RightButton) { if (buttonCount++ > 0) { text.append(sep); } text.append(i18nc("Right Mouse Button", "Right")); } if (buttons & Qt::MidButton) { if (buttonCount++ > 0) { text.append(sep); } text.append(i18nc("Middle Mouse Button", "Middle")); } if (buttons & Qt::XButton1) { if (buttonCount++ > 0) { text.append(sep); } text.append(i18nc("Mouse Back Button", "Back")); } if (buttons & Qt::XButton2) { if (buttonCount++ > 0) { text.append(sep); } text.append(i18nc("Mouse Forward Button", "Forward")); } if (buttonCount == 0) { text.append(i18nc("No mouse buttons for shortcut", "None")); } else { text = i18ncp( "%1 = List of mouse buttons for shortcut. " "Plural form is chosen upon the number of buttons in that list.", "%1 Button", "%1 Buttons", text, buttonCount); } return text; } QString KisShortcutConfiguration::keysToText(const QList &keys) { QString output; Q_FOREACH (Qt::Key key, keys) { if (output.size() > 0) { output.append(i18nc("Separator in the list of keys for shortcut", " + ")); } switch (key) { //Because QKeySequence fails for Ctrl, Alt, Shift and Meta case Qt::Key_Control: output.append(i18nc("Ctrl key", "Ctrl")); break; case Qt::Key_Meta: output.append(i18nc("Meta key", "Meta")); break; case Qt::Key_Alt: output.append(i18nc("Alt key", "Alt")); break; case Qt::Key_Shift: output.append(i18nc("Shift key", "Shift")); break; default: QKeySequence s(key); output.append(s.toString(QKeySequence::NativeText)); break; } } if (output.size() == 0) { output = i18nc("No keys for shortcut", "None"); } return output; } QString KisShortcutConfiguration::wheelToText(KisShortcutConfiguration::MouseWheelMovement wheel) { switch (wheel) { case KisShortcutConfiguration::WheelUp: return i18n("Mouse Wheel Up"); break; case KisShortcutConfiguration::WheelDown: return i18n("Mouse Wheel Down"); break; case KisShortcutConfiguration::WheelLeft: return i18n("Mouse Wheel Left"); break; case KisShortcutConfiguration::WheelRight: return i18n("Mouse Wheel Right"); break; - case KisShortcutConfiguration::WheelTrackpad: - return i18n("Trackpad Pan"); - break; - default: return i18nc("No mouse wheel buttons for shortcut", "None"); break; } } QString KisShortcutConfiguration::buttonsInputToText(const QList &keys, Qt::MouseButtons buttons) { QString buttonsText = KisShortcutConfiguration::buttonsToText(buttons); if (keys.size() > 0) { return i18nc( "%1 = modifier keys in shortcut; %2 = mouse buttons in shortcut", "%1 + %2", KisShortcutConfiguration::keysToText(keys), buttonsText); } else { return buttonsText; } } QString KisShortcutConfiguration::wheelInputToText(const QList &keys, KisShortcutConfiguration::MouseWheelMovement wheel) { QString wheelText = KisShortcutConfiguration::wheelToText(wheel); if (keys.size() > 0) { return i18nc( "%1 = modifier keys in shortcut; %2 = mouse wheel buttons in shortcut", "%1 + %2", KisShortcutConfiguration::keysToText(keys), wheelText); } else { return wheelText; } } diff --git a/libs/ui/input/kis_shortcut_configuration.h b/libs/ui/input/kis_shortcut_configuration.h index 9dd1121cc3..bfb6c223d2 100644 --- a/libs/ui/input/kis_shortcut_configuration.h +++ b/libs/ui/input/kis_shortcut_configuration.h @@ -1,300 +1,297 @@ /* * This file is part of the KDE project * Copyright (C) 2013 Arjen Hiemstra * * 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 KISSHORTCUTCONFIGURATION_H #define KISSHORTCUTCONFIGURATION_H #include #include class QString; class KisAbstractInputAction; /** * \brief A class encapsulating all settings for a single shortcut. * * This class encapsulates mouse buttons, keyboard keys and other settings * related to a single shortcut for a single action. * * \note Each action can have several modes that activate it with usually * different behaviour for each mode. Different shortcuts can activate * different modes. */ class KisShortcutConfiguration { public: /** * The type of shortcut, i.e. what kind of input does it expect. */ enum ShortcutType { UnknownType, ///< Unknown, empty shortcut. KeyCombinationType, ///< A list of keys that should be pressed. MouseButtonType, ///< A mouse button, possibly with key modifiers. MouseWheelType, ///< Mouse wheel movement, possibly with key modifiers. GestureType, ///< A touch gesture. }; /** * The type of mouse wheel movement. */ enum MouseWheelMovement { NoMovement, ///< No movement. WheelUp, ///< Upwards movement, away from the user. WheelDown, ///< Downwards movement, toward the user. WheelLeft, ///< Left movement. WheelRight, ///< Right movement. - WheelTrackpad, ///< A pan movement on a trackpad. }; /** * The type of gesture. */ enum GestureAction { NoGesture, ///< No gesture. PinchGesture, ///< Pinch gesture, fingers moving towards or away from each other. PanGesture, ///< Pan gesture, fingers staying together but moving across the screen. - RotateGesture, /// keys() const; /** * Set the list of keys that will trigger this shortcut. * * \param newKeys The list of keys to use. * * \note Not applicable when type is GestureType. */ void setKeys(const QList &newKeys); /** * \return The mouse buttons that will trigger this shortcut. * * \note Only applicable when type is MouseButtonType. */ Qt::MouseButtons buttons() const; /** * Set the mouse buttons that will trigger this shortcut. * * \param newButtons The mouse buttons to use. * * \note Only applicable when type is MouseButtonType. */ void setButtons(Qt::MouseButtons newButtons); /** * \return The mouse wheel movement that will trigger this shortcut. * * \note Only applicable when type is MouseWheelType. */ MouseWheelMovement wheel() const; /** * Set the mouse wheel movement that will trigger this shortcut. * * \param type The wheel movement to use. * * \note Only applicable when type is MouseWheelType. */ void setWheel(MouseWheelMovement type); /** * \return The gesture that will trigger this shortcut. * * \note Only applicable when type is GestureType. */ GestureAction gesture() const; /** * Set the gesture that will trigger this shortcut. * * \param type The gesture to use. * * \note Only applicable when type is GestureType. */ void setGesture(GestureAction type); /** * Convert a set of mouse buttons into a user-readable * string. * * This will convert the given set of buttons into a * string that can be shown to a user. For example, the * combination Qt::LeftButton + Qt::RightButton will produce * the string "Left + Right Button". * * \param buttons The buttons to convert. * * \return A string representing the buttons that can be shown * to a user. * * \note An empty set will produce the string "None". */ static QString buttonsToText(Qt::MouseButtons buttons); /** * Convert a list of keys to a user-readable string. * * This will convert the given list of keys into a string * that can be shown to a user. For example, the list * [Qt::Key_Shift, Qt::Key_Space] will produce the string * "Shift + Space". * * \param keys The keys to convert. * * \return A string representing the keys that can be shown * to a user. * * \note An empty list will produce the string "None". */ static QString keysToText(const QList &keys); /** * Convert the given mouse wheel movement to a string. * * This will convert the given mouse wheel movement into a * string that can be shown to a user. For example, WheelUp * will produce the string "Mouse Wheel Up". * * \param wheel The mouse wheel movement to convert. * * \return A string representing the mouse wheel movement * that can be shown to a user. * * \note NoMovement will produce the string "None". */ static QString wheelToText(MouseWheelMovement wheel); /** * Convert a shortcut build of a set of keys and a set of mouse * buttons into a user-readable string. * * This will convert the given mouse buttons-based shortcut into a * string that can be shown to a user. For example, the combination * of Qt::Key_Control and Qt::LeftButton + Qt::RightButton will * produce the string "Ctrl + Left + Right Button". * * \param keys The keys to convert. * \param buttons The mouse buttons to convert. * * \return A string representing the shortcut that can be shown * to a user. * * \note An empty set of buttons will appear as the string "None". */ static QString buttonsInputToText(const QList &keys, Qt::MouseButtons buttons); /** * Convert a shortcut build of a set of keys and a set of mouse * wheel buttons into a user-readable string. * * This will convert the given mouse wheel-based shortcut into a * string that can be shown to a user. For example, the combination * of Qt::Key_Control and WheelUp will produce the string * "Ctrl + Mouse Wheel Up". * * \param keys The keys to convert. * \param wheel The mouse wheel buttons to convert. * * \return A string representing the shortcut that can be shown * to a user. * * \note An empty set of wheel buttons will appear as * the string "None". */ static QString wheelInputToText(const QList &keys, MouseWheelMovement wheel); private: class Private; Private *const d; }; Q_DECLARE_METATYPE(KisShortcutConfiguration *); #endif // KISSHORTCUTCONFIGURATION_H diff --git a/libs/ui/input/kis_shortcut_matcher.cpp b/libs/ui/input/kis_shortcut_matcher.cpp index b2a5f00a9b..91a084e8a9 100644 --- a/libs/ui/input/kis_shortcut_matcher.cpp +++ b/libs/ui/input/kis_shortcut_matcher.cpp @@ -1,657 +1,568 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * 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_shortcut_matcher.h" #include #include #include #include "kis_assert.h" #include "kis_abstract_input_action.h" #include "kis_stroke_shortcut.h" #include "kis_touch_shortcut.h" -#include "kis_native_gesture_shortcut.h" #ifdef DEBUG_MATCHER #include #define DEBUG_ACTION(text) dbgInput << __FUNCTION__ << "-" << text; #define DEBUG_SHORTCUT(text, shortcut) dbgInput << __FUNCTION__ << "-" << text << "act:" << shortcut->action()->name(); #define DEBUG_KEY(text) dbgInput << __FUNCTION__ << "-" << text << "keys:" << m_d->keys; #define DEBUG_BUTTON_ACTION(text, button) dbgInput << __FUNCTION__ << "-" << text << "button:" << button << "btns:" << m_d->buttons << "keys:" << m_d->keys; #define DEBUG_EVENT_ACTION(text, event) if (event) {dbgInput << __FUNCTION__ << "-" << text << "type:" << event->type();} #else #define DEBUG_ACTION(text) #define DEBUG_KEY(text) #define DEBUG_SHORTCUT(text, shortcut) #define DEBUG_BUTTON_ACTION(text, button) #define DEBUG_EVENT_ACTION(text, event) #endif class Q_DECL_HIDDEN KisShortcutMatcher::Private { public: Private() : runningShortcut(0) , readyShortcut(0) , touchShortcut(0) - , nativeGestureShortcut(0) , suppressAllActions(false) , cursorEntered(false) , usingTouch(false) - , usingNativeGesture(false) {} ~Private() { qDeleteAll(singleActionShortcuts); qDeleteAll(strokeShortcuts); qDeleteAll(touchShortcuts); } QList singleActionShortcuts; QList strokeShortcuts; QList touchShortcuts; - QList nativeGestureShortcuts; QSet keys; // Model of currently pressed keys QSet buttons; // Model of currently pressed buttons KisStrokeShortcut *runningShortcut; KisStrokeShortcut *readyShortcut; QList candidateShortcuts; KisTouchShortcut *touchShortcut; - KisNativeGestureShortcut *nativeGestureShortcut; bool suppressAllActions; bool cursorEntered; bool usingTouch; - bool usingNativeGesture; inline bool actionsSuppressed() const { return suppressAllActions || !cursorEntered; } inline bool actionsSuppressedIgnoreFocus() const { return suppressAllActions; } - - inline bool isUsingTouch() const { - return usingTouch || usingNativeGesture; - } }; KisShortcutMatcher::KisShortcutMatcher() : m_d(new Private) {} KisShortcutMatcher::~KisShortcutMatcher() { delete m_d; } bool KisShortcutMatcher::hasRunningShortcut() const { return m_d->runningShortcut; } void KisShortcutMatcher::addShortcut(KisSingleActionShortcut *shortcut) { m_d->singleActionShortcuts.append(shortcut); } void KisShortcutMatcher::addShortcut(KisStrokeShortcut *shortcut) { m_d->strokeShortcuts.append(shortcut); } void KisShortcutMatcher::addShortcut( KisTouchShortcut* shortcut ) { m_d->touchShortcuts.append(shortcut); } -void KisShortcutMatcher::addShortcut(KisNativeGestureShortcut *shortcut) { - m_d->nativeGestureShortcuts.append(shortcut); -} - bool KisShortcutMatcher::supportsHiResInputEvents() { return m_d->runningShortcut && m_d->runningShortcut->action() && m_d->runningShortcut->action()->supportsHiResInputEvents(); } bool KisShortcutMatcher::keyPressed(Qt::Key key) { bool retval = false; if (m_d->keys.contains(key)) { DEBUG_ACTION("Peculiar, records show key was already pressed"); } if (!m_d->runningShortcut) { retval = tryRunSingleActionShortcutImpl(key, (QEvent*)0, m_d->keys); } m_d->keys.insert(key); DEBUG_KEY("Pressed"); if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } return retval; } bool KisShortcutMatcher::autoRepeatedKeyPressed(Qt::Key key) { bool retval = false; if (!m_d->keys.contains(key)) { DEBUG_ACTION("Peculiar, autorepeated key but can't remember it was pressed"); } if (!m_d->runningShortcut) { // Autorepeated key should not be included in the shortcut QSet filteredKeys = m_d->keys; filteredKeys.remove(key); retval = tryRunSingleActionShortcutImpl(key, (QEvent*)0, filteredKeys); } return retval; } bool KisShortcutMatcher::keyReleased(Qt::Key key) { if (!m_d->keys.contains(key)) reset("Peculiar, key released but can't remember it was pressed"); else m_d->keys.remove(key); if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } return false; } bool KisShortcutMatcher::buttonPressed(Qt::MouseButton button, QEvent *event) { DEBUG_BUTTON_ACTION("entered", button); bool retval = false; - if (m_d->isUsingTouch()) { + if (m_d->usingTouch) { return retval; } if (m_d->buttons.contains(button)) { DEBUG_ACTION("Peculiar, button was already pressed."); } if (!m_d->runningShortcut) { prepareReadyShortcuts(); retval = tryRunReadyShortcut(button, event); } m_d->buttons.insert(button); if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } return retval; } bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QEvent *event) { DEBUG_BUTTON_ACTION("entered", button); bool retval = false; - if (m_d->isUsingTouch()) { + if (m_d->usingTouch) { return retval; } if (m_d->runningShortcut && !m_d->readyShortcut) { retval = tryEndRunningShortcut(button, event); DEBUG_BUTTON_ACTION("ended", button); } if (!m_d->buttons.contains(button)) reset("Peculiar, button released but we can't remember it was pressed"); else m_d->buttons.remove(button); if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } return retval; } bool KisShortcutMatcher::wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event) { - if (m_d->runningShortcut || m_d->isUsingTouch()) { + if (m_d->runningShortcut || m_d->usingTouch) { DEBUG_ACTION("Wheel event canceled."); return false; } return tryRunWheelShortcut(wheelAction, event); } bool KisShortcutMatcher::pointerMoved(QEvent *event) { - if (m_d->isUsingTouch() || !m_d->runningShortcut) { + if (m_d->usingTouch || !m_d->runningShortcut) { return false; } m_d->runningShortcut->action()->inputEvent(event); return true; } void KisShortcutMatcher::enterEvent() { m_d->cursorEntered = true; if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } } void KisShortcutMatcher::leaveEvent() { m_d->cursorEntered = false; if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } } bool KisShortcutMatcher::touchBeginEvent( QTouchEvent* event ) { Q_UNUSED(event) return true; } bool KisShortcutMatcher::touchUpdateEvent( QTouchEvent* event ) { bool retval = false; if (m_d->touchShortcut && !m_d->touchShortcut->match( event ) ) { retval = tryEndTouchShortcut( event ); } if (!m_d->touchShortcut ) { retval = tryRunTouchShortcut( event ); } else { m_d->touchShortcut->action()->inputEvent( event ); retval = true; } return retval; } bool KisShortcutMatcher::touchEndEvent( QTouchEvent* event ) { m_d->usingTouch = false; // we need to say we are done because qt will not send further event // we should try and end the shortcut too (it might be that there is none? (sketch)) if (tryEndTouchShortcut(event)) { return true; } return false; } -bool KisShortcutMatcher::nativeGestureBeginEvent(QNativeGestureEvent *event) -{ - Q_UNUSED(event) - return true; -} - -bool KisShortcutMatcher::nativeGestureEvent(QNativeGestureEvent *event) -{ - bool retval = false; - - if ( m_d->nativeGestureShortcut && !m_d->nativeGestureShortcut->match( event ) ) { - retval = tryEndNativeGestureShortcut( event ); - } - - if ( !m_d->nativeGestureShortcut ) { - retval = tryRunNativeGestureShortcut( event ); - } - else { - m_d->nativeGestureShortcut->action()->inputEvent( event ); - retval = true; - } - - return retval; -} - -bool KisShortcutMatcher::nativeGestureEndEvent(QNativeGestureEvent *event) -{ - Q_UNUSED(event) - m_d->usingNativeGesture = false; - return true; -} - Qt::MouseButtons listToFlags(const QList &list) { Qt::MouseButtons flags; Q_FOREACH (Qt::MouseButton b, list) { flags |= b; } return flags; } void KisShortcutMatcher::reinitialize() { reset("reinitialize"); if (!m_d->runningShortcut) { prepareReadyShortcuts(); tryActivateReadyShortcut(); } } void KisShortcutMatcher::lostFocusEvent(const QPointF &localPos) { if (m_d->runningShortcut) { forceEndRunningShortcut(localPos); } } void KisShortcutMatcher::reset() { m_d->keys.clear(); m_d->buttons.clear(); DEBUG_ACTION("reset!"); } void KisShortcutMatcher::reset(QString msg) { m_d->keys.clear(); m_d->buttons.clear(); Q_UNUSED(msg); DEBUG_ACTION(msg); } void KisShortcutMatcher::suppressAllActions(bool value) { m_d->suppressAllActions = value; } void KisShortcutMatcher::clearShortcuts() { reset("Clearing shortcuts"); qDeleteAll(m_d->singleActionShortcuts); m_d->singleActionShortcuts.clear(); qDeleteAll(m_d->strokeShortcuts); qDeleteAll(m_d->touchShortcuts); m_d->strokeShortcuts.clear(); m_d->candidateShortcuts.clear(); m_d->touchShortcuts.clear(); m_d->runningShortcut = 0; m_d->readyShortcut = 0; } bool KisShortcutMatcher::tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event) { return tryRunSingleActionShortcutImpl(wheelAction, event, m_d->keys); } // Note: sometimes event can be zero!! template bool KisShortcutMatcher::tryRunSingleActionShortcutImpl(T param, U *event, const QSet &keysState) { if (m_d->actionsSuppressedIgnoreFocus()) { DEBUG_EVENT_ACTION("Event suppressed", event) return false; } KisSingleActionShortcut *goodCandidate = 0; Q_FOREACH (KisSingleActionShortcut *s, m_d->singleActionShortcuts) { if(s->isAvailable() && s->match(keysState, param) && (!goodCandidate || s->priority() > goodCandidate->priority())) { goodCandidate = s; } } if (goodCandidate) { DEBUG_EVENT_ACTION("Beginning action for event", event) goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event); goodCandidate->action()->end(0); } else { DEBUG_EVENT_ACTION("Could not match a candidate for event", event) } return goodCandidate; } void KisShortcutMatcher::prepareReadyShortcuts() { m_d->candidateShortcuts.clear(); if (m_d->actionsSuppressed()) return; Q_FOREACH (KisStrokeShortcut *s, m_d->strokeShortcuts) { if (s->matchReady(m_d->keys, m_d->buttons)) { m_d->candidateShortcuts.append(s); } } } bool KisShortcutMatcher::tryRunReadyShortcut( Qt::MouseButton button, QEvent* event ) { KisStrokeShortcut *goodCandidate = 0; Q_FOREACH (KisStrokeShortcut *s, m_d->candidateShortcuts) { if (s->isAvailable() && s->matchBegin(button) && (!goodCandidate || s->priority() > goodCandidate->priority())) { goodCandidate = s; } } if (goodCandidate) { if (m_d->readyShortcut) { if (m_d->readyShortcut != goodCandidate) { m_d->readyShortcut->action()->deactivate(m_d->readyShortcut->shortcutIndex()); goodCandidate->action()->activate(goodCandidate->shortcutIndex()); } m_d->readyShortcut = 0; } else { DEBUG_EVENT_ACTION("Matched *new* shortcut for event", event); goodCandidate->action()->activate(goodCandidate->shortcutIndex()); } DEBUG_SHORTCUT("Starting new action", goodCandidate); goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event); m_d->runningShortcut = goodCandidate; } return goodCandidate; } void KisShortcutMatcher::tryActivateReadyShortcut() { KisStrokeShortcut *goodCandidate = 0; Q_FOREACH (KisStrokeShortcut *s, m_d->candidateShortcuts) { if (!goodCandidate || s->priority() > goodCandidate->priority()) { goodCandidate = s; } } if (goodCandidate) { if (m_d->readyShortcut && m_d->readyShortcut != goodCandidate) { DEBUG_SHORTCUT("Deactivated previous shortcut action", m_d->readyShortcut); m_d->readyShortcut->action()->deactivate(m_d->readyShortcut->shortcutIndex()); m_d->readyShortcut = 0; } if (!m_d->readyShortcut) { DEBUG_SHORTCUT("Preparing new ready action", goodCandidate); goodCandidate->action()->activate(goodCandidate->shortcutIndex()); m_d->readyShortcut = goodCandidate; } } else if (m_d->readyShortcut) { DEBUG_SHORTCUT("Deactivating action", m_d->readyShortcut); m_d->readyShortcut->action()->deactivate(m_d->readyShortcut->shortcutIndex()); m_d->readyShortcut = 0; } } bool KisShortcutMatcher::tryEndRunningShortcut( Qt::MouseButton button, QEvent* event ) { Q_ASSERT(m_d->runningShortcut); Q_ASSERT(!m_d->readyShortcut); if (m_d->runningShortcut->matchBegin(button)) { // first reset running shortcut to avoid infinite recursion via end() KisStrokeShortcut *runningShortcut = m_d->runningShortcut; m_d->runningShortcut = 0; if (runningShortcut->action()) { DEBUG_EVENT_ACTION("Ending running shortcut at event", event); KisAbstractInputAction* action = runningShortcut->action(); int shortcutIndex = runningShortcut->shortcutIndex(); action->end(event); action->deactivate(shortcutIndex); } } return !m_d->runningShortcut; } void KisShortcutMatcher::forceEndRunningShortcut(const QPointF &localPos) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->runningShortcut); KIS_SAFE_ASSERT_RECOVER_RETURN(!m_d->readyShortcut); // first reset running shortcut to avoid infinite recursion via end() KisStrokeShortcut *runningShortcut = m_d->runningShortcut; m_d->runningShortcut = 0; if (runningShortcut->action()) { DEBUG_ACTION("Forced ending running shortcut at event"); KisAbstractInputAction* action = runningShortcut->action(); int shortcutIndex = runningShortcut->shortcutIndex(); QMouseEvent event = runningShortcut->fakeEndEvent(localPos); action->end(&event); action->deactivate(shortcutIndex); } } bool KisShortcutMatcher::tryRunTouchShortcut( QTouchEvent* event ) { KisTouchShortcut *goodCandidate = 0; if (m_d->actionsSuppressed()) return false; Q_FOREACH (KisTouchShortcut* shortcut, m_d->touchShortcuts) { if( shortcut->match( event ) && (!goodCandidate || shortcut->priority() > goodCandidate->priority()) ) { goodCandidate = shortcut; } } if( goodCandidate ) { if( m_d->runningShortcut ) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, event->touchPoints().at(0).pos().toPoint(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); tryEndRunningShortcut(Qt::LeftButton, &mouseEvent); } goodCandidate->action()->activate(goodCandidate->shortcutIndex()); goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event); m_d->touchShortcut = goodCandidate; m_d->usingTouch = true; } return goodCandidate; } bool KisShortcutMatcher::tryEndTouchShortcut( QTouchEvent* event ) { if(m_d->touchShortcut) { // first reset running shortcut to avoid infinite recursion via end() KisTouchShortcut *touchShortcut = m_d->touchShortcut; touchShortcut->action()->end(event); touchShortcut->action()->deactivate(m_d->touchShortcut->shortcutIndex()); m_d->touchShortcut = 0; // empty it out now that we are done with it return true; } return false; } - -bool KisShortcutMatcher::tryRunNativeGestureShortcut(QNativeGestureEvent* event) -{ - KisNativeGestureShortcut *goodCandidate = 0; - - if (m_d->actionsSuppressed()) - return false; - - Q_FOREACH (KisNativeGestureShortcut* shortcut, m_d->nativeGestureShortcuts) { - if (shortcut->match(event) && (!goodCandidate || shortcut->priority() > goodCandidate->priority())) { - goodCandidate = shortcut; - } - } - - if (goodCandidate) { - goodCandidate->action()->activate(goodCandidate->shortcutIndex()); - goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event); - - m_d->nativeGestureShortcut = goodCandidate; - m_d->usingNativeGesture = true; - - return true; - } - - return false; -} - -bool KisShortcutMatcher::tryEndNativeGestureShortcut(QNativeGestureEvent* event) -{ - if (m_d->nativeGestureShortcut) { - // first reset running shortcut to avoid infinite recursion via end() - KisNativeGestureShortcut *nativeGestureShortcut = m_d->nativeGestureShortcut; - - nativeGestureShortcut->action()->end(event); - nativeGestureShortcut->action()->deactivate(m_d->nativeGestureShortcut->shortcutIndex()); - - m_d->nativeGestureShortcut = 0; // empty it out now that we are done with it - - return true; - } - - return false; -} diff --git a/libs/ui/input/kis_shortcut_matcher.h b/libs/ui/input/kis_shortcut_matcher.h index c25d2b5253..dcd1d93899 100644 --- a/libs/ui/input/kis_shortcut_matcher.h +++ b/libs/ui/input/kis_shortcut_matcher.h @@ -1,249 +1,238 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * 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_SHORTCUT_MATCHER_H #define __KIS_SHORTCUT_MATCHER_H #include #include "kis_single_action_shortcut.h" class QEvent; class QWheelEvent; class QTouchEvent; -class QNativeGestureEvent; class QString; class QPointF; class KisStrokeShortcut; class KisTouchShortcut; -class KisNativeGestureShortcut; /** * The class that manages connections between shortcuts and actions. * * It processes input events and generates state transitions for the * actions basing on the data, represented by the shortcuts. * * The class works with two types of actions: long running * (represented by KisStrokeShortcuts) and "atomic" * (KisSingleActionShortcut). The former one invole some long * interaction with the user by means of a mouse cursor or a tablet, * the latter one simple action like "Zoom 100%" or "Reset Rotation". * * The single action shortcuts are handled quite easily. The matcher * listens to the events coming, manages two lists of the pressed keys * and buttons and when their content corresponds to some single * action shortcut it just runs this shortcut once. * * The strategy for handling the stroke shortcuts is a bit more * complex. Each such action may be in one of the three states: * * Idle <-> Ready <-> Running * * In "Idle" state the action is completely inactive and has no access * to the user * * When the action is in "Ready" state, it means that all the * modifiers for the action are already pressed and we are only * waiting for a user to press the mouse button and start a stroke. In * this state the action can show the user its Cursor to notify the user * what is going to happen next. * * In the "Running" state, the action has full access to the user * input and is considered to perform all the work it was created for. * * To implement such state transitions for the actions, * KisShortcutMatcher first forms a list of the actions which can be * moved to a ready state (m_d->readyShortcuts), then chooses the one * with the highest priority to be the only shortcut in the "Ready" * state and activates it (m_d->readyShortcut). Then when the user * presses the mouse button, the matcher looks through the list of * ready shortcuts, chooses which will be running now, deactivates (if * needed) currently activated action and starts the chosen one. * * \see KisSingleActionShortcut * \see KisStrokeShortcut */ class KRITAUI_EXPORT KisShortcutMatcher { public: KisShortcutMatcher(); ~KisShortcutMatcher(); bool hasRunningShortcut() const; void addShortcut(KisSingleActionShortcut *shortcut); void addShortcut(KisStrokeShortcut *shortcut); void addShortcut(KisTouchShortcut *shortcut); - void addShortcut(KisNativeGestureShortcut *shortcut); /** * Returns true if the currently running shortcut supports * processing hi resolution flow of events from the tablet * device. In most of the cases (except of the painting itself) * too many events make the execution of the action too slow, so * the action can decide whether it needs it. */ bool supportsHiResInputEvents(); /** * Handles a key press event. * No autorepeat events should be passed to this method. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool keyPressed(Qt::Key key); /** * Handles a key press event that has been generated by the * autorepeat. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool autoRepeatedKeyPressed(Qt::Key key); /** * Handles a key release event. * No autorepeat events should be passed to this method. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool keyReleased(Qt::Key key); /** * Handles button presses from a tablet or mouse. * * \param event the event that caused this call. * Must be of type QTabletEvent or QMouseEvent. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool buttonPressed(Qt::MouseButton button, QEvent *event); /** * Handles the mouse button release event * * \param event the event that caused this call. * Must be of type QTabletEvent or QMouseEvent. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool buttonReleased(Qt::MouseButton button, QEvent *event); /** * Handles the mouse wheel event * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event); /** * Handles tablet and mouse move events. * * \param event the event that caused this call * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool pointerMoved(QEvent *event); /** * Handle cursor's Enter event. * We never eat it because it might be used by someone else */ void enterEvent(); /** * Handle cursor's Leave event. * We never eat it because it might be used by someone else */ void leaveEvent(); bool touchBeginEvent(QTouchEvent *event); bool touchUpdateEvent(QTouchEvent *event); bool touchEndEvent(QTouchEvent *event); - - bool nativeGestureBeginEvent(QNativeGestureEvent *event); - bool nativeGestureEvent(QNativeGestureEvent *event); - bool nativeGestureEndEvent(QNativeGestureEvent *event); - /** * Resets the internal state of the matcher and activates the * prepared action if possible. * * This should be done when the window has lost the focus for * some time, so that several events could be lost */ void reinitialize(); /** * Kirta lost focus, it means that all the running actions should be ended * forcefully. */ void lostFocusEvent(const QPointF &localPos); /** * Disables the start of any actions. * * WARNING: the actions that has been started before this call * will *not* be ended. They will be ended in their usual way, * when the mouse button will be released. */ void suppressAllActions(bool value); /** * Remove all shortcuts that have been registered. */ void clearShortcuts(); private: friend class KisInputManagerTest; void reset(); void reset(QString msg); bool tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event); template bool tryRunSingleActionShortcutImpl(T param, U *event, const QSet &keysState); void prepareReadyShortcuts(); bool tryRunReadyShortcut( Qt::MouseButton button, QEvent* event ); void tryActivateReadyShortcut(); bool tryEndRunningShortcut( Qt::MouseButton button, QEvent* event ); void forceEndRunningShortcut(const QPointF &localPos); bool tryRunTouchShortcut(QTouchEvent *event); bool tryEndTouchShortcut(QTouchEvent *event); - bool tryRunNativeGestureShortcut(QNativeGestureEvent *event); - bool tryEndNativeGestureShortcut(QNativeGestureEvent *event); - private: class Private; Private * const m_d; }; #endif /* __KIS_SHORTCUT_MATCHER_H */ diff --git a/libs/ui/input/kis_single_action_shortcut.h b/libs/ui/input/kis_single_action_shortcut.h index 79200774b3..cc9ed96642 100644 --- a/libs/ui/input/kis_single_action_shortcut.h +++ b/libs/ui/input/kis_single_action_shortcut.h @@ -1,57 +1,56 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * 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_SINGLE_ACTION_SHORTCUT_H #define __KIS_SINGLE_ACTION_SHORTCUT_H #include "kis_abstract_shortcut.h" /** * This class represents a shortcut that executes a simple atomic * action. It can be initiated either by a keyboard hotkey or by * a mouse wheel rotation. */ class KRITAUI_EXPORT KisSingleActionShortcut : public KisAbstractShortcut { public: enum WheelAction { WheelUp, ///< Mouse wheel moves up. WheelDown, ///< Mouse wheel moves down. WheelLeft, ///< Mouse wheel moves left. WheelRight, ///< Mouse wheel moves right. - WheelTrackpad, ///< A pan movement on a trackpad. }; KisSingleActionShortcut(KisAbstractInputAction *action, int index); ~KisSingleActionShortcut() override; int priority() const override; void setKey(const QSet &modifiers, Qt::Key key); void setWheel(const QSet &modifiers, WheelAction wheelAction); bool match(const QSet &modifiers, Qt::Key key); bool match(const QSet &modifiers, WheelAction wheelAction); private: class Private; Private * const m_d; }; #endif /* __KIS_SINGLE_ACTION_SHORTCUT_H */ diff --git a/libs/ui/input/kis_zoom_action.cpp b/libs/ui/input/kis_zoom_action.cpp index 97d3d5de08..fe237ad0da 100644 --- a/libs/ui/input/kis_zoom_action.cpp +++ b/libs/ui/input/kis_zoom_action.cpp @@ -1,304 +1,284 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_zoom_action.h" #include -#include #include #include #include #include #include #include "kis_cursor.h" #include "KisViewManager.h" #include "kis_input_manager.h" #include "kis_config.h" inline QPoint pointFromEvent(QEvent *event) { if (!event) { return QPoint(); } else if (QMouseEvent *mouseEvent = dynamic_cast(event)) { return mouseEvent->pos(); } else if (QTabletEvent *tabletEvent = dynamic_cast(event)) { return tabletEvent->pos(); } else if (QWheelEvent *wheelEvent = dynamic_cast(event)) { return wheelEvent->pos(); } return QPoint(); } class KisZoomAction::Private { public: Private(KisZoomAction *qq) : q(qq), distance(0), lastDistance(0.f) {} QPointF centerPoint(QTouchEvent* event); KisZoomAction *q; int distance; Shortcuts mode; QPointF lastPosition; float lastDistance; QPoint startPoint; void zoomTo(bool zoomIn, const QPoint &pos); }; QPointF KisZoomAction::Private::centerPoint(QTouchEvent* event) { QPointF result; int count = 0; Q_FOREACH (QTouchEvent::TouchPoint point, event->touchPoints()) { if (point.state() != Qt::TouchPointReleased) { result += point.screenPos(); count++; } } if (count > 0) { return result / count; } else { return QPointF(); } } void KisZoomAction::Private::zoomTo(bool zoomIn, const QPoint &point) { KoZoomAction *zoomAction = q->inputManager()->canvas()->viewManager()->zoomController()->zoomAction(); if (!point.isNull()) { float oldZoom = zoomAction->effectiveZoom(); float newZoom = zoomIn ? zoomAction->nextZoomLevel() : zoomAction->prevZoomLevel(); KoCanvasControllerWidget *controller = dynamic_cast( q->inputManager()->canvas()->canvasController()); controller->zoomRelativeToPoint(point, newZoom / oldZoom); } else { if (zoomIn) { zoomAction->zoomIn(); } else { zoomAction->zoomOut(); } } } KisZoomAction::KisZoomAction() : KisAbstractInputAction("Zoom Canvas") , d(new Private(this)) { setName(i18n("Zoom Canvas")); setDescription(i18n("The Zoom Canvas action zooms the canvas.")); QHash< QString, int > shortcuts; shortcuts.insert(i18n("Zoom Mode"), ZoomModeShortcut); shortcuts.insert(i18n("Discrete Zoom Mode"), DiscreteZoomModeShortcut); shortcuts.insert(i18n("Relative Zoom Mode"), RelativeZoomModeShortcut); shortcuts.insert(i18n("Relative Discrete Zoom Mode"), RelativeDiscreteZoomModeShortcut); shortcuts.insert(i18n("Zoom In"), ZoomInShortcut); shortcuts.insert(i18n("Zoom Out"), ZoomOutShortcut); shortcuts.insert(i18n("Reset Zoom to 100%"), ZoomResetShortcut); shortcuts.insert(i18n("Fit to Page"), ZoomToPageShortcut); shortcuts.insert(i18n("Fit to Width"), ZoomToWidthShortcut); setShortcutIndexes(shortcuts); } KisZoomAction::~KisZoomAction() { delete d; } int KisZoomAction::priority() const { return 4; } void KisZoomAction::activate(int shortcut) { if (shortcut == DiscreteZoomModeShortcut || shortcut == RelativeDiscreteZoomModeShortcut) { QApplication::setOverrideCursor(KisCursor::zoomDiscreteCursor()); } else /* if (shortcut == SmoothZoomModeShortcut) */ { QApplication::setOverrideCursor(KisCursor::zoomSmoothCursor()); } } void KisZoomAction::deactivate(int shortcut) { Q_UNUSED(shortcut); QApplication::restoreOverrideCursor(); } void KisZoomAction::begin(int shortcut, QEvent *event) { KisAbstractInputAction::begin(shortcut, event); d->lastDistance = 0.f; switch(shortcut) { case ZoomModeShortcut: case RelativeZoomModeShortcut: { d->startPoint = pointFromEvent(event); d->mode = (Shortcuts)shortcut; QTouchEvent *tevent = dynamic_cast(event); if(tevent) d->lastPosition = d->centerPoint(tevent); break; } case DiscreteZoomModeShortcut: case RelativeDiscreteZoomModeShortcut: d->startPoint = pointFromEvent(event); d->mode = (Shortcuts)shortcut; d->distance = 0; break; case ZoomInShortcut: d->zoomTo(true, pointFromEvent(event)); break; case ZoomOutShortcut: d->zoomTo(false, pointFromEvent(event)); break; case ZoomResetShortcut: inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0); break; case ZoomToPageShortcut: inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0); break; case ZoomToWidthShortcut: inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_WIDTH, 1.0); break; } } void KisZoomAction::inputEvent( QEvent* event ) { switch (event->type()) { case QEvent::TouchUpdate: { QTouchEvent *tevent = static_cast(event); QPointF center = d->centerPoint(tevent); int count = 0; float dist = 0.0f; Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) { if (point.state() != Qt::TouchPointReleased) { count++; dist += (point.screenPos() - center).manhattanLength(); } } dist /= count; float delta = qFuzzyCompare(1.0f, 1.0f + d->lastDistance) ? 1.f : dist / d->lastDistance; if(qAbs(delta) > 0.1f) { qreal zoom = inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom(); Q_UNUSED(zoom); static_cast(inputManager()->canvas()->canvasController())->zoomRelativeToPoint(center.toPoint(), delta); d->lastDistance = dist; // Also do panning here, as doing it later requires a further check for validity QPointF moveDelta = center - d->lastPosition; inputManager()->canvas()->canvasController()->pan(-moveDelta.toPoint()); d->lastPosition = center; } return; // Don't try to update the cursor during a pinch-zoom } - case QEvent::NativeGesture: { - QNativeGestureEvent *gevent = static_cast(event); - if (gevent->gestureType() == Qt::ZoomNativeGesture) { - KisCanvas2 *canvas = inputManager()->canvas(); - KisCanvasController *controller = static_cast(canvas->canvasController()); - const float delta = 1.0f + gevent->value(); - controller->zoomRelativeToPoint(canvas->canvasWidget()->mapFromGlobal(gevent->globalPos()), delta); - } else if (gevent->gestureType() == Qt::SmartZoomNativeGesture) { - KisCanvas2 *canvas = inputManager()->canvas(); - KoZoomController *controller = canvas->viewManager()->zoomController(); - - if (controller->zoomMode() != KoZoomMode::ZOOM_WIDTH) { - controller->setZoom(KoZoomMode::ZOOM_WIDTH, 1.0); - } else { - controller->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0); - } - } - return; - } default: break; } KisAbstractInputAction::inputEvent(event); } void KisZoomAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { QPointF diff = -(pos - lastPos); const int stepCont = 100; const int stepDisc = 20; if (d->mode == ZoomModeShortcut || d->mode == RelativeZoomModeShortcut) { KisConfig cfg; float coeff; if (cfg.readEntry("InvertMiddleClickZoom", false)) { coeff = 1.0 - qreal(diff.y()) / stepCont; } else { coeff = 1.0 + qreal(diff.y()) / stepCont; } if (d->mode == ZoomModeShortcut) { float zoom = coeff * inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom(); inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom); } else { KoCanvasControllerWidget *controller = dynamic_cast( inputManager()->canvas()->canvasController()); controller->zoomRelativeToPoint(d->startPoint, coeff); } } else if (d->mode == DiscreteZoomModeShortcut || d->mode == RelativeDiscreteZoomModeShortcut) { d->distance += diff.y(); QPoint stillPoint = d->mode == RelativeDiscreteZoomModeShortcut ? d->startPoint : QPoint(); bool zoomIn = d->distance > 0; while (qAbs(d->distance) > stepDisc) { d->zoomTo(zoomIn, stillPoint); d->distance += zoomIn ? -stepDisc : stepDisc; } } } bool KisZoomAction::isShortcutRequired(int shortcut) const { return shortcut == ZoomModeShortcut; } diff --git a/libs/widgets/KoZoomController.cpp b/libs/widgets/KoZoomController.cpp index 24501406dc..1eed2baef7 100644 --- a/libs/widgets/KoZoomController.cpp +++ b/libs/widgets/KoZoomController.cpp @@ -1,257 +1,252 @@ /* This file is part of the KDE project * Copyright (C) 2007 C. Boemann * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2010 Boudewijn Rempt * Copyright (C) 2011 Arjen Hiemstra * * 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 #include #include #include #include #include #include #include void KoZoomController::Private::init(KoCanvasController *co, KoZoomHandler *zh, KActionCollection *actionCollection) { canvasController = co; fitMargin = co->margin(); zoomHandler = zh; connect(action, SIGNAL(zoomChanged(KoZoomMode::Mode, qreal)), parent, SLOT(setZoom(KoZoomMode::Mode, qreal))); connect(action, SIGNAL(aspectModeChanged(bool)), parent, SIGNAL(aspectModeChanged(bool))); connect(action, SIGNAL(zoomedToSelection()), parent, SIGNAL(zoomedToSelection())); connect(action, SIGNAL(zoomedToAll()), parent, SIGNAL(zoomedToAll())); actionCollection->addAction("view_zoom", action); connect(canvasController->proxyObject, SIGNAL( sizeChanged(const QSize & ) ), parent, SLOT( setAvailableSize() ) ); connect(canvasController->proxyObject, SIGNAL( zoomRelative(const qreal, const QPointF& ) ), parent, SLOT( requestZoomRelative( const qreal, const QPointF& ) ) ); } KoZoomController::KoZoomController(KoCanvasController *co, KoZoomHandler *zh, KActionCollection *actionCollection, KoZoomAction::SpecialButtons specialButtons, QObject *parent) : QObject(parent), d(new Private(this, specialButtons)) { d->init(co, zh, actionCollection); } KoZoomController::~KoZoomController() { delete d; } KoZoomAction *KoZoomController::zoomAction() const { return d->action; } void KoZoomController::setZoomMode(KoZoomMode::Mode mode) { setZoom(mode, 1.0); } -KoZoomMode::Mode KoZoomController::zoomMode() const -{ - return d->zoomHandler->zoomMode(); -} - void KoZoomController::setPageSize(const QSizeF &pageSize) { if(d->pageSize == pageSize) return; d->pageSize = pageSize; if(d->zoomHandler->zoomMode() == KoZoomMode::ZOOM_WIDTH) setZoom(KoZoomMode::ZOOM_WIDTH, 0); if(d->zoomHandler->zoomMode() == KoZoomMode::ZOOM_PAGE) setZoom(KoZoomMode::ZOOM_PAGE, 0); } QSizeF KoZoomController::pageSize() const { return d->pageSize; } void KoZoomController::setTextMinMax(qreal min, qreal max) { if(d->textMinX == min && d->textMaxX == max) { return; } d->textMinX = min; d->textMaxX = max; if(d->zoomHandler->zoomMode() == KoZoomMode::ZOOM_TEXT) setZoom(KoZoomMode::ZOOM_TEXT, 0); } void KoZoomController::setDocumentSize(const QSizeF &documentSize, bool recalculateCenter) { d->documentSize = documentSize; d->canvasController->updateDocumentSize(documentToViewport(d->documentSize), recalculateCenter); // Finally ask the canvasController to recenter d->canvasController->recenterPreferred(); } QSizeF KoZoomController::documentSize() const { return d->documentSize; } void KoZoomController::setZoom(KoZoomMode::Mode mode, qreal zoom) { setZoom(mode, zoom, d->canvasController->preferredCenter()); } void KoZoomController::setZoom(KoZoomMode::Mode mode, qreal zoom, const QPointF &stillPoint) { setZoom(mode, zoom, d->zoomHandler->resolutionX(), d->zoomHandler->resolutionY(), stillPoint); } void KoZoomController::setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY) { setZoom(mode, zoom, resolutionX, resolutionY, d->canvasController->preferredCenter()); } void KoZoomController::setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY, const QPointF &stillPoint) { if (d->zoomHandler->zoomMode() == mode && qFuzzyCompare(d->zoomHandler->zoom(), zoom) && qFuzzyCompare(d->zoomHandler->resolutionX(), resolutionX) && qFuzzyCompare(d->zoomHandler->resolutionY(), resolutionY)) { return; // no change } qreal oldEffectiveZoom = d->action->effectiveZoom(); QSize oldPageViewportSize = documentToViewport(d->pageSize); QSize oldTextViewportSize = documentToViewport(QSizeF(d->textMaxX-d->textMinX, 1)); qreal yfixAlignTop = d->canvasController->viewportSize().height(); if(!qFuzzyCompare(d->zoomHandler->resolutionX(), resolutionX) || !qFuzzyCompare(d->zoomHandler->resolutionY(), resolutionY)) { d->zoomHandler->setResolution(resolutionX, resolutionY); } if(mode == KoZoomMode::ZOOM_CONSTANT) { if(zoom == 0.0) return; d->action->setZoom(zoom); } else if(mode == KoZoomMode::ZOOM_WIDTH) { zoom = (d->canvasController->viewportSize().width() - 2 * d->fitMargin) / (oldPageViewportSize.width() / d->zoomHandler->zoom()); d->action->setSelectedZoomMode(mode); d->action->setEffectiveZoom(zoom); } else if(mode == KoZoomMode::ZOOM_PAGE) { zoom = (d->canvasController->viewportSize().width() - 2 * d->fitMargin) / (oldPageViewportSize.width() / d->zoomHandler->zoom()); zoom = qMin(zoom, (d->canvasController->viewportSize().height() - 2 * d->fitMargin) / (oldPageViewportSize.height() / d->zoomHandler->zoom())); d->action->setSelectedZoomMode(mode); d->action->setEffectiveZoom(zoom); } else if (mode == KoZoomMode::ZOOM_TEXT) { zoom = (d->canvasController->viewportSize().width() - 2 * d->fitMargin) / (oldTextViewportSize.width() / d->zoomHandler->zoom()); d->action->setSelectedZoomMode(mode); d->action->setEffectiveZoom(zoom); } d->zoomHandler->setZoomMode(mode); d->zoomHandler->setZoom(d->action->effectiveZoom()); #ifdef DEBUG if(! d->documentSize.isValid()) warnWidgets << "Setting zoom while there is no document size set, this will fail"; else if (d->pageSize.width() > d->documentSize.width() || d->pageSize.height() > d->documentSize.height()) warnWidgets << "ZoomController; Your page size is larger than your document size (" << d->pageSize << " > " << d->documentSize << ")\n"; #endif QSize documentViewportSize = documentToViewport(d->documentSize); // Tell the canvasController that the zoom has changed // Actually canvasController doesn't know about zoom, but the document in pixels // has changed as a result of the zoom change // To allow listeners of offset changes to react on the real new offset and not on the // intermediate offsets, we block the signals here, and emit by ourselves later. d->canvasController->proxyObject->blockSignals(true); d->canvasController->updateDocumentSize(documentViewportSize, true); d->canvasController->proxyObject->blockSignals(false); // Finally ask the canvasController to recenter if (d->canvasController->canvasMode() == KoCanvasController::Infinite) { QPointF documentCenter; if (mode == KoZoomMode::ZOOM_WIDTH || mode == KoZoomMode::ZOOM_PAGE) { documentCenter = QRectF(QPointF(), documentViewportSize).center(); } else { qreal zoomCoeff = d->action->effectiveZoom() / oldEffectiveZoom; QPointF oldCenter = d->canvasController->preferredCenter(); documentCenter = stillPoint * zoomCoeff - (stillPoint - 1.0 / zoomCoeff * oldCenter); } d->canvasController->setPreferredCenter(documentCenter); } else if (mode == KoZoomMode::ZOOM_TEXT) { QPointF documentCenter = d->canvasController->preferredCenter(); yfixAlignTop -= d->canvasController->viewportSize().height(); documentCenter.setX(d->zoomHandler->documentToViewX(d->textMinX + d->textMaxX) * 0.5); documentCenter.setY(documentCenter.y() - yfixAlignTop); d->canvasController->setPreferredCenter(documentCenter); } else { if (d->canvasController->canvasMode() == KoCanvasController::AlignTop) { QPointF documentCenter = d->canvasController->preferredCenter(); documentCenter.setX(0.0); d->canvasController->setPreferredCenter(documentCenter); } else { d->canvasController->recenterPreferred(); } } // now that we have the final offset, let's emit some signals //d->canvasController->proxyObject->emitCanvasOffsetXChanged(d->canvasController->canvasOffsetX()); //d->canvasController->proxyObject->emitCanvasOffsetYChanged(d->canvasController->canvasOffsetY()); emit zoomChanged(mode, d->action->effectiveZoom()); } QSize KoZoomController::documentToViewport(const QSizeF &size) { return d->zoomHandler->documentToView(size).toSize(); } void KoZoomController::setAspectMode(bool status) { if (d->action) { d->action->setAspectMode(status); } } //have to include this because of Q_PRIVATE_SLOT #include diff --git a/libs/widgets/KoZoomController.h b/libs/widgets/KoZoomController.h index e617cf7c2a..b3210c12aa 100644 --- a/libs/widgets/KoZoomController.h +++ b/libs/widgets/KoZoomController.h @@ -1,212 +1,207 @@ /* This file is part of the KDE project * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007,2012 C. Boemann * 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. */ #ifndef KOZOOMCONTROLLER_H #define KOZOOMCONTROLLER_H #include "KoZoomAction.h" #include "kritawidgets_export.h" #include #include #include class KoCanvasController; class KoZoomAction; class KoZoomHandler; class KActionCollection; class QSize; /** * This controller class handles zoom levels for any canvas. * * For each KoCanvasController you should have one instance of this * class to go with it. This class then creates a KoZoomAction and * basically handles all zooming for you. * * All you need to do is connect to the setDocumentSize() slot and * keep the controller up-to-date if your on-screen document ever * changes (note that this is in document units, so this is a zoom * independent size). * * If you choose to have zoom modes of 'page' and 'width' you are * required to set the page size using the setPageSize() method. * * Additionally you can connect to the zoomChanged() signal if you * want to store the latest zoom level and mode, for example to * restore the last used one at next restart. * * The specialAspectMode toggle is only a UI element. It does nothing * except emit the aspectModeChanged signal. * */ class KRITAWIDGETS_EXPORT KoZoomController : public QObject { Q_OBJECT public: /** * Constructor. Create one per canvasController. The zoomAction is created in the constructor and will * be available to the passed actionCollection for usage by XMLGui. * @param controller the canvasController * @param zoomHandler the zoom handler (viewconverter with setter methods) * @param actionCollection the action collection where the KoZoomAction is added to * @param specialButtons controls which special buttons to show */ KoZoomController(KoCanvasController *controller, KoZoomHandler *zoomHandler, KActionCollection *actionCollection, KoZoomAction::SpecialButtons specialButtons = 0, QObject *parent = 0); /// destructor ~KoZoomController() override; /// returns the zoomAction that is maintained by this controller KoZoomAction *zoomAction() const; /** * Alter the current zoom mode which updates the Gui. * @param mode the new mode that will be used to auto-calculate a new zoom-level if needed. */ void setZoomMode(KoZoomMode::Mode mode); - /** - * @return the current zoom mode. - */ - KoZoomMode::Mode zoomMode() const; - /** * Set the resolution, zoom, the zoom mode for this zoom Controller. * Typically for use just after construction to restore the * persistent data. * * @param mode new zoom mode for the canvas * @param zoom (for ZOOM_CONSTANT zoom mode only) new zoom value for * the canvas * @param resolutionX new X resolution for the document * @param resolutionY new Y resolution for the document * @param stillPoint (for ZOOM_CONSTANT zoom mode only) the point * which will not change its position in widget * during the zooming. It is measured in view * coordinate system *before* zoom. */ void setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY, const QPointF &stillPoint); /** * Convenience function that changes resolution with * keeping the centering unchanged */ void setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY); /** * Convenience function that does not touch the resolution of the * document */ void setZoom(KoZoomMode::Mode mode, qreal zoom, const QPointF &stillPoint); /** * Convenience function with @p center always set to the current * center point of the canvas */ void setZoom(KoZoomMode::Mode mode, qreal zoom); /** * Set Aspect Mode button status and begin a chain of signals */ void setAspectMode(bool status); public Q_SLOTS: /** * Set the size of the current page in document coordinates which allows zoom modes that use the pageSize * to update. * @param pageSize the new page size in points */ void setPageSize(const QSizeF &pageSize); /** * Returns the size of the current page in document coordinates * @returns the page size in points */ QSizeF pageSize() const; /** * Set the dimensions of where text can appear which allows zoom modes that use the text * to update. * @param min the minimum x value (in document coordinates) where text can appear * @param max the maximum x value (in document coordinates) where text can appear */ void setTextMinMax(qreal min, qreal max); /** * Set the size of the whole document currently being shown on the canvas. * The document size will be used together with the current zoom level to calculate the size of the * canvas in the canvasController. * @param documentSize the new document size in points * @param recalculateCenter tells canvas controller not to touch * preferredCenterFraction */ void setDocumentSize(const QSizeF &documentSize, bool recalculateCenter = false); /** * Returns the size of the whole document currently being shown on the canvas. * @returns the document size in points */ QSizeF documentSize() const; Q_SIGNALS: /** * This signal is emitted whenever either the zoommode or the zoom level is changed by the user. * the application can use the emitted data for persistency purposes. */ void zoomChanged (KoZoomMode::Mode mode, qreal zoom); /** * emitted when the special aspect mode toggle changes. * @see KoZoomAction::aspectModeChanged() */ void aspectModeChanged (bool aspectModeActivated); /** * Signal is triggered when the user clicks the zoom to selection button. * Nothing else happens except that this signal is emitted. */ void zoomedToSelection(); /** * Signal is triggered when the user clicks the zoom to all button. * Nothing else happens except that this signal is emitted. */ void zoomedToAll(); protected: virtual QSize documentToViewport(const QSizeF &size); private: Q_PRIVATE_SLOT(d, void setAvailableSize()) Q_PRIVATE_SLOT(d, void requestZoomRelative(const qreal, const QPointF&)) Q_PRIVATE_SLOT(d, void setZoom(KoZoomMode::Mode, qreal)) Q_DISABLE_COPY( KoZoomController ) class Private; Private * const d; }; #endif