diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 99fce40e73..c34319e3e7 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,634 +1,645 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) if (ANDROID) add_definitions(-DQT_OPENGL_ES_3) add_definitions(-DHAS_ONLY_OPENGL_ES) include_directories (${Qt5AndroidExtras_INCLUDE_DIRS}) endif() 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_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 canvas/KisSnapPixelStrategy.cpp canvas/KisMirrorAxisConfig.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/KisSessionManagerDialog.cpp dialogs/KisNewWindowLayoutDialog.cpp dialogs/KisDlgChangeCloneSource.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 KisPaintopPropertiesBase.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 KisOcioConfiguration.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_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.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 KisNodeDisplayModeAdapter.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 KisDecorationsManager.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 KisResourceServerProvider.cpp KisResourceBundleServerProvider.cpp KisSelectedShapesProxy.cpp kis_selection_decoration.cc kis_selection_manager.cc KisSelectionActionsAdapter.cpp kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp KisActionPlugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp KisWelcomePageWidget.cpp KisChangeCloneLayersCommand.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 opengl/kis_texture_tile_info_pool.cpp opengl/KisOpenGLUpdateInfoBuilder.cpp opengl/KisOpenGLModeProber.cpp opengl/KisScreenInformationAdapter.cpp kis_fps_decoration.cpp tool/KisToolChangesTracker.cpp tool/KisToolChangesTrackerData.cpp 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/KisAsyncronousStrokeUpdateHelper.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/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.cpp tool/strokes/move_stroke_strategy.cpp tool/KisSelectionToolFactoryBase.cpp tool/KisToolPaintFactoryBase.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_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_paintop_preset_icon_library.cpp widgets/kis_pattern_chooser.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/KisSelectionPropertySlider.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.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_preset_live_preview_view.cpp widgets/KisScreenColorPicker.cpp widgets/KoDualColorButton.cpp widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp widgets/KisLayerStyleAngleSelector.cpp widgets/KisMemoryReportButton.cpp widgets/KisDitherWidget.cpp KisPaletteEditor.cpp dialogs/KisDlgPaletteEditor.cpp widgets/KisNewsWidget.cpp widgets/KisGamutMaskToolbar.cpp utils/kis_document_aware_spin_box_unit_manager.cpp utils/KisSpinBoxSplineUnitConverter.cpp utils/KisClipboardUtil.cpp utils/KisDitherUtil.cpp + utils/KisUpdaterBase.cpp + utils/KisManualUpdater.cpp + utils/KisUpdaterStatus.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 input/KisInputActionGroup.cpp input/kis_zoom_and_rotate_action.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp actions/KisPasteActionFactories.cpp actions/KisTransformToolActivationCommand.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.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 KisCloneDocumentStroke.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.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 KisUndoActionsUpdateManager.cpp KisView.cpp KisCanvasWindow.cpp KisImportExportErrorCode.cpp KisImportExportAdditionalChecks.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 + KisRssReader.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisSaveGroupVisitor.cpp KisWindowLayoutResource.cpp KisWindowLayoutManager.cpp KisSessionResource.cpp KisReferenceImagesDecoration.cpp KisReferenceImage.cpp flake/KisReferenceImagesLayer.cpp flake/KisReferenceImagesLayer.h KisMouseClickEater.cpp KisDecorationsWrapperLayer.cpp ) if(WIN32) # Private headers are needed for: # * KisDlgCustomTabletResolution # * KisScreenInformationAdapter include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_win.cpp ) if (NOT USE_QT_TABLET_WINDOWS) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp input/wintab/kis_tablet_support_win8.cpp ) else() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} dialogs/KisDlgCustomTabletResolution.cpp ) endif() 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 KisFrameDataSerializer.cpp KisFrameCacheStore.cpp KisFrameCacheSwapper.cpp KisAbstractFrameCacheSwapper.cpp KisInMemoryFrameCacheSwapper.cpp input/wintab/drawpile_tablettester/tablettester.cpp input/wintab/drawpile_tablettester/tablettest.cpp ) if (UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_unix.cpp + utils/KisAppimageUpdater.cpp ) endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() if (ANDROID) set (kritaui_LIB_SRCS ${kritaui_LIB_SRCS} KisAndroidFileManager.cpp) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui widgets/KisDitherWidget.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/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.ui forms/KisWelcomePage.ui forms/WdgDlgPaletteEditor.ui forms/KisNewsPage.ui forms/wdgGamutMaskToolbar.ui forms/wdgchangeclonesource.ui brushhud/kis_dlg_brush_hud_config.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 layerstyles/wdgKisLayerStyleAngleSelector.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui input/wintab/drawpile_tablettester/tablettest.ui ) if(WIN32) if(USE_QT_TABLET_WINDOWS) ki18n_wrap_ui(kritaui_LIB_SRCS dialogs/KisDlgCustomTabletResolution.ui ) else() ki18n_wrap_ui(kritaui_LIB_SRCS input/wintab/kis_screen_size_choice_dialog.ui ) endif() endif() 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} LibExiv2::LibExiv2 + kritaversion kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} LibExiv2::LibExiv2 ) if (ANDROID) target_link_libraries(kritaui GLESv3) target_link_libraries(kritaui Qt5::Gui) target_link_libraries(kritaui Qt5::AndroidExtras) endif() if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (NOT WIN32 AND NOT APPLE AND NOT ANDROID) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB}) 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 AND NOT ANDROID) 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 () + +if (UNIX AND BUILD_TESTING) + install(FILES tests/data/AppImageUpdateDummy + PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +endif () diff --git a/libs/ui/KisMultiFeedRSSModel.cpp b/libs/ui/KisMultiFeedRSSModel.cpp index 5bebd6d6e8..5777d1a033 100644 --- a/libs/ui/KisMultiFeedRSSModel.cpp +++ b/libs/ui/KisMultiFeedRSSModel.cpp @@ -1,226 +1,170 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "KisMultiFeedRSSModel.h" #include #include #include #include #include +#include #include #include #include -QString shortenHtml(QString html) -{ - html.replace(QLatin1String("")); - uint firstParaEndHtml = (uint) html.indexOf(QLatin1String("

"), html.indexOf(QLatin1String("

"))+1); - uint firstParaEndBr = (uint) html.indexOf(QLatin1String("request().url(); - requestUrl = source.toString(); - streamReader.setDevice(reply); - RssItemList list; - while (!streamReader.atEnd()) { - switch (streamReader.readNext()) { - case QXmlStreamReader::StartElement: - if (streamReader.name() == QLatin1String("item")) - list.append(parseItem()); - else if (streamReader.name() == QLatin1String("title")) - blogName = streamReader.readElementText(); - else if (streamReader.name() == QLatin1String("link")) { - if (!streamReader.namespaceUri().isEmpty()) - break; - QString favIconString(streamReader.readElementText()); - QUrl favIconUrl(favIconString); - favIconUrl.setPath(QLatin1String("favicon.ico")); - blogIcon = favIconUrl.toString(); - blogIcon = QString(); // XXX: fix the favicon on krita.org! - } - break; - default: - break; - } - } - return list; - } - -private: - QXmlStreamReader streamReader; - QString requestUrl; - QString blogIcon; - QString blogName; -}; +#include MultiFeedRssModel::MultiFeedRssModel(QObject *parent) : QAbstractListModel(parent), m_networkAccessManager(new KisNetworkAccessManager), m_articleCount(0) { - connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), - SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection); - + initialize(); } +MultiFeedRssModel::MultiFeedRssModel(KisNetworkAccessManager* nam, QObject* parent) + : QAbstractListModel(parent), + m_networkAccessManager(nam), + m_articleCount(0) +{ + initialize(); +} MultiFeedRssModel::~MultiFeedRssModel() { } QHash MultiFeedRssModel::roleNames() const { QHash roleNames; - roleNames[TitleRole] = "title"; - roleNames[DescriptionRole] = "description"; - roleNames[PubDateRole] = "pubDate"; - roleNames[LinkRole] = "link"; - roleNames[CategoryRole] = "category"; - roleNames[BlogNameRole] = "blogName"; - roleNames[BlogIconRole] = "blogIcon"; + roleNames[KisRssReader::RssRoles::TitleRole] = "title"; + roleNames[KisRssReader::RssRoles::DescriptionRole] = "description"; + roleNames[KisRssReader::RssRoles::PubDateRole] = "pubDate"; + roleNames[KisRssReader::RssRoles::LinkRole] = "link"; + roleNames[KisRssReader::RssRoles::CategoryRole] = "category"; + roleNames[KisRssReader::RssRoles::BlogNameRole] = "blogName"; + roleNames[KisRssReader::RssRoles::BlogIconRole] = "blogIcon"; return roleNames; } void MultiFeedRssModel::addFeed(const QString& feed) { + if (m_sites.contains(feed)) { + // do not add the feed twice + return; + } + + m_sites << feed; const QUrl feedUrl(feed); QMetaObject::invokeMethod(m_networkAccessManager, "getUrl", Qt::QueuedConnection, Q_ARG(QUrl, feedUrl)); } bool sortForPubDate(const RssItem& item1, const RssItem& item2) { return item1.pubDate > item2.pubDate; } void MultiFeedRssModel::appendFeedData(QNetworkReply *reply) { - RssReader reader; + KisRssReader reader; m_aggregatedFeed.append(reader.parse(reply)); - std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate); + sortAggregatedFeed(); setArticleCount(m_aggregatedFeed.size()); beginResetModel(); endResetModel(); emit feedDataChanged(); } +void MultiFeedRssModel::sortAggregatedFeed() +{ + std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate); +} + +void MultiFeedRssModel::initialize() +{ + connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), + SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection); +} + void MultiFeedRssModel::removeFeed(const QString &feed) { QMutableListIterator it(m_aggregatedFeed); while (it.hasNext()) { RssItem item = it.next(); if (item.source == feed) it.remove(); } setArticleCount(m_aggregatedFeed.size()); + + m_sites.removeOne(feed); } int MultiFeedRssModel::rowCount(const QModelIndex &) const { return m_aggregatedFeed.size(); } QVariant MultiFeedRssModel::data(const QModelIndex &index, int role) const { RssItem item = m_aggregatedFeed.at(index.row()); switch (role) { case Qt::DisplayRole: { return QString("" + item.title + "" "
(" + item.pubDate.toLocalTime().toString(Qt::DefaultLocaleShortDate) + ") " + item.description.left(90).append("...") + "


"); } - case TitleRole: + case KisRssReader::RssRoles::TitleRole: return item.title; - case DescriptionRole: + case KisRssReader::RssRoles::DescriptionRole: return item.description; - case PubDateRole: + case KisRssReader::RssRoles::PubDateRole: return item.pubDate.toString("dd-MM-yyyy hh:mm"); - case LinkRole: + case KisRssReader::RssRoles::LinkRole: return item.link; - case CategoryRole: + case KisRssReader::RssRoles::CategoryRole: return item.category; - case BlogNameRole: + case KisRssReader::RssRoles::BlogNameRole: return item.blogName; - case BlogIconRole: + case KisRssReader::RssRoles::BlogIconRole: return item.blogIcon; } return QVariant(); } diff --git a/libs/ui/KisMultiFeedRSSModel.h b/libs/ui/KisMultiFeedRSSModel.h index 7575013244..ff0ca9c18e 100644 --- a/libs/ui/KisMultiFeedRSSModel.h +++ b/libs/ui/KisMultiFeedRSSModel.h @@ -1,108 +1,98 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #ifndef MULTIFEEDRSSMODEL_H #define MULTIFEEDRSSMODEL_H #include #include #include +#include + #include -class QThread; + class QNetworkReply; class QNetworkAccessManager; - -struct RssItem { - QString source; - QString title; - QString link; - QString description; - QString category; - QString blogName; - QString blogIcon; - QDateTime pubDate; - -}; -typedef QList RssItemList; - class KisNetworkAccessManager; -enum RssRoles { TitleRole = Qt::UserRole + 1, DescriptionRole, LinkRole, - PubDateRole, CategoryRole, BlogNameRole, BlogIconRole - }; - class KRITAUI_EXPORT MultiFeedRssModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int articleCount READ articleCount WRITE setArticleCount NOTIFY articleCountChanged) public: explicit MultiFeedRssModel(QObject *parent = 0); + explicit MultiFeedRssModel(KisNetworkAccessManager* nam, QObject *parent = 0); ~MultiFeedRssModel() override; + QHash roleNames() const override; void addFeed(const QString& feed); void removeFeed(const QString& feed); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int articleCount() const { return m_articleCount; } public Q_SLOTS: void setArticleCount(int arg) { if (m_articleCount != arg) { m_articleCount = arg; emit articleCountChanged(arg); } } Q_SIGNALS: void articleCountChanged(int arg); void feedDataChanged(); private Q_SLOTS: void appendFeedData(QNetworkReply *reply); private: QStringList m_sites; RssItemList m_aggregatedFeed; QNetworkAccessManager *m_networkAccessManager; - QThread *m_namThread; int m_articleCount; + + void sortAggregatedFeed(); + void initialize(); + + friend class MockMultiFeedRssModel; }; #endif // MULTIFEEDRSSMODEL_H diff --git a/libs/ui/KisNetworkAccessManager.cpp b/libs/ui/KisNetworkAccessManager.cpp index 09adb97dd4..399b9e096a 100644 --- a/libs/ui/KisNetworkAccessManager.cpp +++ b/libs/ui/KisNetworkAccessManager.cpp @@ -1,50 +1,49 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KisNetworkAccessManager.h" #include #include #include #include -#include KisNetworkAccessManager::KisNetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent) { } void KisNetworkAccessManager::getUrl(const QUrl &url) { QNetworkRequest req; req.setUrl(url); get(req); } QNetworkReply* KisNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) { QString agentStr = QString::fromLatin1("%1/%2 (QNetworkAccessManager %3; %4; %5 bit)") .arg(qApp->applicationName()) .arg(qApp->applicationVersion()) .arg(QSysInfo::prettyProductName()) .arg(QLocale::system().name()) .arg(QSysInfo::WordSize); QNetworkRequest req(request); req.setRawHeader("User-Agent", agentStr.toLatin1()); return QNetworkAccessManager::createRequest(op, req, outgoingData); } diff --git a/libs/ui/KisRssReader.cpp b/libs/ui/KisRssReader.cpp new file mode 100644 index 0000000000..fe091bb46d --- /dev/null +++ b/libs/ui/KisRssReader.cpp @@ -0,0 +1,102 @@ +#include "KisRssReader.h" + +#include +#include +#include +#include + +QString shortenHtml(QString html) +{ + html.replace(QLatin1String("")); + uint firstParaEndHtml = (uint) html.indexOf(QLatin1String("

"), html.indexOf(QLatin1String("

"))+1); + uint firstParaEndBr = (uint) html.indexOf(QLatin1String("request().url(); + requestUrl = source.toString(); + m_streamReader.setDevice(reply); + + return parseStream(m_streamReader); +} + +RssItemList KisRssReader::parse(QFile &file) { + requestUrl = file.fileName(); + file.open(QIODevice::ReadOnly); + m_streamReader.setDevice(&file); + + RssItemList itemList(parseStream(m_streamReader)); + + file.close(); + return itemList; +} diff --git a/libs/ui/KisRssReader.h b/libs/ui/KisRssReader.h new file mode 100644 index 0000000000..ed2db537b5 --- /dev/null +++ b/libs/ui/KisRssReader.h @@ -0,0 +1,55 @@ +#ifndef KISRSSREADER_H +#define KISRSSREADER_H + +#include +#include +#include +#include +#include + +#include + +struct RssItem { + QString source; + QString title; + QString link; + QString description; + QString category; + QString blogName; + QString blogIcon; + QDateTime pubDate; + +}; +typedef QList RssItemList; + +Q_DECLARE_METATYPE(RssItem); + + +class KRITAUI_EXPORT KisRssReader +{ +public: + KisRssReader(); + + enum RssRoles { + TitleRole = Qt::UserRole + 1, + DescriptionRole, + LinkRole, + PubDateRole, + CategoryRole, + BlogNameRole, + BlogIconRole + }; + + RssItem parseItem(); + RssItemList parseStream(QXmlStreamReader& streamReader); + RssItemList parse(QNetworkReply *reply); + RssItemList parse(QFile& file); + +private: + QXmlStreamReader m_streamReader; + QString requestUrl; + QString blogIcon; + QString blogName; +}; + +#endif // KISRSSREADER_H diff --git a/libs/ui/KisWelcomePageWidget.cpp b/libs/ui/KisWelcomePageWidget.cpp index 7ab6bcd89a..f4902a8965 100644 --- a/libs/ui/KisWelcomePageWidget.cpp +++ b/libs/ui/KisWelcomePageWidget.cpp @@ -1,456 +1,563 @@ /* This file is part of the KDE project * Copyright (C) 2018 Scott Petrovic * * 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 "KisWelcomePageWidget.h" -#include +//#include #include #include #include #include #include +#include #include "kis_action_manager.h" #include "kactioncollection.h" #include "kis_action.h" #include "KConfigGroup" #include "KSharedConfig" #include #include #include "kis_icon_utils.h" #include "krita_utils.h" #include "KoStore.h" #include "kis_config.h" #include "KisDocument.h" #include #include #include +#include + +#ifdef Q_OS_LINUX +#include +#endif + +#include + +#include +#include + KisWelcomePageWidget::KisWelcomePageWidget(QWidget *parent) : QWidget(parent) { setupUi(this); recentDocumentsListView->setDragEnabled(false); recentDocumentsListView->viewport()->setAutoFillBackground(false); recentDocumentsListView->setSpacing(2); // set up URLs that go to web browser manualLink->setTextFormat(Qt::RichText); manualLink->setTextInteractionFlags(Qt::TextBrowserInteraction); manualLink->setOpenExternalLinks(true); gettingStartedLink->setTextFormat(Qt::RichText); gettingStartedLink->setTextInteractionFlags(Qt::TextBrowserInteraction); gettingStartedLink->setOpenExternalLinks(true); supportKritaLink->setTextFormat(Qt::RichText); supportKritaLink->setTextInteractionFlags(Qt::TextBrowserInteraction); supportKritaLink->setOpenExternalLinks(true); userCommunityLink->setTextFormat(Qt::RichText); userCommunityLink->setTextInteractionFlags(Qt::TextBrowserInteraction); userCommunityLink->setOpenExternalLinks(true); kritaWebsiteLink->setTextFormat(Qt::RichText); kritaWebsiteLink->setTextInteractionFlags(Qt::TextBrowserInteraction); kritaWebsiteLink->setOpenExternalLinks(true); sourceCodeLink->setTextFormat(Qt::RichText); sourceCodeLink->setTextInteractionFlags(Qt::TextBrowserInteraction); sourceCodeLink->setOpenExternalLinks(true); poweredByKDELink->setTextFormat(Qt::RichText); poweredByKDELink->setTextInteractionFlags(Qt::TextBrowserInteraction); poweredByKDELink->setOpenExternalLinks(true); kdeIcon->setIconSize(QSize(20, 20)); kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20)); versionNotificationLabel->setTextFormat(Qt::RichText); versionNotificationLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); versionNotificationLabel->setOpenExternalLinks(true); - connect(chkShowNews, SIGNAL(toggled(bool)), newsWidget, SLOT(toggleNews(bool))); + devBuildIcon->setIcon(KisIconUtils::loadIcon("warning")); + + devBuildLabel->setTextFormat(Qt::RichText); + devBuildLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + devBuildLabel->setOpenExternalLinks(true); + devBuildLabel->setVisible(false); - connect(newsWidget, SIGNAL(newsDataChanged()), this, SLOT(slotUpdateVersionMessage())); + bnVersionUpdate->setVisible(false); + updaterFrame->setVisible(true); + + connect(chkShowNews, SIGNAL(toggled(bool)), newsWidget, SLOT(toggleNews(bool))); + connect(chkShowNews, SIGNAL(toggled(bool)), this, SLOT(slotToggleUpdateChecks(bool))); #ifdef Q_OS_ANDROID // checking this widgets crashes the app, so it is better for it to be hidden for now newsWidget->hide(); helpTitleLabel_2->hide(); chkShowNews->hide(); #endif - // configure the News area KisConfig cfg(true); - bool m_getNews = cfg.readEntry("FetchNews", false); - chkShowNews->setChecked(m_getNews); + bool m_checkUpdates = cfg.readEntry("FetchNews", false); - setAcceptDrops(true); +#ifndef Q_OS_ANDROID + // setup version updater, but do not check for them, unless the user wants it +#ifdef Q_OS_LINUX + if (!qEnvironmentVariable("APPIMAGE").isEmpty()) { + m_versionUpdater.reset(new KisAppimageUpdater()); + } else { + m_versionUpdater.reset(new KisManualUpdater()); + } +#else // os is not linux + m_versionUpdater.reset(new KisManualUpdater()); +#endif // Q_OS_LINUX + + connect(bnVersionUpdate, SIGNAL(clicked()), this, SLOT(slotRunVersionUpdate())); + connect(bnErrorDetails, SIGNAL(clicked()), this, SLOT(slotShowUpdaterErrorDetails())); + connect(m_versionUpdater.data(), SIGNAL(sigUpdateCheckStateChange(KisUpdaterStatus)), + this, SLOT(slotSetUpdateStatus(const KisUpdaterStatus&))); + if (m_checkUpdates) { // only if the user wants them + m_versionUpdater->checkForUpdate(); + } +#endif // Q_OS_ANDROID + + chkShowNews->setChecked(m_checkUpdates); + + setAcceptDrops(true); } KisWelcomePageWidget::~KisWelcomePageWidget() { } void KisWelcomePageWidget::setMainWindow(KisMainWindow* mainWin) { if (mainWin) { m_mainWindow = mainWin; // set the shortcut links from actions (only if a shortcut exists) if ( mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() != "") { newFileLinkShortcut->setText(QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() + QString(")")); } if (mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() != "") { openFileShortcut->setText(QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() + QString(")")); } connect(recentDocumentsListView, SIGNAL(clicked(QModelIndex)), this, SLOT(recentDocumentClicked(QModelIndex))); // we need the view manager to actually call actions, so don't create the connections // until after the view manager is set connect(newFileLink, SIGNAL(clicked(bool)), this, SLOT(slotNewFileClicked())); connect(openFileLink, SIGNAL(clicked(bool)), this, SLOT(slotOpenFileClicked())); connect(clearRecentFilesLink, SIGNAL(clicked(bool)), mainWin, SLOT(clearRecentFiles())); slotUpdateThemeColors(); // allows RSS news items to apply analytics tracking. newsWidget->setAnalyticsTracking("?" + analyticsString); } } void KisWelcomePageWidget::showDropAreaIndicator(bool show) { if (!show) { QString dropFrameStyle = "QFrame#dropAreaIndicator { border: 0px }"; dropFrameBorder->setStyleSheet(dropFrameStyle); } else { QColor textColor = qApp->palette().color(QPalette::Text); QColor backgroundColor = qApp->palette().color(QPalette::Background); QColor blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8); // QColor.name() turns it into a hex/web format QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 2px dotted ").append(blendedColor.name()).append(" }") ; dropFrameBorder->setStyleSheet(dropFrameStyle); } } void KisWelcomePageWidget::slotUpdateThemeColors() { textColor = qApp->palette().color(QPalette::Text); backgroundColor = qApp->palette().color(QPalette::Background); // make the welcome screen labels a subtle color so it doesn't clash with the main UI elements blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8); blendedStyle = QString("color: ").append(blendedColor.name()); // what labels to change the color... startTitleLabel->setStyleSheet(blendedStyle); recentDocumentsLabel->setStyleSheet(blendedStyle); helpTitleLabel->setStyleSheet(blendedStyle); newFileLinkShortcut->setStyleSheet(blendedStyle); openFileShortcut->setStyleSheet(blendedStyle); clearRecentFilesLink->setStyleSheet(blendedStyle); recentDocumentsListView->setStyleSheet(blendedStyle); newFileLink->setStyleSheet(blendedStyle); openFileLink->setStyleSheet(blendedStyle); // giving the drag area messaging a dotted border QString dottedBorderStyle = QString("border: 2px dotted ").append(blendedColor.name()).append("; color:").append(blendedColor.name()).append( ";"); dragImageHereLabel->setStyleSheet(dottedBorderStyle); // make drop area QFrame have a dotted line dropFrameBorder->setObjectName("dropAreaIndicator"); QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 4px dotted ").append(blendedColor.name()).append("}"); dropFrameBorder->setStyleSheet(dropFrameStyle); // only show drop area when we have a document over the empty area showDropAreaIndicator(false); // add icons for new and open settings to make them stand out a bit more openFileLink->setIconSize(QSize(30, 30)); newFileLink->setIconSize(QSize(30, 30)); openFileLink->setIcon(KisIconUtils::loadIcon("document-open")); newFileLink->setIcon(KisIconUtils::loadIcon("document-new")); kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20)); // HTML links seem to be a bit more stubborn with theme changes... setting inline styles to help with color change userCommunityLink->setText(QString("") .append(i18n("User Community")).append("")); gettingStartedLink->setText(QString("") .append(i18n("Getting Started")).append("")); manualLink->setText(QString("") .append(i18n("User Manual")).append("")); supportKritaLink->setText(QString("") .append(i18n("Support Krita")).append("")); kritaWebsiteLink->setText(QString("") .append(i18n("Krita Website")).append("")); sourceCodeLink->setText(QString("") .append(i18n("Source Code")).append("")); poweredByKDELink->setText(QString("") .append(i18n("Powered by KDE")).append("")); slotUpdateVersionMessage(); // text set from RSS feed // re-populate recent files since they might have themed icons populateRecentDocuments(); } void KisWelcomePageWidget::populateRecentDocuments() { m_recentFilesModel.clear(); // clear existing data before it gets re-populated // grab recent files data int numRecentFiles = m_mainWindow->recentFilesUrls().length() > 5 ? 5 : m_mainWindow->recentFilesUrls().length(); // grab at most 5 for (int i = 0; i < numRecentFiles; i++ ) { QStandardItem *recentItem = new QStandardItem(1,2); // 1 row, 1 column recentItem->setIcon(KisIconUtils::loadIcon("document-export")); QString recentFileUrlPath = m_mainWindow->recentFilesUrls().at(i).toLocalFile(); QString fileName = recentFileUrlPath.split("/").last(); QList brokenUrls; if (m_thumbnailMap.contains(recentFileUrlPath)) { recentItem->setIcon(m_thumbnailMap[recentFileUrlPath]); } else { QFileInfo fi(recentFileUrlPath); if (fi.exists()) { if (fi.suffix() == "ora" || fi.suffix() == "kra") { QScopedPointer store(KoStore::createStore(recentFileUrlPath, KoStore::Read)); if (store) { QString thumbnailpath; if (store->hasFile(QString("Thumbnails/thumbnail.png"))){ thumbnailpath = QString("Thumbnails/thumbnail.png"); } else if (store->hasFile(QString("preview.png"))) { thumbnailpath = QString("preview.png"); } if (!thumbnailpath.isEmpty()) { if (store->open(thumbnailpath)) { QByteArray bytes = store->read(store->size()); store->close(); QImage img; img.loadFromData(bytes); img.setDevicePixelRatio(devicePixelRatioF()); recentItem->setIcon(QIcon(QPixmap::fromImage(img))); } } } else { brokenUrls << m_mainWindow->recentFilesUrls().at(i); } } else if (fi.suffix() == "tiff" || fi.suffix() == "tif") { // Workaround for a bug in Qt tiff QImageIO plugin QScopedPointer doc; doc.reset(KisPart::instance()->createDocument()); doc->setFileBatchMode(true); bool r = doc->openUrl(QUrl::fromLocalFile(recentFileUrlPath), KisDocument::DontAddToRecent); if (r) { KisPaintDeviceSP projection = doc->image()->projection(); recentItem->setIcon(QIcon(QPixmap::fromImage(projection->createThumbnail(48, 48, projection->exactBounds())))); } else { brokenUrls << m_mainWindow->recentFilesUrls().at(i); } } else { QImage img; img.setDevicePixelRatio(devicePixelRatioF()); img.load(recentFileUrlPath); if (!img.isNull()) { recentItem->setIcon(QIcon(QPixmap::fromImage(img.scaledToWidth(48)))); } else { brokenUrls << m_mainWindow->recentFilesUrls().at(i); } } if (brokenUrls.size() > 0 && brokenUrls.last().toLocalFile() != recentFileUrlPath) { m_thumbnailMap[recentFileUrlPath] = recentItem->icon(); } } } Q_FOREACH(const QUrl &url, brokenUrls) { m_mainWindow->removeRecentUrl(url); } // set the recent object with the data if (brokenUrls.isEmpty() || brokenUrls.last().toLocalFile() != recentFileUrlPath) { recentItem->setText(fileName); // what to display for the item recentItem->setToolTip(recentFileUrlPath); m_recentFilesModel.appendRow(recentItem); } } // hide clear and Recent files title if there are none bool hasRecentFiles = m_mainWindow->recentFilesUrls().length() > 0; recentDocumentsLabel->setVisible(hasRecentFiles); clearRecentFilesLink->setVisible(hasRecentFiles); recentDocumentsListView->setIconSize(QSize(48, 48)); recentDocumentsListView->setModel(&m_recentFilesModel); } void KisWelcomePageWidget::slotUpdateVersionMessage() { - - alertIcon->setIcon(KisIconUtils::loadIcon("warning")); - alertIcon->setVisible(false); - - // find out if we need an update...or if this is a development version: - // dev builds contain GIT hash in it and the word git - // stable versions do not contain this. - if (qApp->applicationVersion().contains("git")) { - // Development build - QString versionLabelText = QString("setVisible(false); + versionNotificationLabel->setVisible(false); + bnVersionUpdate->setVisible(false); + bnErrorDetails->setVisible(false); + + // always flag developement version + if (isDevelopmentBuild()) { + QString devBuildLabelText = QString("") .append(i18n("DEV BUILD")).append(""); - versionNotificationLabel->setText(versionLabelText); - alertIcon->setVisible(true); - versionNotificationLabel->setVisible(true); + devBuildLabel->setText(devBuildLabelText); + devBuildIcon->setVisible(true); + devBuildLabel->setVisible(true); + } else { + devBuildIcon->setVisible(false); + devBuildLabel->setVisible(false); + } - } else if (newsWidget->hasUpdateAvailable()) { - // build URL for label - QString versionLabelText = QString("versionLink() + "?" + - analyticsString + "version-update" + "\">") - .append(i18n("New Version Available!")).append(""); + if (!m_checkUpdates || !m_versionUpdater) { + return; + } + + QString versionLabelText; + if (m_updaterStatus.status() == UpdaterStatus::StatusID::UPDATE_AVAILABLE) { + updaterFrame->setVisible(true); + versionLabelText = i18n("New version is available."); versionNotificationLabel->setVisible(true); - versionNotificationLabel->setText(versionLabelText); - alertIcon->setVisible(true); - } else { - // no message needed... exit - versionNotificationLabel->setVisible(false); - return; + if (m_versionUpdater->hasUpdateCapability()) { + bnVersionUpdate->setVisible(true); + } else { + // build URL for label + QString downloadLink = QString(" Download Krita %4") + .arg(blendedColor.name()) + .arg(m_updaterStatus.downloadLink()) + .arg(analyticsString + "version-update") + .arg(m_updaterStatus.availableVersion()); + + versionLabelText.append(downloadLink); + } + + } else if ( + (m_updaterStatus.status() == UpdaterStatus::StatusID::UPTODATE) + || (m_updaterStatus.status() == UpdaterStatus::StatusID::CHECK_ERROR) + || (m_updaterStatus.status() == UpdaterStatus::StatusID::IN_PROGRESS) + ){ + // no notifications, if uptodate + // also, stay silent on check error - we do not want to generate lots of user support issues + // because of failing wifis and proxies over the world + updaterFrame->setVisible(false); + + } else if (m_updaterStatus.status() == UpdaterStatus::StatusID::UPDATE_ERROR) { + updaterFrame->setVisible(true); + versionLabelText = i18n("An error occurred during the update"); + versionNotificationLabel->setVisible(true); + bnErrorDetails->setVisible(true); + + } else if (m_updaterStatus.status() == UpdaterStatus::StatusID::RESTART_REQUIRED) { + updaterFrame->setVisible(true); + versionLabelText = i18n("New version was downloaded. To complete the update, close Krita and run the new version."); + versionNotificationLabel->setVisible(true); } + versionNotificationLabel->setText(versionLabelText); if (!blendedStyle.isNull()) { versionNotificationLabel->setStyleSheet(blendedStyle); } - } void KisWelcomePageWidget::dragEnterEvent(QDragEnterEvent *event) { //qDebug() << "dragEnterEvent formats" << event->mimeData()->formats() << "urls" << event->mimeData()->urls() << "has images" << event->mimeData()->hasImage(); showDropAreaIndicator(true); if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisWelcomePageWidget::dropEvent(QDropEvent *event) { //qDebug() << "KisWelcomePageWidget::dropEvent() formats" << event->mimeData()->formats() << "urls" << event->mimeData()->urls() << "has images" << event->mimeData()->hasImage(); showDropAreaIndicator(false); if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { if (url.toLocalFile().endsWith(".bundle")) { bool r = m_mainWindow->installBundle(url.toLocalFile()); if (!r) { qWarning() << "Could not install bundle" << url.toLocalFile(); } } else { m_mainWindow->openDocument(url, KisMainWindow::None); } } } } void KisWelcomePageWidget::dragMoveEvent(QDragMoveEvent *event) { //qDebug() << "dragMoveEvent"; m_mainWindow->dragMoveEvent(event); if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisWelcomePageWidget::dragLeaveEvent(QDragLeaveEvent */*event*/) { //qDebug() << "dragLeaveEvent"; showDropAreaIndicator(false); m_mainWindow->dragLeave(); } void KisWelcomePageWidget::recentDocumentClicked(QModelIndex index) { QString fileUrl = index.data(Qt::ToolTipRole).toString(); m_mainWindow->openDocument(QUrl::fromLocalFile(fileUrl), KisMainWindow::None ); } +void KisWelcomePageWidget::slotRunVersionUpdate() +{ + if (m_checkUpdates) m_versionUpdater->doUpdate(); +} + +void KisWelcomePageWidget::slotToggleUpdateChecks(bool state) +{ + m_checkUpdates = state; + + if (m_checkUpdates) { + m_versionUpdater->checkForUpdate(); + } + + slotUpdateVersionMessage(); +} + +bool KisWelcomePageWidget::isDevelopmentBuild() +{ + QString versionString = KritaVersionWrapper::versionString(true); + + if (versionString.contains("git")) { + return true; + } else { + return false; + } +} void KisWelcomePageWidget::slotNewFileClicked() { m_mainWindow->slotFileNew(); } void KisWelcomePageWidget::slotOpenFileClicked() { m_mainWindow->slotFileOpen(); } +void KisWelcomePageWidget::slotSetUpdateStatus(KisUpdaterStatus updateStatus) +{ + m_updaterStatus = updateStatus; + slotUpdateVersionMessage(); +} + +void KisWelcomePageWidget::slotShowUpdaterErrorDetails() +{ + QMessageBox::warning(0, i18nc("@title:window", "Krita"), m_updaterStatus.updaterOutput()); +} diff --git a/libs/ui/KisWelcomePageWidget.h b/libs/ui/KisWelcomePageWidget.h index 51a4fafa62..e6eb0da6ba 100644 --- a/libs/ui/KisWelcomePageWidget.h +++ b/libs/ui/KisWelcomePageWidget.h @@ -1,93 +1,104 @@ /* This file is part of the KDE project * Copyright (C) 2018 Scott Petrovic * * 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 KISWELCOMEPAGEWIDGET_H #define KISWELCOMEPAGEWIDGET_H #include "kritaui_export.h" #include "KisViewManager.h" #include "KisMainWindow.h" +#include #include #include "ui_KisWelcomePage.h" #include +#include /// A widget for displaying if no documents are open. This will display in the MDI area class KRITAUI_EXPORT KisWelcomePageWidget : public QWidget, public Ui::KisWelcomePage { Q_OBJECT public: explicit KisWelcomePageWidget(QWidget *parent); ~KisWelcomePageWidget() override; void setMainWindow(KisMainWindow* m_mainWindow); public Q_SLOTS: /// if a document is placed over this area, a dotted line will appear as an indicator /// that it is a droppable area. KisMainwindow is what triggers this void showDropAreaIndicator(bool show); void slotUpdateThemeColors(); /// this could be called multiple times. If a recent document doesn't /// have a preview, an icon is used that needs to be updated void populateRecentDocuments(); void slotUpdateVersionMessage(); + void slotSetUpdateStatus(KisUpdaterStatus updateStatus); + void slotShowUpdaterErrorDetails(); + protected: // QWidget overrides void dragEnterEvent(QDragEnterEvent * event) override; void dropEvent(QDropEvent * event) override; void dragMoveEvent(QDragMoveEvent * event) override; void dragLeaveEvent(QDragLeaveEvent * event) override; private: KisMainWindow *m_mainWindow; QStandardItemModel m_recentFilesModel; QMap m_thumbnailMap; /// help us see how many people are clicking startup screen links /// you can see the results in Matomo (stats.kde.org) /// this will be listed in the "Acquisition" section of Matomo /// just append some text to this to associate it with an event/page const QString analyticsString = "pk_campaign=startup-sceen&pk_kwd="; // keeping track of link colors with theme change QColor textColor; QColor backgroundColor; QColor blendedColor; QString blendedStyle; - + QScopedPointer m_versionUpdater; + KisUpdaterStatus m_updaterStatus; + bool m_checkUpdates; private Q_SLOTS: void slotNewFileClicked(); void slotOpenFileClicked(); void recentDocumentClicked(QModelIndex index); + void slotRunVersionUpdate(); + void slotToggleUpdateChecks(bool state); + + bool isDevelopmentBuild(); }; #endif // KISWELCOMEPAGEWIDGET_H diff --git a/libs/ui/forms/KisWelcomePage.ui b/libs/ui/forms/KisWelcomePage.ui index 16ea785892..d073e65e92 100644 --- a/libs/ui/forms/KisWelcomePage.ui +++ b/libs/ui/forms/KisWelcomePage.ui @@ -1,851 +1,894 @@ KisWelcomePage 0 0 - 788 - 476 + 1118 + 593 0 0 false QFrame::Box QFrame::Plain 4 0 - - + + 5 - - 0 - 0 - 30 - - - 0 + 20 - - - - 18 - - - - Community - - - 0 - - - - - - - 0 - + - + + + + 0 + 0 + + + + + 70 + 0 + + - true + 18 - User Manual + Start - - - Qt::Horizontal + + + + 0 + 0 + - - QSizePolicy::Expanding + + + 16 + 16 + - + + + + - 40 - 5 + 16 + 16 - + + true + + - - - - - - 0 - - - - - true - - + - Getting Started + DEV BUILD - + Qt::Horizontal 40 - 5 + 20 - + + + 0 + - + true + + Qt::NoFocus + - Support Krita + New File + + + false + + + true - - - Qt::Horizontal - - + + - 40 - 20 + 20 + 0 - - - - - - - - - true + false - User Community + - + Qt::Horizontal 40 20 - + + + 0 + + + 30 + - + true + + Qt::NoFocus + - Krita Website + Open File + + + false + + + true - - - Qt::Horizontal - - + + - 40 - 20 + 20 + 0 - - - - - - - - 0 - - - 0 - - - - true + false - Source Code + - + Qt::Horizontal 40 20 - - - 20 - + - - - - 20 - 20 - - - - - 20 - 20 - + + + + 18 + - - - - true + Recent Documents - + + + + 0 + 0 + + true + + Qt::NoFocus + - Powered by KDE + Clear + + + false + + + true - + Qt::Horizontal - 40 + 5 20 - - - - - - 0 - 0 - - - - QFrame::Box - - - 2 - - - Drag Image in window to open - - - Qt::AlignCenter - - - true - - - 30 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + + 0 + 0 + + + + + 300 + 250 + + + + + true + false + + + + Qt::NoFocus + + + false + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + false + + + false + + + QAbstractItemView::SelectItems + + + QListView::Snap + + + QListView::Fixed + + + 0 + + + QListView::ListMode + + + true + + + false + + - + Qt::Vertical - 20 - 20 + 5 + 5 - - - - Qt::Horizontal - - - - 5 - 5 - - - - - - - - Qt::Vertical - - - - 5 - 5 - - - - - - - - Qt::Vertical - - - - 5 - 5 - - - - - - - - Qt::Horizontal - - - - 5 - 5 - - - - - - + + 5 + + 0 + 0 - 20 + 30 + + + 0 - + + + + 18 + + + + Community + + + 0 + + + + + + + 0 + - - - - 0 - 0 - + + + + true + - + + User Manual + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + - 70 - 0 + 40 + 5 + + + + + + + + 0 + + + - 18 + true - Start + Getting Started - - - - 0 - 0 - + + + Qt::Horizontal - + - 16 - 16 + 40 + 5 + + + + + + + + + + + true + + - + Support Krita - + + + + + + Qt::Horizontal + + - 16 - 16 + 40 + 20 - - true + + + + + + + + + + + true + + + + User Community - - - - 0 - 0 - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + true - Krita X.X.X Update Available - - - 2 - - - false + Krita Website - + Qt::Horizontal 40 20 - + 0 + + 0 + - + true - - Qt::NoFocus - - - New File - - - false - - - true - - - - - - - - 20 - 0 - - - - - false - - - + Source Code - + Qt::Horizontal 40 20 - - - 0 - + - 30 + 20 - - - - true - + + + + 20 + 20 + - - Qt::NoFocus + + + 20 + 20 + - Open File - - - false + true - - - - 20 - 0 - - + - false + true - + Powered by KDE - + Qt::Horizontal 40 20 - - - - - - 18 - - - - Recent Documents - - - + - + - + 0 0 - - - true - + + QFrame::Box - - Qt::NoFocus + + 2 - Clear + Drag Image in window to open - - false + + Qt::AlignCenter - + true + + 30 + - + Qt::Horizontal - 5 + 40 20 - - - - 0 - 0 - - - - - 300 - 250 - - - - - true - false - - - - Qt::NoFocus - - - false - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - false - - - false - - - QAbstractItemView::SelectItems - - - QListView::Snap - - - QListView::Fixed - - - 0 - - - QListView::ListMode - - - true - - - false - - - - - + Qt::Vertical - 5 - 5 + 20 + 20 - + + + + Qt::Horizontal + + + + 5 + 5 + + + + + 0 0 18 News 0 true 0 0 Show news about Krita: this needs internet to retrieve information from the krita.org website Check for updates - + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + false + + + + Krita X.X.X Update Available + + + true + + + 2 + + + false + + + + + + + Update now! + + + + + + + Show details + + + + + + + + + + + 0 + 0 + + + + + + + Qt::Vertical + + + + 5 + 5 + + + + + + + + Qt::Vertical + + + + 5 + 5 + + + + + + + + Qt::Horizontal + + + + 5 + 5 + + + + KisNewsWidget QWidget

widgets/KisNewsWidget.h
1 diff --git a/libs/ui/tests/CMakeLists.txt b/libs/ui/tests/CMakeLists.txt index 99d892a829..7fe8c710f8 100644 --- a/libs/ui/tests/CMakeLists.txt +++ b/libs/ui/tests/CMakeLists.txt @@ -1,183 +1,202 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include_directories(${CMAKE_SOURCE_DIR}/libs/image/metadata ${CMAKE_SOURCE_DIR}/sdk/tests ) include(ECMAddTests) macro_add_unittest_definitions() ecm_add_tests( kis_image_view_converter_test.cpp kis_shape_selection_test.cpp kis_doc2_test.cpp kis_coordinates_converter_test.cpp kis_grid_config_test.cpp kis_stabilized_events_sampler_test.cpp kis_brush_hud_properties_config_test.cpp kis_shape_commands_test.cpp kis_shape_layer_test.cpp kis_stop_gradient_editor_test.cpp kis_file_layer_test.cpp kis_multinode_property_test.cpp KisFrameSerializerTest.cpp KisFrameCacheStoreTest.cpp kis_animation_exporter_test.cpp kis_prescaled_projection_test.cpp kis_asl_layer_style_serializer_test.cpp kis_animation_importer_test.cpp KisSpinBoxSplineUnitConverterTest.cpp KisDocumentReplaceTest.cpp + KisRssReaderTest.cpp LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-" ) +if (UNIX) + ecm_add_test( KisAppimageUpdaterTest.cpp + TEST_NAME KisAppimageUpdaterTest + LINK_LIBRARIES kritaui Qt5::Test + NAME_PREFIX "libs-ui-") +endif() + +ecm_add_test( KisManualUpdaterTest.cpp ../../../sdk/tests/testutil.cpp MockMultiFeedRssModel.cpp + TEST_NAME KisManualUpdaterTest + LINK_LIBRARIES kritaui Qt5::Test + NAME_PREFIX "libs-ui-") + +ecm_add_test( KisMultiFeedRssModelTest.cpp ../../../sdk/tests/testutil.cpp MockNetworkAccessManager.cpp + TEST_NAME KisMultiFeedRssModelTest + LINK_LIBRARIES kritaui Qt5::Test + NAME_PREFIX "libs-ui-") + + ecm_add_test( kis_selection_decoration_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp TEST_NAME KisSelectionDecorationTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_node_dummies_graph_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisNodeDummiesGraphTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_node_shapes_graph_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisNodeShapesGraphTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_model_index_converter_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisModelIndexConverterTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_categorized_list_model_test.cpp modeltest.cpp TEST_NAME KisCategorizedListModelTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_node_juggler_compressed_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisNodeJugglerCompressedTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_input_manager_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisInputManagerTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( kis_node_model_test.cpp modeltest.cpp TEST_NAME kis_node_model_test LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ##### Tests that currently fail and should be fixed ##### include(KritaAddBrokenUnitTest) krita_add_broken_unit_test( kis_shape_controller_test.cpp kis_dummies_facade_base_test.cpp TEST_NAME kis_shape_controller_test LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_exiv2_test.cpp TEST_NAME KisExiv2Test LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_clipboard_test.cpp TEST_NAME KisClipboardTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( freehand_stroke_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME FreehandStrokeTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( FreehandStrokeBenchmark.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME FreehandStrokeBenchmark LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( KisPaintOnTransparencyMaskTest.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME KisPaintOnTransparencyMaskTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") ecm_add_test( fill_processing_visitor_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME FillProcessingVisitorTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( filter_stroke_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp TEST_NAME FilterStrokeTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_selection_manager_test.cpp TEST_NAME KisSelectionManagerTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") #set_tests_properties(libs-ui-KisSelectionManagerTest PROPERTIES TIMEOUT 300) krita_add_broken_unit_test( kis_node_manager_test.cpp TEST_NAME KisNodeManagerTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_dummies_facade_test.cpp kis_dummies_facade_base_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisDummiesFacadeTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_zoom_and_pan_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME KisZoomAndPanTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") #set_tests_properties(libs-ui-KisZoomAndPanTest PROPERTIES TIMEOUT 300) krita_add_broken_unit_test( kis_action_manager_test.cpp TEST_NAME KisActionManagerTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_categories_mapper_test.cpp testing_categories_mapper.cpp TEST_NAME KisCategoriesMapperTest LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_animation_frame_cache_test.cpp TEST_NAME kis_animation_frame_cache_test LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") krita_add_broken_unit_test( kis_derived_resources_test.cpp TEST_NAME kis_derived_resources_test LINK_LIBRARIES kritaui Qt5::Test NAME_PREFIX "libs-ui-") diff --git a/libs/ui/tests/KisAppimageUpdaterTest.cpp b/libs/ui/tests/KisAppimageUpdaterTest.cpp new file mode 100644 index 0000000000..1bdb3197bb --- /dev/null +++ b/libs/ui/tests/KisAppimageUpdaterTest.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisAppimageUpdaterTest.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +KisAppimageUpdaterTest::KisAppimageUpdaterTest(QObject *parent) : QObject(parent) +{ + +} + +void KisAppimageUpdaterTest::testCheckForUpdate() +{ + QFETCH(QString, controlValue); + QFETCH(UpdaterStatus::StatusID, resultStatus); + + QString updaterDummyPath = QString("%1%2/AppImageUpdateDummy") + .arg(QString(FILES_DATA_DIR)) + .arg(QDir::separator()); + + + qputenv("APPIMAGEUPDATE_DUMMY_STATE", controlValue.toLocal8Bit()); + + QScopedPointer updater(new KisAppimageUpdater(updaterDummyPath)); + + QSignalSpy spy(updater.data(), SIGNAL(sigUpdateCheckStateChange(KisUpdaterStatus))); + + QVERIFY(spy.isValid()); + + updater->checkForUpdate(); + + QTest::qWait(1000); + + QList arguments = spy.takeFirst(); + KisUpdaterStatus firstStatus = arguments.at(0).value(); + + QCOMPARE(firstStatus.status(), UpdaterStatus::StatusID::IN_PROGRESS); + + arguments = spy.takeFirst(); + KisUpdaterStatus secondStatus = arguments.at(0).value(); + + QCOMPARE(secondStatus.status(), resultStatus); +} + +void KisAppimageUpdaterTest::testCheckForUpdate_data() +{ + QTest::addColumn("controlValue"); + QTest::addColumn("resultStatus"); + + QTest::addRow("uptodate") + << QString("check_uptodate") + << UpdaterStatus::StatusID::UPTODATE; + + QTest::addRow("update available") + << QString("check_update_avail") + << UpdaterStatus::StatusID::UPDATE_AVAILABLE; + + QTest::addRow("error") + << QString("check_error") + << UpdaterStatus::StatusID::CHECK_ERROR; + + QTest::addRow("empty update info") + << QString("check_updinfo_empty") + << UpdaterStatus::StatusID::CHECK_ERROR; +} + +void KisAppimageUpdaterTest::testDoUpdate() +{ + QFETCH(QString, controlValue); + QFETCH(UpdaterStatus::StatusID, resultStatus); + + QString updaterDummyPath = QString("%1%2/AppImageUpdateDummy") + .arg(QString(FILES_DATA_DIR)) + .arg(QDir::separator()); + + + qputenv("APPIMAGEUPDATE_DUMMY_STATE", controlValue.toLocal8Bit()); + + QScopedPointer updater(new KisAppimageUpdater(updaterDummyPath)); + + QSignalSpy spy(updater.data(), SIGNAL(sigUpdateCheckStateChange(KisUpdaterStatus))); + + QVERIFY(spy.isValid()); + + updater->doUpdate(); + + QTest::qWait(1000); + + QList arguments = spy.takeFirst(); + KisUpdaterStatus secondStatus = arguments.at(0).value(); + + QCOMPARE(secondStatus.status(), resultStatus); + + // for error also check for output + if (secondStatus.status() == UpdaterStatus::StatusID::UPDATE_ERROR) { + QCOMPARE(secondStatus.updaterOutput(), QString("DUMMY: an error occured\n")); + } +} + +void KisAppimageUpdaterTest::testDoUpdate_data() +{ + QTest::addColumn("controlValue"); + QTest::addColumn("resultStatus"); + + QTest::addRow("update ok") + << QString("update_ok") + << UpdaterStatus::StatusID::RESTART_REQUIRED; + + QTest::addRow("error") + << QString("update_error") + << UpdaterStatus::StatusID::UPDATE_ERROR; + + QTest::addRow("runtime error") + << QString("runtime_error") + << UpdaterStatus::StatusID::UPDATE_ERROR; +} + +QTEST_MAIN(KisAppimageUpdaterTest); diff --git a/libs/ui/tests/KisAppimageUpdaterTest.h b/libs/ui/tests/KisAppimageUpdaterTest.h new file mode 100644 index 0000000000..e850d2f099 --- /dev/null +++ b/libs/ui/tests/KisAppimageUpdaterTest.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISAPPIMAGEUPDATERTEST_H +#define KISAPPIMAGEUPDATERTEST_H + +#include + +class KisAppimageUpdaterTest : public QObject +{ + Q_OBJECT +public: + explicit KisAppimageUpdaterTest(QObject *parent = nullptr); + +private Q_SLOTS: + void testCheckForUpdate(); + void testCheckForUpdate_data(); + void testDoUpdate(); + void testDoUpdate_data(); +}; + +#endif // KISAPPIMAGEUPDATERTEST_H diff --git a/libs/ui/tests/KisManualUpdaterTest.cpp b/libs/ui/tests/KisManualUpdaterTest.cpp new file mode 100644 index 0000000000..de43029161 --- /dev/null +++ b/libs/ui/tests/KisManualUpdaterTest.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisManualUpdaterTest.h" + +#include +#include +#include +#include +#include + +KisManualUpdaterTest::KisManualUpdaterTest(QObject *parent) : QObject(parent) +{ + +} + +void KisManualUpdaterTest::testAvailableVersionIsHigher() +{ + QFETCH(QString, currentVersion); + QFETCH(QString, availableVersion); + QFETCH(bool, expectedReturnValue); + + QScopedPointer updater(new KisManualUpdater()); + bool actualReturnValue = updater->availableVersionIsHigher(currentVersion, availableVersion); + QCOMPARE(actualReturnValue, expectedReturnValue); +} + +void KisManualUpdaterTest::testAvailableVersionIsHigher_data() +{ + QTest::addColumn("currentVersion"); + QTest::addColumn("availableVersion"); + QTest::addColumn("expectedReturnValue"); + + QTest::addRow("equal") << "4.2.5" << "4.2.5" << false; + + QTest::addRow("higher major") << "3.0.0" << "4.2.5" << true; + QTest::addRow("higher minor") << "4.2.4" << "4.3.4" << true; + QTest::addRow("higher rev") << "4.2.4" << "4.2.5" << true; + + QTest::addRow("lower major") << "4.2.5" << "3.2.5" << false; + QTest::addRow("lower minor") << "4.2.5" << "4.1.5" << false; + QTest::addRow("lower rev") << "4.2.5" << "4.2.4" << false; + + QTest::addRow("current is git, available lower stable") << "4.3.0-d8ea4b" << "4.2.5" << false; + QTest::addRow("current is beta, available lower stable") << "4.2.6-beta1" << "4.2.5" << false; + + QTest::addRow("current is git, available higher stable") << "4.3.0-d8ea4b" << "4.4.2" << true; + QTest::addRow("current is beta, available lower stable") << "4.2.6-beta1" << "4.3.1" << true; + + QTest::addRow("current is git, available equal version release") << "4.3.0-d8ea4b" << "4.3.0" << true; + QTest::addRow("current is beta, available equal version release") << "4.2.6-beta1" << "4.2.6" << true; + + QTest::addRow("available is git, current equal version release") << "4.3.0" << "4.3.0-d8ea4b" << false; + QTest::addRow("available is beta, current equal version release") << "4.2.6" << "4.2.6-beta1" << false; + +// this is not solved in the function, as it is unlikely to have such versions in the RSS feed +// QTest::addRow("current is git, available equal version beta") << "4.3.0-d8ea4b" << "4.3.0-beta1" << true; + // QTest::addRow("available is beta, current equal version git") << "4.2.6-beta1" << "4.2.6-d8ea4b" << false; +} + +void KisManualUpdaterTest::testCheckForUpdate() +{ + QFETCH(RssItemList, feed); + QFETCH(QString, currentVersion); + QFETCH(QString, availableVersion); + QFETCH(QString, downloadLink); + QFETCH(UpdaterStatus::StatusID, resultStatus); + + MockMultiFeedRssModel* mockRssModel(new MockMultiFeedRssModel()); + mockRssModel->loadFeedData(feed); + + QScopedPointer updater(new KisManualUpdater(mockRssModel, currentVersion)); + + QSignalSpy spy(updater.data(), SIGNAL(sigUpdateCheckStateChange(KisUpdaterStatus))); + + QVERIFY(spy.isValid()); + + updater->checkForUpdate(); + + QTest::qWait(1000); + + QList arguments = spy.takeFirst(); + KisUpdaterStatus updaterStatus = arguments.at(0).value(); + + QCOMPARE(updaterStatus.status(), resultStatus); + QCOMPARE(updaterStatus.availableVersion(), availableVersion); + QCOMPARE(updaterStatus.downloadLink(), downloadLink); +} + +void KisManualUpdaterTest::testCheckForUpdate_data() +{ + QTest::addColumn("feed"); + QTest::addColumn("currentVersion"); + QTest::addColumn("availableVersion"); + QTest::addColumn("downloadLink"); + QTest::addColumn("resultStatus"); + + RssItem item1 = RssItem(); + item1.title = "Krita 4.3.0 Released"; + item1.link = "https://krita.org/en/item/krita-4-3-0/"; + item1.category = "Official Release"; + item1.pubDate = QDateTime::fromString(QString("Wed, 10 Sep 2019 09:42:15 +0000"), Qt::RFC2822Date); + + RssItem item2 = RssItem(); + item2.title = "Krita 4.2.4 Released"; + item2.link = "https://krita.org/en/item/krita-4-2-4/"; + item2.category = "Official Release"; + item2.pubDate = QDateTime::fromString(QString("Wed, 10 Sep 2018 09:42:15 +0000"), Qt::RFC2822Date); + + RssItem item3 = RssItem(); + item3.title = "Interview with The Artist"; + item3.link = "https://krita.org/en/item/interview-with-the-artist/"; + item3.category = "Artist Interview"; + item3.pubDate = QDateTime::fromString(QString("Wed, 15 Sep 2019 09:42:15 +0000"), Qt::RFC2822Date); + + + RssItemList feed = RssItemList() << item1 << item2 << item3; + + QTest::addRow("uptodate") + << feed + << QString("4.3.0") + << QString("4.3.0") + << QString("https://krita.org/en/item/krita-4-3-0/") + << UpdaterStatus::StatusID::UPTODATE; + + QTest::addRow("update available") + << feed + << QString("4.2.4") + << QString("4.3.0") + << QString("https://krita.org/en/item/krita-4-3-0/") + << UpdaterStatus::StatusID::UPDATE_AVAILABLE; +} + +QTEST_MAIN(KisManualUpdaterTest); diff --git a/libs/ui/tests/KisManualUpdaterTest.h b/libs/ui/tests/KisManualUpdaterTest.h new file mode 100644 index 0000000000..e3febaeea4 --- /dev/null +++ b/libs/ui/tests/KisManualUpdaterTest.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISMANUALUPDATERTEST_H +#define KISMANUALUPDATERTEST_H + +#include + +class KisManualUpdaterTest : public QObject +{ + Q_OBJECT +public: + explicit KisManualUpdaterTest(QObject *parent = nullptr); + +private Q_SLOTS: + void testAvailableVersionIsHigher(); + void testAvailableVersionIsHigher_data(); + + void testCheckForUpdate(); + void testCheckForUpdate_data(); +}; + +#endif // KISMANUALUPDATERTEST_H diff --git a/libs/ui/tests/KisMultiFeedRssModelTest.cpp b/libs/ui/tests/KisMultiFeedRssModelTest.cpp new file mode 100644 index 0000000000..5aa7b4cec0 --- /dev/null +++ b/libs/ui/tests/KisMultiFeedRssModelTest.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisMultiFeedRssModelTest.h" + +#include +#include + +#include +#include +#include + + +KisMultiFeedRssModelTest::KisMultiFeedRssModelTest(QObject *parent) : QObject(parent) +{ + +} + +void KisMultiFeedRssModelTest::testAddFeed() +{ + QFETCH(QList, replyDataList); + QFETCH(int, resultArticleCount); + + + MockNetworkAccessManager* nam(new MockNetworkAccessManager()); + + QStringList feedList; + for(FakeReplyData& replyData: replyDataList) { + feedList << replyData.url.toString(); + nam->setReplyData(replyData); + } + + MultiFeedRssModel* rssModel(new MultiFeedRssModel(nam)); + QSignalSpy spy(rssModel, SIGNAL(feedDataChanged())); + QVERIFY(spy.isValid()); + + for(QString& feedUrl: feedList) { + rssModel->addFeed(feedUrl); + } + + // wait for signal + QVERIFY(spy.wait()); + + QCOMPARE(rssModel->articleCount(), resultArticleCount); +} + +void KisMultiFeedRssModelTest::testAddFeed_data() +{ + QTest::addColumn>("replyDataList"); + QTest::addColumn("resultArticleCount"); + + // setup feed1 + QFile rssFile1(TestUtil::fetchDataFileLazy("rss_feeds/feed.xml")); + bool fileOpened = rssFile1.open(QIODevice::ReadOnly | QIODevice::Text); + QVERIFY(fileOpened); + + QString urlFeed1("https://krita.org/en/feed/"); + + // create reply data + FakeReplyData replyFeed1; + replyFeed1.url = QUrl(urlFeed1); + replyFeed1.statusCode = 200; + replyFeed1.requestMethod = QNetworkAccessManager::GetOperation; + replyFeed1.contentType = "application/atom+xml"; + replyFeed1.responseBody = rssFile1.readAll(); + + + // setup feed2 + QFile rssFile2(TestUtil::fetchDataFileLazy("rss_feeds/feed.xml")); + fileOpened = rssFile2.open(QIODevice::ReadOnly | QIODevice::Text); + QVERIFY(fileOpened); + + QString urlFeed2("https://krita.org/en/another_feed/"); + + FakeReplyData replyFeed2; + replyFeed2.url = QUrl(urlFeed2); + replyFeed2.statusCode = 200; + replyFeed2.requestMethod = QNetworkAccessManager::GetOperation; + replyFeed2.contentType = "application/atom+xml"; + replyFeed2.responseBody = rssFile2.readAll(); + + + // test with 1 feed + QList listTest1 = { replyFeed1 }; + QTest::addRow("1 feed") + << listTest1 + << 10; + + // test with 2 feeds + QList listTest2 = { replyFeed1, replyFeed2 }; + QTest::addRow("2 feeds") + << listTest2 + << 20; +} + +void KisMultiFeedRssModelTest::testRemoveFeed() +{ + QFETCH(QList, replyDataList); + QFETCH(QString, removeFeedUrl); + QFETCH(int, resultArticleCount); + + + MockNetworkAccessManager* nam(new MockNetworkAccessManager()); + + QStringList feedList; + for(FakeReplyData& replyData: replyDataList) { + feedList << replyData.url.toString(); + nam->setReplyData(replyData); + } + + MultiFeedRssModel* rssModel(new MultiFeedRssModel(nam)); + QSignalSpy spyAdd(rssModel, SIGNAL(feedDataChanged())); + QVERIFY(spyAdd.isValid()); + + for(QString& feedUrl: feedList) { + rssModel->addFeed(feedUrl); + } + + // wait for signal + QVERIFY(spyAdd.wait()); + + // the remove test itself + rssModel->removeFeed(removeFeedUrl); + + QCOMPARE(rssModel->articleCount(), resultArticleCount); +} + +void KisMultiFeedRssModelTest::testRemoveFeed_data() +{ + QTest::addColumn>("replyDataList"); + QTest::addColumn("removeFeedUrl"); + QTest::addColumn("resultArticleCount"); + + // setup feed1 + QFile rssFile1(TestUtil::fetchDataFileLazy("rss_feeds/feed.xml")); + bool fileOpened = rssFile1.open(QIODevice::ReadOnly | QIODevice::Text); + QVERIFY(fileOpened); + + QString urlFeed1("https://krita.org/en/feed/"); + + // create reply data + FakeReplyData replyFeed1; + replyFeed1.url = QUrl(urlFeed1); + replyFeed1.statusCode = 200; + replyFeed1.requestMethod = QNetworkAccessManager::GetOperation; + replyFeed1.contentType = "application/atom+xml"; + replyFeed1.responseBody = rssFile1.readAll(); + + // setup feed2 + QFile rssFile2(TestUtil::fetchDataFileLazy("rss_feeds/feed.xml")); + fileOpened = rssFile2.open(QIODevice::ReadOnly | QIODevice::Text); + QVERIFY(fileOpened); + + QString urlFeed2("https://krita.org/en/another_feed/"); + + FakeReplyData replyFeed2; + replyFeed2.url = QUrl(urlFeed2); + replyFeed2.statusCode = 200; + replyFeed2.requestMethod = QNetworkAccessManager::GetOperation; + replyFeed2.contentType = "application/atom+xml"; + replyFeed2.responseBody = rssFile2.readAll(); + + + // test with 1 feed + QList listTest1 = { replyFeed1 }; + QTest::addRow("1 feed") + << listTest1 + << urlFeed1 + << 0; + + // test with 2 feeds + QList listTest2 = { replyFeed1, replyFeed2 }; + QTest::addRow("2 feeds") + << listTest2 + << urlFeed1 + << 10; +} + +QTEST_MAIN(KisMultiFeedRssModelTest); + diff --git a/libs/ui/tests/KisMultiFeedRssModelTest.h b/libs/ui/tests/KisMultiFeedRssModelTest.h new file mode 100644 index 0000000000..d0e024a5d3 --- /dev/null +++ b/libs/ui/tests/KisMultiFeedRssModelTest.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISMULTIFEEDRSSMODELTEST_H +#define KISMULTIFEEDRSSMODELTEST_H + +#include + +class KisMultiFeedRssModelTest : public QObject +{ + Q_OBJECT +public: + explicit KisMultiFeedRssModelTest(QObject *parent = nullptr); + +private Q_SLOTS: + void testAddFeed(); + void testAddFeed_data(); + + void testRemoveFeed(); + void testRemoveFeed_data(); +}; + +#endif // KISMULTIFEEDRSSMODELTEST_H diff --git a/libs/ui/tests/KisRssReaderTest.cpp b/libs/ui/tests/KisRssReaderTest.cpp new file mode 100644 index 0000000000..b632333bc5 --- /dev/null +++ b/libs/ui/tests/KisRssReaderTest.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisRssReaderTest.h" +#include +#include +#include +#include + +KisRssReaderTest::KisRssReaderTest(QObject *parent) : QObject(parent) +{ + +} + +void KisRssReaderTest::testParseData() +{ + KisRssReader reader; + QFile rssFile(TestUtil::fetchDataFileLazy("rss_feeds/feed.xml")); + + RssItemList itemList = reader.parse(rssFile); + + QCOMPARE(itemList.count(), 10); +} + +QTEST_MAIN(KisRssReaderTest); diff --git a/libs/ui/tests/KisRssReaderTest.h b/libs/ui/tests/KisRssReaderTest.h new file mode 100644 index 0000000000..7c43412271 --- /dev/null +++ b/libs/ui/tests/KisRssReaderTest.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISRSSREADERTEST_H +#define KISRSSREADERTEST_H + +#include +#include + +class KisRssReaderTest : public QObject +{ + Q_OBJECT +public: + explicit KisRssReaderTest(QObject *parent = nullptr); + +private Q_SLOTS: + void testParseData(); +}; + +#endif // KISRSSREADERTEST_H diff --git a/libs/ui/tests/MockMultiFeedRssModel.cpp b/libs/ui/tests/MockMultiFeedRssModel.cpp new file mode 100644 index 0000000000..8b3761e3cd --- /dev/null +++ b/libs/ui/tests/MockMultiFeedRssModel.cpp @@ -0,0 +1,28 @@ +#include "MockMultiFeedRssModel.h" + +#include +#include +#include +#include + +MockMultiFeedRssModel::MockMultiFeedRssModel(QObject *parent) + : MultiFeedRssModel(parent) +{ + +} + +void MockMultiFeedRssModel::addFeed(const QString &feed) +{ + Q_UNUSED(feed); + + emit feedDataChanged(); +} + +void MockMultiFeedRssModel::loadFeedData(const RssItemList& feed) +{ + m_aggregatedFeed = feed; + sortAggregatedFeed(); + setArticleCount(m_aggregatedFeed.size()); + beginResetModel(); + endResetModel(); +} diff --git a/libs/ui/tests/MockMultiFeedRssModel.h b/libs/ui/tests/MockMultiFeedRssModel.h new file mode 100644 index 0000000000..c3416fde8c --- /dev/null +++ b/libs/ui/tests/MockMultiFeedRssModel.h @@ -0,0 +1,25 @@ +#ifndef MOCKMULTIFEEDRSSMODEL_H +#define MOCKMULTIFEEDRSSMODEL_H + +#include + +#include +#include "kritaui_export.h" + +class KRITAUI_EXPORT MockMultiFeedRssModel : public MultiFeedRssModel +{ + Q_OBJECT + +public: + explicit MockMultiFeedRssModel(QObject *parent = 0); + + void addFeed(const QString& feed); + + /** + * @brief to be called in the setup phase of the unittest, before call to addFeed + * @param rssFile + */ + void loadFeedData(const RssItemList& feed); +}; + +#endif // MOCKMULTIFEEDRSSMODEL_H diff --git a/libs/ui/tests/MockNetworkAccessManager.cpp b/libs/ui/tests/MockNetworkAccessManager.cpp new file mode 100644 index 0000000000..ead092efea --- /dev/null +++ b/libs/ui/tests/MockNetworkAccessManager.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "MockNetworkAccessManager.h" + +#include +#include +#include + + +FakeNetworkReply::FakeNetworkReply(const QNetworkRequest& originalRequest, FakeReplyData &replyData) + : QNetworkReply() +{ + setRequest(originalRequest); + + setUrl(replyData.url); + setOperation(replyData.requestMethod); + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, replyData.statusCode); + setHeader(QNetworkRequest::ContentTypeHeader, replyData.contentType); + setHeader(QNetworkRequest::ContentLengthHeader, replyData.responseBody.size()); + + m_data.setData(replyData.responseBody); + + m_data.open(QIODevice::ReadOnly); + + open(QIODevice::ReadOnly); + setFinished(true); + QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); +} + +void FakeNetworkReply::abort() +{ + return; +} + +bool FakeNetworkReply::atEnd() const +{ + return m_data.atEnd(); +} + +qint64 FakeNetworkReply::bytesAvailable() const +{ + return m_data.bytesAvailable(); +} + +bool FakeNetworkReply::canReadLine() const +{ + return m_data.canReadLine(); +} + +void FakeNetworkReply::close() +{ + return m_data.close(); +} + +qint64 FakeNetworkReply::size() const +{ + return m_data.size(); +} + +qint64 FakeNetworkReply::pos() const +{ + return m_data.pos(); +} + +qint64 FakeNetworkReply::readData(char *data, qint64 maxLen) +{ + return m_data.read(data, maxLen); +} + +qint64 FakeNetworkReply::writeData(const char *data, qint64 maxLen) +{ + return m_data.write(data, maxLen); +} + +MockNetworkAccessManager::MockNetworkAccessManager(QObject *parent) + : KisNetworkAccessManager(parent) +{ +} + +void MockNetworkAccessManager::setReplyData(FakeReplyData &replyData) +{ + m_replyData = replyData; +} + +QNetworkReply *MockNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) +{ + Q_UNUSED(op); + Q_UNUSED(outgoingData); + + return new FakeNetworkReply(request, m_replyData); +} diff --git a/libs/ui/tests/MockNetworkAccessManager.h b/libs/ui/tests/MockNetworkAccessManager.h new file mode 100644 index 0000000000..5a0b94e14c --- /dev/null +++ b/libs/ui/tests/MockNetworkAccessManager.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 MOCKNETWORKACCESSMANAGER_H +#define MOCKNETWORKACCESSMANAGER_H + +#include +#include +#include +#include + +#include + +#include "kritaui_export.h" + + +struct FakeReplyData { + QUrl url; + QNetworkAccessManager::Operation requestMethod; + int statusCode; + QString contentType; + QByteArray responseBody; +}; +Q_DECLARE_METATYPE(FakeReplyData); + +class FakeNetworkReply : public QNetworkReply +{ + Q_OBJECT + +public: + explicit FakeNetworkReply(const QNetworkRequest &originalRequest, FakeReplyData& replyData); + + void abort() override; + bool atEnd() const override; + qint64 bytesAvailable() const override; + bool canReadLine() const override; + void close() override; + qint64 size() const override; + qint64 pos() const override; + +protected: + qint64 readData(char *data, qint64 maxLen) override; + qint64 writeData(const char *data, qint64 maxLen) override; + +private: + QBuffer m_data; +}; + +class KRITAUI_EXPORT MockNetworkAccessManager : public KisNetworkAccessManager +{ + Q_OBJECT + +public: + explicit MockNetworkAccessManager(QObject *parent = 0); + + void setReplyData(FakeReplyData& replyData); + + QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData); + +private: + FakeReplyData m_replyData; +}; + +#endif // MOCKNETWORKACCESSMANAGER_H diff --git a/libs/ui/tests/data/AppImageUpdateDummy b/libs/ui/tests/data/AppImageUpdateDummy new file mode 100755 index 0000000000..215a0289d7 --- /dev/null +++ b/libs/ui/tests/data/AppImageUpdateDummy @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +import sys +import os +import argparse +import os.path + +EXIT_RUNTIME_ERROR = 255 +EXIT_OK = 0 +EXIT_ERROR = 1 + +EXIT_CHECK_UPTODATE = 0 +EXIT_CHECK_ERROR = 2 +EXIT_CHECK_UPDATE_AVAIL = 1 + +EXIT_INVALID_USAGE = 100 + +# files that govern the output of this dummy +states = { + "check_uptodate": EXIT_CHECK_UPTODATE, + "check_update_avail": EXIT_CHECK_UPDATE_AVAIL, + "check_error": EXIT_CHECK_ERROR, + "check_updinfo_empty": EXIT_RUNTIME_ERROR, + "update_ok": EXIT_OK, + "update_error": EXIT_ERROR, + "runtime_error": EXIT_RUNTIME_ERROR +} + +parser = argparse.ArgumentParser() +parser.add_argument('--check-for-update', dest='check_for_update', action='store_true', + help='only check if updates are available') +parser.add_argument('appimage_path', metavar='APPIMAGE_PATH', type=str, help='path to the appimage') + +args = parser.parse_args() + + +if "APPIMAGEUPDATE_DUMMY_STATE" not in os.environ.keys(): + sys.exit(EXIT_INVALID_USAGE) + +state_string = str(os.environ["APPIMAGEUPDATE_DUMMY_STATE"]) + +# if we want to test the update, return update available for --check-for-update +if (args.check_for_update): + if state_string in [ 'update_ok', 'update_error', 'runtime_error' ]: + sys.exit(EXIT_CHECK_UPDATE_AVAIL) + +# else lookup the state in the dict +if (state_string in states.keys()): + state = states[state_string] + + if state_string in ['update_error', 'runtime_error']: + print("DUMMY: an error occured") + + sys.exit(states[state_string]) + +else: + sys.exit(EXIT_INVALID_USAGE) diff --git a/libs/ui/tests/data/rss_feeds/feed.xml b/libs/ui/tests/data/rss_feeds/feed.xml new file mode 100644 index 0000000000..10d1e8f269 --- /dev/null +++ b/libs/ui/tests/data/rss_feeds/feed.xml @@ -0,0 +1,661 @@ + + + + Krita + + https://krita.org + Digital Painting. Creative Freedom. + Fri, 04 Oct 2019 13:43:35 +0000 + en-US + + hourly + + 1 + https://wordpress.org/?v=5.2.3 + + Krita 4.2.7 Released + https://krita.org/en/item/krita-4-2-7-released/ + Thu, 03 Oct 2019 08:42:40 +0000 + + + + https://krita.org/?p=9790 + + Today, we’re releasing the sixth bug fix release of Krita 4.2. As discussed in our development update, we intend to release a few more monthly 4.2 bug fix releases before releasing Krita 4.3. There are a lot of bug fixes!

+

And to celebrate the release, we have a new video by Ramon Miranda which comes with a very nice present: a free new bundle of six sketching brush presets!

+

+

Bugs Fixed in 4.2.7

+
    +
  • Improve the layout and functionality of the color selector dialog and make it perform much better. (BUG:381529). Patches by Mathias Wein.
  • +
  • Do not crash when trying to merge an invisible group layer (BUG:411124)
  • +
  • Make it possible to save group layers to file layers even if they are empty (BUG:411101)
  • +
  • Make the initial location of the OCIO profile selector sensible
  • +
  • Fix possible crashes when a broken file ends up in the Recent Documents List (BUG:411416)
  • +
  • Use locale-based formatting of numbers in the measure tool and other places. Patch by Karl Ove Lufthammer.
  • +
  • Make HTML markup in the Search Field tooltips work. Patch by Karl Ove Lufthammer.
  • +
  • Fix a crash when moving multiple vector shapes (BUG:409872)
  • +
  • Fix the sort order of images imported as frames if they are not numbered with prefix 0’s (BUG:375885)
  • +
  • Make it possible again to run the Python Scripting Debugger on Linux (BUG:410807) Patch by Rebecca Breu.
  • +
  • Cache ICC profiles when loading layers: this speeds up loading images with thousands of layers (BUG:411532)
  • +
  • Fix file layer and comics manager page updating on Windows (BUG:410409, BUG:389544)
  • +
  • Use LittleCMS’ copy alpha channel flag to speed up color transformations
  • +
  • Fix outline move mode (BUG:411057)
  • +
  • Fix a hang in the text shape if an UTF-8 Line Break character is used (BUG:410402)
  • +
  • Fix a random crash if there is not enough space in the swapfile location for AMD Ryzen 3500 CPU’s (BUG:411081)
  • +
  • Fix checking whether the swapfile location is actually writable on Windows (BUG:411129, BUG:411081)
  • +
  • Fix another random crash when painting (BUG:411280)
  • +
  • Fix artifacts when moving control points of a path shape (BUG:411334)
  • +
  • Fix a crash when cropping a particular image (BUG:411536)
  • +
  • Fix move action in the bezier selection tool (BUG:398294)
  • +
  • Fix artifacts in Gaussian Blur on transparent layers (BUG:411719)
  • +
  • Fix a crash when the Liquify Transform is started too quickly (BUG:411703)
  • +
  • Fix a bad memory leak in the jpeg converter (BUG:410864)
  • +
  • Fix a crash when loading a JPEG image with a broken color profile (BUG:410864)
  • +
  • Fix problems when zooming with a touchpad (BUG:410940)
  • +
  • Fix issues when using the calculation capabilities of the specific color selector’s spin boxes (BUG:409818). Patch by Jasper Hartog
  • +
  • Make sure all layers are shown in the animation timeline by default
  • +
  • Fix a crash when the colorize tool is active on closing Krita
  • +
  • Fix a crash when converting a colorspace with OCIO enabled (BUG:411045)
  • +
  • Fix the Strength parameter not being used in Rotation – Fuzzy Dab (BUG:376179)
  • +
  • Fix a crash when using the mouse wheel while an image is opening
  • +
  • Re-add error messages lost when refactoring the error messages for loading images
  • +
  • Do not crash if libjpeg encounters any kind of error (BUG:364350)
  • +
  • Fix presets with random offset of texture being marked dirty all the time (BUG:406427)
  • +
  • Fix curves changing randomly with sensors with Use Same Curve enabled (BUG:383909)
  • +
  • Add a simple progress bar when saving .kra files
  • +
  • Ensure that the temporary folder isn’t suggested as a save-location as this can result in lost work.
  • +
  • Make sure toolbars don’t get enabled after editing the toolbar buttons (BUG:402679)
  • +
  • Do not crash when loading a tiled TIFF file with planar color data. (BUG:407171)
  • +
  • Fix freezes when changing some brush properties or curves (BUG:410158)
  • +
  • Fix wrong borders in the Edge Detection and Height To Normal Map Filters (BUG:411922)
  • +
  • Fix outline of Group Layers in Move Tool and Transform Tool (BUG:392717)
  • +
  • Fix preview of shape layers in Transform Tool and Move Tool (BUG:392717)
  • +
  • Raise the maximum FPS limit to 300 fps from 100 fps
  • +
  • Do not allow clone layers from pass-through group layers (BUG:409949)
  • +
  • Fix the color of a selected shape being synchronized with the color selectors (BUG:381784)
  • +
  • Fix updating the current shape color when doing undo/redo (BUG:404975)
  • +
  • Fix the broken TestKisSwatchGroup test (BUG:410387) Patch by Krysztof Kurek.
  • +
  • Make the splash render pixel-perfect on fractionally scaled displays. Patch by Guo Yunhe.
  • +
  • Fix a crash in Feather Selection, Wavelets, Blur and Edge Detection (BUG:412057)
  • +
  • Include reference images in the screen color picker (BUG:411816) Patch by Matthias Wein.
  • +
  • Clean up the SVG files used for icons and license the SVG files properly. Patch by Raghavendra Kamath.
  • +
  • Fix updating the assistants when moving the handles. Patch by Matthias Wein.
  • +
  • Fix a bad memory corruption error color handling. Patch by Matthias Wein.
  • +
+

Download

+

Windows

+ + +

Linux

+ +

(If, for some reason, Firefox thinks it needs to load this as text: to download, right-click on the link.)

+

OSX

+ +

Note: the gmic-qt is not available on OSX.

+

Source code

+

Note: Linux distributions building Krita against Qt 5.13 have to be aware that a regression in Qt crashes Krita on exit. There’s a workaround for that, but that was not ready for this release.

+ +

md5sum

+

For all downloads:

+ +

Support Krita

+

Krita is a free and open source project. Please consider supporting the project with donations or by buying training videos or the artbook! With your support, we can keep the core team working on Krita full-time.

+

 

+]]>
+
+ + September Development Update + https://krita.org/en/item/september-development-update/ + Mon, 30 Sep 2019 13:50:22 +0000 + + + + https://krita.org/?p=9779 + + We’re about to do another bug-fix release of Krita: that’ll be 4.2.7. We’re seeing that the number of new bug reports is dropping a bit, week over week: even though most bug reports can be closed instantly as duplicates or not bugs at all, that’s probably a sign that our stable releases are getting more and more stable. (See here to learn how to make good bug reports.) Though the total number of open bugs remains high, we worked really hard all September to make Krita better:

+

+

We also managed make 538 changes to the code in September with 23 developers — and that excludes translations, since those aren’t in our code repository.

+

We also went back to the Coverity Static Code analyzer and started analyzing Krita again. That was good for at least a hundred potential bug fixes, and it’s something that’s ongoing. We hadn’t done that for quite some time! There is still plenty to do, but the average defect density for projects the size of Krita is 0.7, so we’re not that bad.

+

+

Of course, focus on fixing bugs means that there’s less time for cool new features, or extending existing features. We did merge Sharaf Zaman’s Android port, though, which means that pretty soon we should have signed nightly Android APK’s!

+

Today during our weekly contributors meeting (notes are here), we decided we wanted to continue and do another couple of montly bug fix releases in the 4.2 series. In what’s currently our master branch, there are quite a few nice new features we’d like to share, though, and we’ll start making alpha builds of master in October, and invite everyone to help test what will become Krita 4.3.0.

+

We had originally wanted to release 4.3.0 in October, and have a fundraiser to celebrate the release. Part of the reason we’re postponing 4.3.0 is that we’d like to continue for a while with the stable 4.2 releases. Another reason is that we want 4.3.0 to have a completely reworked system for loading, handling and saving things like brush presets. We’ve been working on that for a long time now, and things are coming together, but there’s still a lot that needs to be done.

+]]>
+
+ + Interview with Samantha Skoros + https://krita.org/en/item/interview-with-samantha-skoros/ + Mon, 30 Sep 2019 07:22:39 +0000 + + + + https://krita.org/?p=9758 + + +

Could you tell us something about yourself?

+

Oh I’m terrible at talking about myself. I guess the easiest thing would be that I’m 26 years old and a mother. I identify as genderfluid and pansexual, I freaking love Dungeons and Dragons, and I also am a super avid knitter/fiber artist. I mainly just love doing things with my hands and gravitate to any hobby or craft that lets me do that.

+

Do you paint professionally, as a hobby artist, or both?

+

A complete hobbyist. I actually only started drawing maybe a year ago, so I’m still learning a lot and growing. I’d love to do this professionally some day, but right now I just really enjoy drawing for my own self.

+

What genre(s) do you work in?

+

I’m so new to this I don’t even know if I know how to answer this. I mean I guess I mostly work in portraits? I’m trying to get better at drawing buildings and scenes. I’d love to get into comics at some point but my skill level just isn’t quite there yet.

+

+

Whose work inspires you most — who are your role models as an artist?

+

I love seeing all sorts of art on twitter, I follow a lot of digital and traditional artists and a lot of them draw Dungeons and Dragons characters and I just love seeing people’s imaginations come to life in that way. I also love Jen Bartel and This Is Angle (This Is Angle writes a comic that I absolutely love the art style of, The Devil Is A Handsome Man) But both of them are just amazing artists and if I could draw half as well as them I would feel accomplished.

+

How and when did you get to try digital painting for the first time?

+

About a year ago I decided I really wanted to get into drawing. It was always something I wanted to do but just never took that leap. But the time I got there, I realized doing traditional art would be hard simply because we don’t have a lot of space in our home. The house is filled with yarn, fabric, leather, pets, and kid toys. There just wasn’t space for another hobby so I realized digital art would be the only way for me to do this thing. So my partner found me a refurbished Wacom pen tablet and I just fell in love with it.

+

+

What makes you choose digital over traditional painting?

+

The biggest thing for me is the space it saves. But also, boy, do I absolutely love the undo function. I make a LOT of mistakes and so having the ability to just ctrl+z a bad stroke is phenomenal. I also love the freedom it gives, you can do so much with it that I feel like you just can’t accomplish with traditional. I guess another thing too would be how easy it is to share, I can just upload the finished work to wherever and don’t have to worry about trying to scan traditional art into the computer in order to show people.

+

+

How did you find out about Krita?

+

So when I first got my pen tablet, I was looking at a few programs. I knew I wasn’t going to mess with anything expensive like PS, but I still wanted a good program. Krita was actually the first thing that I found and it just worked perfectly for everything I needed and it was super user friendly. I had people recommend other programs but honestly I just fell in love with Krita and didn’t even bother looking at anything else.

+

+

What was your first impression?

+

Sheer joy. It was the first thing I found and it was just perfect, everything I wanted in a drawing program.

+

What do you love about Krita?

+

I love the quality of it, It’s just as good as the pricier programs and it’s free. It’s also got an amazing community of people around it.

+

What do you think needs improvement in Krita? Is there anything that really annoys you?

+

I think the biggest issue I run into is just wanting to undo more than the allotted undo’s. Sometimes I just zone out and do a bunch of strokes and then realize I’m on the wrong layer and I can’t undo all of my mistakes. But I suppose that’s more of a user error.

+

What sets Krita apart from the other tools that you use?

+

It’s layout is very user friendly and intuitive. I never have to look long for something, it’s always right where I expected it would be. So I didn’t have to waste a bunch of time learning some complicated program, it’s just so well designed and supported that even if I couldn’t figure something out, there’s a great community that is prepared to help.

+

If you had to pick one favourite of all your work done in Krita so far, what would it be, and why?

+

+

Oh that’s a hard one. I don’t know that I have a favorite one, per say, but I do love all of the demons I draw. I think I just like drawing horns on people and blacking out their eyes. My body of work though is small so it’s hard to pick a favorite when my style keeps changing and evolving.

+

What techniques and brushes did you use in it?

+

I like using the sketch tools and then going over the sketch with the basic paint brush. I also have some of David Revoy’s brushes and I love using them for fun backgrounds and effects. But honestly I mainly use the basic brushes, they can accomplish so much.

+

Where can people see more of your work?

+

I’m on twitter: @moderbjorn_
+and I am also on Instagram: @moderbjorn
+I also occasionally draw on Twitch: ModerBjorn

+

Anything else you’d like to share?

+

I think I’d like to just say that it’s never too late for you to do the art thing. You don’t have to be one of those people that drew constantly in high school to be someone who’s good at art later in life. Even if it isn’t drawing, maybe it’s writing or wood carving, maybe you really want to try sewing. I’m telling you to do it, it’s so rewarding and you get to watch yourself get better with everything that you do. Don’t let anyone stop you from doing the thing that you want to do, especially yourself. I’m a stay at home mom with no time and sometimes no energy to do it. But just take a breath and understand that it’s okay to go at your own pace, you don’t owe anyone anything and you’re doing this for you. Yeah, I think that’s it. Just do the thing and you’ll be so proud of yourself, I believe in you.

+]]>
+
+ + Let’s Test Krita 4.2.7 Beta! + https://krita.org/en/item/lets-test-krita-4-2-7-beta/ + Wed, 25 Sep 2019 10:52:21 +0000 + + + + https://krita.org/?p=9751 + + We’re almost ready for a new release of Krita, with lots of bug fixes! So, please help with testing. As with 4.2.6, there’s a link on the welcome screen to a survey. Filling out the survey helps us figure out regressions and other problems. You can safely use any beta build on any operating system next to your production version of Krita. Settings and resources will be shared, but you won’t overwrite you existing installation by using one of the portable beta downloads.

+

In addition to reported bugs, this release fixes a lot of issues found by the Coverity Static Code checker.

+

We also have a bunch of great patches by new Krita hackers: Karl Ove Lufthammer, Rebecca Breu, Matthias Wein, Jasper Hartog, Krysztof Kurek and Guo Yunhe. Yay!

+

If all is well, we will release 4.2.7 in a week.

+

Bugs Fixed in 4.2.7

+
    +
  • Improve the layout and functionality of the color selector dialog and make it perform much better. (BUG:381529). Patches by Mathias Wein.
  • +
  • Do not crash when trying to merge an invisible group layer (BUG:411124)
  • +
  • Make it possible to save group layers to file layers even if they are empty (BUG:411101)
  • +
  • Make the initial location of the OCIO profile selector sensible
  • +
  • Fix possible crashes when a broken file ends up in the Recent Documents List (BUG:411416)
  • +
  • Use locale-based formatting of numbers in the measure tool and other places. Patch by Karl Ove Lufthammer.
  • +
  • Make HTML markup in the Search Field tooltips work. Patch by Karl Ove Lufthammer.
  • +
  • Fix a crash when moving multiple vector shapes (BUG:409872)
  • +
  • Fix the sort order of images imported as frames if they are not numbered with prefix 0’s (BUG:375885)
  • +
  • Make it possible again to run the Python Scripting Debugger on Linux (BUG:410807) Patch by Rebecca Breu.
  • +
  • Cache ICC profiles when loading layers: this speeds up loading images with thousands of layers (BUG:411532)
  • +
  • Fix file layer and comics manager page updating on Windows (BUG:410409, BUG:389544)
  • +
  • Use LittleCMS’ copy alpha channel flag to speed up color transformations
  • +
  • Fix outline move mode (BUG:411057)
  • +
  • Fix a hang in the text shape if an UTF-8 Line Break character is used (BUG:410402)
  • +
  • Fix a random crash if there is not enough space in the swapfile location for AMD Ryzen 3500 CPU’s (BUG:411081)
  • +
  • Fix checking whether the swapfile location is actually writable on Windows (BUG:411129, BUG:411081)
  • +
  • Fix another random crash when painting (BUG:411280)
  • +
  • Fix artifacts when moving control points of a path shape (BUG:411334)
  • +
  • Fix a crash when cropping a particular image (BUG:411536)
  • +
  • Fix move action in the bezier selection tool (BUG:398294)
  • +
  • Fix artifacts in Gaussian Blur on transparent layers (BUG:411719)
  • +
  • Fix a crash when the Liquify Transform is started too quickly (BUG:411703)
  • +
  • Fix a bad memory leak in the jpeg converter (BUG:410864)
  • +
  • Fix a crash when loading a JPEG image with a broken color profile (BUG:410864)
  • +
  • Fix problems when zooming with a touchpad (BUG:410940)
  • +
  • Fix issues when using the calculation capabilities of the specific color selector’s spin boxes (BUG:409818). Patch by Jasper Hartog
  • +
  • Make sure all layers are shown in the animation timeline by default
  • +
  • Fix a crash when the colorize tool is active on closing Krita
  • +
  • Fix a crash when converting a colorspace with OCIO enabled (BUG:411045)
  • +
  • Fix the Strength parameter not being used in Rotation – Fuzzy Dab (BUG:376179)
  • +
  • Fix a crash when using the mouse wheel while an image is opening
  • +
  • Re-add error messages lost when refactoring the error messages for loading images
  • +
  • Do not crash if libjpeg encounters any kind of error (BUG:364350)
  • +
  • Fix presets with random offset of texture being marked dirty all the time (BUG:406427)
  • +
  • Fix curves changing randomly with sensors with Use Same Curve enabled (BUG:383909)
  • +
  • Add a simple progress bar when saving .kra files
  • +
  • Ensure that the temporary folder isn’t suggested as a save-location as this can result in lost work.
  • +
  • Make sure toolbars don’t get enabled after editing the toolbar buttons (BUG:402679)
  • +
  • Do not crash when loading a tiled TIFF file with planar color data. (BUG:407171)
  • +
  • Fix freezes when changing some brush properties or curves (BUG:410158)
  • +
  • Fix wrong borders in the Edge Detection and Height To Normal Map Filters (BUG:411922)
  • +
  • Fix outline of Group Layers in Move Tool and Transform Tool (BUG:392717)
  • +
  • Fix preview of shape layers in Transform Tool and Move Tool (BUG:392717)
  • +
  • Raise the maximum FPS limit to 300 fps from 100 fps
  • +
  • Do not allow clone layers from pass-through group layers (BUG:409949)
  • +
  • Fix the color of a selected shape being synchronized with the color selectors (BUG:381784)
  • +
  • Fix updating the current shape color when doing undo/redo (BUG:404975)
  • +
  • Fix the broken TestKisSwatchGroup test (BUG:410387) Patch by Krysztof Kurek.
  • +
  • Make the splash render pixel-perfect on fractionally scaled displays. Patch by Guo Yunhe.
  • +
  • Fix a crash in Feather Selection, Wavelets, Blur and Edge Detection (BUG:412057)
  • +
  • Include reference images in the screen color picker (BUG:411816) Patch by Matthias Wein.
  • +
  • Clean up the SVG files used for icons and license the SVG files properly. Patch by Raghavendra Kamath.
  • +
  • Fix updating the assistants when moving the handles. Patch by Matthias Wein.
  • +
  • Fix a bad memory corruption error color handling. Patch by Matthias Wein.
  • +
+

Download

+

Windows

+

For the beta, you should only only use the portable zip files. Just open the zip file in Explorer and drag the folder somewhere convenient, then double-click on the krita icon in the folder. This will not impact an installed version of Krita, though it will share your settings and custom resources with your regular installed version of Krita. For reporting crashes, also get the debug symbols folder.

+ + +

Linux

+ +

(If, for some reason, Firefox thinks it needs to load this as text: to download, right-click on the link.)

+

OSX

+ +

Note: the gmic-qt is not available on OSX.

+

Source code

+ +

md5sum

+

For all downloads:

+ +

Support Krita

+

Krita is a free and open source project. Please consider supporting the project with donations or by buying training videos or the artbook! With your support, we can keep the core team working on Krita full-time.

+

 

+]]>
+
+ + Interview with Julius Grels + https://krita.org/en/item/interview-with-julius-grels/ + Mon, 16 Sep 2019 08:00:25 +0000 + + + + https://krita.org/?p=9727 + + +

Could you tell us something about yourself?

+

Sure. I’m Julius Grels, and “I like to call myself an artist whenever I’m wasted enough”. In all seriousness though, I think I’d describe myself more as a self-taught caricaturist or illustrator. I usually like to take some existing premise from real life or history, e.g. painting a picture of a (famous) person, depicting wildlife, et cetera. I’m also very fond of making comics, music and video games whenever I have the time (if only?).

+

Do you paint professionally, as a hobby artist, or both?

+

I’m most definitely a hobbyist, since I haven’t done any professional commissions (apart from some miniscule design work in the past), and what I do for living right now isn’t even remotely connected to art! That said, I’d most certainly would love to work as an illustrator, comic artist, or anything alike! My biggest wish would be to illustrate (and/or write) a children’s book someday.

+

+

What genre(s) do you work in?

+

I’m most comfortable when doing caricatures and comics; in the latter I can also infuse my story-telling abilities, the little there are. I prefer to illustrate living things; people, animals and nature itself. I’m not exactly keen on drawing in-animate objects, though I’m learning to force myself out of this comfort zone. I like to keep things simple and clean, or at least I try my best not to get lost in time-consuming detailing. Guess that’s one argument I can use as to why I prefer using simple black background on most of my works…

+

Whose work inspires you most — who are your role models as an artist?

+

I mostly draw (clever, eh?) influence and inspiration from animation, comics and video game art. I’m hesitant to drop any names because I don’t really have role models per se, and I tend to find pretty much any artist’s work interesting and inspiring. I guess as a Finn I could mention Tove Jansson and Mauri Kunnas. Jansson is of course famous for creating the Moomins, but she was also an accomplished artist and writer in her own right. Kunnas is a well-loved artist best known for his children’s books – pretty much every child in Finland has read at least one of his stories. In my opinion he’s also one of the greatest illustrators this country has to offer.

+

In addition to the aforementioned, I basically inhaled Franco-Belgian comics as a child; I loved reading Astérix, Lucky Luke, Iznogoud and the rest, so artists like Uderzo, Tabary and Tardi have also had a huge influence on me. Whenever possible, I browse through concept art of different video games. Video games are an interesting subject anyway because they not only combine art, design and technology but have to make all three work together even-handedly to create an enjoyable interactive experience.

+

 

+

How and when did you get to try digital painting for the first time?

+

If we don’t count MS Paint doodles, I think my first real try at digital painting was at elementary school somewhere around the late 90’s where we were introduced to Paint Shop Pro as a part of some “build your own website” -course. We were only able to use mouse for drawing, which was extremely clunky and made me think the whole idea of drawing and painting with a computer was just insane; I’d rather stick to my pencils and brushes, thank you very much. It wasn’t until later when I realised there are equipment specifically made for digital artwork, and once I got my hands on a Wacom tablet, I was sold.

+

What makes you choose digital over traditional painting?

+

Nothing? I mean, they are completely different working methods, and I still paint traditionally. Nevertheless, I’ve started to slowly leer towards digital painting, since it’s much easier to control your work and you can experiment more without the fear of ruining something irreversibly (especially when it comes to inking comics and other drawings). While it’s arguable whether digital painting is more cost-efficient than traditional methods in the long run, I think it’s at least less painful to start working with; you only need to set up your computer and programs ready, while with traditional painting you need to take out easel, canvas, gesso, colours, brushes, pencils? you get the idea. Furthermore, in my case where I don’t have a separate studio I have to find and clear a space in my apartment to set all that stuff up. Hassle, hassle!

+

+

How did you find out about Krita?

+

At one point I started to search for open source alternatives for the myriad number of programs I was using, and Krita was a recommendation somewhere to replace Photoshop, with high ratings from users.

+

What was your first impression?

+

I guess I’m still languishing in my first impression, because I haven’t been able to use Krita as much as I have wanted. In any case, my very first impression upon opening the program was a relieved “this looks familiar” sigh, and it was incredibly easy to start using Krita from there on.

+

What do you love about Krita?

+

Like I mentioned above, I find the interface very easy to use. I also love the fact the community is so alive, and you can find answers to just about any dilemma. All in all, Krita is a magnificent tool for making 2d artwork.

+

+

What do you think needs improvement in Krita? Is there anything that really annoys you?

+

Majority of my problems with Krita are due to the fact I’ve yet to learn most of its nuances, so I can’t really say about improvements that much. Krita seems to be quite a memory-hog, which can cause lot of lag and freezing especially when working with bigger canvases. That, and I’m not too happy with the text tool/editor either and prefer not to use it at all. It’s the one thing in Krita that’s needlessly complicated in my opinion.

+

What sets Krita apart from the other tools that you use?

+

Krita is the only digital painting software I use. Being open source is probably what makes it stand out the most. I use other open source programs as well, e.g. Blender, OpenToonz and Aseprite, but they obviously aren’t that much similar to Krita?

+

If you had to pick one favourite of all your work done in Krita so far, what would it be, and why?

+

+

Not much to choose from, but nevertheless, I’d say Megantereon, which was my first serious Krita artwork that I actually managed to finish. The main reason why I like it so much is simply because I had no initial planning; I took my Wacom, opened Krita and started doodling “something tiger-like”. After an hour or so, I began to realise there might be more to it, and continued working. The first version had plain fur, simplistic ear and lifeless green eye. I published it on deviantart.com, but wasn’t happy with the result, and later on decided to tweak the cat a bit. The fur got more detailed with stripes and spots, and I completely overhauled both the ear and the eye. The final result is what you see here, and I quickly replaced my previous attempt with this better version. I also like Megantereon as it neatly represents my interests (wildlife, history) and my preferred style (stylized, semi-realistic).

+

What techniques and brushes did you use in it?

+

Every brush I used is a Krita default. I used Basic and Fill Circle for outlining and some detail, Bristle Texture and Square for texturing and Inkpens for smaller detailing. There might be some Smudge tool I used too, but I honestly can’t remember.
+I made a rough outlining on one layer against a black background and constructed the beast from bones to muscles to fur et cetera from there on. In the end I had laid out 23 layers with such genius descriptions as “skull”, “Layer 17”, “BLOOD SALIVA”, “hideTheBeard” and “washing”. In retrospect, I highly recommend people to use layers more scarcely if possible – they slow down the program, and the whole working process ends up confusing. At least name your layers better than I did!
+I have two versions of the final work; with and without Noise Effect, which I used to achieve a more horror-esque vibe. I send the noiseless version here so the details aren’t obstructed too much.

+

Where can people see more of your work?

+

At the moment, my most recent work will be published at https://www.deviantart.com/jgrels . Don’t hold your breath, though; I publish work at a snail’s pace. You can also follow me on Twitter @JuliusGrels if you so desire, where I’m giving more information about my possible future projects, like a couple of webcomics I’ve planned to do.

+

Anything else you’d like to share?

+

Maybe just some general advice to any aspiring artist: never stop honing your skills, get out of your comfort zone, don’t fear experimenting, and most importantly; have confidence. Believe in yourself. Even if you don’t think you’re that great of an artist but love doing art, just keep working and publishing your work for everyone to see. You can doubt your talent, but never doubt your passion.

+

Finally, I want to send my thanks to the Krita Foundation and give my highest appreciation for the great work you’ve done. Thank you!

+]]>
+
+ + Krita 4.2.6 released + https://krita.org/en/item/krita-4-2-6-released/ + Tue, 10 Sep 2019 10:55:39 +0000 + + + + + https://krita.org/?p=9735 + + A bit later than expected, because of a regression found during beta testing, we’re releasing Krita 4.2.6. Over 120 people have participated in the beta test survey, so this is something we’ll repeat for the next release.

+

This release also contains an important workaround for users with an AMD Ryzen 5 3500 CPU. This CPU has a bug in its hardware random generator that caused crashes.

+

New features:

+
    +
  • Add new layer from visible to layer right-click context menu.
  • +
  • When running Krita for the first time on Windows, Angle is now the default renderer. Note that if you have an NVidia GPU and Krita’s window is transparent, you need to select Angle manually in Krita’s settings; if you have another GPU and you have problems with the canvas not updating, you might need to manually select OpenGL in the same window.
  • +
+

We want to especially thank Karl Ove Hufthammer for his extensive work on polishing the translatable string.

+

Bugs fixed

+
    +
  • Allow selection overlay to be reset to default. (BUG:410470)
  • +
  • Set date for bundle creation to use ISO-Date. (BUG:410490)
  • +
  • Fix freeze with 32bit float tiff by using our own tiff reader for the thumbnails. (BUG:408731)
  • +
  • Ensure filter mask button is disabled appropriately depending on whether the filter supports it. (BUG:410374)
  • +
  • Enable the small color selector if opengles is available as well (BUG:410602)
  • +
  • Fix mixed Zoom, Pan, Rotate on macOS (BUG:410698)
  • +
  • Ensure that checkboxes are shown in menus even when using the fusion theme
  • +
  • Isolate Layer Crash (BUG:408785)
  • +
  • Properly fix font resetting when all the text in the editor removed (BUG:409243)
  • +
  • Fix lags in Move Tool when using tablet device (BUG:410838)
  • +
  • Fix Shift and Alt modifiers in Outline Selection Tool (BUG:410532)
  • +
  • Ensure Convert group to Animated Layer shows text in the toolbar. (BUG:410500)
  • +
  • Allow ‘Add Clone Layer’ to Work on Multiple Layers (BUG:373338)
  • +
  • Fix saving animated transparency masks created through conversion (BUG:409895)
  • +
  • Partially fix the curve change despite ‘Share curve across all settings’ checked (BUG:383909)
  • +
  • Try harder to make sure that the swap location is writable
  • +
  • Properly handle timezones in bundles
  • +
  • Make sure all the settings dialogs pages are always shown in the same order
  • +
  • Make the settings dialog fit in low-res screens (BUG:410793)
  • +
  • Remove misleading ‘px’ suffix for ‘move amount’ shortcut setting
  • +
  • Make string for reasons for image export problems translatable (BUG:406973)
  • +
  • Fix crash when creating a bezier curve (BUG:410572)
  • +
  • Fix deadlocks in KoShapeManager (BUG:410909, BUG:410572)
  • +
  • Fix a deadlock when using broken Wacom drivers on Linux (BUG:410797)
  • +
  • Fix absolute brush rotation on rotated canvas (BUG:292726)
  • +
  • Fix deadlock when removing reference image (BUG:411212)
  • +
  • Fix a deadlock in handling of vector objects (BUG:411365)
  • +
  • Fix autosave saving only once (BUG:411631)
  • +
+

Download

+

Windows

+

Note for Windows users: if you encounter crashes, please follow these instructions to use the debug symbols so we can figure out where Krita crashes.

+ + +

Linux

+ +

(If, for some reason, Firefox thinks it needs to load this as text: to download, right-click on the link.)

+

OSX

+ +

Note: the gmic-qt is not available on OSX.

+

Source code

+ +

md5sum

+

For all downloads:

+ +

Key

+

The Linux appimage and the source .tar.gz and .tar.xz tarballs are signed. You can retrieve the public key over https here: 0x58b9596c722ea3bd.asc. The signatures are here (filenames ending in .sig).

+

Support Krita

+

Krita is a free and open source project. Please consider supporting the project with donations or by buying training videos or the artbook! With your support, we can keep the core team working on Krita full-time.

+]]>
+
+ + Interview with Wojtek Trybus + https://krita.org/en/item/interview-with-wojtek-trybus/ + Mon, 02 Sep 2019 08:00:37 +0000 + + + + https://krita.org/?p=9692 + +

+

Could you tell us something about yourself?

+

My name is Wojtek Trybus and I publish artworks as wojtryb. I’m in the last year of Computer Science studies in Poland. I’m also a pianist since 6, and happen to be a hobbyist and self-taught illustrator for something like 8 years now.

+

Do you paint professionally, as a hobby artist, or both?

+

Drawing was always something that, despite being quite hard to learn, was making me happy and fulfilled. I guess this counts as a hobby then. I don’t make any money from it right now and I still haven’t decided if I will be more of a programmer or a graphic designer in the nearest future.

+

+

What genre(s) do you work in?

+

My artistic journey started with some simple and minimalistic illustrations – by keeping them so simple, I never got discouraged, as most beginners do, jumping straight into complex artworks. As my knowledge and experience grew, it all became more and more complicated, though the need to keep this feeling of simplicity in my artworks still remains.

+

Whose work inspires you most — who are your role models as an artist?

+

Vlad Gerasimov, founder of Vladstudio, was my first great inspiration – my minimalistic illustrations originated from his way of seeing the world. I think it is still a bit visible in my artworks, even though our ways parted long time ago, as I switched to a much different approach. Currently, I guess I’m more into David Revoy – his open source ideology – and Nathan Fowkes http://www.nathanfowkesart.com/, whose work, especially for the “How to train your dragon” movie, is for me the absolute level cap that an artist can achieve. Julia Blattman is also worth mentioning, as her artworks full of imagination remind me of what I felt looking at those Vladstudio illustrations long time ago.

+

How and when did you get to try digital painting for the first time?

+

As a kid, as many other kids, I used to love computer games – when I grew up a bit, I noticed that it was not the games I liked that much, but the worlds they took me to. When in 2011 I found out how you can use GIMP and a digital tablet to create something similar, it all began.

+

+

What makes you choose digital over traditional painting?

+

Making a lot of mess was always something that bothered me. I still remember how I hated art classes in primary school with kids covering every single thing in the room with paint. In digital painting you can always elegantly save and quit with your hands clean. While I still really enjoy sketching with pencil and pen, I’m afraid I will have a tough job to convince myself to use real paint again (as I know traditional media can give you a lot).

+

How did you find out about Krita?

+

It was 2015, I guess, when I read about it on David Revoy’s blog for the first time – I suppose many artists switched from GIMP to Krita somewhere in that time because of his support to the program.

+

What was your first impression?

+

Insanely laggy 😀 It took me one year to accidentally switch something in settings, that suddenly made it work normally. Luckily, with each update, it’s less likely for someone to have such a problem again. After I made my first artwork in 2016, Krita happened to exceed GIMP in any way possible. I fell in love instantly.

+

+

What do you love about Krita?

+

I love how Krita makes it possible for everyone to learn how to draw, while not wanting anything in exchange. It kind of restores my faith in humanity every time I think about it. It’s even harder to believe that developers manage to constantly update and improve it, while their budget and human resources are hard to compare with those that other art programs have.

+

What do you think needs improvement in Krita? Is there anything that really annoys you?

+

I don’t think there are too many flaws right now in the program itself. All the bugs I noticed in 2016 have been fixed by now. It also seems really stable on Kubuntu – the crashes are happening less often than in the past. Developers did a gorgeous job again and I guess, and with this whole “squash the bugs” campaign they really meant it.

+

The thing that bothers me though, is how Krita is sometimes perceived across the internet – some people think it is still laggy or full of bugs – maybe it used to be that way, but in my opinion changed a lot in recent years. Some others may underappreciate it, as Photoshop is still the standard required in the industry.

+

I’m really grateful for each and every artist who already helps to promote the program on social media and art-related websites, but I believe it still deserves more – more recognition, professional users, more mind-blowing, “Made in Krita” artworks that would inspire beginners, finally maybe even more donations for further development.

+

I try to promote it the best I can and it even encourages me to practice and improve as an artist, but I still think we lack some PR – come on guys. Let’s show what we can do with Krita 🙂

+

What sets Krita apart from the other tools that you use?

+

Actually I don’t use any other drawing application as I simply don’t need one. I think that says a lot about it. Krita still allows me to constantly develop, and I’m sure that the only thing now that limits me is my current lack of skills. But I’m constantly working on that.

+

If you had to pick one favourite of all your work done in Krita so far, what would it be, and why?

+

+

One of my most recent illustrations is “festival preparation” – it indicates the direction I want to go with my art – to improve in art foundations, while keeping it fun and full of imagination. A lot of things I studied recently were used here, and it seems it all went particularly well.

+

What techniques and brushes did you use in it?

+

By coincidence, this artwork was actually a final test of brushes I use, that recently had some serious modifications to them. As I like things simple, they don’t mess that much with brush tips and textures as most of presets available on the internet do, but are quite tricky in terms of dynamics – they can randomize rotation and colors to differentiate the strokes. You can actually check them out, as I made them available to use freely some time ago: [Link to brushes]

+

Where can people see more of your work?

+

I use artstation as my more refined portfolio and facebook for loose updates. Some of my older artworks can still be found on deviantArt.

+

https://www.facebook.com/trybus.wojciech
+https://www.artstation.com/wojtryb
+https://www.deviantart.com/wojtryb

+

Anything else you’d like to share?

+

Soon I’ll be graduating from college and there are lots of changes and decisions ahead of me for sure – keep your fingers crossed in case I need them to still make progress at the current pace and create the art I love 🙂

+]]>
+
+ + Help Beta Test Krita 4.2.6! + https://krita.org/en/item/help-beta-test-krita-4-2-6/ + Sat, 31 Aug 2019 10:00:56 +0000 + + + + https://krita.org/?p=9667 + + This will be the first Krita release since the big sprint. We’re aiming to do monthly bugfix releases again from now on! But we also want to cut down on the regressions that come with rapid development so we’re making beta releases again. Please help the team out and check these beta releases for bugs and regressions. Right there in the welcome screen is a link to a survey where you can give your feedback:

+

The survey idea is new — we want to get your impressions of what we’re about to release. If it works, we’ll be refining the surveys.

+

What’s new in 4.2.6?

+

New features:

+
    +
  • Add new layer from visible to layer right-click context menu.
  • +
  • When running Krita for the first time on Windows, Angle is now the default renderer. Note that if you have an NVidia GPU and Krita’s window is transparent, you need to select Angle manually in Krita’s settings; if you have another GPU and you have problems with the canvas not updating, you might need to manually select OpenGL in the same window.
  • +
+

We want to especially thank Karl Ove Hufthammer for his extensive work on polishing the translatable string.

+

Bugs fixed

+
    +
  • Allow selection overlay to be reset to default. (BUG:410470)
  • +
  • Set date for bundle creation to use ISO-Date. (BUG:410490)
  • +
  • Fix freeze with 32bit float tiff by using our own tiff reader for the thumbnails. (BUG:408731)
  • +
  • Ensure filter mask button is disabled appropriately depending on whether the filter supports it. (BUG:410374)
  • +
  • Enable the small color selector if opengles is available as well (BUG:410602)
  • +
  • Fix mixed Zoom, Pan, Rotate on macOS (BUG:410698)
  • +
  • Ensure that checkboxes are shown in menus even when using the fusion theme
  • +
  • Isolate Layer Crash (BUG:408785)
  • +
  • Properly fix font resetting when all the text in the editor removed (BUG:409243)
  • +
  • Fix lags in Move Tool when using tablet device (BUG:410838)
  • +
  • Fix Shift and Alt modifiers in Outline Selection Tool (BUG:410532)
  • +
  • Ensure Convert group to Animated Layer shows text in the toolbar. (BUG:410500)
  • +
  • Allow ‘Add Clone Layer’ to Work on Multiple Layers (BUG:373338)
  • +
  • Fix saving animated transparency masks created through conversion (BUG:409895)
  • +
  • Fix curve change despite ‘Use same curve’ checked (BUG:383909)
  • +
  • Try harder to make sure that the swap location is writable
  • +
  • Properly handle timezones in bundles
  • +
  • Allow ‘Add Clone Layer’ to Work on Multiple Layers (BUG:373338)
  • +
  • Fix saving animated transparency masks created through conversion (BUG:409895)
  • +
  • Make sure all the settings dialogs pages are always shown in the same order
  • +
  • Make the settings dialog fit in low-res screens (BUG:410793)
  • +
  • Remove misleading ‘px’ suffix for ‘move amount’ shortcut setting
  • +
  • Make string for reasons for image export problems translatable (BUG:406973)
  • +
  • Fix crash when creating a bezier curve (BUG:410572)
  • +
  • Fix deadlocks in KoShapeManager (BUG:410909, BUG:410572)
  • +
  • Fix a deadlock when using broken Wacom drivers on Linux (BUG:410797)
  • +
  • Fix absolute brush rotation on rotated canvas (BUG:292726)
  • +
  • Fix deadlock when removing reference image (BUG:411212)
  • +
  • Fix a deadlock in handling of vector objects (BUG:411365)
  • +
+

Download

+

Windows

+

For the beta, only portable zip files are available. Just open the zip file in Explorer and drag the folder somewhere convenient, then double-click on the krita icon in the folder. This will not impact an installed version of Krita, though it will share your settings and custom resources with your regular installed version of Krita. For reporting crashes, also get the debug symbols folder.

+ + +

Linux

+ +

(If, for some reason, Firefox thinks it needs to load this as text: to download, right-click on the link.)

+

OSX

+ +

Note: the gmic-qt is not available on OSX.

+

Source code

+ +

md5sum

+

For all downloads:

+ +

Support Krita

+

Krita is a free and open source project. Please consider supporting the project with donations or by buying training videos or the artbook! With your support, we can keep the core team working on Krita full-time.

+

 

+]]>
+
+ + Mounamnamat Médias Teaches Animation using Krita + https://krita.org/en/item/mounamnamat-medias-teaches-animation-using-krita/ + Thu, 29 Aug 2019 09:20:27 +0000 + + + + https://krita.org/?p=9689 + + Amine Sossi Alaoui and Sonia Didier write to tell us about their experience teaching children 2D animation using Krita, with some very cool results:

+

We’re a Moroccan animation studio, created 6 months ago and based in Rabat, Morocco. Before that we worked in animation studios in France during 10 years. Our goal now is to develop animation industry in Morocco and Africa. It’s a long way to go, and for the moment, we’re just beginning with 2d animation. Krita is a great tool for that, and we’re very happy to use it, and to share the knowledge we have about it.

+

So, this summer, we wanted children to learn about 2D animation, so we created an one-week animation course for children from 8 to 14 years old. It was 2 hours per day during 5 days, for a group of 8 to 12 children. The goal was to create an one-minute animated shortfilm in 2D, from the writing of the story, storyboard, background, animation, colorisation and compositing.

+

For that we chose to use Krita (and Shotcut for the final compositing and sound). It’s great software, very complete and fun to work with. And as it’s free, we’re sure that the children could use it at home if they like, to make their own projects.

+

Finally, four groups of children have worked on Krita this summer, and the result is on Youtube for anyone to watch on our youtube channel. Here are the first three films; the fourth film is not yet on youtube. Should be shortly, when it’s finalized.

+

We hope you’ll all like it !

+

+

+

+

Now we hope that we’ll have others projects with Krita, and we’ll be happy
+to share them with you.

+]]>
+
+ + Interview with Chayse Goodall + https://krita.org/en/item/interview-with-chayse-goodall/ + Mon, 19 Aug 2019 08:00:17 +0000 + + + + https://krita.org/?p=9642 + + +

Could you tell us something about yourself?

+

Hi, my name is Chayse Goodall. I am 14 years old. I just draw for fun!

+

Do you paint professionally, as a hobby artist, or both?

+

I do animate and draw for a hobby. My artwork improves little by little, but I don’t think it’ll be good enough for a profession!

+

What genre(s) do you work in?

+

I don’t know what my style is, but people tell me that I have an anime style.

+

Whose work inspires you most — who are your role models as an artist?

+

I have many inspirers, two of which was my art teachers, and one of which was my math tutor. They both motivate me to keep going with my progress.

+

How and when did you get to try digital painting for the first time?

+

I tried digital art in 2015. I only had a phone at the time, so it was hard at first. It’s a million times better now, and I have a touch screen computer, so I’ve been trying different art programs!

+

What makes you choose digital over traditional painting?

+

There are more tools, and there is an undo button!

+

How did you find out about Krita?

+

I was looking up free art programs, and stumbled across this program.

+

What was your first impression?

+

My experience was great! It was easy to follow once you get the hang of it!

+

What do you love about Krita?

+

All the brushes and how easy it is to save.

+

What do you think needs improvement in Krita? Is there anything that really annoys you?

+

It is a bit confusing and it looks complex to newcomers.

+

What sets Krita apart from the other tools that you use?

+

It has amazing features and it doesn’t require me to “sign up” or pay for anything!

+

If you had to pick one favourite of all your work done in Krita so far, what would it be, and why?

+

I only made one picture. She doesn’t have a name, she was just a test character to see how good the program was. (It came out great!)

+

What techniques and brushes did you use in it?

+

I normally draw the sketch first in a dark red color. Then I draw the plain body in a light green. I sketch the clothes, hair, and accessories on in a neon color.

+

I just use the pen for coloring and shading.

+

Where can people see more of your work?

+

My instagram- panstables
+My youtube- Nubbins

+]]>
+
+
+
+ + \ No newline at end of file diff --git a/libs/ui/utils/KisAppimageUpdater.cpp b/libs/ui/utils/KisAppimageUpdater.cpp new file mode 100644 index 0000000000..721aa2ce88 --- /dev/null +++ b/libs/ui/utils/KisAppimageUpdater.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisAppimageUpdater.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +KisAppimageUpdater::KisAppimageUpdater() + : m_checkProcess(new QProcess(this)) + , m_updateProcess(new QProcess(this)) +{ + QString updaterPath; + +#if defined(KRITA_GIT_SHA1_STRING) + if (qEnvironmentVariableIsSet("KRITA_APPIMAGEUPDATER_USE_DUMMY")) { + updaterPath = QString("%1%2AppImageUpdateDummy") + .arg(QCoreApplication::applicationDirPath()) + .arg(QDir::separator()); + } else { + updaterPath = QString("%1%2AppImageUpdate") + .arg(QCoreApplication::applicationDirPath()) + .arg(QDir::separator()); + } +#else + updaterPath = QString("%1%2AppImageUpdate") + .arg(QCoreApplication::applicationDirPath()) + .arg(QDir::separator()); +#endif + + initialize(updaterPath); +} + +#if defined(KRITA_GIT_SHA1_STRING) +KisAppimageUpdater::KisAppimageUpdater(QString dummyUpdaterPath) + : m_checkProcess(new QProcess(this)) + , m_updateProcess(new QProcess(this)) +{ + initialize(dummyUpdaterPath); +} +#endif + +void KisAppimageUpdater::checkForUpdate() +{ + if (m_updaterInProgress) { + return; + } + + if (!m_updateCapability) { + return; + } + + // reset output for subsequent checks, is this needed? + m_checkOutput = QString(); + m_updateOutput = QString(); + m_updaterStatus.setUpdaterOutput(QString()); + + QStringList args = QStringList() << "--check-for-update" << m_appimagePath; + + m_checkProcess->start(m_updaterBinary, args); + m_updaterInProgress = true; +} + +bool KisAppimageUpdater::hasUpdateCapability() +{ + return m_updateCapability; +} + +void KisAppimageUpdater::doUpdate() +{ + QStringList args = QStringList() << m_appimagePath; + m_updateProcess->start(m_updaterBinary, args); +} + +void KisAppimageUpdater::slotUpdateCheckFinished(int result, QProcess::ExitStatus exitStatus) +{ + KisUsageLogger::log( + QString("KisAppimageUpdater: update check finished. Result: %1 Exit status: %2\npath: %3\noutput: %4") + .arg(result) + .arg(exitStatus) + .arg(m_appimagePath) + .arg(m_updateOutput) + ); + + UpdaterStatus::StatusID updateStatus; + + if (exitStatus == QProcess::CrashExit) { + updateStatus = UpdaterStatus::StatusID::CHECK_ERROR; + + } else { + switch (result) { + case 0: + updateStatus = UpdaterStatus::StatusID::UPTODATE; + break; + case 1: + updateStatus = UpdaterStatus::StatusID::UPDATE_AVAILABLE; + break; + case 2: + updateStatus = UpdaterStatus::StatusID::CHECK_ERROR; + break; + default: + // some errors have exit code of 255 (modified by system, when AppImageUpdate returns -1) + // one source of 255 is when the AppImage does not contain update information + updateStatus = UpdaterStatus::StatusID::CHECK_ERROR; + break; + } + } + + m_updaterInProgress = false; + + m_updaterStatus.setStatus(updateStatus); + m_updaterStatus.setUpdaterOutput(m_updateOutput); + + emit sigUpdateCheckStateChange(m_updaterStatus); +} + +void KisAppimageUpdater::slotUpdateCheckStarted() +{ + m_updaterStatus.setStatus(UpdaterStatus::StatusID::IN_PROGRESS); + emit sigUpdateCheckStateChange(m_updaterStatus); +} + +void KisAppimageUpdater::slotUpdateCheckErrorOccurred(QProcess::ProcessError error) +{ + KisUsageLogger::log( + QString("KisAppimageUpdater: error occurred during update check: %1\npath: %2\noutput: %3") + .arg(error) + .arg(m_appimagePath) + .arg(m_checkOutput) + ); + + m_updaterInProgress = false; + + m_updaterStatus.setStatus(UpdaterStatus::StatusID::CHECK_ERROR); + + emit sigUpdateCheckStateChange(m_updaterStatus); +} + +void KisAppimageUpdater::slotUpdateFinished(int result, QProcess::ExitStatus exitStatus) +{ + KisUsageLogger::log( + QString("KisAppimageUpdater: update finished. Result: %1\nExit status: %2\npath: %3\noutput: %4") + .arg(result) + .arg(exitStatus) + .arg(m_appimagePath) + .arg(m_updateOutput) + ); + + UpdaterStatus::StatusID updateStatus; + + if (exitStatus == QProcess::CrashExit) { + updateStatus = UpdaterStatus::StatusID::UPDATE_ERROR; + + } else { + switch (result) { + case 0: + updateStatus = UpdaterStatus::StatusID::RESTART_REQUIRED; + break; + default: + // some errors have exit code of 255 (modified by system, when AppImageUpdate returns -1) + updateStatus = UpdaterStatus::StatusID::UPDATE_ERROR; + break; + } + } + + m_updaterInProgress = false; + + m_updaterStatus.setStatus(updateStatus); + m_updaterStatus.setUpdaterOutput(m_updateOutput); + + emit sigUpdateCheckStateChange(m_updaterStatus); +} + +void KisAppimageUpdater::slotUpdateErrorOccurred(QProcess::ProcessError error) +{ + KisUsageLogger::log( + QString("KisAppimageUpdater: error occurred during update: %1\npath: %2\noutput: %3") + .arg(error) + .arg(m_appimagePath) + .arg(m_updateOutput) + ); + + m_updaterInProgress = false; + + m_updaterStatus.setStatus(UpdaterStatus::StatusID::UPDATE_ERROR); + m_updaterStatus.setUpdaterOutput(m_updateOutput); + + emit sigUpdateCheckStateChange(m_updaterStatus); +} + +void KisAppimageUpdater::slotAppendCheckOutput() +{ + m_checkOutput.append(m_checkProcess->readAllStandardOutput()); +} + +void KisAppimageUpdater::slotAppendUpdateOutput() +{ + m_updateOutput.append(m_updateProcess->readAllStandardOutput()); +} + +void KisAppimageUpdater::initialize(QString& updaterPath) +{ + m_appimagePath = qEnvironmentVariable("APPIMAGE"); + m_updaterBinary = updaterPath; + + m_updateCapability = findUpdaterBinary(); + + m_checkProcess->setProcessChannelMode(QProcess::MergedChannels); + m_updateProcess->setProcessChannelMode(QProcess::MergedChannels); + + connect(m_checkProcess.data(), SIGNAL(started()), this, SLOT(slotUpdateCheckStarted())); + connect(m_checkProcess.data(), SIGNAL(errorOccurred(QProcess::ProcessError)), + this, SLOT(slotUpdateCheckErrorOccurred(QProcess::ProcessError))); + connect(m_checkProcess.data(), SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotUpdateCheckFinished(int, QProcess::ExitStatus))); + connect(m_checkProcess.data(), SIGNAL(readyReadStandardOutput()), + this, SLOT(slotAppendCheckOutput())); + + + connect(m_updateProcess.data(), SIGNAL(errorOccurred(QProcess::ProcessError)), + this, SLOT(slotUpdateErrorOccurred(QProcess::ProcessError))); + connect(m_updateProcess.data(), SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotUpdateFinished(int, QProcess::ExitStatus))); + connect(m_updateProcess.data(), SIGNAL(readyReadStandardOutput()), + this, SLOT(slotAppendUpdateOutput())); +} + +bool KisAppimageUpdater::findUpdaterBinary() +{ + QFileInfo finfo = QFileInfo(m_updaterBinary); + if (finfo.isExecutable()) { + return true; + } else { + KisUsageLogger::log( + QString("KisAppimageUpdater: AppImageUpdate (%1) was not found within the Krita appimage, or is not executable") + .arg(m_updaterBinary) + ); + return false; + } +} diff --git a/libs/ui/utils/KisAppimageUpdater.h b/libs/ui/utils/KisAppimageUpdater.h new file mode 100644 index 0000000000..1c8947ca34 --- /dev/null +++ b/libs/ui/utils/KisAppimageUpdater.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISAPPIMAGEUPDATER_H +#define KISAPPIMAGEUPDATER_H + +#include +#include +#include + +#include + +#include "kritaui_export.h" + + +class QString; + +class KRITAUI_EXPORT KisAppimageUpdater : public KisUpdaterBase +{ + Q_OBJECT + +public: + KisAppimageUpdater(); +#ifdef KRITA_GIT_SHA1_STRING + explicit KisAppimageUpdater(QString dummyUpdaterPath); +#endif + + void checkForUpdate() override; + bool hasUpdateCapability() override; + void doUpdate() override; + +private Q_SLOTS: + void slotUpdateCheckFinished(int result, QProcess::ExitStatus exitStatus); + void slotUpdateCheckStarted(); + void slotUpdateCheckErrorOccurred(QProcess::ProcessError error); + + void slotUpdateFinished(int result, QProcess::ExitStatus exitStatus); + void slotUpdateErrorOccurred(QProcess::ProcessError error); + + void slotAppendCheckOutput(); + void slotAppendUpdateOutput(); + +private: + void initialize(QString& updaterPath); + + bool findUpdaterBinary(); + QString m_updaterBinary; + QString m_appimagePath; + bool m_updateCapability; + bool m_updaterInProgress {false}; + QString m_checkOutput; + QString m_updateOutput; + + QScopedPointer m_checkProcess; + QScopedPointer m_updateProcess; +}; + +#endif // KISAPPIMAGEUPDATER_H diff --git a/libs/ui/utils/KisManualUpdater.cpp b/libs/ui/utils/KisManualUpdater.cpp new file mode 100644 index 0000000000..9f8cd675fc --- /dev/null +++ b/libs/ui/utils/KisManualUpdater.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisManualUpdater.h" +#include +#include + +#include +#include +#include +#include + + +KisManualUpdater::KisManualUpdater() + : m_currentVersion(KritaVersionWrapper::versionString()) +{ + m_rssModel.reset(new MultiFeedRssModel()); +} + +KisManualUpdater::KisManualUpdater(MultiFeedRssModel* rssModel, QString ¤tVersion) + : m_currentVersion(currentVersion) +{ + m_rssModel.reset(rssModel); +} + +void KisManualUpdater::checkForUpdate() +{ +// m_rssModel->addFeed(QLatin1String("https://krita.org/en/feed/")); + m_rssModel->addFeed(QLatin1String("http://localhost:8000/feed.xml")); + connect(m_rssModel.data(), SIGNAL(feedDataChanged()), this, SLOT(rssDataChanged())); +} + +void KisManualUpdater::rssDataChanged() +{ + // grab the latest release post and URL for reference later + // if we need to update + QString availableVersion; + QString downloadLink; + + for (int i = 0; i < m_rssModel->rowCount(); i++) { + const QModelIndex &idx = m_rssModel->index(i); + + if (idx.isValid()) { + // only use official release announcements to get version number + if ( idx.data(KisRssReader::RssRoles::CategoryRole).toString() != "Official Release") { + continue; + } + + QString linkTitle = idx.data(KisRssReader::RssRoles::TitleRole).toString(); + + // regex to capture version number + QRegularExpression versionRegex("\\d\\.\\d\\.?\\d?\\.?\\d"); + QRegularExpressionMatch matched = versionRegex.match(linkTitle); + + // only take the top match for release version since that is the newest + if (matched.hasMatch()) { + availableVersion = matched.captured(0); + downloadLink = idx.data(KisRssReader::RssRoles::LinkRole).toString(); + break; + } + } + } + + UpdaterStatus::StatusID status; + + if (availableVersionIsHigher(m_currentVersion, availableVersion)) { + status = UpdaterStatus::StatusID::UPDATE_AVAILABLE; + } else { + status = UpdaterStatus::StatusID::UPTODATE; + } + + m_updaterStatus.setStatus(status); + m_updaterStatus.setAvailableVersion(availableVersion); + m_updaterStatus.setDownloadLink(downloadLink); + + emit sigUpdateCheckStateChange(m_updaterStatus); +} + +bool KisManualUpdater::availableVersionIsHigher(QString currentVersion, QString availableVersion) +{ + int currentSuffixIndex {5}; + int availableSuffixIndex {5}; + + QVersionNumber currentVersionNumber = QVersionNumber::fromString(currentVersion, ¤tSuffixIndex); + QVersionNumber availableVersionNumber = QVersionNumber::fromString(availableVersion, &availableSuffixIndex); + + QString currentSuffix = currentVersion.mid(currentSuffixIndex); + QString availableSuffix = availableVersion.mid(availableSuffixIndex); + + if (currentVersionNumber.normalized() == availableVersionNumber.normalized()) { + if (!currentSuffix.isEmpty() && availableSuffix.isEmpty()) { + return true; + } else { + return false; + } + } else { + return (currentVersionNumber.normalized() < availableVersionNumber.normalized()); + } +} + diff --git a/libs/ui/utils/KisManualUpdater.h b/libs/ui/utils/KisManualUpdater.h new file mode 100644 index 0000000000..c29847aaeb --- /dev/null +++ b/libs/ui/utils/KisManualUpdater.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISMANUALUPDATER_H +#define KISMANUALUPDATER_H + +#include + +#include +#include +#include "kritaui_export.h" + + +class KRITAUI_EXPORT KisManualUpdater : public KisUpdaterBase +{ + Q_OBJECT + +public: + KisManualUpdater(); + /** + * @brief KisManualUpdater - constructor for testing + * @param rssModel + * @param currentVersion + */ + explicit KisManualUpdater(MultiFeedRssModel* rssModel, QString& currentVersion); + + void checkForUpdate() override; + + // this updater can only check for updates + /** + * @brief the manual updater can only check for available versions + * @return false + */ + inline bool hasUpdateCapability() override { return false; } + inline void doUpdate() override { return; } + +public Q_SLOTS: + void rssDataChanged(); + +private: + bool availableVersionIsHigher(QString currentVersion, QString availableVersion); + + QScopedPointer m_rssModel; + QString m_currentVersion; + + friend class KisManualUpdaterTest; +}; + +#endif // KISMANUALUPDATER_H diff --git a/libs/ui/utils/KisUpdaterBase.cpp b/libs/ui/utils/KisUpdaterBase.cpp new file mode 100644 index 0000000000..cd4daf4e04 --- /dev/null +++ b/libs/ui/utils/KisUpdaterBase.cpp @@ -0,0 +1,6 @@ +#include "KisUpdaterBase.h" + +KisUpdaterBase::KisUpdaterBase() +{ + +} diff --git a/libs/ui/utils/KisUpdaterBase.h b/libs/ui/utils/KisUpdaterBase.h new file mode 100644 index 0000000000..c79c85fda0 --- /dev/null +++ b/libs/ui/utils/KisUpdaterBase.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISUPDATERBASE_H +#define KISUPDATERBASE_H + +#include +#include + +class QString; + +class KisUpdaterBase : public QObject +{ + Q_OBJECT + +public: + KisUpdaterBase(); + + /** + * @brief start the process checking whether there is an update available or not + * When the check is done, the updater emits sigUpdateCheckStateChange with the check result + */ + virtual void checkForUpdate() = 0; + + /** + * @brief Returns true if this updater can actually perform an update. + * If it can only check for new versions, return false. + * @return bool + */ + virtual bool hasUpdateCapability() = 0; + + /** + * @brief if the updater has update capability, start the update process + * When the update is done, the updater emits sigUpdateCheckStateChange with the check result + */ + virtual void doUpdate() = 0; + +Q_SIGNALS: + void sigUpdateCheckStateChange(KisUpdaterStatus); + +protected: + KisUpdaterStatus m_updaterStatus; +}; + +#endif // KISUPDATERBASE_H diff --git a/libs/ui/utils/KisUpdaterStatus.cpp b/libs/ui/utils/KisUpdaterStatus.cpp new file mode 100644 index 0000000000..7d95991de3 --- /dev/null +++ b/libs/ui/utils/KisUpdaterStatus.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 "KisUpdaterStatus.h" +#include + +KisUpdaterStatus::KisUpdaterStatus() +{ + +} + +KisUpdaterStatus::KisUpdaterStatus(const KisUpdaterStatus& rhs) + : QObject(0) + , m_status(rhs.m_status) + , m_availableVersion(rhs.m_availableVersion) + , m_downloadLink(rhs.m_downloadLink) + , m_updaterOutput(rhs.m_updaterOutput) +{ + +} + +KisUpdaterStatus::~KisUpdaterStatus() +{ + +} + +UpdaterStatus::StatusID KisUpdaterStatus::status() { + return m_status; +} + +QString KisUpdaterStatus::availableVersion() { + return m_availableVersion; +} + +QString KisUpdaterStatus::downloadLink() { + return m_downloadLink; +} + +QString KisUpdaterStatus::updaterOutput() { + return m_updaterOutput; +} + +void KisUpdaterStatus::setStatus(const UpdaterStatus::StatusID& status) +{ + m_status = status; +} + +void KisUpdaterStatus::setAvailableVersion(const QString& availableVersion) +{ + m_availableVersion = availableVersion; +} + +void KisUpdaterStatus::setDownloadLink(const QString& downloadLink) +{ + m_downloadLink = downloadLink; +} + +void KisUpdaterStatus::setUpdaterOutput(const QString& updaterOutput) +{ + m_updaterOutput = updaterOutput; +} + +KisUpdaterStatus& KisUpdaterStatus::operator=(KisUpdaterStatus& secondArg) +{ + m_status = secondArg.status(); + m_availableVersion = secondArg.availableVersion(); + m_downloadLink = secondArg.downloadLink(); + m_updaterOutput = secondArg.updaterOutput(); + + return *this; +} + +bool KisUpdaterStatus::operator==(KisUpdaterStatus& secondArg) +{ + if (m_status == secondArg.status()) { + return true; + } else { + return false; + } +} diff --git a/libs/ui/utils/KisUpdaterStatus.h b/libs/ui/utils/KisUpdaterStatus.h new file mode 100644 index 0000000000..7b63af4840 --- /dev/null +++ b/libs/ui/utils/KisUpdaterStatus.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Anna Medonosova + * + * 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 KISUPDATERSTATUS_H +#define KISUPDATERSTATUS_H + +#include +#include +#include + +#include "kritaui_export.h" + + +namespace UpdaterStatus { + + enum class KRITAUI_EXPORT StatusID { + UPTODATE, + UPDATE_AVAILABLE, + CHECK_ERROR, + UPDATE_ERROR, + IN_PROGRESS, + RESTART_REQUIRED, + INITIALIZED + }; + +} + +Q_DECLARE_METATYPE(UpdaterStatus::StatusID); + + +class KRITAUI_EXPORT KisUpdaterStatus : public QObject +{ + Q_OBJECT + +public: + KisUpdaterStatus(); + KisUpdaterStatus(const KisUpdaterStatus& rhs); + ~KisUpdaterStatus(); + + UpdaterStatus::StatusID status(); + QString availableVersion(); + QString downloadLink(); + QString updaterOutput(); + + void setStatus(const UpdaterStatus::StatusID& status); + void setAvailableVersion(const QString& availableVersion); + void setDownloadLink(const QString& downloadLink); + void setUpdaterOutput(const QString& updaterOutput); + + KisUpdaterStatus& operator=(KisUpdaterStatus& secondArg); + bool operator==(KisUpdaterStatus& secondArg); + +private: + UpdaterStatus::StatusID m_status { UpdaterStatus::StatusID::INITIALIZED }; + QString m_availableVersion; + QString m_downloadLink; + QString m_updaterOutput; +}; + +Q_DECLARE_METATYPE(KisUpdaterStatus); + +#endif // KISUPDATERSTATUS_H diff --git a/libs/ui/widgets/KisNewsWidget.cpp b/libs/ui/widgets/KisNewsWidget.cpp index 273a05f973..e244b42eef 100644 --- a/libs/ui/widgets/KisNewsWidget.cpp +++ b/libs/ui/widgets/KisNewsWidget.cpp @@ -1,223 +1,140 @@ /* * Copyright (c) 2018 boud * * 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 "KisNewsWidget.h" #include #include #include #include #include #include #include #include "kis_config.h" #include "KisMultiFeedRSSModel.h" #include "QRegularExpression" KisNewsDelegate::KisNewsDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void KisNewsDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); QStyleOptionViewItem optionCopy = option; initStyleOption(&optionCopy, index); QStyle *style = optionCopy.widget? optionCopy.widget->style() : QApplication::style(); QTextDocument doc; doc.setHtml(optionCopy.text); doc.setDocumentMargin(10); /// Painting item without text optionCopy.text = QString(); style->drawControl(QStyle::CE_ItemViewItem, &optionCopy, painter); QAbstractTextDocumentLayout::PaintContext ctx; // Highlighting text if item is selected if (optionCopy.state & QStyle::State_Selected) { ctx.palette.setColor(QPalette::Text, optionCopy.palette.color(QPalette::Active, QPalette::HighlightedText)); } painter->translate(optionCopy.rect.left(), optionCopy.rect.top()); QRect clip(0, 0, optionCopy.rect.width(), optionCopy.rect.height()); doc.setPageSize(clip.size()); doc.drawContents(painter, clip); painter->restore(); } QSize KisNewsDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem optionCopy = option; initStyleOption(&optionCopy, index); QTextDocument doc; doc.setHtml(optionCopy.text); doc.setTextWidth(optionCopy.rect.width()); return QSize(doc.idealWidth(), doc.size().height()); } KisNewsWidget::KisNewsWidget(QWidget *parent) : QWidget(parent) { setupUi(this); m_rssModel = new MultiFeedRssModel(this); connect(m_rssModel, SIGNAL(feedDataChanged()), this, SLOT(rssDataChanged())); setCursor(Qt::PointingHandCursor); listNews->setModel(m_rssModel); listNews->setItemDelegate(new KisNewsDelegate(listNews)); connect(listNews, SIGNAL(clicked(QModelIndex)), this, SLOT(itemSelected(QModelIndex))); } void KisNewsWidget::setAnalyticsTracking(QString text) { analyticsTrackingParameters = text; } -bool KisNewsWidget::hasUpdateAvailable() -{ - return needsVersionUpdate; -} - -QString KisNewsWidget::versionNumber() -{ - return newVersionNumber; -} - -QString KisNewsWidget::versionLink() -{ - return newVersionLink; -} - void KisNewsWidget::toggleNews(bool toggle) { KisConfig cfg(false); cfg.writeEntry("FetchNews", toggle); if (toggle) { m_rssModel->addFeed(QLatin1String("https://krita.org/en/feed/")); } else { m_rssModel->removeFeed(QLatin1String("https://krita.org/en/feed/")); } } void KisNewsWidget::itemSelected(const QModelIndex &idx) { if (idx.isValid()) { - QString link = idx.data(RssRoles::LinkRole).toString(); + QString link = idx.data(KisRssReader::RssRoles::LinkRole).toString(); // append query string for analytics tracking if we set it if (analyticsTrackingParameters != "") { // use title in analytics query string - QString linkTitle = idx.data(RssRoles::TitleRole).toString(); + QString linkTitle = idx.data(KisRssReader::RssRoles::TitleRole).toString(); linkTitle = linkTitle.simplified(); // trims and makes 1 white space linkTitle = linkTitle.replace(" ", ""); analyticsTrackingParameters = analyticsTrackingParameters.append(linkTitle); QDesktopServices::openUrl(QUrl(link.append(analyticsTrackingParameters))); } else { QDesktopServices::openUrl(QUrl(link)); } } } void KisNewsWidget::rssDataChanged() { - - // grab the latest release post and URL for reference later - // if we need to update - for (int i = 0; i < m_rssModel->rowCount(); i++) - { - const QModelIndex &idx = m_rssModel->index(i); - - if (idx.isValid()) { - - // only use official release announcements to get version number - if ( idx.data(RssRoles::CategoryRole).toString() != "Official Release") { - continue; - } - - QString linkTitle = idx.data(RssRoles::TitleRole).toString(); - - // regex to capture version number - QRegularExpression versionRegex("\\d\\.\\d\\.?\\d?\\.?\\d"); - QRegularExpressionMatch matched = versionRegex.match(linkTitle); - - // only take the top match for release version since that is the newest - if (matched.hasMatch()) { - newVersionNumber = matched.captured(0); - newVersionLink = idx.data(RssRoles::LinkRole).toString(); - break; - } - - } - } - - // see if we need to update our version, or we are on a dev version - calculateVersionUpdateStatus(); - emit newsDataChanged(); } - -void KisNewsWidget::calculateVersionUpdateStatus() -{ - // do version compare to see if there is a new version available - // also check to see if we are on a dev version (newer than newest release) - QStringList currentVersionParts = qApp->applicationVersion().split("."); - QStringList onlineReleaseAnnouncement = newVersionNumber.split("."); - - // is the major version different? - if (onlineReleaseAnnouncement[0] > currentVersionParts[0] ) { - needsVersionUpdate = true; // we are a major version behind - return; - } - - // major versions are the same, so check minor versions - if (onlineReleaseAnnouncement[1] > currentVersionParts[1] ) { - needsVersionUpdate = true; // we are a minor version behind - return; - } - - // minor versions are the same, so maybe bugfix version is different - // sometimes we don't communicate this, implictly make 0 if it doesn't exist - if (onlineReleaseAnnouncement[2].isNull()) { - onlineReleaseAnnouncement[2] = "0"; - } - if (currentVersionParts[2].isNull()) { - currentVersionParts[2] = "0"; - } - - if (onlineReleaseAnnouncement[2] > currentVersionParts[2] ) { - needsVersionUpdate = true; // we are a bugfix version behind - return; - } - -} diff --git a/libs/ui/widgets/KisNewsWidget.h b/libs/ui/widgets/KisNewsWidget.h index 7acfa7239d..5c55caa63d 100644 --- a/libs/ui/widgets/KisNewsWidget.h +++ b/libs/ui/widgets/KisNewsWidget.h @@ -1,75 +1,62 @@ /* * Copyright (c) 2018 boud * * 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 KISNEWSWIDGET_H #define KISNEWSWIDGET_H #include #include #include #include class MultiFeedRssModel; class KisNewsDelegate : public QStyledItemDelegate { Q_OBJECT public: KisNewsDelegate(QObject *parent = 0); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; /** * @brief The KisNewsWidget class shows the latest news from Krita.org */ class KisNewsWidget : public QWidget, public Ui::KisNewsPage { Q_OBJECT public: explicit KisNewsWidget(QWidget *parent = nullptr); void setAnalyticsTracking(QString text); - bool hasUpdateAvailable(); - QString versionNumber(); - QString versionLink(); - Q_SIGNALS: void newsDataChanged(); private Q_SLOTS: void toggleNews(bool toggle); void itemSelected(const QModelIndex &idx); void rssDataChanged(); private: bool m_getNews {false}; MultiFeedRssModel *m_rssModel {0}; QString analyticsTrackingParameters; - - /// for new Krita version notification - QString newVersionNumber; - QString newVersionLink; - - // version checking logic tells us we need to update our Krita - void calculateVersionUpdateStatus(); - bool needsVersionUpdate = false; - }; #endif // KISNEWSWIDGET_H diff --git a/packaging/linux/appimage/build-image.sh b/packaging/linux/appimage/build-image.sh index 9c2f943981..63871b2a33 100755 --- a/packaging/linux/appimage/build-image.sh +++ b/packaging/linux/appimage/build-image.sh @@ -1,110 +1,155 @@ #!/bin/bash # Halt on errors and be verbose about what we are doing -set -e +#set -e set -x # Read in our parameters export BUILD_PREFIX=$1 export KRITA_SOURCES=$2 # Save some frequently referenced locations in variables for ease of use / updating export APPDIR=$BUILD_PREFIX/krita.appdir export PLUGINS=$APPDIR/usr/lib/kritaplugins/ -# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. +# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. # That's not always the case, so make sure it is export LC_ALL=en_US.UTF-8 export LANG=en_us.UTF-8 # We want to use $prefix/deps/usr/ for all our dependencies export DEPS_INSTALL_PREFIX=$BUILD_PREFIX/deps/usr/ export DOWNLOADS_DIR=$BUILD_PREFIX/downloads/ # Setup variables needed to help everything find what we built export LD_LIBRARY_PATH=$DEPS_INSTALL_PREFIX/lib/:$DEPS_INSTALL_PREFIX/lib/x86_64-linux-gnu/:$APPDIR/usr/lib/:$LD_LIBRARY_PATH export PATH=$DEPS_INSTALL_PREFIX/bin/:$PATH export PKG_CONFIG_PATH=$DEPS_INSTALL_PREFIX/share/pkgconfig/:$DEPS_INSTALL_PREFIX/lib/pkgconfig/:/usr/lib/pkgconfig/:$PKG_CONFIG_PATH export CMAKE_PREFIX_PATH=$DEPS_INSTALL_PREFIX:$CMAKE_PREFIX_PATH export PYTHONPATH=$DEPS_INSTALL_PREFIX/sip/:$DEPS_INSTALL_PREFIX/lib/python3.5/site-packages/:$DEPS_INSTALL_PREFIX/lib/python3.5/ export PYTHONHOME=$DEPS_INSTALL_PREFIX +# download +mkdir -p $DOWNLOADS_DIR +cd $DOWNLOADS_DIR +wget "https://github.com/AppImage/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" -O AppImageUpdate + # Switch over to our build prefix cd $BUILD_PREFIX # # Now we can get the process started! # # Step 0: place the translations where ki18n and Qt look for them if [ -d $APPDIR/usr/share/locale ] ; then mv $APPDIR/usr/share/locale $APPDIR/usr/share/krita fi -# Step 1: Copy over all the resources provided by dependencies that we need +# Step 1: Copy over all the resources provided by dependencies that we need cp -r $DEPS_INSTALL_PREFIX/share/locale $APPDIR/usr/share/krita cp -r $DEPS_INSTALL_PREFIX/share/kf5 $APPDIR/usr/share cp -r $DEPS_INSTALL_PREFIX/share/mime $APPDIR/usr/share cp -r $DEPS_INSTALL_PREFIX/lib/python3.5 $APPDIR/usr/lib cp -r $DEPS_INSTALL_PREFIX/share/sip $APPDIR/usr/share cp -r $DEPS_INSTALL_PREFIX/translations $APPDIR/usr/ # Step 2: Relocate x64 binaries from the architecture specific directory as required for Appimages mv $APPDIR/usr/lib/x86_64-linux-gnu/* $APPDIR/usr/lib rm -rf $APPDIR/usr/lib/x86_64-linux-gnu/ # Step 3: Update the rpath in the various plugins we have to make sure they'll be loadable in an Appimage context for lib in $PLUGINS/*.so*; do - patchelf --set-rpath '$ORIGIN/..' $lib; + patchelf --set-rpath '$ORIGIN/..' $lib; done for lib in $APPDIR/usr/lib/python3.5/site-packages/PyQt5/*.so*; do - patchelf --set-rpath '$ORIGIN/../..' $lib; + patchelf --set-rpath '$ORIGIN/../..' $lib; done for lib in $APPDIR/usr/lib/python3.5/lib-dynload/*.so*; do - patchelf --set-rpath '$ORIGIN/../..' $lib; + patchelf --set-rpath '$ORIGIN/../..' $lib; done patchelf --set-rpath '$ORIGIN/../../../..' $APPDIR/usr/lib/qml/org/krita/draganddrop/libdraganddropplugin.so patchelf --set-rpath '$ORIGIN/../../../..' $APPDIR/usr/lib/qml/org/krita/sketch/libkritasketchplugin.so patchelf --set-rpath '$ORIGIN/../..' $APPDIR/usr/lib/krita-python-libs/PyKrita/krita.so patchelf --set-rpath '$ORIGIN/../..' $APPDIR/usr/lib/python3.5/site-packages/PyQt5/sip.so +# Step 4: Install AppImageUpdate +if [ -f $DOWNLOADS_DIR/AppImageUpdate ]; then + cp $DOWNLOADS_DIR/AppImageUpdate $APPDIR/usr/bin/ + chmod +x $APPDIR/usr/bin/AppImageUpdate +fi + # Step 5: Find out what version of Krita we built and give the Appimage a proper name cd $BUILD_PREFIX/krita-build KRITA_VERSION=$(grep "#define KRITA_VERSION_STRING" libs/version/kritaversion.h | cut -d '"' -f 2) - # Also find out the revision of Git we built # Then use that to generate a combined name we'll distribute cd $KRITA_SOURCES if [[ -d .git ]]; then GIT_REVISION=$(git rev-parse --short HEAD) - VERSION=$KRITA_VERSION-$GIT_REVISION + export VERSION=$KRITA_VERSION-$GIT_REVISION + VERSION_TYPE="developement" + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + if [ "$BRANCH" = "master" ]; then + CHANNEL="Next" + else + CHANNEL="Plus" + fi else - VERSION=$KRITA_VERSION + export VERSION=$KRITA_VERSION + + #if KRITA_BETA is set, set channel to Beta, otherwise set it to stable + grep "define KRITA_BETA 1" libs/version/kritaversion.h; + is_beta=$? + if [ is_beta -eq 0 ]; then + VERSION_TYPE="developement" + CHANNEL="Beta" + else + VERSION_TYPE="stable" + CHANNEL="Stable" + fi fi +DATE=$(git log -1 --format="%ct" | xargs -I{} date -d @{} +%Y-%m-%d) +if [ "$DATE" = "" ] ; then + DATE=$(date +%Y-%m-%d) +fi + +sed -e "s|||" -i $APPDIR/usr/share/metainfo/org.kde.krita.appdata.xml + +# set zsync url for linuxdeployqt +# TODO: replace with real urls +if [ "$CHANNEL" = "Next" ]; then + ZSYNC_URL="zsync|http://localhost:8000/nightly/Krita-${CHANNEL}-x86_64.AppImage.zsync" +elif [ "$CHANNEL" = "Plus" ]; then + ZSYNC_URL="zsync|http://localhost:8000/nightly/Krita-${CHANNEL}-x86_64.AppImage.zsync" +elif [ "$CHANNEL" = "Stable" ]; then + ZSYNC_URL="zsync|http://localhost:8000/stable/Krita-${CHANNEL}-x86_64.AppImage.zsync" +elif [ "$CHANNEL" = "Beta" ]; then + ZSYNC_URL="zsync|http://localhost:8000/unstable/Krita-${CHANNEL}-x86_64.AppImage.zsync" +fi # Return to our build root cd $BUILD_PREFIX +# TODO: icons for channels # place the icon where linuxdeployqt seems to expect it find $APPDIR -name krita.png cp /home/appimage//appimage-workspace/krita.appdir/usr/share/icons/hicolor/256x256/apps/krita.png $APPDIR ls $APPDIR # Step 4: Build the image!!! linuxdeployqt $APPDIR/usr/share/applications/org.kde.krita.desktop \ -executable=$APPDIR/usr/bin/krita \ -qmldir=$DEPS_INSTALL_PREFIX/qml \ -verbose=2 \ -bundle-non-qt-libs \ -extra-plugins=$PLUGINS,$APPDIR/usr/lib/krita-python-libs/PyKrita/krita.so,$APPDIR/usr/lib//qml/org/krita/sketch/libkritasketchplugin.so,$APPDIR/usr/lib/qml/org/krita/draganddrop/libdraganddropplugin.so \ - -appimage - -# Generate a new name for the Appimage file and rename it accordingly -APPIMAGE=krita-"$VERSION"-x86_64.appimage + -updateinformation="${ZSYNC_URL}" \ + -appimage -mv Krita*x86_64.AppImage $APPIMAGE +mv Krita-${VERSION}-x86_64.AppImage.zsync "Krita-${CHANNEL}-x86_64.AppImage.zsync" diff --git a/packaging/linux/appimage/build-krita.sh b/packaging/linux/appimage/build-krita.sh index 84288455e0..956aee95bc 100755 --- a/packaging/linux/appimage/build-krita.sh +++ b/packaging/linux/appimage/build-krita.sh @@ -1,52 +1,69 @@ #!/bin/bash # Halt on errors and be verbose about what we are doing set -e set -x # Read in our parameters export BUILD_PREFIX=$1 export KRITA_SOURCES=$2 -# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. +# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. # That's not always the case, so make sure it is export LC_ALL=en_US.UTF-8 export LANG=en_us.UTF-8 # We want to use $prefix/deps/usr/ for all our dependencies export DEPS_INSTALL_PREFIX=$BUILD_PREFIX/deps/usr/ export DOWNLOADS_DIR=$BUILD_PREFIX/downloads/ # Setup variables needed to help everything find what we build export LD_LIBRARY_PATH=$DEPS_INSTALL_PREFIX/lib:$LD_LIBRARY_PATH export PATH=$DEPS_INSTALL_PREFIX/bin:$PATH export PKG_CONFIG_PATH=$DEPS_INSTALL_PREFIX/share/pkgconfig:$DEPS_INSTALL_PREFIX/lib/pkgconfig:/usr/lib/pkgconfig:$PKG_CONFIG_PATH export CMAKE_PREFIX_PATH=$DEPS_INSTALL_PREFIX:$CMAKE_PREFIX_PATH export PYTHONPATH=$DEPS_INSTALL_PREFIX/sip:$DEPS_INSTALL_PREFIX/lib/python3.5/site-packages:$DEPS_INSTALL_PREFIX/lib/python3.5 export PYTHONHOME=$DEPS_INSTALL_PREFIX +cd $KRITA_SOURCES + +#if [[ -d .git ]]; then +# BUILD_TYPE="Debug" +#else +# #if KRITA_BETA is set, set channel to Beta, otherwise set it to stable +# grep "define KRITA_BETA 1" libs/version/kritaversion.h; +# is_beta=$? +# if [ is_beta -eq 0 ]; then +# BUILD_TYPE="Debug" +# else +# BUILD_TYPE="Release" +# fi +#fi + +BUILD_TYPE="Release" + # Make sure our build directory exists if [ ! -d $BUILD_PREFIX/krita-build/ ] ; then mkdir -p $BUILD_PREFIX/krita-build/ fi # Now switch to it cd $BUILD_PREFIX/krita-build/ # Determine how many CPUs we have CPU_COUNT=`grep processor /proc/cpuinfo | wc -l` + # Configure Krita cmake $KRITA_SOURCES \ -DCMAKE_INSTALL_PREFIX:PATH=$BUILD_PREFIX/krita.appdir/usr \ -DDEFINE_NO_DEPRECATED=1 \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ -DFOUNDATION_BUILD=1 \ -DHIDE_SAFE_ASSERTS=ON \ - -DBUILD_TESTING=FALSE \ + -DBUILD_TESTING=TRUE \ -DPYQT_SIP_DIR_OVERRIDE=$DEPS_INSTALL_PREFIX/share/sip/ \ -DHAVE_MEMORY_LEAK_TRACKER=FALSE - + # Build and Install Krita (ready for the next phase) make -j$CPU_COUNT install - diff --git a/packaging/linux/appimage/sign_appimage.sh b/packaging/linux/appimage/sign_appimage.sh new file mode 100755 index 0000000000..b206938162 --- /dev/null +++ b/packaging/linux/appimage/sign_appimage.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +set -x +set -e + +APPIMAGE_PATH="${1}" +CHANNEL="${2}" +GPG_KEY="${3}" + +if [ -z $APPIMAGE_PATH ]; then + echo "path to appimage (arg1) is not set" + exit 1 +fi + +if [ -z $CHANNEL ]; then + echo "channel (arg2) is not set" + exit 1 +fi + +if [ -z $GPG_KEY ]; then + echo "gpg key id (arg3) is not set" + exit 1 +fi + +tempdir="$(mktemp sign_appimage.XXXXXX -d -p /tmp)" + +destination=$(basename $APPIMAGE_PATH) + +ascfile="${tempdir}/${destination}.digest.asc" +digestfile="${tempdir}/${destination}.digest" +sigkeyfile="${tempdir}/sig_pubkey" + +if [ -f $digestfile ]; then rm $digestfile; fi +if [ -f $ascfile ]; then rm $ascfile; fi +if [ -f $sigkeyfile ]; then rm $sigkeyfile; fi + +# get offsets and lengths of .sha256_sig and .sig_key sections of the AppImage +SIG_OFFSET=$(objdump -h "${APPIMAGE_PATH}" | grep .sha256_sig | awk '{print $6}') +SIG_LENGTH=$(objdump -h "${APPIMAGE_PATH}" | grep .sha256_sig | awk '{print $3}') + +KEY_OFFSET=$(objdump -h "${APPIMAGE_PATH}" | grep .sig_key | awk '{print $6}') +KEY_LENGTH=$(objdump -h "${APPIMAGE_PATH}" | grep .sig_key | awk '{print $3}') + +# Null the sections +dd if=/dev/zero bs=1 seek=$(($(echo 0x$SIG_OFFSET))) count=$(($(echo 0x$SIG_LENGTH))) of="${APPIMAGE_PATH}" conv=notrunc +dd if=/dev/zero bs=1 seek=$(($(echo 0x$KEY_OFFSET))) count=$(($(echo 0x$KEY_LENGTH))) of="${APPIMAGE_PATH}" conv=notrunc + +# generate sha256sum +# BEWARE THE NEWLINE! if it is not stripped, AppImageUpdate validaton will fail +sha256sum $APPIMAGE_PATH | cut -d " " -f 1 | tr -d '\n' > $digestfile + +#sign the sha256sum +gpg2 --detach-sign --armor -u $GPG_KEY -o $ascfile $digestfile +gpg2 --export --armor $GPG_KEY > $sigkeyfile + +# Embed the signature +dd if=${ascfile} bs=1 seek=$(($(echo 0x$SIG_OFFSET))) count=$(($(echo 0x$SIG_LENGTH))) of="${APPIMAGE_PATH}" conv=notrunc +# Embed the public part of the signing key +dd if=${sigkeyfile} bs=1 seek=$(($(echo 0x$KEY_OFFSET))) count=$(($(echo 0x$KEY_LENGTH))) of="${APPIMAGE_PATH}" conv=notrunc + +# regenerate zsync file +# TODO: replace with real urls +if [ "$CHANNEL" = "Next" ]; then + URL="http://localhost:8000/nightly" +elif [ "$CHANNEL" = "Plus" ]; then + URL="http://localhost:8000/nightly" +elif [ "$CHANNEL" = "Stable" ]; then + URL="http://localhost:8000/stable" +elif [ "$CHANNEL" = "Beta" ]; then + URL="http://localhost:8000/unstable" +fi + +zsyncmake -u "${URL}/$(basename ${APPIMAGE_PATH})" -o $APPIMAGE_PATH.zsync.new $APPIMAGE_PATH +ret=$? +if [ $ret -eq 0 ]; then + mv $APPIMAGE_PATH.zsync.new Krita-${CHANNEL}-x86_64.AppImage.zsync +fi + +# cleanup +rm -rf $tempdir diff --git a/packaging/linux/appimage/update_updinfo.sh b/packaging/linux/appimage/update_updinfo.sh new file mode 100755 index 0000000000..76a1c084ef --- /dev/null +++ b/packaging/linux/appimage/update_updinfo.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -x +set -e + +APPIMAGE_PATH="${1}" +UPDURL="${2}" + +if [ -z $APPIMAGE_PATH ]; then + echo "path to appimage (arg1) is not set" + exit 1 +fi + +if [ -z $UPDURL ]; then + echo "update url (arg2) is not set" + exit 1 +fi + + +tempdir="$(mktemp update_updinfo.XXXXXX -d -p /tmp)" + +destination=$(basename $APPIMAGE_PATH) +updinfo_file="${tempdir}/upd_info" + +echo -n "zsync|${UPDURL}" > $updinfo_file + +# get offsets and lengths of .upd_info section of the AppImage +OFFSET=$(objdump -h "${APPIMAGE_PATH}" | grep .upd_info | awk '{print $6}') +LENGTH=$(objdump -h "${APPIMAGE_PATH}" | grep .upd_info | awk '{print $3}') + +# Null the section +dd if=/dev/zero bs=1 seek=$(($(echo 0x$OFFSET))) count=$(($(echo 0x$LENGTH))) of="${APPIMAGE_PATH}" conv=notrunc + +# Embed the updinfo +dd if=${updinfo_file} bs=1 seek=$(($(echo 0x$OFFSET))) count=$(($(echo 0x$LENGTH))) of="${APPIMAGE_PATH}" conv=notrunc + +# cleanup +rm -rf $tempdir diff --git a/packaging/linux/appimage/validate_appimage_signature.sh b/packaging/linux/appimage/validate_appimage_signature.sh new file mode 100755 index 0000000000..52618dd85c --- /dev/null +++ b/packaging/linux/appimage/validate_appimage_signature.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -x +set -e + +APPIMAGE_PATH="${1}" + +if [ -z $APPIMAGE_PATH ]; then + echo "path to appimage (arg1) is not set" + exit 1 +fi + +tempdir="$(mktemp validate_signature.XXXXXX -d -p /tmp)" + +destination=$(basename $APPIMAGE_PATH) + +ascfile="${tempdir}/${destination}.digest.asc" +digestfile="${tempdir}/${destination}.digest" +sigkeyfile="${tempdir}/sig_pubkey" +tempkeyringpath="${tempdir}/keyring" +tmpappimage="$tempdir/tmp.AppImage" + +# get offsets and lengths of .sha256_sig and .sig_key sections of the AppImage +SIG_OFFSET=$(objdump -h "${APPIMAGE_PATH}" | grep .sha256_sig | awk '{print $6}') +SIG_LENGTH=$(objdump -h "${APPIMAGE_PATH}" | grep .sha256_sig | awk '{print $3}') + +KEY_OFFSET=$(objdump -h "${APPIMAGE_PATH}" | grep .sig_key | awk '{print $6}') +KEY_LENGTH=$(objdump -h "${APPIMAGE_PATH}" | grep .sig_key | awk '{print $3}') + +cp $APPIMAGE_PATH $tmpappimage + +# restore the original, for generating checksum +dd if=/dev/zero bs=1 seek=$(($(echo 0x$SIG_OFFSET))) count=$(($(echo 0x$SIG_LENGTH))) of="${tmpappimage}" conv=notrunc +dd if=/dev/zero bs=1 seek=$(($(echo 0x$KEY_OFFSET))) count=$(($(echo 0x$KEY_LENGTH))) of="${tmpappimage}" conv=notrunc + +sha256sum $tmpappimage | cut -d " " -f 1 | tr -d '\n' > $digestfile + +# extract signature +dd if="${APPIMAGE_PATH}" bs=1 skip=$(($(echo 0x$SIG_OFFSET))) count=$(($(echo 0x$SIG_LENGTH))) of="${ascfile}" +# extract the public part of the signing key +dd if=${APPIMAGE_PATH} bs=1 skip=$(($(echo 0x$KEY_OFFSET))) count=$(($(echo 0x$KEY_LENGTH))) of="${sigkeyfile}" + +cat $sigkeyfile | gpg2 --no-default-keyring --keyring $tempkeyringpath --import +gpg2 --no-default-keyring --keyring $tempkeyringpath --verify $ascfile $digestfile + +# cleanup +rm -rf $tempdir