Author: tgoettlicher Date: Mon Aug 1 09:44:45 2011 New Revision: 65095
URL: http://svn.opensuse.org/viewcvs/yast?rev=65095&view=rev Log: (empty)
Added: branches/SuSE-Code-11-SP2-Branch/gtk/CMakeLists.txt branches/SuSE-Code-11-SP2-Branch/gtk/FindGTK2.cmake branches/SuSE-Code-11-SP2-Branch/gtk/HACKING branches/SuSE-Code-11-SP2-Branch/gtk/MAINTAINER branches/SuSE-Code-11-SP2-Branch/gtk/Makefile branches/SuSE-Code-11-SP2-Branch/gtk/Makefile.cvs branches/SuSE-Code-11-SP2-Branch/gtk/README branches/SuSE-Code-11-SP2-Branch/gtk/RPMNAME branches/SuSE-Code-11-SP2-Branch/gtk/TODO branches/SuSE-Code-11-SP2-Branch/gtk/VERSION branches/SuSE-Code-11-SP2-Branch/gtk/VERSION.cmake branches/SuSE-Code-11-SP2-Branch/gtk/config.h.cmake branches/SuSE-Code-11-SP2-Branch/gtk/package/ branches/SuSE-Code-11-SP2-Branch/gtk/package/yast2-gtk.changes branches/SuSE-Code-11-SP2-Branch/gtk/src/ branches/SuSE-Code-11-SP2-Branch/gtk/src/CMakeLists.txt branches/SuSE-Code-11-SP2-Branch/gtk/src/Y2CCGtk.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGBarGraph.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGComboBox.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.h branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDumbTab.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGFrame.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGImage.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGInputField.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGIntField.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLabel.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLayout.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGMenuButton.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginIf.h branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginStub.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGProgressBar.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPushButton.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGRadioButton.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.h branches/SuSE-Code-11-SP2-Branch/gtk/src/YGText.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGTreeView.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.h branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.h branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.h branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWizard.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/YGi18n.h branches/SuSE-Code-11-SP2-Branch/gtk/src/dummy.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/hr.xpm branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/CMakeLists.txt branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/pkg-selector-help.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.c branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.c branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.c branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerywidget.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.h branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.h branches/SuSE-Code-11-SP2-Branch/gtk/src/test.cc branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktimezonepicker.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktimezonepicker.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktreeview.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktreeview.h branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkwizard.c branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkwizard.h branches/SuSE-Code-11-SP2-Branch/gtk/tests/ branches/SuSE-Code-11-SP2-Branch/gtk/tests/ButtonOrder.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Cursor.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Dialog-Several.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/FileDialog.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Frame.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/IconButton.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/IconItems-ComboBox.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/IconItems-MultiSelectionBox.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/IconItems-SelectionBox.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Image.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/ImageMng.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/InputField-Event.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/InvalidWidget.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Label-Underline.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Align-Weight.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-AlignStretch.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Expand.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Frame2.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-HVCenter-Align.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-ReplacePoint.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Split-Split.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Truncated-Widget.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Wizard-BackgroundWidget.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Wizard-OneWidget.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Layout-Wizard3.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/LogView.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/MainDialogs.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/MultiLine.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/MultiProgressMeterHuge.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/MultiProgressMeterVer.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/MultiSelectionBox.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/PollInput.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/ProgressBar.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/RichText1.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/RichText2.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/RussianAccel.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/SelectionBox.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Sleep.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Slider-Event.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/StockButtons.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/StockIcons.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Table-Edit.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Table3-4.ycp (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/Tree-rebuild.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Tree3.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Wizard-ChangingTitle.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Wizard.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/Wizard2.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/WizardList.ycp branches/SuSE-Code-11-SP2-Branch/gtk/tests/image.gif (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie1.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie2.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie3.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie4.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie5.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie6.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie7.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/tests/movie8.mng (with props) branches/SuSE-Code-11-SP2-Branch/gtk/yast2-gtk.spec.in branches/SuSE-Code-11-SP2-Branch/gtk/ycc/ branches/SuSE-Code-11-SP2-Branch/gtk/ycc/Makefile branches/SuSE-Code-11-SP2-Branch/gtk/ycc/ycc.cpp
Added: branches/SuSE-Code-11-SP2-Branch/gtk/CMakeLists.txt URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/CM... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/CMakeLists.txt (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/CMakeLists.txt Mon Aug 1 09:44:45 2011 @@ -0,0 +1,53 @@ +# cmake 2.6 requires this +cmake_minimum_required (VERSION 2.4) + +project (yast2-gtk) + +## configure + + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_BINARY_DIR}/../") + +find_package (GTK2 REQUIRED gtk) +find_package (Libyui REQUIRED) +find_package (Zypp REQUIRED) + +# debug while GTK2 support is shaky +message (STATUS "GTK2 includes:" ${GTK2_INCLUDE_DIRS}) +message (STATUS "GTK2 libraries:" ${GTK2_LIBRARIES}) + +# set libyui and libzypp versions +execute_process (COMMAND pkg-config --modversion yast2-libyui COMMAND awk -F. "{ printf "%d", ($1 * 1000 + $2) * 1000 + $3;}" OUTPUT_VARIABLE YAST2_VERSION) +if (NOT YAST2_VERSION) + message (FATAL_ERROR "Could not parse YaST2 (libyui) version.") +endif (NOT YAST2_VERSION) + +execute_process (COMMAND pkg-config --modversion libzypp COMMAND awk -F. "{ printf "%d", ($1 * 1000 + $2) * 1000 + $3;}" OUTPUT_VARIABLE ZYPP_VERSION) +if (NOT ZYPP_VERSION) + message (FATAL_ERROR "Could not parse Zypp version.") +endif (NOT ZYPP_VERSION) + +set (CMAKE_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/share/YaST2/data/devtools/cmake/modules" ${CMAKE_MODULE_PATH}) +include (YastCommon) +set (YAST_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/include/YaST2) + +#set (DATADIR ${CMAKE_INSTALL_PREFIX}/share/YaST2/gtk/) +set (LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/YaST2/locale) +set (THEMEDIR ${CMAKE_INSTALL_PREFIX}/share/YaST2/theme/current) + +# create config.h (see config.h.cmake) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +## gcc flags + +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Woverloaded-virtual -Wno-deprecated") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall") +set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG") + +## make package + +set (RPMNAME "yast2-gtk") +generate_packaging (${RPMNAME} ${VERSION}) + +add_subdirectory (src) +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/FindGTK2.cmake URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/Fi... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/FindGTK2.cmake (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/FindGTK2.cmake Mon Aug 1 09:44:45 2011 @@ -0,0 +1,554 @@ +# - FindGTK2.cmake +# This module can find the GTK2 widget libraries and several of its other +# optional components like gtkmm, glade, and glademm. +# +# NOTE: If you intend to use version checking, CMake 2.6.2 or later is +# required. +# +# Specify one or more of the following components +# as you call this find module. See example below. +# +# gtk +# gtkmm +# glade +# glademm +# +# The following variables will be defined for your use +# +# GTK2_FOUND - Were all of your specified components found? +# GTK2_INCLUDE_DIRS - All include directories +# GTK2_LIBRARIES - All libraries +# +# GTK2_VERSION - The version of GTK2 found (x.y.z) +# GTK2_MAJOR_VERSION - The major version of GTK2 +# GTK2_MINOR_VERSION - The minor version of GTK2 +# GTK2_PATCH_VERSION - The patch version of GTK2 +# +# Optional variables you can define prior to calling this module: +# +# GTK2_DEBUG - Enables verbose debugging of the module +# GTK2_SKIP_MARK_AS_ADVANCED - Disable marking cache variables as advanced +# +#================= +# Example Usage: +# +# Call find_package() once, here are some examples to pick from: +# +# Require GTK 2.6 or later +# find_package(GTK2 2.6 REQUIRED gtk) +# +# Require GTK 2.10 or later and Glade +# find_package(GTK2 2.10 REQUIRED gtk glade) +# +# Search for GTK/GTKMM 2.8 or later +# find_package(GTK2 2.8 COMPONENTS gtk gtkmm) +# +# if(GTK2_FOUND) +# include_directories(${GTK2_INCLUDE_DIRS}) +# add_executable(mygui mygui.cc) +# target_link_libraries(mygui ${GTK2_LIBRARIES}) +# endif() +# + +#============================================================================= +# Copyright 2009 Kitware, Inc. +# Copyright 2008-2009 Philip Lowman philip@yhbt.com +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +# Version 0.8 (1/4/2010) +# * Get module working under MacOSX fink by adding /sw/include, /sw/lib +# to PATHS and the gobject library +# Version 0.7 (3/22/09) +# * Checked into CMake CVS +# * Added versioning support +# * Module now defaults to searching for GTK if COMPONENTS not specified. +# * Added HKCU prior to HKLM registry key and GTKMM specific environment +# variable as per mailing list discussion. +# * Added lib64 to include search path and a few other search paths where GTK +# may be installed on Unix systems. +# * Switched to lowercase CMake commands +# * Prefaced internal variables with _GTK2 to prevent collision +# * Changed internal macros to functions +# * Enhanced documentation +# Version 0.6 (1/8/08) +# Added GTK2_SKIP_MARK_AS_ADVANCED option +# Version 0.5 (12/19/08) +# Second release to cmake mailing list + +#============================================================= +# _GTK2_GET_VERSION +# Internal function to parse the version number in gtkversion.h +# _OUT_major = Major version number +# _OUT_minor = Minor version number +# _OUT_micro = Micro version number +# _gtkversion_hdr = Header file to parse +#============================================================= +function(_GTK2_GET_VERSION _OUT_major _OUT_minor _OUT_micro _gtkversion_hdr) + file(READ ${_gtkversion_hdr} _contents) + if(_contents) + string(REGEX REPLACE ".*#define GTK_MAJOR_VERSION[ \t]+\(([0-9]+)\).*" "\1" ${_OUT_major} "${_contents}") + string(REGEX REPLACE ".*#define GTK_MINOR_VERSION[ \t]+\(([0-9]+)\).*" "\1" ${_OUT_minor} "${_contents}") + string(REGEX REPLACE ".*#define GTK_MICRO_VERSION[ \t]+\(([0-9]+)\).*" "\1" ${_OUT_micro} "${_contents}") + + if(NOT ${_OUT_major} MATCHES "[0-9]+") + message(FATAL_ERROR "Version parsing failed for GTK2_MAJOR_VERSION!") + endif() + if(NOT ${_OUT_minor} MATCHES "[0-9]+") + message(FATAL_ERROR "Version parsing failed for GTK2_MINOR_VERSION!") + endif() + if(NOT ${_OUT_micro} MATCHES "[0-9]+") + message(FATAL_ERROR "Version parsing failed for GTK2_MICRO_VERSION!") + endif() + + set(${_OUT_major} ${${_OUT_major}} PARENT_SCOPE) + set(${_OUT_minor} ${${_OUT_minor}} PARENT_SCOPE) + set(${_OUT_micro} ${${_OUT_micro}} PARENT_SCOPE) + else() + message(FATAL_ERROR "Include file ${_gtkversion_hdr} does not exist") + endif() +endfunction() + +#============================================================= +# _GTK2_FIND_INCLUDE_DIR +# Internal function to find the GTK include directories +# _var = variable to set +# _hdr = header file to look for +#============================================================= +function(_GTK2_FIND_INCLUDE_DIR _var _hdr) + + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "_GTK2_FIND_INCLUDE_DIR( ${_var} ${_hdr} )") + endif() + + set(_relatives + # FIXME + glibmm-2.4 + glib-2.0 + atk-1.0 + atkmm-1.6 + cairo + cairomm-1.0 + gdkmm-2.4 + giomm-2.4 + gtk-2.0 + gtkmm-2.4 + libglade-2.0 + libglademm-2.4 + pango-1.0 + pangomm-1.4 + sigc++-2.0 + ) + + set(_suffixes) + foreach(_d ${_relatives}) + list(APPEND _suffixes ${_d}) + list(APPEND _suffixes ${_d}/include) # for /usr/lib/gtk-2.0/include + endforeach() + + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "include suffixes = ${_suffixes}") + endif() + + find_path(${_var} ${_hdr} + PATHS + /usr/local/lib64 + /usr/local/lib + /usr/lib64 + /usr/lib + /opt/gnome/include + /opt/gnome/lib + /opt/openwin/include + /usr/openwin/lib + /sw/include + /sw/lib + $ENV{GTKMM_BASEPATH}/include + $ENV{GTKMM_BASEPATH}/lib + [HKEY_CURRENT_USER\SOFTWARE\gtkmm\2.4;Path]/include + [HKEY_CURRENT_USER\SOFTWARE\gtkmm\2.4;Path]/lib + [HKEY_LOCAL_MACHINE\SOFTWARE\gtkmm\2.4;Path]/include + [HKEY_LOCAL_MACHINE\SOFTWARE\gtkmm\2.4;Path]/lib + PATH_SUFFIXES + ${_suffixes} + ) + + if(${_var}) + set(GTK2_INCLUDE_DIRS ${GTK2_INCLUDE_DIRS} ${${_var}} PARENT_SCOPE) + if(NOT GTK2_SKIP_MARK_AS_ADVANCED) + mark_as_advanced(${_var}) + endif() + endif() + +endfunction(_GTK2_FIND_INCLUDE_DIR) + +#============================================================= +# _GTK2_FIND_LIBRARY +# Internal function to find libraries packaged with GTK2 +# _var = library variable to create +#============================================================= +function(_GTK2_FIND_LIBRARY _var _lib _expand_vc _append_version) + + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "_GTK2_FIND_LIBRARY( ${_var} ${_lib} ${_expand_vc} ${_append_version} )") + endif() + + # Not GTK versions per se but the versions encoded into Windows + # import libraries (GtkMM 2.14.1 has a gtkmm-vc80-2_4.lib for example) + # Also the MSVC libraries use _ for . (this is handled below) + set(_versions 2.20 2.18 2.16 2.14 2.12 + 2.10 2.8 2.6 2.4 2.2 2.0 + 1.20 1.18 1.16 1.14 1.12 + 1.10 1.8 1.6 1.4 1.2 1.0) + + set(_library) + set(_library_d) + + set(_library ${_lib}) + + if(_expand_vc) + # Add vc80/vc90 midfixes + if(MSVC80) + set(_library ${_library}-vc80) + set(_library_d ${_library}-d) + elseif(MSVC90) + set(_library ${_library}-vc90) + set(_library_d ${_library}-d) + endif() + endif() + + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "After midfix addition = ${_library} and ${_library_d}") + endif() + + set(_lib_list) + set(_libd_list) + if(_append_version) + foreach(_ver ${_versions}) + list(APPEND _lib_list "${_library}-${_ver}") + list(APPEND _libd_list "${_library_d}-${_ver}") + endforeach() + else() + set(_lib_list ${_library}) + set(_libd_list ${_library_d}) + endif() + + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "library list = ${_lib_list} and library debug list = ${_libd_list}") + endif() + + # For some silly reason the MSVC libraries use _ instead of . + # in the version fields + if(_expand_vc AND MSVC) + set(_no_dots_lib_list) + set(_no_dots_libd_list) + foreach(_l ${_lib_list}) + string(REPLACE "." "_" _no_dots_library ${_l}) + list(APPEND _no_dots_lib_list ${_no_dots_library}) + endforeach() + # And for debug + set(_no_dots_libsd_list) + foreach(_l ${_libd_list}) + string(REPLACE "." "_" _no_dots_libraryd ${_l}) + list(APPEND _no_dots_libd_list ${_no_dots_libraryd}) + endforeach() + + # Copy list back to original names + set(_lib_list ${_no_dots_lib_list}) + set(_libd_list ${_no_dots_libd_list}) + endif() + + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "While searching for ${_var}, our proposed library list is ${_lib_list}") + endif() + + find_library(${_var} + NAMES ${_lib_list} + PATHS + /opt/gnome/lib + /opt/gnome/lib64 + /usr/openwin/lib + /usr/openwin/lib64 + /sw/lib + $ENV{GTKMM_BASEPATH}/lib + [HKEY_CURRENT_USER\SOFTWARE\gtkmm\2.4;Path]/lib + [HKEY_LOCAL_MACHINE\SOFTWARE\gtkmm\2.4;Path]/lib + ) + + if(_expand_vc AND MSVC) + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "While searching for ${_var}_DEBUG our proposed library list is ${_libd_list}") + endif() + + find_library(${_var}_DEBUG + NAMES ${_libd_list} + PATHS + $ENV{GTKMM_BASEPATH}/lib + [HKEY_CURRENT_USER\SOFTWARE\gtkmm\2.4;Path]/lib + [HKEY_LOCAL_MACHINE\SOFTWARE\gtkmm\2.4;Path]/lib + ) + + if(${_var} AND ${_var}_DEBUG) + if(NOT GTK2_SKIP_MARK_AS_ADVANCED) + mark_as_advanced(${_var}_DEBUG) + endif() + set(GTK2_LIBRARIES ${GTK2_LIBRARIES} optimized ${${_var}} debug ${${_var}_DEBUG}) + set(GTK2_LIBRARIES ${GTK2_LIBRARIES} PARENT_SCOPE) + endif() + else() + if(NOT GTK2_SKIP_MARK_AS_ADVANCED) + mark_as_advanced(${_var}) + endif() + set(GTK2_LIBRARIES ${GTK2_LIBRARIES} ${${_var}}) + set(GTK2_LIBRARIES ${GTK2_LIBRARIES} PARENT_SCOPE) + # Set debug to release + set(${_var}_DEBUG ${${_var}}) + set(${_var}_DEBUG ${${_var}} PARENT_SCOPE) + endif() +endfunction(_GTK2_FIND_LIBRARY) + +#============================================================= + +# +# main() +# + +set(GTK2_FOUND) +set(GTK2_INCLUDE_DIRS) +set(GTK2_LIBRARIES) + +if(NOT GTK2_FIND_COMPONENTS) + # Assume they only want GTK + set(GTK2_FIND_COMPONENTS gtk) +endif() + +# +# If specified, enforce version number +# +if(GTK2_FIND_VERSION) + cmake_minimum_required(VERSION 2.6.2) + set(GTK2_FAILED_VERSION_CHECK true) + if(GTK2_DEBUG) + message(STATUS "[FindGTK2.cmake:${CMAKE_CURRENT_LIST_LINE}] " + "Searching for version ${GTK2_FIND_VERSION}") + endif() + _GTK2_FIND_INCLUDE_DIR(GTK2_GTK_INCLUDE_DIR gtk/gtk.h) + if(GTK2_GTK_INCLUDE_DIR) + _GTK2_GET_VERSION(GTK2_MAJOR_VERSION + GTK2_MINOR_VERSION + GTK2_PATCH_VERSION + ${GTK2_GTK_INCLUDE_DIR}/gtk/gtkversion.h) + set(GTK2_VERSION + ${GTK2_MAJOR_VERSION}.${GTK2_MINOR_VERSION}.${GTK2_PATCH_VERSION}) + if(GTK2_FIND_VERSION_EXACT) + if(GTK2_VERSION VERSION_EQUAL GTK2_FIND_VERSION) + set(GTK2_FAILED_VERSION_CHECK false) + endif() + else() + if(GTK2_VERSION VERSION_EQUAL GTK2_FIND_VERSION OR + GTK2_VERSION VERSION_GREATER GTK2_FIND_VERSION) + set(GTK2_FAILED_VERSION_CHECK false) + endif() + endif() + else() + # If we can't find the GTK include dir, we can't do version checking + if(GTK2_FIND_REQUIRED AND NOT GTK2_FIND_QUIETLY) + message(FATAL_ERROR "Could not find GTK2 include directory") + endif() + return() + endif() + + if(GTK2_FAILED_VERSION_CHECK) + if(GTK2_FIND_REQUIRED AND NOT GTK2_FIND_QUIETLY) + if(GTK2_FIND_VERSION_EXACT) + message(FATAL_ERROR "GTK2 version check failed. Version ${GTK2_VERSION} was found, version ${GTK2_FIND_VERSION} is needed exactly.") + else() + message(FATAL_ERROR "GTK2 version check failed. Version ${GTK2_VERSION} was found, at least version ${GTK2_FIND_VERSION} is required") + endif() + endif() + + # If the version check fails, exit out of the module here + return() + endif() +endif() + +# +# Find all components +# + +foreach(_GTK2_component ${GTK2_FIND_COMPONENTS}) + if(_GTK2_component STREQUAL "gtk") + _GTK2_FIND_INCLUDE_DIR(GTK2_GLIB_INCLUDE_DIR glib.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GLIBCONFIG_INCLUDE_DIR glibconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GLIB_LIBRARY glib false true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_GOBJECT_INCLUDE_DIR gobject/gobject.h) + _GTK2_FIND_LIBRARY (GTK2_GOBJECT_LIBRARY gobject false true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_GDK_INCLUDE_DIR gdk/gdk.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GDKCONFIG_INCLUDE_DIR gdkconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GDK_LIBRARY gdk-x11 false true) + _GTK2_FIND_LIBRARY (GTK2_GDK_LIBRARY gdk-win32 false true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_GTK_INCLUDE_DIR gtk/gtk.h) + _GTK2_FIND_LIBRARY (GTK2_GTK_LIBRARY gtk-x11 false true) + _GTK2_FIND_LIBRARY (GTK2_GTK_LIBRARY gtk-win32 false true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_CAIRO_INCLUDE_DIR cairo.h) + _GTK2_FIND_LIBRARY (GTK2_CAIRO_LIBRARY cairo false false) + + _GTK2_FIND_INCLUDE_DIR(GTK2_PANGO_INCLUDE_DIR pango/pango.h) + _GTK2_FIND_LIBRARY (GTK2_PANGO_LIBRARY pango false true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_ATK_INCLUDE_DIR atk/atk.h) + _GTK2_FIND_LIBRARY (GTK2_ATK_LIBRARY atk false true) + + #elseif(_GTK2_component STREQUAL "gdk_pixbuf") + #_GTK2_FIND_INCLUDE_DIR(GTK2_GDKPIXBUF_INCLUDE_DIR gdk-pixbuf/gdk-pixbuf.h) + #_GTK2_FIND_LIBRARY (GTK2_GDKPIXBUF_LIBRARY gdk_pixbuf false true) + + elseif(_GTK2_component STREQUAL "gtkmm") + + _GTK2_FIND_INCLUDE_DIR(GTK2_GLIBMM_INCLUDE_DIR glibmm.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GLIBMMCONFIG_INCLUDE_DIR glibmmconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GLIBMM_LIBRARY glibmm true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_GDKMM_INCLUDE_DIR gdkmm.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GDKMMCONFIG_INCLUDE_DIR gdkmmconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GDKMM_LIBRARY gdkmm true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_GTKMM_INCLUDE_DIR gtkmm.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GTKMMCONFIG_INCLUDE_DIR gtkmmconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GTKMM_LIBRARY gtkmm true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_CAIROMM_INCLUDE_DIR cairomm/cairomm.h) + _GTK2_FIND_LIBRARY (GTK2_CAIROMM_LIBRARY cairomm true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_PANGOMM_INCLUDE_DIR pangomm.h) + _GTK2_FIND_LIBRARY (GTK2_PANGOMM_LIBRARY pangomm true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_SIGC++_INCLUDE_DIR sigc++/sigc++.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_SIGC++CONFIG_INCLUDE_DIR sigc++config.h) + _GTK2_FIND_LIBRARY (GTK2_SIGC++_LIBRARY sigc true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_GIOMM_INCLUDE_DIR giomm.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GIOMMCONFIG_INCLUDE_DIR giommconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GIOMM_LIBRARY giomm true true) + + _GTK2_FIND_INCLUDE_DIR(GTK2_ATKMM_INCLUDE_DIR atkmm.h) + _GTK2_FIND_LIBRARY (GTK2_ATKMM_LIBRARY atkmm true true) + + elseif(_GTK2_component STREQUAL "glade") + + _GTK2_FIND_INCLUDE_DIR(GTK2_GLADE_INCLUDE_DIR glade/glade.h) + _GTK2_FIND_LIBRARY (GTK2_GLADE_LIBRARY glade false true) + + elseif(_GTK2_component STREQUAL "glademm") + + _GTK2_FIND_INCLUDE_DIR(GTK2_GLADEMM_INCLUDE_DIR libglademm.h) + _GTK2_FIND_INCLUDE_DIR(GTK2_GLADEMMCONFIG_INCLUDE_DIR libglademmconfig.h) + _GTK2_FIND_LIBRARY (GTK2_GLADEMM_LIBRARY glademm true true) + + else() + message(FATAL_ERROR "Unknown GTK2 component ${_component}") + endif() +endforeach() + +# +# Solve for the GTK2 version if we haven't already +# +if(NOT GTK2_FIND_VERSION AND GTK2_GTK_INCLUDE_DIR) + _GTK2_GET_VERSION(GTK2_MAJOR_VERSION + GTK2_MINOR_VERSION + GTK2_PATCH_VERSION + ${GTK2_GTK_INCLUDE_DIR}/gtk/gtkversion.h) + set(GTK2_VERSION ${GTK2_MAJOR_VERSION}.${GTK2_MINOR_VERSION}.${GTK2_PATCH_VERSION}) +endif() + +# +# Try to enforce components +# + +set(_GTK2_did_we_find_everything true) # This gets set to GTK2_FOUND + +include(FindPackageHandleStandardArgs) + +foreach(_GTK2_component ${GTK2_FIND_COMPONENTS}) + string(TOUPPER ${_GTK2_component} _COMPONENT_UPPER) + + if(_GTK2_component STREQUAL "gtk") + FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK2_${_COMPONENT_UPPER} "Some or all of the gtk libraries were not found." + GTK2_GTK_LIBRARY + GTK2_GTK_INCLUDE_DIR + + GTK2_GLIB_INCLUDE_DIR + GTK2_GLIBCONFIG_INCLUDE_DIR + GTK2_GLIB_LIBRARY + + GTK2_GDK_INCLUDE_DIR + GTK2_GDKCONFIG_INCLUDE_DIR + GTK2_GDK_LIBRARY + ) + elseif(_GTK2_component STREQUAL "gtkmm") + FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK2_${_COMPONENT_UPPER} "Some or all of the gtkmm libraries were not found." + GTK2_GTKMM_LIBRARY + GTK2_GTKMM_INCLUDE_DIR + GTK2_GTKMMCONFIG_INCLUDE_DIR + + GTK2_GLIBMM_INCLUDE_DIR + GTK2_GLIBMMCONFIG_INCLUDE_DIR + GTK2_GLIBMM_LIBRARY + + GTK2_GDKMM_INCLUDE_DIR + GTK2_GDKMMCONFIG_INCLUDE_DIR + GTK2_GDKMM_LIBRARY + ) + elseif(_GTK2_component STREQUAL "glade") + FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK2_${_COMPONENT_UPPER} "The glade library was not found." + GTK2_GLADE_LIBRARY + GTK2_GLADE_INCLUDE_DIR + ) + elseif(_GTK2_component STREQUAL "glademm") + FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK2_${_COMPONENT_UPPER} "The glademm library was not found." + GTK2_GLADEMM_LIBRARY + GTK2_GLADEMM_INCLUDE_DIR + GTK2_GLADEMMCONFIG_INCLUDE_DIR + ) + endif() + + if(NOT GTK2_${_COMPONENT_UPPER}_FOUND) + set(_GTK2_did_we_find_everything false) + endif() +endforeach() + +if(_GTK2_did_we_find_everything AND NOT GTK2_VERSION_CHECK_FAILED) + set(GTK2_FOUND true) +else() + # Unset our variables. + set(GTK2_FOUND false) + set(GTK2_VERSION) + set(GTK2_VERSION_MAJOR) + set(GTK2_VERSION_MINOR) + set(GTK2_VERSION_PATCH) + set(GTK2_INCLUDE_DIRS) + set(GTK2_LIBRARIES) +endif() + +if(GTK2_INCLUDE_DIRS) + list(REMOVE_DUPLICATES GTK2_INCLUDE_DIRS) +endif() +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/HACKING URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/HA... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/HACKING (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/HACKING Mon Aug 1 09:44:45 2011 @@ -0,0 +1,250 @@ +/* Documentation on YaST-GTK */ + +YaST basis + + Yast-core is a simple byte-code interpreted programming + language. On execution, two threads will be fired: one + for the interpreter that runs the YCP application, the other + for the interface. This is so because a user may want + to setup another machine while sitting confortably + with its GTK+ interface. + + Yast-core offers an interface API, called YWidgets, that + is then wrapped, by dynamic loading at run-time, by + one of the available interface modules that the user + specified. This code is one of these modules. + + Yast is usually distributed with the /sbin/yast script + that automatically looks for the best interface for + the environment. + Yast-core is located at /usr/lib/YaST2/bin/y2base , + on the Suse distribution. + + You don't need to know to program YCP, but you should + have a look at its API because it will give you some + insight into the platform. Yast documentation: + http://forgeftp.novell.com//yast/doc/SL10.1/tdg/ + +Code structure + + Hierarchy of some of the widgets: + + YGWidget YWidget + | | + | /| + | / | + |\ YContainerWidget | + |\ / | + |\YGSplit | + |\YGEmpty | + |\YGSpacing | + | \YSSquash | + | YGAlignment /-------| + | / | + |\ YLabel | + | \ / | + | ---YGLabel | + | | + | | + |\ /| + | \ YTextEntry | + | YGLabeledWidget / | + | | \ / | + | | YGTextEntry | + | | | + | \ | + | YGScrolledWidget /---| + | \ / | + | \ YRichText | + | \ / | + | YGRichText | + | | + |..........................| + + + As you may see, everything inherits from both YGWidget + and YWidget. YWidget derivated classes provide virtual functions + that we must fill. A pointer to the interface class wrapper may + always be obtain with widgetRep(). + + On the other hand, YGWidget provides a common set of functionaly + with great value for code re-use and also makes the code more + robust and flexible since it provides an infrastructure to the + whole code. + YGWidgets always creates an associated GtkWidget object, as asked + by the class that inherits it, and that can be obtained via + getWidget(). + + Every YGWidget has an associated parent, with the single exception + of YGDialog, that provides their children with a GtkFixed container + that they sit upon. getFixed() is the recursive function that asks + the parent for its associated GtkFixed. Container widgets replace + getFixed() by one that returns a GtkFixed that they created, thus + closing the cycle. + + YGLabeledWidget and YGSrolledWidget are simple children of + YGWidget that create the asked widget with a label or inside + a GtkScrolledWindow for code re-use value. + + The force behind the all thing is in YGUI. Its header has the declaration + for the creation of all the widgets, while its implementation is done + on the widgets files, and also specifies whatever one widget is + supported or not. YGUI.cc has, for the most part, the code that switches + between our code and the main YWidget, since we all use the same thread. + +On specific widgets + + Layout widgets + + Old approach + What yast-ncurses and -qt do, and we used to follow, is to let + YWidget layout framework do that work for us. + + Every container widget we implemented had an associated GtkFixed + that it returned with a getFixed(). Other widgets getFixed() + would request the parent's getFixed(). + + However this approach was actually quite complicated, because we + had to implement nicesize() and setSize() on container widgets in + a way that the containee would be placed there correctly. So, for + instance, for YGDumbTab, we had to request informations like the + tabs labels height, the frame around the page, etc. Furthermore, + the ultimate decision to drop the approach was when we redesigned + YGtkWizard. This time the steps widget was dynamic and the work + to check of any size change and report to YWidget just felt too + hackish. Attempts at abstracting this were successful, but not + very satisfactory from a code's point of view. + + Current approach + Currently, we just implement the layout widgets ourselves. The + most important is YGSplit, which would be the equivalent of + GtkBox (though we use our YGtkRatioBox to support the weight + childs attribute). + + The YGLayout code is just a little bigger than the previous, it's + easy to understand, and overall it made the code base smaller. + Furthermore, YWidgets had bugs like labels getting truncated + (which had to be avoided by an explicit request for recalculating + the whole layout geometry) that we don't suffer. The layout is + also a lot faster. + + To set minimum size to widgets, to make them look prettier, we + install all widgets in our YGtkMinSize container. To be able + to honor the stretchable attribute, we also install this + into a GtkAlignment. So, YGWidget's getWidget() returns the + inner widget that is the one that our YWidget implementation ask + it to create, while getLayout() should be used for layouts to get + the outter widget, which would be the GtkAlignment. Furthermore + the GtkAlignment allows us to align the widget on YGAlignment + (which is just a proxy). + + Most of the widgets are a no brainer. Any other widget should have + some comments as appropriate. + +Code practices + + As you may have noticed from YGWidget.h, we use macros a lot to + enable common functionality, instead of virtual functions. This is + because simply in a few cases virtual functions would not apply -- + ie. the need to call a parent class function. You would have to + override the virtual function everytime. With a macro, we may do: + #define YGLABEL_WIDGET_IMPL_SET_LABEL_CHAIN (ParentClass) \ + virtual setLabel (const YCPString &label) { \ + doSetLabel (label); \ + ParentClass::setLabel (label); \ + } + + The code style is heavily inspired on the Linux guideline: + http://pantransit.reptiles.org/prog/CodingStyle.html + There may be some differences as it obviously doesn't mention + C++ classes. + For indentations, tabs are used. For layout, spaces are used. + Example: + <tab>int variable_with_very_long_name = function (arg1, + <tab>.............................................arg2); + This ensures the code lays well in whatever editor the reader uses. + Lines must just be ensured to be breaked before the 80th column on + 4 stop tabs. + + Speaking of classes, we don't use headers, unless we have to, as + opposite to the other interfaces code. There is no reason to + make them publicaly available and it just adds even more + files to the pool. + Also, we generally don't split classes into declaration and + implementation code, for the same reasons. + + Variables and functions names follow Yast style (variableNameOne), + for the most part. Our Gtk widgets (on ygtk* files) are written + in C and follow this_name_style to fit better with GTK+ one. + We always prefix m_ to class memeber variables. + + For GTK+ widgets, we follow some scheme. Most standing, we avoid declaring + functions, and do the class initilization, where the overloading is set, + at the bottom. When doing a widget, just copy and paste from another. + +Morphing Suse to use Yast-GTK + + We haven't yet managed to create a Suse GTK-based installer, but + these steps should be close to that end. + + 1. Pick the 1st CD of some Suse edition version ISO + (let's say it is named SUSE.iso) + + 2. Ensure you have a live / working gtk+/Gnome install + + 3. Get cramfs (not included on suse cds, just google it and it will be 1st + result), and compile it. + + 4. Tweak the variables in gtkize-iso to suit + + you need a chunk of tmp space to unpack & re-build the iso + + 5. Run ./gtkize-iso SUSE.iso /tmp/new.iso + + 6. Ready to burn and try. + +LibZypp API overview + + The Zypp library is a wrapper around at least two package repository + systems (zmd and rug). It is quite a monster with no clear API or + documentation, thus this section. + + The first thing we need to do when using Zypp is to initializate it. + This is done for us by yast-core, so we don't have to care about that. + + To get a list of packages, you iterate the Zypp pool, that you can get + with zypp::getZYpp()->poolProxy(). So, if you want to iterate through + the package pool, you would do: + for (zypp::ResPoolProxy::const_iterator it = + zypp::getZYpp()->poolProxy().byKindBegin zypp::Package(); + it != zypp::getZYpp()->poolProxy().byKindEnd zypp::Package(); it++) + To iterate through patches, use zypp::Patch as the kind. + + The iterators point to objects of type zypp::ui::Selectable::Ptr, where Ptr + is a boost intrusive_ptr -- so, you may turn that into a C pointers with + a "zypp::ui::Selectable *sel = get_pointer (*it);" + + A Selectable object contains pointers to the installed version and all + available (through repositories) versions of some packages whose name + you may get with a zypp::ui::Selectable::name(). The package it considers + as the best from the available packages is what it calls a candidate. + Check /usr/include/zypp/ui/Selectable.h for its API. + + You may get more details on a given packages by retriving the correspondent + zypp::ResObject from the Selectable of the version you want. + Check /usr/include/zypp/ResObject.h for its API. + + To get even more information, you may cast the zypp::ResObject to a + zypp::Package or zypp::Patch, according to what you are iterating. + Since this is boost pointers we are dealing with, you would cast a + zypp::ResObject::constPtr like + zypp::dynamic_pointer_cast <const zypp::Package> (res_object); + Check /usr/include/zypp/Package.h / Patch.h for their API. + + The reason why our categories tree is build in an ackward way is because you + get the category of a package with zypp::Package::group() which returns a + std::string like "Productivity/Networking/Email/Utilities". You can't build + the tree before you start adding packages since you don't have a way to know + its hierarchy. + + To avoid the lengthy names of Zypp, we use some typedefs and inline helper + functions that you may find right on the start of YGPackageSelector.cc.
Added: branches/SuSE-Code-11-SP2-Branch/gtk/MAINTAINER URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/MA... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/MAINTAINER (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/MAINTAINER Mon Aug 1 09:44:45 2011 @@ -0,0 +1,2 @@ +Ricardo Cruz rpmcruz@alunos.dcc.fc.up.pt \ +Michael Meeks michael.meeks@novell.com
Added: branches/SuSE-Code-11-SP2-Branch/gtk/Makefile URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/Ma... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/Makefile (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/Makefile Mon Aug 1 09:44:45 2011 @@ -0,0 +1,21 @@ +# Makefile meant only for convenience and to provide a nicer +# interface for the newcomer. + +all: + make -f Makefile.cvs + +install: + make -f Makefile.cvs install + +clean: + rm -rf build + +package: + cd build ; make package_source + mv build/*.tar.bz2 build/package/* package + +test: + build/src/test + +.PHONY: all install clean package test +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/Makefile.cvs URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/Ma... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/Makefile.cvs (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/Makefile.cvs Mon Aug 1 09:44:45 2011 @@ -0,0 +1,23 @@ +# +# Makefile.cvs +# + +LIB = $(shell y2tool get-lib) + +PREFIX = /usr + +configure: + mkdir -p build ;\ + cd build ;\ + cmake -DCMAKE_INSTALL_PREFIX=$(PREFIX) -DLIB=$(LIB) .. && make + +# use "VERBOSE=1 make" to read full gcc argument + +install: configure + cd build ;\ + make && make install + +reconf: + cd build ;\ + cmake rebuild_cache +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/README URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/RE... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/README (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/README Mon Aug 1 09:44:45 2011 @@ -0,0 +1,58 @@ +/* Light information on YaST-GTK */ + +License + + This code is licensed under the GNU Lesser General Public License (LGPL) + version 2, or, at your option, any higher. + +A native GTK+ YaST2 UI ! + + This code creates a GTK+ plugin for YaST2. This plugin parallel + installs with everything else, and will never be used unless + explicitely invoked on the yast2 command line - ie. it won't break + your system; go-on you know you want to install it. + +Build pre-requisites + + To make this compile you'll need the following packages installed + (at a minimum): + + gcc-c++ cmake gtk2-devel yast2-devtools yast2-core-devel yast2-libyui-devel libzypp-devel + + They all are distributed with Suse's CDs. + +Building / Installing + + make && sudo make install [1] + + [1] - if you want sudo not to ask you a password, add yourself to the + wheel group, and uncomment the related lines in /etc/sudoers + +Running + + /usr/lib/YaST2/bin/y2base /usr/share/doc/packages/yast2-core/libyui/examples/HelloWorld.ycp gtk + + From OpenSuse 10.3 on, it should be picked up automatically if you use Gnome. + For other desktops, you can set yast-gtk to be used by editing /etc/sysconfig/yast2 . + + In case of oddness append --nothreads to the cmdline, + Also check the log file: ~/.y2log + +Documentation + + A code overview is given on the HACKING file. + For documentation of YaST in general: + http://developer.novell.com/wiki/index.php/Special:Downloads/yast/doc + +ChangeLog + + Changes associated with version releases are recorded at + package/yast2-gtk.changes + + Individual svn changes can be read by issueing: svn log | less + + Other usage examples: + Last 50 changes: svn log -l 50 + Show also files changed: svn log -v + Changes for a given file: svn log src/YGUI.cc +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/RPMNAME URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/RP... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/RPMNAME (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/RPMNAME Mon Aug 1 09:44:45 2011 @@ -0,0 +1 @@ +yast2-gtk
Added: branches/SuSE-Code-11-SP2-Branch/gtk/TODO URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/TO... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/TODO (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/TODO Mon Aug 1 09:44:45 2011 @@ -0,0 +1,58 @@ +/* List of missing features, actual bugs and other issues */ + +Bugs + + tests/Layout-TruncatedWidget.ycp + unlike yast2-qt, we trigger recalculation events on widget property + changes like the label or the button text. + Difficulty: High - Priority: Low + + Honor YDialog's YWizardDialog directive + see bug 457758. We also want to make multiple Wizard instances + share the same help window. Overall, move a bunch of YGWizard + stuff into YGDialog domain. + Difficulty: Medium - Priority: Low + +Enhancements + + Package manager search syntax + add proper Google syntax support. Consider also adding regex (add + radio boxes to entry's context menu). + Difficulty: Medium - Priority: Low + + YGTextEntry / YGMultiLineEdit + Add undo/redo support. (Possibly work out a patch for GTK+.) + Difficulty: Low - Priority: Medium + + YGtkFindEntry + Deprecate YGtkFindEntry usage in favor of GTK 2.16 GtkEntry new + APIs. Notice the requirements for the Package Manager: change + icon to reflect that of the context menu, and show arrow next + to the search icon. + Difficulty: Low - Priority: Low + + YGPackageSelector.cc: FlexPane class + Deprecate in favor of GTK 2.16 GtkOrientation API for GtkPaned. + Difficulty: Low - Priority: Low + + Installer look + Port or adopt yast2-qt's installer style file. + Difficulty: High - Priority: Low + +Build procedure + + YGRichText + should use GtkHtml when available, so that we get support for tables. + + Road block: the problem here is that Webkit doesn't work well for our + package-selector use (outside a scrolled window). Does it make sense to + keep YGtkRichText for that sole purpose? + Difficulty: Medium - Priority: Medium + +Test Framework + + Regression tests + setup LDTP to run through all the tests/ and provoke as many code-paths + as possible. + Difficulty: High (tiresome) - Priority: Low +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/VERSION URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/VE... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/VERSION (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/VERSION Mon Aug 1 09:44:45 2011 @@ -0,0 +1 @@ +2.21.96
Added: branches/SuSE-Code-11-SP2-Branch/gtk/VERSION.cmake URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/VE... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/VERSION.cmake (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/VERSION.cmake Mon Aug 1 09:44:45 2011 @@ -0,0 +1,3 @@ +SET(VERSION_MAJOR "2") +SET(VERSION_MINOR "21") +SET(VERSION_PATCH "96")
Added: branches/SuSE-Code-11-SP2-Branch/gtk/config.h.cmake URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/co... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/config.h.cmake (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/config.h.cmake Mon Aug 1 09:44:45 2011 @@ -0,0 +1,8 @@ +/* Important paths */ +#cmakedefine THEMEDIR "${THEMEDIR}" +#cmakedefine LOCALEDIR "${LOCALEDIR}" + +/* Version enumerations to develop backward compability */ +#cmakedefine YAST2_VERSION ${YAST2_VERSION} +#cmakedefine ZYPP_VERSION ${ZYPP_VERSION} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/package/yast2-gtk.changes URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/pa... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/package/yast2-gtk.changes (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/package/yast2-gtk.changes Mon Aug 1 09:44:45 2011 @@ -0,0 +1,1177 @@ +------------------------------------------------------------------- +Mon Jun 20 10:52:12 CET 2011 - tgoettlicher@suse.de + +- fixed build on SLE11SP2 (missing cmake module) + +------------------------------------------------------------------- +Sat Apr 16 13:51:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.96 +- Tree widget: add recursiveSelection flag from yast2-libyui +2.20.3. + +------------------------------------------------------------------- +Fri Apr 15 20:45:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.95 +- use same keyboard string mnemonics as rest of yast2, to +avoid confusing translators. (bnc#686452). + +------------------------------------------------------------------- +Wed Mar 16 10:50:31 CET 2011 - tgoettlicher@suse.de + +- 2.21.94 +- fixed dependencies (bnc#667938) + +------------------------------------------------------------------- +Fri Mar 04 09:07:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.93 +- firewall tool crash fix (bnc#677007). +- refer to LibreOffice rather than OpenOffice.org in help text +(bnc#675486). + +------------------------------------------------------------------- +Fri Feb 11 15:05:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.92 +- fixed freeze in webpin search tool, and possibly others +(bnc#670625). + +------------------------------------------------------------------- +Tue Feb 08 18:28:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.91 +- fixed compatibility with upcoming yast2-libyui 2.20.1. + +------------------------------------------------------------------- +Tue Feb 01 22:43:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.90 +- printer crash fix: printer tool was misbehaving, somestimes +crashing, when selecting tree item. (Reported by Atri Bhattacharya.) + +------------------------------------------------------------------- +Fri Jan 28 18:03:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.89 +- sw_single bug fix: the versions box is showing the to-install +as marked for all versions, not merely the one who is actually +going to get installed. (Reported by Atri.) + +------------------------------------------------------------------- +Thu Jan 20 20:22:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.88 +- sw_single: using buttons instead of radios for the +versions-box. +- allow moving window by dragging the header. + +------------------------------------------------------------------- +Wed Jan 05 23:00:00 WET 2011 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.87 +- bug fix: YTree was crashing with new libyui +- YMultiSelectionBox and YTree: show selection count +when main widget +- sw_single: show "re-install" popup menu option +for single package selections. + +------------------------------------------------------------------- +Tue Dec 23 03:10:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.86 +- bug fix: combo box honor selection flag. + +------------------------------------------------------------------- +Tue Dec 23 02:57:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.85 +- cope with upcoming libyui 2.19.2: implemented new +YTree::multi-selection flag. + +------------------------------------------------------------------- +Tue Dec 15 13:13:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.84 +- code to close packagekit deamon when running will be moved +to the sw_single ycp level (bnc#659522) + +------------------------------------------------------------------- +Thu Dec 09 23:33:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.83 +- fix: can't use combo-box popup menu when items are too +long (bnc#595560). +- table fix: not showing all popup entries on right-click. +- log view fix: fixed flickering, and allow user to freely +scroll. +- sw_single: use same height for "all packages" item, and +keep items sorted by name when sorting by status or another +column. + +------------------------------------------------------------------- +Wed Nov 10 18:07:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.82 +- tables: when pressing the context-menu key, show the popup +menu next to the selected row, not under the mouse cursor. +- sw_single: use bold for "all packages" or "all patches" row. + +------------------------------------------------------------------- +Mon Nov 01 16:49:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.81 +- fix build for 11.2 (added cmake requirement version). + +------------------------------------------------------------------- +Mon Nov 01 16:49:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.80 +- fix: show the software manager header bar. + +------------------------------------------------------------------- +Sun Oct 31 18:43:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.79 +- work-around bug in global-menubar-applet, which isn't +showing the software manager menu bar (bnc#595560) +- improved performance of the upgrades filter. + +------------------------------------------------------------------- +Wed Oct 20 21:06:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.78 +- fixes crash on inetd when sorting table (bnc#620513) + +------------------------------------------------------------------- +Fri Oct 08 21:36:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.77 +- another string review. +- added mnemonics to menu-bar. + +------------------------------------------------------------------- +Thu Oct 07 13:22:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.76 +- sw_single: run solver at end, fixes bnc#620513. +- rpm spec fix (now compatible with both 11.3 and factory). +- got rid of included FindGTK2.cmake module. +- overall string review. +- text domain changed from "yast2-gtk" to "gtk". + +------------------------------------------------------------------- +Mon Aug 30 10:48:27 CEST 2010 - vuntz@opensuse.org + +- Add yast2-gtk-fix-FindGTK2-gdk-pixbuf.patch to fix build with + gtk2 2.21.x: gdk-pixbuf was split and the include paths are + different now. + +------------------------------------------------------------------- +Mon Aug 23 00:40:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.70 +- sw_single: package count wrong on the filters box when +show-debug or show-devel when de-selected (bcn#632377) +- fixed potential crash when file picking file of esoteric +filename (bnc#633173) +- window size too small when using big fonts: correlate +window size to font size (bnc#633498) + +------------------------------------------------------------------- +Mon Jul 26 19:28:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.69 +- sw_single: fix file > import / export menu items (bcn#624310) + +------------------------------------------------------------------- +Wed Jul 14 13:39:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.68 +- sw_single: fixed duplicate separator item in context menu. + +------------------------------------------------------------------- +Wed Jul 14 11:41:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.67 +- build for bnc#620513 patch. + +------------------------------------------------------------------- +Tue Jul 13 16:04:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.66 +- adjust window size to cope with big fonts. + +------------------------------------------------------------------- +Mon Jul 12 15:32:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.65 +- more comsmetic fixes related to bnc#620513. + +------------------------------------------------------------------- +Sat Jul 10 21:56:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.64 +- comsmetic fixes related to bnc#620513. + +------------------------------------------------------------------- +Sat Jul 10 18:42:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.63 +- typo correction. + +------------------------------------------------------------------- +Sat Jul 10 18:32:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.62 +- fix sw_single post-procedure scrolling glitch. + +------------------------------------------------------------------- +Sat Jul 10 13:49:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.61 +- dependencies box sometimes freezes when open (bnc#620513). + +------------------------------------------------------------------- +Wed Jul 06 20:10:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.60 +- versions box not visible enough: swap position with +details box (bnc#620513). + +------------------------------------------------------------------- +Tue Jul 05 18:32:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.59 +- ommit online-update configuration menu item when +not running online-update (bnc#609778s) + +------------------------------------------------------------------- +Fri Jul 02 13:25:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.58 +- sw_single window too big for some screens (bnc#618951) + +------------------------------------------------------------------- +Fri Jun 26 15:37:00 WET 2010 - badshah400@gmail.com + +- 2.21.57 +- don't steal window focus (bnc#615927) + +------------------------------------------------------------------- +Sun Jun 13 10:28:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.56 +- don't reset zypp options. +- omit options menu for online update. + +------------------------------------------------------------------- +Fri Jun 11 17:45:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.55 +- qt plugin gap: "cleanup deps" and "allow vendor" flags +missing. + +------------------------------------------------------------------- +Tue Jun 8 17:35:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.54 +- honor run solver dialog's cancel button, on switch repository. + +------------------------------------------------------------------- +Tue Jun 8 17:12:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.53 +- internationalization of some files broken (bnc#610313) +- solver not automatically run on repository switch and +on a couple of the menus, when dependencies check enabled. + +------------------------------------------------------------------- +Sun Apr 25 11:15:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.52 +- sw_single: undo window: show 'inconsistent' when +env variable PKGMGR_ACTION_AT_EXIT==summary. + +------------------------------------------------------------------- +Sun Apr 25 10:48:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.51 +- sw_single: upgrades: don't lose patch information after +package is set for upgrade. + +------------------------------------------------------------------- +Fri Apr 23 23:15:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.50 +- sw_single: history window: fixed potential crash. + +------------------------------------------------------------------- +Fri Apr 23 20:16:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.49 +- sw_single: history window fix: show downgrades and +re-installs as such, not upgrades. +- sw_single list: show patch mark next to version number, +not name. + +------------------------------------------------------------------- +Fri Apr 23 12:16:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.48 +- sw_single: added patch mark for upgrades. + +------------------------------------------------------------------- +Fri Apr 23 08:59:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.47 +- sw_single: bug fix: patches were being (redundantly) +marked as "auto" in the history window. + +------------------------------------------------------------------- +Thu Apr 22 15:52:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.46 +- sw_single: bug fix: ups: broke the is-installed flag +for patterns in the previous version. +- sw_single (main list and history dialog): bug fix: on +arabic, tooltips were showing in the wrong columns. +- sw_single: don't show patterns view when none is available. + +------------------------------------------------------------------- +Wed Apr 21 13:09:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.45 +- online_update: several fixes, that encompassthe list and the +version widgets. + +------------------------------------------------------------------- +Tue Apr 20 15:53:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.44 +- sw_single: fixed visually disconsonant sub-lines between +the package lists and the repository/pattern ones. + +------------------------------------------------------------------- +Tue Apr 20 14:02:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.43 +- online_update: several fixes, especially in the versions +lists. +- sw_single: bug fix: history window wouldn't function +when closing by pressing Escape. +- sw_single: bug fix: upgrade button wouldn't show up. +- sw_single: show summary lines in gray. + +------------------------------------------------------------------- +Mon Apr 19 14:39:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.42 +- wizard & sw_single: several bug fixes for users of +esoteric color schemes. + +------------------------------------------------------------------- +Mon Apr 19 11:51:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.41 +- sw_single: fix patterns filter padding on the headers. + +------------------------------------------------------------------- +Mon Apr 19 11:32:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.40 +- sw_single: fix glitches while loading the sidebar count +numbers. + +------------------------------------------------------------------- +Sun Apr 18 17:26:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.39 +- sw_single: bug fix: upgrade button should not be selected +when the button presses anywhere in the column it is in. + +------------------------------------------------------------------- +Thu Apr 15 14:23:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.38 +- sw_single: arabic fixes on the detail box. +- table: using gtk stock image renderer. + +------------------------------------------------------------------- +Wed Apr 14 12:32:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.37 +- sw_single: added visual feedback that history dialog may +take a bit to load, and cached it for repetitive use. + +------------------------------------------------------------------- +Sun Apr 11 14:42:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.36 +- sw_single: some polishing to the history dialog. + +------------------------------------------------------------------- +Sun Apr 11 13:55:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.35 +- sw_single: added go-to as a context-menu entry to the +history dialog. + +------------------------------------------------------------------- +Sun Apr 11 12:39:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.34 +- sw_single: fixed crash on history dialog when parsing some +logs. +- selection-box, multi-box and tree widgets: fixed crash on +right-click. + +------------------------------------------------------------------- +Sat Apr 10 16:29:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.33 +- sw_single: several improvements to the "history of changes" +window. +- over-load GTK Arabic treatment for lists and trees (bnc#594840) + +------------------------------------------------------------------- +Fri Apr 02 23:23:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.32 +- sw_single: bug fix: find entry didn't show clear icon when +requires or provides links were clicked. +- button icons: map icons for untranslated terms. + +------------------------------------------------------------------- +Thu Apr 01 04:42:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.31 +- sw_single: added history of changes dialog. +- sw_single: align side-bar count number to the right. + +------------------------------------------------------------------- +Thu Mar 25 18:54:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.30 +- sw_single: speed up query a little bit. +- sw_single: show busy cursor when modifying some list item. +- sw_single: disable Lock context-menu item when package is being +installed or remove, in which case it won't have an effect. +And replaced I/U/R by Undo when package changed. + +------------------------------------------------------------------- +Thu Mar 25 05:22:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.29 +- sw_single: bug fix2: highlight could (still) get mangled +when searching for more than one keyword, when the 2nd was part +of a tag. + +------------------------------------------------------------------- +Thu Mar 25 01:05:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.28 +- sw_single: bug fix: various glitches when searching for +certain small sub-strings. +- sw_single: speed fix: pin-down the cause of the overall +slowness of a query update. + +------------------------------------------------------------------- +Wed Mar 24 15:56:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.27 +- sw_single: improved yielding of our refresh pseudo-thread. + +------------------------------------------------------------------- +Sun Mar 21 15:26:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.26 +- fixed crash when clicking on the search patterns tooltip. + +------------------------------------------------------------------- +Sun Mar 21 14:54:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.25 +- repo view: added undo button. +- bug fix: repositories count was broken, as was the 'none' entry. +- wizard look: set buttons spacing. + +------------------------------------------------------------------- +Sun Mar 21 13:06:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.24 +- changed pkg-manager refresh strategy: it should feel faster. + +------------------------------------------------------------------- +Thu Mar 18 18:37:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.23 +- fixed potential crash. + +------------------------------------------------------------------- +Thu Mar 18 18:27:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.22 +- look fix: enlarge wizard and dialog buttons to reflect settings +from GtkButtonBox. +- look fix: use yellow color for search entry when changed like +Rhythmbox and other gnome apps. + +------------------------------------------------------------------- +Tue Mar 16 14:51:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.21 +- wrong installed number in pattern list (bnc#588561) + +------------------------------------------------------------------- +Thu Mar 11 01:53:45 CET 2010 - ro@suse.de + +- added rpmlintrc as source in the specfile + +------------------------------------------------------------------- +Wed Mar 10 18:16:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.20 +- changed undo line and dialog as response to feedback +by Atri Bhattacharya. +- undo line fix: truncate text when there is not enough +room. + +------------------------------------------------------------------- +Wed Mar 05 00:54:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.19 +- software manager fixes: +- ensured description view visible by default in small +displays. +- fixed crash on search with trailing whitespace. +- show better status-bar string for initial packages set +for install after installing a new system. + +------------------------------------------------------------------- +Wed Mar 04 00:09:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.18 +- online_update enhancements. + +------------------------------------------------------------------- +Wed Mar 03 17:17:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.17 +- fixed strings untranslated (bnc#582148) + +------------------------------------------------------------------- +Fri Feb 25 16:45:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.16 +- several small icon fixes (bnc#582536), (bnc#582560), +(bnc#582533). + +------------------------------------------------------------------- +Fri Feb 24 17:14:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.15 +- change fallback icon for the expert button to Edit (bnc#581800). + +------------------------------------------------------------------- +Fri Feb 24 16:52:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.14 +- GTK maps some icons wrongly (bnc#581800). + +------------------------------------------------------------------- +Fri Feb 24 14:37:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.13 +- software manager bug fix: fixed the dependencies +links to search only for the package name. + +------------------------------------------------------------------- +Fri Feb 23 16:03:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.12 +- html renderer: <li> element and headers ending with colon were +not being given right-to-left treatment for languages like Arabic +(bnc#581800). + +------------------------------------------------------------------- +Fri Feb 22 17:02:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.11 +- extend previous fix to groups of packages. + +------------------------------------------------------------------- +Fri Feb 22 16:26:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.10 +- revert user change when he clicks Cancel on the dependency +solver. + +------------------------------------------------------------------- +Fri Feb 21 19:00:00 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.1 - 2.21.9 +- build attempts. + +------------------------------------------------------------------- +Fri Feb 19 16:51:03 WET 2010 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.21.0 +- Many UI fixes and improvements to the software manager and +online update. +- software manager code now resides in its own library, linked +only when needed. +- several bug fixes. + +------------------------------------------------------------------- +Tue Oct 27 05:23:28 UTC 2009 - coolo@novell.com + +- cherry pick gcc 4.4 issues + +------------------------------------------------------------------- +Fri Oct 21 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.20.1 - 2.20.5 +- build attempts. + +------------------------------------------------------------------- +Fri Oct 21 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.20.0 +- revert to 11.1 code (bcn#547075). + +------------------------------------------------------------------- +Fri Oct 16 15:44:01 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.17 +- fixes crash on online_update (on applies-to property). + +------------------------------------------------------------------- +Fri Oct 16 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.16 +- improved speed up fix: only query libzypp when +respective expander is open. +- bug fix: don't over-suffix "B" to size strings. +- popup confirm dialog, rather than having the user to +double-click apply button. +- don't disable installed tab on repositories view. +- disable version action button if the action is already +undertaken. + +------------------------------------------------------------------- +Fri Oct 02 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.15 +- another try. + +------------------------------------------------------------------- +Fri Oct 02 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.14 +- trigger rebuild. + +------------------------------------------------------------------- +Fri Oct 01 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.13 +- bug fix: failing to map packages to a couple of categories. +- speed fix: improved startup speed. + +------------------------------------------------------------------- +Thu Sep 30 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.11 +- bug fix: package details box now respond to scroll keys. + +------------------------------------------------------------------- +Thu Sep 24 00:00:00 GMT 2009 - rpmcruz@alunos.dcc.fc.up.pt + +- 2.19.10 +- package manager got a face re-lift +- fire the browser as the session's user (bcn#523694). +- table selection ignored for a few seconds (bnc#513085). +- complement our label mapping strategy with an array +of translatable yast2 labels (bnc#446524) +- always use black color on the text against the white +background regardless style (bnc#471059). +- don't show lock&remove options for patterns (bnc#489016). + +------------------------------------------------------------------- +Thu Jun 4 15:23:35 CEST 2009 - mkudlvasr@novell.com + +- teared away PackageSelector. It now resides in a separate plugin + package yast2-gtk-pkg + +------------------------------------------------------------------- +Thu Jun 4 11:52:25 CEST 2009 - coolo@novell.com + +- adapt to new libzypp +- 2.18.1 + +------------------------------------------------------------------- +Mon Apr 27 11:28:40 CEST 2009 - coolo@suse.de + +- fix compilation with gcc 4.4 +- 2.18.0 + +------------------------------------------------------------------- +Wed Jan 07 12:20:00 CET 2009 - michael.meeks@novell.com + + * label / tick spacing in wizard #449320# + * table multi-select fix #460746# + * layout / alignment fix #460728# + * another Arabic fixes #449627#, #449600# + * fixed issue with small displays #442052# + * fixed labeling and button icon problems. #449740#, #446524# + * fix window / titling issues #449385#, #449194# + * package selector fixes #457563#, #448309#, #459313# + * misc. other fixes #443214#, #446352#, #445950# + +------------------------------------------------------------------- +Wed Dec 01 10:25:17 CET 2008 - coolo@suse.de + + * fixes to cope with Arabic and other languages of + a right-to-left writing style. + * 2.17.8 + +------------------------------------------------------------------- +Wed Nov 21 10:25:17 CET 2008 - coolo@suse.de + + * fixed online update (bnc#446352) + * fixed patterns order. + * 2.17.7 + +------------------------------------------------------------------- +Wed Nov 19 10:25:17 CET 2008 - coolo@suse.de + +- new version on behalf of Ricardo: 2.17.6 + * a couple of esoteric yet nasty bugs (read: + crashes) have since been fixed. + (bnc#442052 & bnc#445158) + +------------------------------------------------------------------- +Sun Nov 9 09:35:05 CET 2008 - coolo@suse.de + +- Update to 2.17.5 + * several fixes in the wizard and package selector +- fix spec file to build + +------------------------------------------------------------------- +Wed Oct 29 10:54:07 CEST 2008 - michael.meeks@novell.com + +- Update to 2.17.4 + * sizing fixes to wizard, partition splitter, ratiobox + * improved tooltips + * package selector fixes: updated zypper API use, + add unsupported support, updated recent spin box, png + icons, ergonomic fixes, sync groups with qt, support + non-edit mode + * bar graph color fixes + * help / history + * selectable multi-line labels + * fixes to scrolling widgets + +------------------------------------------------------------------- +Mon Oct 13 10:21:07 CEST 2008 - michael.meeks@novell.com + +- Update to 2.17.3 + * fix up unit tests + * fix strechability & sizing + +------------------------------------------------------------------- +Wed Oct 08 13:56:07 CEST 2008 - michael.meeks@novell.com + +- Update to 2.17.2 + * tons of bug fixes: 428974, 432705, 428965, 428965, + 428193, 428162, 388172 including: + * improved accessibility support: dialog roles, wizard overview + * improved tooltip support, and new tooltips + * use WebKit for better html rendering + * fix g_log redirection crasher + * support background image with html + * use gtk+ stock icons for stock dialogs + +------------------------------------------------------------------- +Tue Sep 30 13:56:07 CEST 2008 - kmachalkova@suse.cz + +- Update to 2.17.1 + * YGDialog: support for setDialogTitle(). + * ygtkfindentry: added support embed context menu which pairs the find icon to + the menu item. + * YGPackageSelector: changed find entry check-based menu popup to + a criteria choice one. + * ygtkfindentry: added auto-completion support. + * makes progress bar a bit smoother. + * show download size in the progress bar. + * bnc #421794: clean any emitted event when destroyed. The new + libyui crashes when doing YWidget::isValid() + * YGText: set MultiLineEdit wrap mode as WORD_CHAR. + * YGDialog: only enable spy code for recent libyuis. + * YGTable: expand tree's row when selected + * YGPushButton: only enable role code for recent libyuis. + Also moved helpButton support to set icon code. + * added support for YDialog::highlight() + * coupled dumpGtk and dumpYast trees into the same dialog + * YGPackageSelector: fixed context menu for objects like Language + that can't be locked. + * YGUtils: added fallback to setStockIcon(). + * YGPushButton: use Role as a fallback to derive the stock icon. + * YGWizard: made the wizard button wrappers to change the button + label & etc through the YGtkWizard API + +------------------------------------------------------------------- +Thu Sep 11 08:35:23 CEST 2008 - jsrain@suse.cz + +- merged texts from proofread + +------------------------------------------------------------------- +Tue Sep 9 13:36:25 CEST 2008 - visnov@suse.cz + +- Update to 2.17.0 +* added support for Languages in package selector +* fixed Pattern installed status. +* got rid of patterns remove button. +* clear search entry on UI change. +* bug fix: not syncing icon properly when entry text was deleted by + instruction as an insert is triggered. +* redirect GTK log messages to libyui system. +* enable html parsing warnings that go to libyui. +* fixed repositories query (related to bnc #418781). +* scaping the URL as some characters (like &) were confusing the Pango parser. + Use the FILE stock icon for the ISO. + +------------------------------------------------------------------- +Mon Sep 8 22:32:59 EDT 2008 - jpr@novell.com + +- Update to 2.16.18 +* use "Software Manager" instead of "Package Selector" for + consistency (bnc #418781) +* follow GNOME HIG for button layout +* when filtering a repository, the most appropriate version wasn't + the one selected (bnc #418896) +* catch null package repo (bnc #418781) +* use title capitalization for group names (bnc #418676) +* add ATK hooks so the screen reader does its job in pixbuf cell + renderer (bnc #416554) +* show bugzilla entries in patch description +* ygtkrichtext glitches (bnc #396692, bnc #404817) +* fix occasional crash in printer tool +* show arch for Version and url for Repository +* new widgets: handlebox, linklabel +* tabs can now have shortcuts: honor them (bnc #411890) + +------------------------------------------------------------------- +Mon Jul 21 20:36:09 EDT 2008 - jpr@novell.com + +- Update to 2.16.17 +* extra whitespace typo in a string (bnc#383897) +* honor flag for last column (bnc#410341) +* let the wrapper do an initial check on changes (bnc#400635) +* implement YTable's multiSelection flag +* implement shortcutChanged() to set tab labels shortcuts +* implement setHelpButton() set the help stock icon +* remove the space for the expanders if the side navegation bar + is a plain list, as is the case for the firewall tool +* tweaked some icons as suggested (bnc#404818) +* don't always iuse the label "upgrade" if re-installs or + downgrades (bnc#404382) +* keyboard mnemonics for install/remove buttons (bnc#399256) +* consider a pattern or a patch installed or not using + isSatisfied() like yast2-qt. Also made initial patterns loading + faster. And removed some checks on patches that were causing them + not to be displayed (bnc#402855) +* added a background for <pre>. + +------------------------------------------------------------------- +Sun Jun 22 18:34:15 CEST 2008 - jpr@suse.de + +- Update to 2.16.16 +* fixed bnc#402316: run the dependency solver at the startup. +* fixed bnc#402262: fix textdomain error when packaging. +* fixed bnc#401767: popup menu install entry was using the remove + icon, while the remove entry was using the + install icon. + +------------------------------------------------------------------- +Fri Jun 20 18:34:15 CEST 2008 - jpr@suse.de + +- Update to 2.16.15 +* fixed bnc#401336: unable to resize column in sysconfig editor +* fixed bnc#386410: label widgets are set uppercase. +* fixed bnc#401835: if radio buttons weren't set in a radio group, + then they'd crash when clicked +* fixed bnc#401228: "Children widgets aren't disabled in gtk ui +* fixed bnc#401285: HSpacing broken in gtk ui +* fixed bnc#399465: for some package show empty software + dependence window +* fixed bnc#396692: glitches with breaklines and spaces around + inline tag: for some package show empty + software dependence window + +------------------------------------------------------------------- +Sun Jun 8 10:34:15 CEST 2008 - coolo@suse.de + +- Update to 2.16.14 +* fixed bnc#395484: Wrong check that assumed error was set, crashing... +* fixed bnc#396731: popup menu from the button up when near the bottom corner. +* fixed bnc#396360: ignoring the alignment for the last column, so it aligns left +* fixed bnc#396033: Some minor bug +* fixed bnc#393143: cursor ownership was not judged properly +* fixed bnc#398272: enable scroll bar on Filtered Repositories + +------------------------------------------------------------------- +Tue Jun 3 07:46:59 CEST 2008 - coolo@suse.de + +- Update to version 2.16.13 +* tag more missing strings for translation (bnc#395803) +* fixed bnc#395484 +* various fixes in the package selector by Ricardo + +------------------------------------------------------------------- +Mon May 19 11:05:00 BST 2008 - mmichael@suse.de + +- Update to version 2.16.12 +* make PackageSelector more usable bnc #390471 +* impl. mnemonics on frames bnc #390244 +* fix window transience (hidden popups) bnc #389635 +* add recommended/selected to categories +* tag main package-selector buttons for translation +* calm progress bar warnings (for stupid values eg. -5%) + +------------------------------------------------------------------- +Wed May 14 12:17:00 BST 2008 - mmichael@suse.de + +- Update to version 2.16.11 +* fix various 640x480 sizing problems +* fix window-larger-than-screen issue bnc #373159 +* fix size allocation issue bnc #390031 + +------------------------------------------------------------------- +Mon May 12 13:50:49 CEST 2008 - mmichael@suse.de + +- Update to version 2.16.10 +* fix i18n issues +* remove size cropping bnc #388715 +* fix focus issue bnc #388718 +* repo filter fix bnc #386536 + +------------------------------------------------------------------- +Mon May 7 20:13:49 CEST 2008 - jpr@suse.de + +- Update to version 2.16.9 +* only set 'fullscreen' on the main bnc #385563 +* property OpenItems of Tree widget broken bnc #381648 +* CurrentItem in Tree widget also grabs keyboard focus bnc #387114 +* allow for smaller windows bnc #381689, partly bnc #373159 +* Query items of DumbTab broken bnc #381195 +* support all radio buttons being disabled bnc #373160 +* property SelectedItems for widget MultiSelectionBox broken + bnc #381746 +* debug message on abort User and Group Administration bnc #384135) +* scroll YLog text down properly +* finish button not highlighted bnc #384649 +* fix patterns installstate, and added popup with info about them + like -qt. +* added highlighting for exact package matches +* add support for repoMgrEnabled bnc #381956 + +------------------------------------------------------------------- +Mon Apr 21 20:13:49 CEST 2008 - jpr@suse.de + +- Fix live installer sizing (bnc #381153) +- Combo box improvements +- Minor warning fixes + +------------------------------------------------------------------- +Thu Apr 3 23:04:05 CEST 2008 - coolo@suse.de + +- adopt to latest libzypp + +------------------------------------------------------------------- +Tue Mar 28 21:28:56 CET 2008 - jpr@suse.de + +- don't hide package info/control view once shown (bnc #373474) + +------------------------------------------------------------------- +Tue Mar 25 21:28:56 CET 2008 - coolo@suse.de + +- update from SVN to compile with latest libzypp + +------------------------------------------------------------------- +Thu Feb 28 13:54:27 CET 2008 - coolo@suse.de + +- fix make dist + +------------------------------------------------------------------- +Wed Feb 27 16:37:02 CET 2008 - coolo@suse.de + +- make it link again + +------------------------------------------------------------------- +Mon Feb 25 16:24:12 CET 2008 - sh@suse.de + +- V 2.16.5 +- Added "Provides: yast2_ui_pkg" to .spec file (bnc #363958) + +------------------------------------------------------------------- +Thu Feb 21 12:22:22 CET 2008 - coolo@suse.de + +- fix build + +------------------------------------------------------------------- +Thu Feb 21 07:29:11 CET 2008 - coolo@suse.de + +- compile against yast2-core and libzypp +- V 2.16.4 + +------------------------------------------------------------------- +Mon Feb 4 20:19:03 CET 2008 - coolo@suse.de + +- adapt once more to latest yast2-core + +------------------------------------------------------------------- +Thu Jan 31 20:11:40 CET 2008 - coolo@suse.de + +- make it compile against latest yast2-core + +------------------------------------------------------------------- +Sun Jan 27 21:03:57 CET 2008 - coolo@suse.de + +- fix changelog + +------------------------------------------------------------------- +Fri Jan 18 14:08:57 CET 2008 - coolo@suse.de + +- Ricardo fixed make test + +------------------------------------------------------------------- +Wed Jan 16 14:42:56 CET 2008 - coolo@suse.de + +- update to latest yast2-core API + +------------------------------------------------------------------- +Fri Jan 11 23:36:13 CET 2008 - btimothy@suse.de + +- Updating the tarball directly from SVN Trunk revision to be + version 2.15.9r43566 so the package won't fail to build anymore. +- Removed all the old patches since they are no longer necessary + with newer code. + +------------------------------------------------------------------- +Wed Oct 24 19:21:59 CEST 2007 - btimothy@suse.de + +- Include patches from SVN head to fix various package selector + bugs. Included are fixes for bugzilla.novell.com bugs: 300390 + 300750, 300737, 330235, 330467, 336124. + +------------------------------------------------------------------- +Wed Oct 6 15:32:01 BST 2007 - mmichael@suse.de + +- n#300750: big speedup wrt. search / sort + +------------------------------------------------------------------- +Wed Sep 26 01:29:22 CEST 2007 - sreeves@suse.de + +- Partial fix for Bug# 328213 - package selector untranslated + +------------------------------------------------------------------- +Wed Aug 26 15:32:01 BST 2007 - mmichael@suse.de + +- n#300390: warn if cancelling a non-submitted transaction. + +------------------------------------------------------------------- +Fri Aug 10 18:01:20 CEST 2007 - mvidner@suse.cz + +- Finished integrating Feature #120292, UI as a namespace callable + from yast2-*-bindings. +- 2.5.9 + +------------------------------------------------------------------- +Wed Aug 8 15:32:01 BST 2007 - mmichael@suse.de + +- #296945: pulse progress bar impl. +- FaTE #302018: details on how to print the license +- 2.15.8 + +------------------------------------------------------------------- +Wed Aug 8 09:22:32 CEST 2007 - mvidner@suse.cz + +- Don't build-require yast2-devel-doc. +- 2.15.7.2 + +------------------------------------------------------------------- +Mon Aug 6 14:00:37 CEST 2007 - mvidner@suse.cz + +- Adapted to changes in yast2-core needed for making UI callable from + yast2-*-bindings. +- 2.15.7.1 + +------------------------------------------------------------------- +Thu Jul 26 07:28:31 CEST 2007 - jsrain@suse.cz + +- removed yast2-devel from BuildRequires +- 2.15.7 + +------------------------------------------------------------------- +Mon Jul 23 15:32:01 BST 2007 - mmichael@suse.de + +- fix crash-on-start n#293065 +- fix uninitialized warnings +- fix empty-partition divide by 0 case +- 2.15.6 + +------------------------------------------------------------------- +Mon Jul 16 17:07:01 CEST 2007 - jsrain@suse.cz + +- adapted to new libzypp +- added support for translations +- 2.15.5 + +------------------------------------------------------------------- +Thu Jun 21 10:24:55 CEST 2007 - mmichael@suse.de + +- upgrade to yast2-gtk-2.15.4, fixes many bugs, pwrt. + package selector, improved search, multi-select for + installations &, large scale robustness & functionality + table-sorting fix, progress bar fix, initial easter-egg, + add rpmlintrc to avoid complaints about plugin .so name + +------------------------------------------------------------------- +Fri May 11 18:19:10 CEST 2007 - mmichael@suse.de + +- upgrade to yast2-gtk-2.15.3, fixes #268146, #271716 + and improves package selector search / sort. + +------------------------------------------------------------------- +Tue Apr 17 15:58:40 CEST 2007 - mmichael@suse.de + +- upgrade to yast2-gtk-2.15.2, fixes upgrade.ycp + +------------------------------------------------------------------- +Fri Mar 16 13:36:04 CET 2007 - mmichael@suse.de + +- upgrade to yast2-gtk-2.15.1, several fixes, including #247682, #247683 + +------------------------------------------------------------------- +Wed Feb 7 11:26:07 CET 2007 - mmichael@suse.de + +- update to yast2-gtk-2.15.0, fixes build and adds new Frame type + +------------------------------------------------------------------- +Wed Aug 30 10:56:28 CEST 2006 - cthiel@suse.de + +- added yast2-gtk-autogen.patch to fix build with latests yast2-devtools + +------------------------------------------------------------------- +Mon Aug 28 11:34:13 CEST 2006 - cthiel@suse.de + +- fix build + +------------------------------------------------------------------- +Wed Aug 23 18:20:30 CEST 2006 - mmichael@suse.de + +- import yast2-gtk into autobuild. + +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/CMakeLists.txt URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/CMakeLists.txt (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/CMakeLists.txt Mon Aug 1 09:44:45 2011 @@ -0,0 +1,74 @@ +## Makefile.am + +set (gtk_yast_plugin_SRCS + YGBarGraph.cc + YGComboBox.cc + YGDialog.cc + YGDumbTab.cc + YGFrame.cc + YGInputField.cc + YGIntField.cc + YGImage.cc + YGMenuButton.cc + YGLabel.cc + YGLayout.cc + YGPackageSelectorPluginStub.cc + YGPushButton.cc + YGProgressBar.cc + YGRadioButton.cc + YGSelectionStore.cc + YGText.cc + YGTreeView.cc + YGUtils.cc + YGUI.cc + YGWidget.cc + YGWizard.cc + ygdkmngloader.c + ygtkbargraph.c + ygtkfieldentry.c + ygtkhtmlwrap.c + ygtkmenubutton.c + ygtklinklabel.c + ygtkfixed.c + ygtkimage.c + ygtkratiobox.c + ygtkrichtext.c + ygtksteps.c + ygtktextview.c + ygtktimezonepicker.c + ygtktreeview.c + ygtkwizard.c + ) + +## includes: + +include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) +include_directories (${GTK2_INCLUDE_DIRS}) +include_directories (${LIBYUI_INCLUDE_DIR}) + +## target: + +add_library (py2gtk SHARED ${gtk_yast_plugin_SRCS}) + +## libraries: + +target_link_libraries (py2gtk ${GTK2_LIBRARIES}) +target_link_libraries (py2gtk ${LIBYUI_LIBRARY}) +set_target_properties (py2gtk PROPERTIES SOVERSION 2) +set_target_properties (py2gtk PROPERTIES LINK_FLAGS "--no-undefined") + +## install: + +install (TARGETS py2gtk LIBRARY DESTINATION ${UI_PLUGIN_DIR}) + +add_subdirectory (pkg) + +## test: + +link_directories (${CMAKE_BINARY_DIR}/src) +add_executable (test test.cc) +add_dependencies (test py2gtk) +target_link_libraries (test libpy2gtk.so) +target_link_libraries (test ${GTK2_LIBRARIES}) +target_link_libraries (test ${LIBYUI_LIBRARY}) +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/Y2CCGtk.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/Y2CCGtk.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/Y2CCGtk.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,20 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#include <config.h> +#include <YGUI.h> +#include <YGi18n.h> + +YUI * createUI( bool withThreads ) +{ + static YGUI *_ui = 0; + + if ( ! _ui ) + { + _ui = new YGUI( withThreads ); + } + + return _ui; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGBarGraph.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGBarGraph.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGBarGraph.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,172 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#include <config.h> +#include "YGUI.h" +#include "YGWidget.h" +#include "ygtkbargraph.h" + +#include "YBarGraph.h" + +class YGBarGraph : public YBarGraph, public YGWidget +{ +public: + YGBarGraph (YWidget *parent) + : YBarGraph (NULL) + , YGWidget (this, parent, YGTK_TYPE_BAR_GRAPH, NULL) + {} + + // YBarGraph + virtual void doUpdate() + { + YGtkBarGraph *graph = YGTK_BAR_GRAPH (getWidget()); + ygtk_bar_graph_create_entries (graph, segments()); + for (int i = 0; i < segments(); i++) { + const YBarGraphSegment &s = segment (i); + ygtk_bar_graph_setup_entry (graph, i, s.label().c_str(), s.value()); + if (s.hasSegmentColor()) { + GdkColor color = ycolorToGdk (s.segmentColor()); + ygtk_bar_graph_customize_bg (graph, i, &color); + } + if (s.hasTextColor()) { + GdkColor color = ycolorToGdk (s.textColor()); + ygtk_bar_graph_customize_fg (graph, i, &color); + } + } + } + + static GdkColor ycolorToGdk (const YColor &ycolor) + { + GdkColor color = { 0, guint16(ycolor.red() << 8), guint16(ycolor.green() << 8), guint16(ycolor.blue() << 8) }; + return color; + } + + virtual unsigned int getMinSize (YUIDimension dim) + { return dim == YD_HORIZ ? 80 : 30; } + + YGWIDGET_IMPL_COMMON (YBarGraph) +}; + +YBarGraph *YGOptionalWidgetFactory::createBarGraph (YWidget *parent) +{ + return new YGBarGraph (parent); +} + +#include "YPartitionSplitter.h" + +class YGPartitionSplitter : public YPartitionSplitter, public YGWidget +{ +public: + YGtkBarGraph *m_barGraph; + GtkWidget *m_scale, *m_free_spin, *m_new_spin; + + YGPartitionSplitter (YWidget *parent, int usedSize, int totalFreeSize, int newPartSize, + int minNewPartSize, int minFreeSize, const string &usedLabel, const string &freeLabel, + const string &newPartLabel, const string &freeFieldLabel, const string &newPartFieldLabel) + : YPartitionSplitter (NULL, usedSize, totalFreeSize, newPartSize, minNewPartSize, + minFreeSize, usedLabel, freeLabel, newPartLabel, freeFieldLabel, newPartFieldLabel) + , YGWidget (this, parent, GTK_TYPE_VBOX, NULL) + { + /* Bar graph widget */ + GtkWidget *graph = ygtk_bar_graph_new(); + m_barGraph = YGTK_BAR_GRAPH (graph); + ygtk_bar_graph_create_entries (m_barGraph, 3); + ygtk_bar_graph_setup_entry (m_barGraph, 0, usedLabel.c_str(), usedSize); + + /* Labels over the slider */ + GtkWidget *labels_box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (labels_box), + gtk_label_new (freeFieldLabel.c_str()), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (labels_box), gtk_label_new (NULL), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (labels_box), + gtk_label_new (newPartFieldLabel.c_str()), FALSE, TRUE, 0); + + /* Slider and the spinners */ + GtkWidget *slider_box = gtk_hbox_new (FALSE, 0); + m_scale = gtk_hscale_new_with_range ((gdouble) minFreeSize, maxFreeSize(), 1); + gtk_scale_set_draw_value (GTK_SCALE (m_scale), FALSE); + m_free_spin = gtk_spin_button_new_with_range + (minFreeSize, maxFreeSize(), 1); + m_new_spin = gtk_spin_button_new_with_range + (minNewPartSize, maxNewPartSize(), 1); + + // keep the partition's order + gtk_widget_set_direction (labels_box, GTK_TEXT_DIR_LTR); + gtk_widget_set_direction (slider_box, GTK_TEXT_DIR_LTR); + + gtk_box_pack_start (GTK_BOX (slider_box), m_free_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (slider_box), m_scale, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (slider_box), m_new_spin, FALSE, FALSE, 0); + + connect (m_scale, "value-changed", G_CALLBACK (scale_changed_cb), this); + connect (m_free_spin, "value-changed", G_CALLBACK (free_spin_changed_cb), this); + connect (m_new_spin, "value-changed", G_CALLBACK (new_spin_changed_cb), this); + + /* Main layout */ + gtk_box_pack_start (GTK_BOX (getWidget()), graph, TRUE, TRUE, 6); + gtk_box_pack_start (GTK_BOX (getWidget()), labels_box, FALSE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (getWidget()), slider_box, FALSE, TRUE, 2); + + setValue (newPartSize); // initialization + gtk_widget_show_all (getWidget()); + } + + // YPartitionSplitter + virtual int value() + { + return gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (m_new_spin)); + } + + virtual void setValue (int newValue) + { + BlockEvents block (this); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (m_new_spin), newValue); + int freeSize = totalFreeSize() - newValue; + gtk_spin_button_set_value (GTK_SPIN_BUTTON (m_free_spin), freeSize); + gtk_range_set_value (GTK_RANGE (m_scale), freeSize); + + gtk_spin_button_set_value (GTK_SPIN_BUTTON (m_free_spin), freeSize); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (m_new_spin), newValue); + + ygtk_bar_graph_setup_entry (m_barGraph, 1, freeLabel().c_str(), freeSize); + ygtk_bar_graph_setup_entry (m_barGraph, 2, newPartLabel().c_str(), newValue); + } + + static void scale_changed_cb (GtkRange *range, YGPartitionSplitter *pThis) + { + int newFreeSize = (int) gtk_range_get_value (range); + int newPartSize = pThis->totalFreeSize() - newFreeSize; + + pThis->setValue (newPartSize); + pThis->emitEvent (YEvent::ValueChanged); + } + + static void free_spin_changed_cb (GtkSpinButton *spin, YGPartitionSplitter *pThis) + { + int newFreeSize = gtk_spin_button_get_value_as_int (spin); + int newPartSize = pThis->totalFreeSize() - newFreeSize; + pThis->setValue (newPartSize); + pThis->emitEvent (YEvent::ValueChanged); + } + + static void new_spin_changed_cb (GtkSpinButton *spin, YGPartitionSplitter *pThis) + { + pThis->setValue (gtk_spin_button_get_value_as_int (spin)); + pThis->emitEvent (YEvent::ValueChanged); + } + + YGWIDGET_IMPL_COMMON (YPartitionSplitter) +}; + +YPartitionSplitter *YGOptionalWidgetFactory::createPartitionSplitter (YWidget *parent, + int usedSize, int totalFreeSize, int newPartSize, int minNewPartSize, + int minFreeSize, const string &usedLabel, const string &freeLabel, + const string &newPartLabel, const string &freeFieldLabel, + const string &newPartFieldLabel) +{ + return new YGPartitionSplitter (parent, usedSize, totalFreeSize, newPartSize, + minNewPartSize, minFreeSize, usedLabel, freeLabel, newPartLabel, freeFieldLabel, + newPartFieldLabel); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGComboBox.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGComboBox.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGComboBox.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,139 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include <YGUI.h> +#include "YGUtils.h" +#include "YComboBox.h" +#include "YGSelectionStore.h" +#include "YGWidget.h" + +class YGComboBox : public YComboBox, public YGLabeledWidget, public YGSelectionStore +{ + public: + YGComboBox (YWidget *parent, const string &label, bool editable) + : YComboBox (NULL, label, editable), + YGLabeledWidget (this, parent, label, YD_HORIZ, + editable ? GTK_TYPE_COMBO_BOX_ENTRY : GTK_TYPE_COMBO_BOX, NULL), + YGSelectionStore (false) + { + const GType types[2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING }; + createStore (2, types); + gtk_combo_box_set_model (getComboBox(), getModel()); + + GtkCellRenderer* cell = gtk_cell_renderer_pixbuf_new(); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (getWidget()), cell, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (getWidget()), cell, + "pixbuf", 0, NULL); + + if (editable) + gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (getWidget()), 1); + else { + cell = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (getWidget()), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (getWidget()), cell, + "text", 1, NULL); + } + + connect (getWidget(), "changed", G_CALLBACK (selected_changed_cb), this); + } + + inline GtkComboBox *getComboBox() + { return GTK_COMBO_BOX (getWidget()); } + + GtkEntry *getEntry() + { return GTK_ENTRY (gtk_bin_get_child (GTK_BIN (getWidget()))); } + + // YGSelectionModel + + void blockSelected() {} + + void doAddItem (YItem *item) + { + GtkTreeIter iter; + addRow (item, &iter); + setRowText (&iter, 0, item->iconName(), 1, item->label(), this); + if (item->selected()) + doSelectItem (item, true); + } + + void doSelectItem (YItem *item, bool select) + { + if (select) { + BlockEvents block (this); + GtkTreeIter iter; + getTreeIter (item, &iter); + gtk_combo_box_set_active_iter (getComboBox(), &iter); + } + } + + void doDeselectAllItems() + { + BlockEvents block (this); + gtk_combo_box_set_active (getComboBox(), -1); + } + + YItem *doSelectedItem() + { + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter (getComboBox(), &iter)) + return getYItem (&iter); + return NULL; + } + + // YComboBox + + virtual std::string text() + { + gchar *str; + if (GTK_IS_COMBO_BOX_ENTRY (getWidget())) + str = gtk_combo_box_get_active_text (getComboBox()); + else { + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter (getComboBox(), &iter)) + gtk_tree_model_get (getModel(), &iter, 1, &str, -1); + else + return ""; + } + std::string ret (str); + g_free (str); + return ret; + } + + virtual void setText (const std::string &value) + { + BlockEvents block (this); + GtkTreeIter iter; + if (findLabel (1, value, &iter)) + gtk_combo_box_set_active_iter (getComboBox(), &iter); + else + gtk_entry_set_text (getEntry(), value.c_str()); + } + + virtual void setInputMaxLength (int length) + { + YComboBox::setInputMaxLength (length); + gtk_entry_set_width_chars (getEntry(), length); + } + + virtual void setValidChars (const string &validChars) + { + YComboBox::setValidChars (validChars); + YGUtils::setFilter (getEntry(), validChars); + } + + // callbacks + static void selected_changed_cb (GtkComboBox *widget, YGComboBox *pThis) + { pThis->emitEvent (YEvent::ValueChanged); } + + YGLABEL_WIDGET_IMPL (YComboBox) + YGSELECTION_WIDGET_IMPL (YComboBox) +}; + +YComboBox *YGWidgetFactory::createComboBox (YWidget *parent, const string &label, bool editable) +{ + return new YGComboBox (parent, label, editable); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,585 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGDialog.h" +#include "YGUtils.h" +#if YAST2_VERSION >= 2017006 +#include <YDialogSpy.h> +#endif +#include <gdk/gdkkeysyms.h> +#include <math.h> // easter +#include <string.h> + +/* In the main dialog case, it doesn't necessarly have a window of its own. If + there is already a main window, it should replace its content -- and when closed, + the previous dialog restored. + + Therefore, we have a YGDialog (the YDialog implementation), and a YGWindow + that does the windowing work and has a YWidget has its children, which can + be a YGDialog and is swap-able. +*/ + +//#define DEFAULT_WIDTH 750 +//#define DEFAULT_HEIGHT 650 +#define DEFAULT_CHAR_WIDTH 60 +#define DEFAULT_CHAR_HEIGHT 28 +#define DEFAULT_PIXEL_WIDTH 330 +#define DEFAULT_PIXEL_HEIGHT 200 + +class YGWindow; +static YGWindow *main_window = 0; + +class YGWindow +{ + GtkWidget *m_widget; + int m_refcount; + // we keep a pointer of the child just for debugging + // (ie. dump yast tree) + YWidget *m_child; + GdkCursor *m_busyCursor; + bool m_isBusy; + +public: + YGWindowCloseFn m_canClose; + void *m_canCloseData; + + YGWindow (bool _main_window, YGDialog *ydialog) + { + m_widget = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_object_ref_sink (G_OBJECT (m_widget)); + g_object_set (G_OBJECT (m_widget), "allow-shrink", TRUE, NULL); + + m_refcount = 0; + m_child = NULL; + m_canClose = NULL; + m_busyCursor = NULL; + m_isBusy = false; + + { + std::stack<YDialog *> &stack = YDialog::_dialogStack; + YDialog *ylast = stack.size() ? stack.top() : 0; + if (ylast == ydialog) { + if (stack.size() > 1) { + YDialog *t = ylast; + stack.pop(); + ylast = stack.top(); + stack.push (t); + } + else + ylast = NULL; + } + + GtkWindow *parent = NULL; + if (ylast) { + YGDialog *yglast = static_cast <YGDialog *> (ylast); + parent = GTK_WINDOW (yglast->m_window->getWidget()); + } + GtkWindow *window = GTK_WINDOW (m_widget); + + if (parent) { + // if there is a parent, this would be a dialog + gtk_window_set_title (window, ""); + gtk_window_set_modal (window, TRUE); + gtk_window_set_transient_for (window, parent); + gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_DIALOG); + AtkObject *peer = gtk_widget_get_accessible (GTK_WIDGET (window)); + if (peer != NULL) + atk_object_set_role (peer, ATK_ROLE_DIALOG); + } + else { + gtk_window_set_title (window, "YaST"); + if (YGUI::ui()->unsetBorder()) + gtk_window_set_decorated (window, FALSE); + } + + if (_main_window) { + // window default width is calculated as a proportion of a default + // char and pixel width to compensate for the fact that each widget's + // required size comes from a proportion of both parameters + int width = YGUtils::getCharsWidth (m_widget, DEFAULT_CHAR_WIDTH); + width += DEFAULT_PIXEL_WIDTH; + int height = YGUtils::getCharsHeight (m_widget, DEFAULT_CHAR_HEIGHT); + height += DEFAULT_PIXEL_HEIGHT; + + if (YGUI::ui()->isSwsingle()) + height += YGUtils::getCharsHeight (m_widget, 10); + + width = MIN (width, YUI::app()->displayWidth()); + height = MIN (height, YUI::app()->displayHeight()); + + gtk_window_set_default_size (window, width, height); + if (YGUI::ui()->setFullscreen()) + gtk_window_fullscreen (window); + else if (YUI::app()->displayWidth() <= 800 || YUI::app()->displayHeight() <= 600) + // maximize window for small displays + gtk_window_maximize (window); + } + + gtk_window_set_role (window, "yast2"); + } + + if (_main_window) + main_window = this; + + g_signal_connect (G_OBJECT (m_widget), "delete-event", + G_CALLBACK (close_window_cb), this); + g_signal_connect_after (G_OBJECT (m_widget), "key-press-event", + G_CALLBACK (key_pressed_cb), this); + g_signal_connect (G_OBJECT (m_widget), "focus-in-event", + G_CALLBACK (focus_in_event_cb), this); + // set busy cursor at start + g_signal_connect_after (G_OBJECT (m_widget), "realize", + G_CALLBACK (realize_cb), this); + } + + ~YGWindow() + { + setChild (NULL); + if (m_busyCursor) + gdk_cursor_unref (m_busyCursor); + gtk_widget_destroy (m_widget); + g_object_unref (G_OBJECT (m_widget)); + } + + void show() + { gtk_widget_show (m_widget); } + + void normalCursor() + { + if (m_isBusy) + gdk_window_set_cursor (m_widget->window, NULL); + m_isBusy = false; + } + + void busyCursor() + { + if (!m_busyCursor) { + GdkDisplay *display = gtk_widget_get_display (m_widget); + m_busyCursor = gdk_cursor_new_for_display (display, GDK_WATCH); + gdk_cursor_ref (m_busyCursor); + } + if (!m_isBusy) + gdk_window_set_cursor (m_widget->window, m_busyCursor); + m_isBusy = true; + } + + void setChild (YWidget *new_child) + { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (m_widget)); + if (child) + gtk_container_remove (GTK_CONTAINER (m_widget), child); + if (new_child) { + child = YGWidget::get (new_child)->getLayout(); + gtk_container_add (GTK_CONTAINER (m_widget), child); + } + m_child = new_child; + } + + static void ref (YGWindow *window) + { + window->m_refcount++; + } + + static void unref (YGWindow *window) + { + if (--window->m_refcount == 0) { + bool is_main_window = (window == main_window); + delete window; + if (is_main_window) + main_window = NULL; + } + } + + // Y(G)Widget-like methods + GtkWidget *getWidget() { return m_widget; } + YWidget *getChild() { return m_child; } + +private: + void close() + { + if (!m_canClose || m_canClose (m_canCloseData)) + YGUI::ui()->sendEvent (new YCancelEvent()); + } + + static gboolean close_window_cb (GtkWidget *widget, GdkEvent *event, + YGWindow *pThis) + { + // never let GTK+ destroy the window! just inform YCP, and let it + // do its thing. + pThis->close(); + return TRUE; + } + + static gboolean key_pressed_cb (GtkWidget *widget, GdkEventKey *event, + YGWindow *pThis) + { + // if not main dialog, close it on escape + if (event->keyval == GDK_Escape && + /* not main window */ main_window != pThis) { + pThis->close(); + return TRUE; + + } + + if (event->state & GDK_SHIFT_MASK) { + switch (event->keyval) { + case GDK_F8: + YGUI::ui()->askSaveLogs(); + return TRUE; + default: + break; + } + } + if ((event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK) + && (event->state & GDK_MOD1_MASK)) { + yuiMilestone() << "Caught YaST2 magic key combination\n"; + switch (event->keyval) { + case GDK_S: + YGUI::ui()->makeScreenShot(); + return TRUE; + case GDK_M: + YGUI::ui()->toggleRecordMacro(); + return TRUE; + case GDK_P: + YGUI::ui()->askPlayMacro(); + return TRUE; + case GDK_D: + YGUI::ui()->sendEvent (new YDebugEvent()); + return TRUE; + case GDK_X: + yuiMilestone() << "Starting xterm\n"; + (void) system ("/usr/bin/xterm &"); + return TRUE; + case GDK_H: + dumpYastHtml (pThis->getChild()); + return TRUE; + case GDK_E: // easter egg + static guint explode_timeout = 0; + if (explode_timeout == 0) + explode_timeout = g_timeout_add (10000, expode_window_timeout_cb, pThis); + else { + g_source_remove (explode_timeout); + explode_timeout = 0; + } + return TRUE; + case GDK_Y: + yuiMilestone() << "Opening dialog spy" << endl; + YDialogSpy::showDialogSpy(); + YGUI::ui()->normalCursor(); + break; + default: + break; + } + } + return FALSE; + } + + static gboolean focus_in_event_cb (GtkWidget *widget, GdkEventFocus *event) + { gtk_window_set_urgency_hint (GTK_WINDOW (widget), FALSE); return FALSE; } + + static void realize_cb (GtkWidget *widget, YGWindow *pThis) + { pThis->busyCursor(); } + + static gboolean expode_window_timeout_cb (gpointer data) + { + YGWindow *pThis = (YGWindow *) data; + GtkWindow *window = GTK_WINDOW (pThis->m_widget); + srand (time (NULL)); + gint x, y; + gtk_window_get_position (window, &x, &y); + #if 0 + // OVAL MOVE + for (int i = 180; i < 360+180; i++) { + gtk_window_move (window, x+(int)(sin((i*G_PI)/180)*50), + y+(int)(cos((i*G_PI)/180)*50)+50); + while (gtk_events_pending()) + gtk_main_iteration(); + usleep (25); + } + #else + // EXPLOSION + for (int i = 0; i < 40; i++) { + gtk_window_move (window, x+(int)((((float)(rand())/RAND_MAX)*40)-20), + y+(int)((((float)(rand())/RAND_MAX)*40)-20)); + while (gtk_events_pending()) + gtk_main_iteration(); + usleep (200); + } + #endif + gtk_window_move (window, x, y); + return TRUE; + } +}; + +YGDialog::YGDialog (YDialogType dialogType, YDialogColorMode colorMode) + : YDialog (dialogType, colorMode), + YGWidget (this, NULL, GTK_TYPE_HBOX, NULL) +{ + setBorder (0); + m_stickyTitle = false; + m_containee = gtk_event_box_new(); + if (dialogType == YMainDialog && main_window) + m_window = main_window; + else + m_window = new YGWindow (dialogType == YMainDialog, this); + YGWindow::ref (m_window); + + if (colorMode != YDialogNormalColor) { + // emulate a warning / info dialog + GtkWidget *icon = gtk_image_new_from_stock + (colorMode == YDialogWarnColor ? GTK_STOCK_DIALOG_WARNING : GTK_STOCK_DIALOG_INFO, + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0); + gtk_misc_set_padding (GTK_MISC (icon), 0, 12); + + gtk_box_pack_start (GTK_BOX (getWidget()), icon, FALSE, FALSE, 12); + gtk_box_pack_start (GTK_BOX (getWidget()), m_containee, TRUE, TRUE, 0); + } + else + gtk_box_pack_start (GTK_BOX (getWidget()), m_containee, TRUE, TRUE, 0); + gtk_widget_show_all (getWidget()); + + // NOTE: we need to add this containter to the window right here, else + // weird stuff happens (like if we set a pango font description to a + // GtkLabel, size request would output the size without that description + // set...) + m_window->setChild (this); +} + +YGDialog::~YGDialog() +{ + YGWindow::unref (m_window); +} + +void YGDialog::openInternal() +{ + m_window->show(); +} + +void YGDialog::activate() +{ + m_window->setChild (this); +} + +void YGDialog::present() +{ + GtkWindow *window = GTK_WINDOW (m_window->getWidget()); + if (!gtk_window_is_active (window)) + gtk_window_set_urgency_hint (window, TRUE); +} + +YGDialog *YGDialog::currentDialog() +{ + YDialog *ydialog = YDialog::currentDialog (false); + if (ydialog) + return static_cast <YGDialog *> (ydialog); + return NULL; +} + +GtkWindow *YGDialog::currentWindow() +{ + YGDialog *ydialog = YGDialog::currentDialog(); + if (ydialog) + return GTK_WINDOW (ydialog->m_window->getWidget()); + return NULL; +} + +void YGDialog::setCloseCallback (YGWindowCloseFn canClose, void *canCloseData) +{ + m_window->m_canClose = canClose; + m_window->m_canCloseData = canCloseData; +} + +void YGDialog::unsetCloseCallback() +{ + m_window->m_canClose = NULL; +} + +void YGDialog::normalCursor() +{ + m_window->normalCursor(); +} + +void YGDialog::busyCursor() +{ + m_window->busyCursor(); +} + +// YWidget + +void YGDialog::doSetSize (int width, int height) +{ + // libyui calls YDialog::setSize() to force a geometry recalculation as a + // result of changed layout properties + GtkWidget *window = m_window->getWidget(); + if (GTK_WIDGET_REALIZED (window)) { + gtk_widget_queue_resize (window); + width = MIN (width, YUI::app()->displayWidth()); + height = MIN (height, YUI::app()->displayHeight()); +#if 1 + bool resize = false; + if (isMainDialog()) { + if (window->allocation.width < width || window->allocation.height < height) { + resize = true; + width = MAX (width, window->allocation.width), + height = MAX (height, window->allocation.height); + } + } + else + resize = true; + if (resize) +#else + if (!isMainDialog()) +#endif + gtk_window_resize (GTK_WINDOW (window), width, height); + } +} + +void YGDialog::highlight (YWidget *ywidget) +{ + struct inner { + static gboolean expose_highlight_cb (GtkWidget *widget, + GdkEventExpose *event) + { + GtkAllocation *alloc = &widget->allocation; + int x = alloc->x, y = alloc->y, w = alloc->width, h = alloc->height; + + cairo_t *cr = gdk_cairo_create (widget->window); + cairo_rectangle (cr, x, y, w, h); + cairo_set_source_rgb (cr, 0xff/255.0, 0x88/255.0, 0); + cairo_fill (cr); + cairo_destroy (cr); + return FALSE; + } + + static bool hasWindow (GtkWidget *widget) + { + if (!GTK_WIDGET_NO_WINDOW (widget)) + return true; + // widgets like GtkButton add their windows to parent's + for (GList *children = gdk_window_peek_children (widget->window); + children; children = children->next) { + GdkWindow *child = (GdkWindow *) children->data; + gpointer data; + gdk_window_get_user_data (child, &data); + if ((GtkWidget *) data == widget) + return true; + } + return false; + } + + }; + static YWidget *previousWidget = NULL; + if (previousWidget && previousWidget->isValid()) { + YGWidget *prev = YGWidget::get (previousWidget); + if (prev) { + GtkWidget *widget = prev->getWidget(); + if (inner::hasWindow (widget)) { + gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, NULL); + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, NULL); + } + else { + g_signal_handlers_disconnect_by_func (widget, + (gpointer) inner::expose_highlight_cb, NULL); + gtk_widget_queue_draw (widget); + } + } + } + if (ywidget) { + YGWidget *ygwidget = YGWidget::get (ywidget); + if (ygwidget) { + GtkWidget *widget = ygwidget->getWidget(); + if (inner::hasWindow (widget)) { + GdkColor bg_color = { 0, 0xffff, 0xaaaa, 0 }; + GdkColor base_color = { 0, 0xffff, 0xeeee, 0 }; + gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &bg_color); + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, &base_color); + } + else { + g_signal_connect (G_OBJECT (widget), "expose-event", + G_CALLBACK (inner::expose_highlight_cb), NULL); + gtk_widget_queue_draw (widget); + } + } + } + previousWidget = ywidget; +} + +void YGDialog::setTitle (const std::string &title, bool sticky) +{ + if (title.empty()) + return; + if (!m_stickyTitle || sticky) { + GtkWindow *window = GTK_WINDOW (m_window->getWidget()); + gchar *str = g_strdup_printf ("%s - YaST", title.c_str()); + gtk_window_set_title (window, str); + g_free (str); + m_stickyTitle = sticky; + } + present(); +} + +extern "C" { + void ygdialog_setTitle (const gchar *title, gboolean sticky); +}; + +void ygdialog_setTitle (const gchar *title, gboolean sticky) +{ + YGDialog::currentDialog()->setTitle (title, sticky); +} + +void YGDialog::setIcon (const std::string &icon) +{ + GtkWindow *window = GTK_WINDOW (m_window->getWidget()); + GdkPixbuf *pixbuf = YGUtils::loadPixbuf (icon); + if (pixbuf) { + gtk_window_set_icon (window, pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + } +} + +typedef bool (*FindWidgetsCb) (YWidget *widget, void *data) ; + +static void findWidgets ( + std::list <YWidget *> *widgets, YWidget *widget, FindWidgetsCb find_cb, void *cb_data) +{ + if (find_cb (widget, cb_data)) + widgets->push_back (widget); + for (YWidgetListConstIterator it = widget->childrenBegin(); + it != widget->childrenEnd(); it++) + findWidgets (widgets, *it, find_cb, cb_data); +} + +static bool IsFunctionWidget (YWidget *widget, void *data) +{ return widget->functionKey() == GPOINTER_TO_INT (data); } + +YWidget *YGDialog::getFunctionWidget (int key) +{ + std::list <YWidget *> widgets; + findWidgets (&widgets, this, IsFunctionWidget, GINT_TO_POINTER (key)); + return widgets.empty() ? NULL : widgets.front(); +} + +static bool IsClassWidget (YWidget *widget, void *data) +{ return !strcmp (widget->widgetClass(), (char *) data); } + +std::list <YWidget *> YGDialog::getClassWidgets (const char *className) +{ + std::list <YWidget *> widgets; + findWidgets (&widgets, this, IsClassWidget, (void *) className); + return widgets; +} + +YDialog *YGWidgetFactory::createDialog (YDialogType dialogType, YDialogColorMode colorMode) +{ return new YGDialog (dialogType, colorMode); } + +YEvent *YGDialog::waitForEventInternal (int timeout_millisec) +{ return YGUI::ui()->waitInput (timeout_millisec, true); } + +YEvent *YGDialog::pollEventInternal() +{ return YGUI::ui()->waitInput (0, false); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDialog.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,58 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGDIALOG_H +#define YGDIALOG_H + +#include "YGWidget.h" +#include "YDialog.h" + +class YGWindow; +typedef bool (*YGWindowCloseFn) (void *closure); + +class YGDialog : public YDialog, public YGWidget +{ + friend class YGWindow; + GtkWidget *m_containee; + YGWindow *m_window; + bool m_stickyTitle; + +public: + YGDialog (YDialogType dialogType, YDialogColorMode colorMode); + virtual ~YGDialog(); + + virtual GtkWidget *getContainer() { return m_containee; } + + void setCloseCallback (YGWindowCloseFn closeCallback, void *closeData); + void unsetCloseCallback(); + + void normalCursor(); + void busyCursor(); + + // convenience function to be used rather than currentDialog() + static YGDialog *currentDialog(); + static GtkWindow *currentWindow(); + + virtual void doSetSize (int width, int height); + + virtual void openInternal(); + virtual void activate(); + void present(); + + virtual YEvent *waitForEventInternal (int timeout_millisec); + virtual YEvent *pollEventInternal(); + + virtual void highlight (YWidget * child); + + void setTitle (const std::string &title, bool sticky = false); + void setIcon (const std::string &icon); + + YWidget *getFunctionWidget (int key); + std::list <YWidget *> getClassWidgets (const char *className); + + YGWIDGET_IMPL_CONTAINER (YDialog) +}; + +#endif // YGDIALOG_H +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDumbTab.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDumbTab.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGDumbTab.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,160 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGWidget.h" +#include "YGUtils.h" +#include "YDumbTab.h" + +#include "ygtkratiobox.h" + +class YGDumbTab : public YDumbTab, public YGWidget +{ + GtkWidget *m_containee; + GtkWidget *m_last_tab; + +public: + YGDumbTab (YWidget *parent) + : YDumbTab (NULL), + YGWidget (this, parent, GTK_TYPE_NOTEBOOK, NULL) + { + m_containee = gtk_event_box_new(); + g_object_ref_sink (G_OBJECT (m_containee)); + gtk_widget_show (m_containee); + + m_last_tab = 0; + // GTK+ keeps the notebook size set to the biggset page. We can't + // do this since pages are set dynamically, but at least don't let + // the notebook reduce its size. + ygtk_adj_size_set_only_expand (YGTK_ADJ_SIZE (m_adj_size), TRUE); + + connect (getWidget(), "switch-page", G_CALLBACK (switch_page_cb), this); + } + + virtual ~YGDumbTab() + { + gtk_widget_destroy (m_containee); + g_object_unref (G_OBJECT (m_containee)); + } + + virtual GtkWidget *getContainer() + { return m_containee; } + + virtual void addItem (YItem *item) + { + BlockEvents block (this); + YDumbTab::addItem (item); + GtkWidget *tab_label, *image = 0, *label; + label = gtk_label_new (YGUtils::mapKBAccel (item->label()).c_str()); + gtk_label_set_use_underline (GTK_LABEL (label), TRUE); + if (item->hasIconName()) { + string path = iconFullPath (item->iconName()); + GdkPixbuf *pixbuf = YGUtils::loadPixbuf (path); + if (pixbuf) { + image = gtk_image_new_from_pixbuf (pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + } + } + if (image) { + tab_label = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (tab_label), image, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (tab_label), label, TRUE, TRUE, 0); + } + else + tab_label = label; + gchar *label_id = g_strdup_printf ("label-%d", item->index()); + g_object_set_data (G_OBJECT (getWidget()), label_id, label); + g_free (label_id); + gtk_widget_show_all (tab_label); + + GtkNotebook *notebook = GTK_NOTEBOOK (getWidget()); + + GtkWidget *page = gtk_event_box_new(); + gtk_widget_show (page); + item->setData ((void *) page); + g_object_set_data (G_OBJECT (page), "yitem", item); + + gtk_notebook_append_page (notebook, page, tab_label); + selectItem (item, item->selected() || !m_last_tab /*first tab*/); + } + + virtual void deleteAllItems() + { + GList *children = gtk_container_get_children (GTK_CONTAINER (getWidget())); + for (GList *i = children; i; i = i->next) + gtk_container_remove (GTK_CONTAINER (getWidget()), (GtkWidget *) i->data); + g_list_free (children); + YDumbTab::deleteAllItems(); + } + + // to re-use the same widget in all tabs (m_fixed), we will remove and + // add to the tabs' child as tabs are changed + void syncTabPage() + { + if (m_last_tab) + gtk_container_remove (GTK_CONTAINER (m_last_tab), m_containee); + + GtkNotebook *notebook = GTK_NOTEBOOK (getWidget()); + int nb = gtk_notebook_get_current_page (notebook); + m_last_tab = gtk_notebook_get_nth_page (notebook, nb); + gtk_container_add (GTK_CONTAINER (m_last_tab), m_containee); + } + + virtual YItem *selectedItem() + { + GtkNotebook *notebook = GTK_NOTEBOOK (getWidget()); + int nb = gtk_notebook_get_current_page (notebook); + if (nb < 0) return NULL; + GtkWidget *child = gtk_notebook_get_nth_page (notebook, nb); + return (YItem *) g_object_get_data (G_OBJECT (child), "yitem"); + } + + virtual void selectItem (YItem *item, bool selected) + { + if (selected) { + BlockEvents block (this); + GtkWidget *child = (GtkWidget *) item->data(); + int page = gtk_notebook_page_num (GTK_NOTEBOOK (getWidget()), child); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (getWidget()), page); + syncTabPage(); + } + YDumbTab::selectItem (item, selected); + } + + virtual void shortcutChanged() + { + for (YItemConstIterator it = itemsBegin(); it != itemsEnd(); it++) { + YItem *item = *it; + gchar *label_id = g_strdup_printf ("label-%d", item->index()); + GtkWidget *label; + label = (GtkWidget *) g_object_get_data (G_OBJECT (getWidget()), label_id); + g_free (label_id); + + std::string text = YGUtils::mapKBAccel (item->label()); + gtk_label_set_text (GTK_LABEL (label), text.c_str()); + gtk_label_set_use_underline (GTK_LABEL (label), TRUE); + } + } + + // callbacks + static void switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page, + guint tab_nb, YGDumbTab *pThis) + { + GtkWidget *child = gtk_notebook_get_nth_page (notebook, tab_nb); + YItem *item = (YItem *) g_object_get_data (G_OBJECT (child), "yitem"); + + pThis->YDumbTab::selectItem (item); + YGUI::ui()->sendEvent (new YMenuEvent (item)); + pThis->syncTabPage(); + } + + YGWIDGET_IMPL_CONTAINER (YDumbTab) +}; + +YDumbTab *YGOptionalWidgetFactory::createDumbTab (YWidget *parent) +{ return new YGDumbTab (parent); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGFrame.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGFrame.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGFrame.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,182 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGWidget.h" +#include "YGUtils.h" + +// Instead of traditional looking frames, we use Gnome convention for the +// frame's look. That is: don't draw a frame, use bold header and pad the child. +#define CHILD_INDENTATION 8 + +class YGBaseFrame : public YGWidget +{ +protected: +// a GtkAlignment to set some indentation on the child +GtkWidget *m_containee; + +public: + YGBaseFrame (YWidget *ywidget, YWidget *parent) + : YGWidget (ywidget, parent, + GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_NONE, NULL) + { + m_containee = gtk_alignment_new (0, 0, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (m_containee), + 0, 0, CHILD_INDENTATION, 0); + gtk_widget_show (m_containee); + gtk_container_add (GTK_CONTAINER (getWidget()), m_containee); + } + + virtual GtkWidget *getContainer() + { return m_containee; } +}; + +#include "YFrame.h" + +static GtkWidget *findFirstFocusable (GtkContainer *container) +{ + g_return_val_if_fail (container != NULL, NULL); + + for (GList *l = gtk_container_get_children (container); + l; l = l->next) { + if (GTK_WIDGET_CAN_FOCUS (l->data)) + return GTK_WIDGET (l->data); + else if (GTK_IS_CONTAINER (l->data)) { + GtkWidget *ret = findFirstFocusable (GTK_CONTAINER (l->data)); + if (ret) + return ret; + } + } + return NULL; +} + +extern "C" { + static gboolean + frame_label_mnemonic_activate (GtkWidget *widget, + gboolean group_cycling, + GtkContainer *frame_container) + { + GtkWidget *focusable = findFirstFocusable (frame_container); + if (focusable == NULL) { + g_warning ("no focusable widgets for mnemonic"); + return FALSE; + } else + return gtk_widget_mnemonic_activate (focusable, group_cycling); + } +} + +class YGFrame : public YFrame, public YGBaseFrame +{ +public: + YGFrame (YWidget *parent, const string &label) + : YFrame (NULL, label), + YGBaseFrame (this, parent) + { + GtkWidget *label_widget = gtk_label_new_with_mnemonic (""); + g_signal_connect (G_OBJECT (label_widget), "mnemonic_activate", + G_CALLBACK (frame_label_mnemonic_activate), + getWidget()); + YGUtils::setWidgetFont (GTK_WIDGET (label_widget), PANGO_STYLE_NORMAL, + PANGO_WEIGHT_BOLD, PANGO_SCALE_MEDIUM); + gtk_widget_show (label_widget); + gtk_frame_set_label_widget (GTK_FRAME (getWidget()), label_widget); + setLabel (label); + } + + // YFrame + virtual void setLabel (const string &_str) + { + GtkWidget *label = gtk_frame_get_label_widget (GTK_FRAME (getWidget())); + string str (YGUtils::mapKBAccel (_str)); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), str.c_str()); + YFrame::setLabel (_str); + } + + YGWIDGET_IMPL_CONTAINER (YFrame) + + // YGWidget + virtual string getDebugLabel() const + { return label(); } +}; + + +YFrame *YGWidgetFactory::createFrame (YWidget *parent, const string &label) +{ return new YGFrame (parent, label); } + +#include "YCheckBoxFrame.h" + +class YGCheckBoxFrame : public YCheckBoxFrame, public YGBaseFrame +{ +public: + YGCheckBoxFrame (YWidget *parent, const string &label, bool checked) + : YCheckBoxFrame (NULL, label, checked), + YGBaseFrame (this, parent) + { + GtkWidget *button = gtk_check_button_new_with_mnemonic(""); + YGUtils::setWidgetFont (gtk_bin_get_child (GTK_BIN (button)), PANGO_STYLE_NORMAL, + PANGO_WEIGHT_BOLD, PANGO_SCALE_MEDIUM); + gtk_widget_show_all (button); + gtk_frame_set_label_widget (GTK_FRAME (getWidget()), button); + + setLabel (label); + setValue (checked); + connect (button, "toggled", G_CALLBACK (toggled_cb), this); + } + + // YCheckBoxFrame + virtual void setLabel (const string &_str) + { + GtkWidget *button = gtk_frame_get_label_widget (GTK_FRAME (getWidget())); + GtkLabel *label = GTK_LABEL (GTK_BIN (button)->child); + + string str (YGUtils::mapKBAccel (_str)); + gtk_label_set_text_with_mnemonic (label, str.c_str()); + YCheckBoxFrame::setLabel (_str); + } + + bool value() + { + GtkWidget *button = gtk_frame_get_label_widget (GTK_FRAME (getWidget())); + return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + } + + void setValue (bool value) + { + BlockEvents (this); + GtkWidget *button = gtk_frame_get_label_widget (GTK_FRAME (getWidget())); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), value); + } + + // YGWidget + virtual void doSetEnabled (bool enabled) + { + GtkWidget *frame = getWidget(); + if (enabled) { + gtk_widget_set_sensitive (frame, TRUE); + handleChildrenEnablement (value()); + } + else { + gtk_widget_set_sensitive (frame, FALSE); + YWidget::setChildrenEnabled (false); + } + YWidget::setEnabled (enabled); + } + + YGWIDGET_IMPL_CONTAINER (YCheckBoxFrame) + +private: + static void toggled_cb (GtkWidget *widget, YGCheckBoxFrame *pThis) + { + pThis->setEnabled (true); + if (pThis->notify()) + YGUI::ui()->sendEvent (new YWidgetEvent (pThis, YEvent::ValueChanged)); + } +}; + +YCheckBoxFrame *YGWidgetFactory::createCheckBoxFrame ( + YWidget *parent, const string &label, bool checked) +{ return new YGCheckBoxFrame (parent, label, checked); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGImage.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGImage.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGImage.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,59 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "ygdkmngloader.h" +#include "YGUI.h" +#include "YGWidget.h" +#include "YImage.h" +#include "ygtkimage.h" +#include <string.h> + +static inline bool endsWith (const std::string &str1, const char *str2) +{ + size_t len = strlen (str2); + if (str1.size() < len) return false; + return str1.compare (str1.size()-len, len, str2, len) == 0; +} + +class YGImage : public YImage, public YGWidget +{ +public: + YGImage (YWidget *parent, const string &filename, bool animated) + : YImage (NULL, filename, animated), + YGWidget (this, parent, YGTK_TYPE_IMAGE, NULL) + { + YGtkImage *image = YGTK_IMAGE (getWidget()); + const char *stock = NULL; + if (endsWith (filename, "/msg_question.png")) + stock = GTK_STOCK_DIALOG_QUESTION; + else if (endsWith (filename, "/msg_info.png")) + stock = GTK_STOCK_DIALOG_INFO; + else if (endsWith (filename, "/msg_warning.png")) + stock = GTK_STOCK_DIALOG_WARNING; + else if (endsWith (filename, "/msg_error.png")) + stock = GTK_STOCK_DIALOG_ERROR; + if (stock && gtk_style_lookup_icon_set (m_widget->style, stock)) { + GdkPixbuf *pixbuf = gtk_widget_render_icon (m_widget, stock, GTK_ICON_SIZE_DIALOG, NULL); + ygtk_image_set_from_pixbuf (image, pixbuf); + } + else + ygtk_image_set_from_file (image, filename.c_str(), animated); + } + + virtual void setAutoScale (bool scale) + { + YGtkImageAlign align = CENTER_IMAGE_ALIGN; + if (scale) + align = SCALE_IMAGE_ALIGN; + ygtk_image_set_props (YGTK_IMAGE (getWidget()), align, NULL); + } + + YGWIDGET_IMPL_COMMON (YImage) +}; + +YImage *YGWidgetFactory::createImage (YWidget *parent, const string &filename, bool animated) +{ return new YGImage (parent, filename, animated); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGInputField.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGInputField.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGInputField.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,338 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGWidget.h" +#include "YGUtils.h" +#include "ygtkfieldentry.h" + +#include <YInputField.h> + +class YGInputField : public YInputField, public YGLabeledWidget +{ +public: + YGInputField (YWidget *parent, const string &label, bool passwordMode) + : YInputField (NULL, label, passwordMode), + YGLabeledWidget (this, parent, label, YD_HORIZ, + YGTK_TYPE_FIELD_ENTRY, NULL) + { + gtk_widget_set_size_request (getWidget(), 0, -1); // let min size, set width + YGtkFieldEntry *field = YGTK_FIELD_ENTRY (getWidget()); + ygtk_field_entry_add_field (field, 0); + + GtkEntry *entry = ygtk_field_entry_get_field_widget (field, 0); + gtk_entry_set_activates_default (entry, TRUE); + gtk_entry_set_visibility (entry, !passwordMode); + + connect (getWidget(), "field-entry-changed", G_CALLBACK (value_changed_cb), this); + } + + // YInputField + virtual string value() + { + YGtkFieldEntry *field = YGTK_FIELD_ENTRY (getWidget()); + return ygtk_field_entry_get_field_text (field, 0); + } + + virtual void setValue (const string &text) + { + BlockEvents block (this); + YGtkFieldEntry *field = YGTK_FIELD_ENTRY (getWidget()); + ygtk_field_entry_set_field_text (field, 0, text.c_str()); + } + + void updateProps() + { + YGtkFieldEntry *field = YGTK_FIELD_ENTRY (getWidget()); + ygtk_field_entry_setup_field (field, 0, inputMaxLength(), validChars().c_str()); + } + + virtual void setInputMaxLength (int len) + { + YInputField::setInputMaxLength (len); + updateProps(); + } + + virtual void setValidChars (const string &validChars) + { + YInputField::setValidChars (validChars); + updateProps(); + } + + // callbacks + static void value_changed_cb (YGtkFieldEntry *entry, gint field_nb, YGInputField *pThis) + { pThis->emitEvent (YEvent::ValueChanged); } + + // YGWidget + virtual bool doSetKeyboardFocus() + { + YGtkFieldEntry *field = YGTK_FIELD_ENTRY (getWidget()); + return ygtk_field_entry_set_focus (field); + } + + virtual unsigned int getMinSize (YUIDimension dim) + { return dim == YD_HORIZ ? (shrinkable() ? 30 : 200) : 0; } + + YGLABEL_WIDGET_IMPL (YInputField) +}; + +YInputField *YGWidgetFactory::createInputField (YWidget *parent, const string &label, + bool passwordMode) +{ + return new YGInputField (parent, label, passwordMode); +} + +#include "YTimeField.h" + +class YGTimeField : public YTimeField, public YGLabeledWidget +{ +public: + YGTimeField (YWidget *parent, const string &label) + : YTimeField (NULL, label), + YGLabeledWidget (this, parent, label, YD_HORIZ, + YGTK_TYPE_FIELD_ENTRY, NULL) + { + YGtkFieldEntry *field = YGTK_FIELD_ENTRY (getWidget()); + ygtk_field_entry_add_field (field, ':'); + ygtk_field_entry_add_field (field, ':'); + ygtk_field_entry_setup_field (field, 0, 2, "0123456789"); + ygtk_field_entry_setup_field (field, 1, 2, "0123456789"); + + connect (getWidget(), "field-entry-changed", G_CALLBACK (value_changed_cb), this); + } + + // YTimeField + virtual void setValue (const string &time) + { + BlockEvents block (this); + if (time.empty()) return; + char hours[3], mins[3]; + sscanf (time.c_str(), "%2s:%2s", hours, mins); + + YGtkFieldEntry *entry = YGTK_FIELD_ENTRY (getWidget()); + ygtk_field_entry_set_field_text (entry, 0, hours); + ygtk_field_entry_set_field_text (entry, 1, mins); + } + + virtual string value() + { + const gchar *hours, *mins; + YGtkFieldEntry *entry = YGTK_FIELD_ENTRY (getWidget()); + hours = ygtk_field_entry_get_field_text (entry, 0); + mins = ygtk_field_entry_get_field_text (entry, 1); + + gchar *time = g_strdup_printf ("%02d:%02d:00", atoi (hours), atoi (mins)); + string str (time); + g_free (time); + return str; + } + + // callbacks + static void value_changed_cb (YGtkFieldEntry *entry, gint field_nb, + YGTimeField *pThis) + { pThis->emitEvent (YEvent::ValueChanged); } + + YGLABEL_WIDGET_IMPL (YTimeField) +}; + +YTimeField *YGOptionalWidgetFactory::createTimeField (YWidget *parent, const string &label) +{ return new YGTimeField (parent, label); } + +#include "YDateField.h" +#include "ygtkmenubutton.h" + +class YGDateField : public YDateField, public YGLabeledWidget +{ +GtkWidget *m_calendar, *m_popup_calendar; + +public: + YGDateField (YWidget *parent, const string &label) + : YDateField (NULL, label), + YGLabeledWidget (this, parent, label, YD_HORIZ, YGTK_TYPE_FIELD_ENTRY, NULL) + { + ygtk_field_entry_add_field (getField(), '-'); + ygtk_field_entry_add_field (getField(), '-'); + ygtk_field_entry_add_field (getField(), '-'); + ygtk_field_entry_setup_field (getField(), 0, 4, "0123456789"); + ygtk_field_entry_setup_field (getField(), 1, 2, "0123456789"); + ygtk_field_entry_setup_field (getField(), 2, 2, "0123456789"); + + m_calendar = gtk_calendar_new(); + gtk_widget_show (m_calendar); + GtkWidget *popup = ygtk_popup_window_new (m_calendar); + + GtkWidget *menu_button = ygtk_menu_button_new_with_label (""); + ygtk_menu_button_set_popup (YGTK_MENU_BUTTON (menu_button), popup); + gtk_widget_show (menu_button); + gtk_box_pack_start (GTK_BOX (getWidget()), menu_button, FALSE, TRUE, 6); + + connect (getWidget(), "field-entry-changed", G_CALLBACK (value_changed_cb), this); + connect (m_calendar, "day-selected", G_CALLBACK (calendar_changed_cb), this); + g_signal_connect (G_OBJECT (m_calendar), "day-selected-double-click", + G_CALLBACK (double_click_cb), popup); + } + + inline GtkCalendar *getCalendar() + { return GTK_CALENDAR (m_calendar); } + inline YGtkFieldEntry *getField() + { return YGTK_FIELD_ENTRY (getWidget()); } + + // YDateField + virtual void setValue (const string &date) + { + BlockEvents block (this); + if (date.empty()) return; + char year[5], month[3], day[3]; + sscanf (date.c_str(), "%4s-%2s-%2s", year, month, day); + + gtk_calendar_select_month (getCalendar(), atoi (month)-1, atoi (year)); + gtk_calendar_select_day (getCalendar(), atoi (day)); + + ygtk_field_entry_set_field_text (getField(), 0, year); + ygtk_field_entry_set_field_text (getField(), 1, month); + ygtk_field_entry_set_field_text (getField(), 2, day); + } + + virtual string value() + { + const gchar *year, *month, *day; + year = ygtk_field_entry_get_field_text (getField(), 0); + month = ygtk_field_entry_get_field_text (getField(), 1); + day = ygtk_field_entry_get_field_text (getField(), 2); + + gchar *time = g_strdup_printf ("%04d-%02d-%02d", atoi (year), + atoi (month), atoi (day)); + string str (time); + g_free (time); + return str; + } + + // callbacks + static void value_changed_cb (YGtkFieldEntry *entry, gint field_nb, + YGDateField *pThis) + { + int year, month, day; + year = atoi (ygtk_field_entry_get_field_text (pThis->getField(), 0)); + month = atoi (ygtk_field_entry_get_field_text (pThis->getField(), 1)); + day = atoi (ygtk_field_entry_get_field_text (pThis->getField(), 2)); + + if (day < 1 || day > 31 || month < 1 || month > 12) + return; // avoid GtkCalendar warnings + + g_signal_handlers_block_by_func (pThis->getCalendar(), + (gpointer) calendar_changed_cb, pThis); + + gtk_calendar_select_month (pThis->getCalendar(), month-1, year); + gtk_calendar_select_day (pThis->getCalendar(), day); + + g_signal_handlers_unblock_by_func (pThis->getCalendar(), + (gpointer) calendar_changed_cb, pThis); + + pThis->emitEvent (YEvent::ValueChanged); + } + + static void calendar_changed_cb (GtkCalendar *calendar, YGDateField *pThis) + { + guint year, month, day; + gtk_calendar_get_date (calendar, &year, &month, &day); + month += 1; // GTK calendar months go from 0 to 11 + + gchar *year_str, *month_str, *day_str; + year_str = g_strdup_printf ("%d", year); + month_str = g_strdup_printf ("%d", month); + day_str = g_strdup_printf ("%d", day); + + g_signal_handlers_block_by_func (pThis->getField(), + (gpointer) value_changed_cb, pThis); + + YGtkFieldEntry *entry = pThis->getField(); + ygtk_field_entry_set_field_text (entry, 0, year_str); + ygtk_field_entry_set_field_text (entry, 1, month_str); + ygtk_field_entry_set_field_text (entry, 2, day_str); + + g_signal_handlers_unblock_by_func (pThis->getField(), + (gpointer) value_changed_cb, pThis); + + g_free (year_str); + g_free (month_str); + g_free (day_str); + + pThis->emitEvent (YEvent::ValueChanged); + } + + static void double_click_cb (GtkCalendar *calendar, YGtkPopupWindow *popup) + { + // close popup + gtk_widget_hide (GTK_WIDGET (popup)); + } + + YGLABEL_WIDGET_IMPL (YDateField) +}; + +YDateField *YGOptionalWidgetFactory::createDateField (YWidget *parent, const string &label) +{ return new YGDateField (parent, label); } + +#include "YTimezoneSelector.h" +#include "ygtktimezonepicker.h" + +class YGTimezoneSelector : public YTimezoneSelector, public YGWidget +{ +public: + YGTimezoneSelector (YWidget *parent, const std::string &pixmap, + const std::map <std::string, std::string> &timezones) + : YTimezoneSelector (NULL, pixmap, timezones), + YGWidget (this, parent, YGTK_TYPE_TIME_ZONE_PICKER, NULL) + { + setStretchable (YD_HORIZ, true); + setStretchable (YD_VERT, true); + ygtk_time_zone_picker_set_map (YGTK_TIME_ZONE_PICKER (getWidget()), + pixmap.c_str(), convert_code_to_name, (gpointer) &timezones); + + connect (getWidget(), "zone-clicked", G_CALLBACK (zone_clicked_cb), this); + } + + // YTimezoneSelector + virtual std::string currentZone() const + { + YGTimezoneSelector *pThis = const_cast <YGTimezoneSelector *> (this); + const gchar *zone = ygtk_time_zone_picker_get_current_zone ( + YGTK_TIME_ZONE_PICKER (pThis->getWidget())); + if (zone) + return zone; + return std::string(); + } + + virtual void setCurrentZone (const std::string &zone, bool zoom) + { + BlockEvents block (this); + ygtk_time_zone_picker_set_current_zone (YGTK_TIME_ZONE_PICKER (getWidget()), + zone.c_str(), zoom); + } + + // YGtkTimeZonePicker + static const gchar *convert_code_to_name (const gchar *code, gpointer pData) + { + const std::map <std::string, std::string> *timezones = + (std::map <std::string, std::string> *) pData; + std::map <std::string, std::string>::const_iterator name = + timezones->find (code); + if (name == timezones->end()) + return NULL; + return name->second.c_str(); + } + + // callbacks + static void zone_clicked_cb (YGtkTimeZonePicker *picker, const gchar *zone, + YGTimezoneSelector *pThis) + { pThis->emitEvent (YEvent::ValueChanged); } + + YGWIDGET_IMPL_COMMON (YTimezoneSelector) +}; + +YTimezoneSelector *YGOptionalWidgetFactory::createTimezoneSelector (YWidget *parent, + const std::string &pixmap, const std::map <std::string, std::string> &timezones) +{ return new YGTimezoneSelector (parent, pixmap, timezones); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGIntField.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGIntField.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGIntField.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,140 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "YGUI.h" +#include "YGUtils.h" +#include "YGWidget.h" + +class YGSpinBox : public YGLabeledWidget +{ + YIntField *m_yfield; + GtkWidget *m_spiner, *m_slider; + +public: + YGSpinBox (YWidget *ywidget, YWidget *parent, const string &label, + int minValue, int maxValue, int initialValue, bool show_slider) + : YGLabeledWidget (ywidget, parent, label, YD_HORIZ, + GTK_TYPE_HBOX, "spacing", 6, NULL) + { + m_spiner = gtk_spin_button_new_with_range (minValue, maxValue, 1); + + if (show_slider) { + m_slider = gtk_hscale_new_with_range (minValue, maxValue, 1); + if (maxValue - minValue < 20) + // GtkScale by default uses a step of 10 -- use a lower for low values + gtk_range_set_increments (GTK_RANGE (m_slider), 1, 2); + gtk_scale_set_draw_value (GTK_SCALE (m_slider), FALSE); + YGLabeledWidget::setBuddy (m_slider); + gtk_widget_set_size_request (m_slider, 100, -1); + + gtk_box_pack_start (GTK_BOX (getWidget()), m_slider, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (getWidget()), m_spiner, FALSE, TRUE, 0); + gtk_widget_show (m_slider); + } + else { + m_slider = NULL; + YGLabeledWidget::setBuddy (m_spiner); + gtk_container_add (GTK_CONTAINER (getWidget()), m_spiner); + } + gtk_widget_show (m_spiner); + + doSetValue (initialValue); + connect (m_spiner, "value-changed", G_CALLBACK (spiner_changed_cb), this); + if (m_slider) + connect (m_slider, "value-changed", G_CALLBACK (slider_changed_cb), this); + } + + GtkSpinButton *getSpiner() + { return GTK_SPIN_BUTTON (m_spiner); } + + bool useSlider() + { return m_slider != NULL; } + GtkRange *getSlider() + { return GTK_RANGE (m_slider); } + + virtual void reportValue (int value) = 0; + + int doGetValue() + { return gtk_spin_button_get_value_as_int (getSpiner()); } + + void doSetValue (int value) + { + BlockEvents block (this); + gtk_spin_button_set_value (getSpiner(), value); + if (useSlider()) + gtk_range_set_value (getSlider(), value); + } + + // Events callbacks + static void spiner_changed_cb (GtkSpinButton *widget, YGSpinBox *pThis) + { + int value = gtk_spin_button_get_value_as_int (pThis->getSpiner()); + pThis->reportValue (value); + if (pThis->useSlider()) + gtk_range_set_value (pThis->getSlider(), value); + pThis->emitEvent (YEvent::ValueChanged); + } + + static void slider_changed_cb (GtkRange *range, YGSpinBox *pThis) + { + int value = (int) gtk_range_get_value (range); + gtk_spin_button_set_value (pThis->getSpiner(), value); + pThis->reportValue (value); + pThis->emitEvent (YEvent::ValueChanged); + } +}; + +#define YGSPIN_BOX_IMPL(ParentClass) \ + virtual void reportValue (int value) { \ + ParentClass::setValue (value); \ + } \ + virtual int value() { \ + return doGetValue(); \ + } \ + virtual void setValueInternal (int value) { \ + doSetValue (value); \ + } + +#include "YIntField.h" + +class YGIntField : public YIntField, public YGSpinBox +{ +public: + YGIntField (YWidget *parent, const string &label, int minValue, int maxValue, + int initialValue) + : YIntField (NULL, label, minValue, maxValue) + , YGSpinBox (this, parent, label, minValue, maxValue, initialValue, false) + {} + + YGLABEL_WIDGET_IMPL (YIntField) + YGSPIN_BOX_IMPL (YIntField) +}; + +YIntField *YGWidgetFactory::createIntField (YWidget *parent, const string &label, + int minValue, int maxValue, int initialValue) +{ return new YGIntField (parent, label, minValue, maxValue, initialValue); } + +#include "YSlider.h" + +class YGSlider : public YSlider, public YGSpinBox +{ +public: + YGSlider (YWidget *parent, const string &label, int minValue, int maxValue, + int initialValue) + : YSlider (NULL, label, minValue, maxValue) + , YGSpinBox (this, parent, label, minValue, maxValue, initialValue, true) + {} + + virtual unsigned int getMinSize (YUIDimension dim) + { return dim == YD_HORIZ ? 200 : 0; } + + YGLABEL_WIDGET_IMPL (YSlider) + YGSPIN_BOX_IMPL (YSlider) +}; + +YSlider *YGOptionalWidgetFactory::createSlider (YWidget *parent, const string &label, + int minValue, int maxValue, int initialValue) +{ return new YGSlider (parent, label, minValue, maxValue, initialValue); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLabel.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLabel.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLabel.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,55 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "YGUI.h" +#include "YGUtils.h" +#include "YGWidget.h" +#include "YLabel.h" + +class YGLabel : public YLabel, public YGWidget +{ +public: + YGLabel (YWidget *parent, const std::string &text, bool heading, bool outputField) + : YLabel (NULL, text, heading, outputField), + YGWidget (this, parent, GTK_TYPE_LABEL, NULL) + { + gtk_misc_set_alignment (GTK_MISC (getWidget()), 0.0, 0.5); + if (outputField) { + gtk_label_set_selectable (GTK_LABEL (getWidget()), TRUE); + gtk_label_set_single_line_mode (GTK_LABEL (getWidget()), TRUE); + YGUtils::setWidgetFont (getWidget(), PANGO_STYLE_ITALIC, PANGO_WEIGHT_NORMAL, + PANGO_SCALE_MEDIUM); + } + if (heading) + YGUtils::setWidgetFont (getWidget(), PANGO_STYLE_NORMAL, PANGO_WEIGHT_BOLD, + PANGO_SCALE_LARGE); + setLabel (text); + } + + virtual void setText (const std::string &label) + { + YLabel::setText (label); + gtk_label_set_label (GTK_LABEL (getWidget()), label.c_str()); + std::string::size_type i = label.find ('\n', 0); + if (isOutputField()) { // must not have a breakline + if (i != std::string::npos) { + std::string l (label, 0, i); + gtk_label_set_label (GTK_LABEL (getWidget()), l.c_str()); + } + } + else { + bool selectable = i != std::string::npos && i != label.size()-1; + gtk_label_set_selectable (GTK_LABEL (getWidget()), selectable); + } + } + + YGWIDGET_IMPL_COMMON (YLabel) + YGWIDGET_IMPL_USE_BOLD (YLabel) +}; + +YLabel *YGWidgetFactory::createLabel (YWidget *parent, + const std::string &text, bool heading, bool outputField) +{ return new YGLabel (parent, text, heading, outputField); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLayout.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLayout.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGLayout.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,285 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGWidget.h" +#include "YGUtils.h" + +/* Our layout stuff is a hybrid between GTK and libyui. We use libyui + for YLayoutBox and a couple of other widgets, but we do things GTK + friendly, so for single-child containers like GtkNotebook we don't + have to do any work. */ + +#include "ygtkfixed.h" + +static void doMoveChild (GtkWidget *fixed, YWidget *ychild, int x, int y) +{ + GtkWidget *child = YGWidget::get (ychild)->getLayout(); + ygtk_fixed_set_child_pos (YGTK_FIXED (fixed), child, x, y); +} + +#define YGLAYOUT_INIT \ + ygtk_fixed_setup (YGTK_FIXED (getWidget()), preferred_size_cb, set_size_cb, this); +#define YGLAYOUT_PREFERRED_SIZE_IMPL(ParentClass) \ + static void preferred_size_cb (YGtkFixed *fixed, gint *width, gint *height, \ + gpointer pThis) { \ + *width = ((ParentClass *) pThis)->ParentClass::preferredWidth(); \ + *height = ((ParentClass *) pThis)->ParentClass::preferredHeight(); \ + } +#define YGLAYOUT_SET_SIZE_IMPL(ParentClass) \ + static void set_size_cb (YGtkFixed *fixed, gint width, gint height, \ + gpointer pThis) { \ + ((ParentClass *) pThis)->ParentClass::setSize (width, height); \ + } \ + virtual void moveChild (YWidget *ychild, int x, int y) \ + { doMoveChild (getWidget(), ychild, x, y); } \ + +#include <YPushButton.h> + +class ButtonHeightGroup +{ +GtkSizeGroup *group; + +public: + ButtonHeightGroup() { group = NULL; } + + void addWidget (YWidget *ywidget) + { + if (dynamic_cast <YPushButton *> (ywidget)) { + bool create_group = !group; + if (create_group) + group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); + gtk_size_group_add_widget (group, YGWidget::get (ywidget)->getLayout()); + if (create_group) + g_object_unref (G_OBJECT (group)); + } + } +}; + +#include <YLayoutBox.h> + +class YGLayoutBox : public YLayoutBox, public YGWidget +{ +// set all buttons in a HBox the same height (some may have icons) +ButtonHeightGroup group; + +public: + YGLayoutBox (YWidget *parent, YUIDimension dim) + : YLayoutBox (NULL, dim), + YGWidget (this, parent, YGTK_TYPE_FIXED, NULL) + { + setBorder (0); + YGLAYOUT_INIT + } + + virtual void doAddChild (YWidget *ychild, GtkWidget *container) + { + YGWidget::doAddChild (ychild, container); + if (primary() == YD_HORIZ) + group.addWidget (ychild); + } + + YGWIDGET_IMPL_CONTAINER (YLayoutBox) + YGLAYOUT_PREFERRED_SIZE_IMPL (YLayoutBox) + YGLAYOUT_SET_SIZE_IMPL (YLayoutBox) +}; + +YLayoutBox *YGWidgetFactory::createLayoutBox (YWidget *parent, YUIDimension dimension) +{ return new YGLayoutBox (parent, dimension); } + +#if YAST2_VERSION >= 2017006 +#include <YButtonBox.h> + +class YGButtonBox : public YButtonBox, public YGWidget +{ +ButtonHeightGroup group; + +public: + YGButtonBox (YWidget *parent) + : YButtonBox (NULL), + YGWidget (this, parent, YGTK_TYPE_FIXED, NULL) + { + setBorder (0); + // YUI system variable test for layout policy doesn't work flawlessly + setLayoutPolicy (gnomeLayoutPolicy()); + YGLAYOUT_INIT + } + + virtual void doAddChild (YWidget *ychild, GtkWidget *container) + { + YGWidget::doAddChild (ychild, container); + group.addWidget (ychild); + } + + YGWIDGET_IMPL_CONTAINER (YButtonBox) + YGLAYOUT_PREFERRED_SIZE_IMPL (YButtonBox) + YGLAYOUT_SET_SIZE_IMPL (YButtonBox) +}; + +YButtonBox *YGWidgetFactory::createButtonBox (YWidget *parent) +{ return new YGButtonBox (parent); } + +#endif + +#include <YAlignment.h> + +class YGAlignment : public YAlignment, public YGWidget +{ + GdkPixbuf *m_background_pixbuf; + +public: + YGAlignment (YWidget *parent, YAlignmentType halign, YAlignmentType valign) + : YAlignment (NULL, halign, valign), + YGWidget (this, parent, YGTK_TYPE_FIXED, NULL) + { + setBorder (0); + m_background_pixbuf = 0; + YGLAYOUT_INIT + } + + virtual ~YGAlignment() + { + if (m_background_pixbuf) + g_object_unref (G_OBJECT (m_background_pixbuf)); + } + + YGWIDGET_IMPL_CONTAINER (YAlignment) + YGLAYOUT_PREFERRED_SIZE_IMPL (YAlignment) + YGLAYOUT_SET_SIZE_IMPL (YAlignment) + + virtual void setBackgroundPixmap (const std::string &_filename) + { + YAlignment::setBackgroundPixmap (_filename); + // YAlignment will prepend a path to the image + std::string filename (YAlignment::backgroundPixmap()); + + if (m_background_pixbuf) + g_object_unref (G_OBJECT (m_background_pixbuf)); + + if (filename.empty()) { + m_background_pixbuf = 0; + g_signal_handlers_disconnect_by_func (G_OBJECT (getWidget()), + (void*) expose_event_cb, this); + } + else { + GError *error = 0; + m_background_pixbuf = gdk_pixbuf_new_from_file (filename.c_str(), &error); + if (!m_background_pixbuf) + g_warning ("Setting YAlignment background - couldn't load image '%s' - %s", + filename.c_str(), error->message); + else + g_signal_connect (G_OBJECT (getWidget()), "expose-event", + G_CALLBACK (expose_event_cb), this); + } + } + + static gboolean expose_event_cb (GtkWidget *widget, GdkEventExpose *event, + YGAlignment *pThis) + { + GtkAllocation *alloc = &widget->allocation; + cairo_t *cr = gdk_cairo_create (widget->window); + + gdk_cairo_set_source_pixbuf (cr, pThis->m_background_pixbuf, 0, 0); + cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT); + + cairo_rectangle (cr, alloc->x, alloc->y, alloc->width, alloc->height); + cairo_fill (cr); + + cairo_destroy (cr); + + gtk_container_propagate_expose (GTK_CONTAINER (widget), + GTK_BIN (widget)->child, event); + return TRUE; + } +}; + +YAlignment *YGWidgetFactory::createAlignment (YWidget *parent, YAlignmentType halign, + YAlignmentType valign) +{ return new YGAlignment (parent, halign, valign); } + +#include <YEmpty.h> + +// Just an empty space. +class YGEmpty : public YEmpty, public YGWidget +{ +public: + YGEmpty (YWidget *parent) + : YEmpty (NULL), + YGWidget (this, parent, GTK_TYPE_EVENT_BOX, NULL) + { + setBorder (0); + } + + YGWIDGET_IMPL_COMMON (YEmpty) +}; + +YEmpty *YGWidgetFactory::createEmpty (YWidget *parent) +{ return new YGEmpty (parent); } + +#include <YSpacing.h> + +// Empty space, with a fixed size. +class YGSpacing : public YSpacing, public YGWidget +{ +public: + YGSpacing (YWidget *parent, YUIDimension dim, bool stretchable, YLayoutSize_t size) + : YSpacing (NULL, dim, stretchable, size), + YGWidget (this, parent, YGTK_TYPE_FIXED, NULL) + { + setBorder (0); + YGLAYOUT_INIT + } + + YGWIDGET_IMPL_COMMON (YSpacing) + YGLAYOUT_PREFERRED_SIZE_IMPL (YSpacing) + static void set_size_cb (YGtkFixed *fixed, gint width, gint height, + gpointer pThis) {} +}; + +YSpacing *YGWidgetFactory::createSpacing (YWidget *parent, YUIDimension dim, + bool stretchable, YLayoutSize_t size) +{ + return new YGSpacing (parent, dim, stretchable, size); +} + +#include <YReplacePoint.h> + +// an empty space that will get replaced +class YGReplacePoint : public YReplacePoint, public YGWidget +{ +public: + YGReplacePoint (YWidget *parent) + : YReplacePoint (NULL), + YGWidget (this, parent, GTK_TYPE_EVENT_BOX, NULL) + { + setBorder (0); + } + + YGWIDGET_IMPL_CONTAINER (YReplacePoint) +}; + +YReplacePoint *YGWidgetFactory::createReplacePoint (YWidget *parent) +{ return new YGReplacePoint (parent); } + +#include <YSquash.h> + +// A-like YAlignment, YSquash messes around child settings. +// In this case, it can remove the stretchable attribute. +class YGSquash : public YSquash, public YGWidget +{ +public: + YGSquash (YWidget *parent, bool hsquash, bool vsquash) + : YSquash (NULL, hsquash, vsquash), + YGWidget (this, parent, GTK_TYPE_EVENT_BOX, NULL) + { + setBorder (0); + } + + YGWIDGET_IMPL_CONTAINER (YSquash) +}; + +YSquash *YGWidgetFactory::createSquash (YWidget *parent, bool hsquash, bool vsquash) +{ return new YGSquash (parent, hsquash, vsquash); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGMenuButton.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGMenuButton.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGMenuButton.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,139 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGUtils.h" +#include "YGWidget.h" +#include "YMenuButton.h" +#include "ygtkmenubutton.h" + +static void selected_item_cb (GtkMenuItem *menuitem, YItem *item) +{ + // HACK: gtk_menu_get_active() doesn't work properly + GtkWidget *menu = gtk_widget_get_ancestor (GTK_WIDGET (menuitem), GTK_TYPE_MENU); + g_object_set_data (G_OBJECT (menu), "active", menuitem); + + YGUI::ui()->sendEvent (new YMenuEvent (item)); +} + +static void doCreateMenu (GtkWidget *parent, YItemIterator begin, YItemIterator end) +{ + for (YItemIterator it = begin; it != end; it++) { + GtkWidget *entry, *image = 0; + string str = YGUtils::mapKBAccel ((*it)->label()); + + if ((*it)->hasIconName()) { + GdkPixbuf *pixbuf = YGUtils::loadPixbuf ((*it)->iconName()); + if (pixbuf) { + image = gtk_image_new_from_pixbuf (pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + } + } + else { + const char *stock = YGUtils::mapStockIcon (str); + if (stock) + image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU); + } + + if (image) { + entry = gtk_image_menu_item_new_with_mnemonic (str.c_str()); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (entry), image); + } + else + entry = gtk_menu_item_new_with_mnemonic (str.c_str()); + + gtk_menu_shell_append (GTK_MENU_SHELL (parent), entry); + + if ((*it)->hasChildren()) { + GtkWidget *submenu = gtk_menu_new(); + doCreateMenu (submenu, (*it)->childrenBegin(), (*it)->childrenEnd()); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (entry), submenu); + } + else + g_signal_connect (G_OBJECT (entry), "activate", + G_CALLBACK (selected_item_cb), *it); + } +} + +class YGMenuButton : public YMenuButton, public YGWidget +{ +public: + YGMenuButton (YWidget *parent, const string &label) + : YMenuButton (NULL, label), + YGWidget (this, parent, YGTK_TYPE_MENU_BUTTON, NULL) + { setLabel (label); } + + // YMenuButton + virtual void setLabel (const std::string &label) + { + string str = YGUtils::mapKBAccel (label.c_str()); + ygtk_menu_button_set_label (YGTK_MENU_BUTTON (getWidget()), str.c_str()); + YMenuButton::setLabel (label); + } + + virtual void rebuildMenuTree() + { + GtkWidget *menu = gtk_menu_new(); + doCreateMenu (menu, itemsBegin(), itemsEnd()); + gtk_widget_show_all (menu); + ygtk_menu_button_set_popup (YGTK_MENU_BUTTON (getWidget()), menu); + } + + YGWIDGET_IMPL_COMMON (YMenuButton) +}; + +YMenuButton *YGWidgetFactory::createMenuButton (YWidget *parent, const string &label) +{ return new YGMenuButton (parent, label); } + +#if YAST2_VERSION > 2018003 +#include <YContextMenu.h> + +class YGContextMenu : public YContextMenu, public YGWidget +{ +public: + YGContextMenu() + : YContextMenu(), + YGWidget (this, NULL, GTK_TYPE_MENU, NULL) + { + // "cancel" signal doesnt seem to work properly + connect (getWidget(), "deactivate", G_CALLBACK (deactivate_cb), this); + } + + // YContextMenu + virtual void rebuildMenuTree() + { + GtkWidget *menu = getWidget(); + doCreateMenu (menu, itemsBegin(), itemsEnd()); + gtk_widget_show_all (menu); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time()); + } + + // callbacks + static void deactivate_cb (GtkMenuShell *menu, YGContextMenu *pThis) + { // ugly: we need to make sure a selection was made before this callback called + g_idle_add_full (G_PRIORITY_LOW, cancel_cb, pThis, NULL); + } + + static gboolean cancel_cb (gpointer data) + { + YGContextMenu *pThis = (YGContextMenu *) data; + if (!g_object_get_data (G_OBJECT (pThis->getWidget()), "active")) + YGUI::ui()->sendEvent (new YCancelEvent()); + delete pThis; + return FALSE; + } + + YGWIDGET_IMPL_COMMON (YContextMenu) +}; + +bool YGApplication::openContextMenu (const YItemCollection &itemCollection) +{ + YGContextMenu *menu = new YGContextMenu(); + menu->addItems (itemCollection); + return true; +} +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginIf.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginIf.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginIf.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,20 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGPACKAGE_SELECTOR_PLUGIN_IF +#define YGPACKAGE_SELECTOR_PLUGIN_IF + +struct YPackageSelector; +struct YWidget; + +class YGPackageSelectorPluginIf +{ +public: + virtual YPackageSelector *createPackageSelector (YWidget *parent, long modeFlags) = 0; + virtual YWidget *createPatternSelector (YWidget *parent, long modeFlags) = 0; + virtual YWidget *createSimplePatchSelector (YWidget *parent, long modeFlags) = 0; +}; + +#endif /*YGPACKAGE_SELECTOR_PLUGIN_IF*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginStub.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginStub.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPackageSelectorPluginStub.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,89 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#include "YGUI.h" +#include "YGPackageSelectorPluginIf.h" + +#define PLUGIN_BASE_NAME "gtk_pkg" +#include <YPackageSelectorPlugin.h> +#include <YDialog.h> +#include <YEvent.h> +#include <string.h> + +class YGPackageSelectorPluginStub : public YPackageSelectorPlugin +{ +YGPackageSelectorPluginIf *impl; + +public: + YGPackageSelectorPluginStub() + : YPackageSelectorPlugin (PLUGIN_BASE_NAME) + { + if (success()) + yuiMilestone() << "Loaded " << PLUGIN_BASE_NAME + << " plugin successfully from " << pluginLibFullPath() << endl; + impl = (YGPackageSelectorPluginIf *) locateSymbol ("PSP"); + if (!impl) + yuiError() << "Plugin " << PLUGIN_BASE_NAME + << " does not provide PSP symbol" << endl; + } + + static YGPackageSelectorPluginStub *get() + { + static YGPackageSelectorPluginStub *plugin = 0; + if (!plugin) + // deliberately keep plugin open + plugin = new YGPackageSelectorPluginStub(); + return plugin; + } + + virtual YPackageSelector *createPackageSelector (YWidget *parent, long modeFlags) + { + if (!impl) + YUI_THROW (YUIPluginException (PLUGIN_BASE_NAME)); + return impl->createPackageSelector (parent, modeFlags); + } + + virtual YWidget *createPatternSelector (YWidget *parent, long modeFlags) + { + if (!impl) + YUI_THROW (YUIPluginException (PLUGIN_BASE_NAME)); + return impl->createPatternSelector (parent, modeFlags); + } + + virtual YWidget *createSimplePatchSelector (YWidget *parent, long modeFlags) + { + if (!impl) + YUI_THROW (YUIPluginException (PLUGIN_BASE_NAME)); + return impl->createSimplePatchSelector (parent, modeFlags); + } +}; + +// YWidgetFactory + +YPackageSelector* YGWidgetFactory::createPackageSelector (YWidget* parent, long modeFlags) +{ + YGPackageSelectorPluginStub *plugin = YGPackageSelectorPluginStub::get(); + if (plugin) + return plugin->createPackageSelector (parent, modeFlags); + return NULL; +} + +// YOptionalWidgetFactory + +YWidget* YGOptionalWidgetFactory::createPatternSelector (YWidget* parent, long modeFlags) +{ + YGPackageSelectorPluginStub *plugin = YGPackageSelectorPluginStub::get(); + if (plugin) + return plugin->createPatternSelector (parent, modeFlags); + return NULL; +} + +YWidget* YGOptionalWidgetFactory::createSimplePatchSelector (YWidget* parent, long modeFlags) +{ + YGPackageSelectorPluginStub *plugin = YGPackageSelectorPluginStub::get(); + if (plugin) + return plugin->createSimplePatchSelector (parent, modeFlags); + return NULL; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGProgressBar.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGProgressBar.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGProgressBar.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,257 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* + Textdomain "gtk" + */ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGWidget.h" +#include "YGi18n.h" + +std::string size_stdform (YFileSize_t size) +{ + long double mantissa = size; + int unit = 0; + for (; mantissa/1024 > 1; unit++) + mantissa /= 1024; + + const char *unit_str = ""; + switch (unit) { + // translator: byte shorthand + case 0: unit_str = _("B"); break; + case 1: unit_str = _("KB"); break; + case 2: unit_str = _("MB"); break; + case 3: unit_str = _("GB"); break; + case 4: unit_str = _("TB"); break; + default: mantissa = 0; break; + } + + gchar *text = g_strdup_printf ("%.1f %s", (float) mantissa, unit_str); + std::string str (text); + g_free (text); + return str; +} + +#include "YProgressBar.h" + +class YGProgressBar : public YProgressBar, public YGLabeledWidget +{ +public: + YGProgressBar (YWidget *parent, const string &label, int maxValue) + : YProgressBar (NULL, label, maxValue) + // NOTE: its label widget is positionated at the vertical, because its label + // may change often and so will its size, which will look odd (we may want + // to make the label widget to only grow). + , YGLabeledWidget (this, parent, label, YD_VERT, GTK_TYPE_PROGRESS_BAR, NULL) + {} + + // YProgressBar + virtual void setValue (int value) + { + YProgressBar::setValue (value); + GtkProgressBar *bar = GTK_PROGRESS_BAR (getWidget()); + float fraction = CLAMP ((float) value / maxValue(), 0, 1); + gtk_progress_bar_set_fraction (bar, fraction); +/* + char *text = g_strdup_printf ("%d %%", (int) (fraction*100)); + gtk_progress_bar_set_text (bar, text); + g_free (text); +*/ + } + + virtual unsigned int getMinSize (YUIDimension dim) + { return dim == YD_HORIZ ? 200 : 0; } + + YGLABEL_WIDGET_IMPL (YProgressBar) +}; + +YProgressBar *YGWidgetFactory::createProgressBar (YWidget *parent, const string &label, + int maxValue) +{ + return new YGProgressBar (parent, label, maxValue); +} + +#include "YDownloadProgress.h" + +class YGDownloadProgress : public YDownloadProgress, public YGLabeledWidget +{ +guint timeout_id; + +public: + YGDownloadProgress (YWidget *parent, const string &label, + const string &filename, YFileSize_t expectedFileSize) + : YDownloadProgress (NULL, label, filename, expectedFileSize) + , YGLabeledWidget (this, parent, label, YD_HORIZ, GTK_TYPE_PROGRESS_BAR, NULL) + { + timeout_id = g_timeout_add (250, timeout_cb, this); + } + + virtual ~YGDownloadProgress() + { + g_source_remove (timeout_id); + } + + virtual void setExpectedSize (YFileSize_t size) + { + YDownloadProgress::setExpectedSize (size); + timeout_cb (this); // force an update + } + + static gboolean timeout_cb (void *pData) + { + YGDownloadProgress *pThis = (YGDownloadProgress*) pData; + GtkProgressBar *bar = GTK_PROGRESS_BAR (pThis->getWidget()); + + gtk_progress_bar_set_fraction (bar, pThis->currentPercent() / 100.0); + if (pThis->expectedSize() > 0) { + std::string current (size_stdform (pThis->currentFileSize())); + std::string total (size_stdform (pThis->expectedSize())); + std::string text = current + " " + _("of") + " " + total; + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), text.c_str()); + } + return TRUE; + } + + YGLABEL_WIDGET_IMPL (YDownloadProgress) +}; + +YDownloadProgress *YGOptionalWidgetFactory::createDownloadProgress (YWidget *parent, + const string &label, const string &filename, YFileSize_t expectedFileSize) +{ return new YGDownloadProgress (parent, label, filename, expectedFileSize); } + +#include "ygtkratiobox.h" +#include "YMultiProgressMeter.h" + +class YGMultiProgressMeter : public YMultiProgressMeter, public YGWidget +{ +public: + YGMultiProgressMeter (YWidget *parent, YUIDimension dim, const vector <float> &maxValues) + : YMultiProgressMeter (NULL, dim, maxValues) + , YGWidget (this, parent, + horizontal() ? YGTK_TYPE_RATIO_HBOX : YGTK_TYPE_RATIO_VBOX, NULL) + { + ygtk_ratio_box_set_spacing (YGTK_RATIO_BOX (getWidget()), 2); + for (int s = 0; s < segments(); s++) { + GtkWidget *bar = gtk_progress_bar_new(); + gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (bar), + horizontal() ? GTK_PROGRESS_LEFT_TO_RIGHT : GTK_PROGRESS_BOTTOM_TO_TOP); + // Progress bars would ask for too much size with weight... + const int min_size = 5; + if (horizontal()) + gtk_widget_set_size_request (bar, min_size, -1); + else + gtk_widget_set_size_request (bar, -1, min_size); + ygtk_ratio_box_pack (YGTK_RATIO_BOX (getWidget()), bar, getSegmentWeight (s)); + } + + ygtk_adj_size_set_max (YGTK_ADJ_SIZE (m_adj_size), horizontal() ? 200 : 0, + horizontal() ? 0 : 200); + gtk_widget_show_all (getWidget()); + } + + virtual void doUpdate() + { + GList* children = gtk_container_get_children (GTK_CONTAINER (getWidget())); + int s = 0; + for (GList *i = children; i && s < segments(); i = i->next, s++) { + GtkProgressBar *bar = GTK_PROGRESS_BAR (i->data); + gtk_progress_bar_set_fraction (bar, getSegmentValue (s)); + } + g_list_free (children); + } + + int getSegmentWeight (int n) + { + if (vertical()) + n = (segments() - n) - 1; + return maxValue (n); + } + float getSegmentValue (int n) + { + if (vertical()) + n = (segments() - n) - 1; + if (currentValue (n) < 0) + return 0; + return 1.0 - (((float) currentValue (n)) / maxValue (n)); + } + + YGWIDGET_IMPL_COMMON (YMultiProgressMeter) +}; + +YMultiProgressMeter *YGOptionalWidgetFactory::createMultiProgressMeter (YWidget *parent, + YUIDimension dim, const vector <float> &maxValues) +{ return new YGMultiProgressMeter (parent, dim, maxValues); } + +#include "YBusyIndicator.h" + +/* YBusyIndicator semantics are pretty contrived. It seems we should animate the widget + until timeout is reached. The application will ping setAlive(true) calls -- and we + reset the timeout -- as an indication that the program hasn't hang in some operation. */ + +#define PULSE_INTERVAL 100 +#define PULSE_STEP 0.050 + +class YGBusyIndicator : public YBusyIndicator, public YGLabeledWidget +{ +guint pulse_timeout_id; +int alive_timeout; + +public: + YGBusyIndicator (YWidget *parent, const string &label, int timeout) + : YBusyIndicator (NULL, label, timeout) + , YGLabeledWidget (this, parent, label, YD_VERT, + GTK_TYPE_PROGRESS_BAR, "pulse-step", PULSE_STEP, NULL) + { + pulse_timeout_id = 0; + pulse(); + } + + virtual ~YGBusyIndicator() + { stop(); } + + void pulse() + { + alive_timeout = timeout(); + if (!pulse_timeout_id) + pulse_timeout_id = g_timeout_add (PULSE_INTERVAL, pulse_timeout_cb, this); + } + + void stop() + { + alive_timeout = 0; + if (pulse_timeout_id) { + g_source_remove (pulse_timeout_id); + pulse_timeout_id = 0; + } + } + + // YBusyIndicator + virtual void setAlive (bool alive) + { + YBusyIndicator::setAlive (alive); + alive ? pulse() : stop(); + } + + // callbacks + static gboolean pulse_timeout_cb (void *pData) + { + YGBusyIndicator *pThis = (YGBusyIndicator*) pData; + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (pThis->getWidget())); + pThis->alive_timeout -= PULSE_INTERVAL; + if (pThis->alive_timeout <= 0) { + pThis->pulse_timeout_id = 0; + return FALSE; + } + return TRUE; + } + + YGLABEL_WIDGET_IMPL (YBusyIndicator) +}; + +YBusyIndicator *YGWidgetFactory::createBusyIndicator (YWidget *parent, const string &label, int timeout) +{ return new YGBusyIndicator (parent, label, timeout); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPushButton.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPushButton.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGPushButton.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,222 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include <YPushButton.h> +#include "YGUtils.h" +#include "YGWidget.h" +#include <string.h> + +#include <YLayoutBox.h> + +class YGPushButton : public YPushButton, public YGWidget +{ +bool m_customIcon, m_labelIcon; + +public: + YGPushButton (YWidget *parent, const string &label) + : YPushButton (NULL, label), + YGWidget (this, parent, GTK_TYPE_BUTTON, "can-default", TRUE, NULL) + { + m_customIcon = m_labelIcon = false; + gtk_button_set_use_underline (GTK_BUTTON (getWidget()), TRUE); + setLabel (label); + connect (getWidget(), "clicked", G_CALLBACK (clicked_cb), this); + g_signal_connect (getWidget(), "size-request", G_CALLBACK (size_request_cb), this); + } + + void setStockIcon (const std::string &label) + { + if (!m_customIcon) { + const char *stock = NULL; + switch (functionKey()) { + case 1: stock = GTK_STOCK_HELP; break; + case 2: stock = GTK_STOCK_INFO; break; // Info + case 3: stock = GTK_STOCK_ADD; break; + case 4: stock = GTK_STOCK_EDIT; break; + case 5: stock = GTK_STOCK_DELETE; break; + case 6: stock = GTK_STOCK_EXECUTE; break; // Test + case 7: stock = GTK_STOCK_EDIT; break; // Expert + // old expert icon: GTK_STOCK_PREFERENCES + //case 8: stock = GTK_STOCK_GO_BACK; break; + case 9: stock = GTK_STOCK_CANCEL; break; + case 10: stock = GTK_STOCK_OK; break; // Next/Finish/OK + default: break; + } +#if YAST2_VERSION >= 2017006 + switch (role()) { + case YOKButton: stock = GTK_STOCK_OK; break; + case YApplyButton: stock = GTK_STOCK_APPLY; break; + case YCancelButton: stock = GTK_STOCK_CANCEL; break; + case YHelpButton: stock = GTK_STOCK_HELP; break; + case YCustomButton: case YMaxButtonRole: break; + } +#endif + m_labelIcon = YGUtils::setStockIcon (getWidget(), label, stock); + } + } + + // YPushButton + virtual void setLabel (const std::string &label) + { + YPushButton::setLabel (label); + string str = YGUtils::mapKBAccel (label); + gtk_button_set_label (GTK_BUTTON (getWidget()), str.c_str()); + setStockIcon (str); + } + +#if YAST2_VERSION >= 2017006 + virtual void setRole (YButtonRole role) + { + YPushButton::setRole (role); + if (!m_labelIcon && role != YCustomButton) // to avoid duplications + setStockIcon (label()); + } +#endif + + virtual void setFunctionKey (int key) + { + YPushButton::setFunctionKey (key); + if (!m_labelIcon && hasFunctionKey()) + setStockIcon (label()); + } + + virtual void setHelpButton (bool helpButton) + { + YPushButton::setHelpButton (helpButton); + if (!m_labelIcon && helpButton) + setStockIcon (label()); + } + + virtual void setIcon (const string &icon) + { + GtkButton *button = GTK_BUTTON (getWidget()); + if (icon.empty()) { + m_customIcon = false; + // no need to worry about freeing the image, let it live with button + GtkWidget *image = gtk_button_get_image (button); + if (image) + gtk_widget_hide (image); + } + else { + m_customIcon = true; + string path (icon); + if (path[0] != '/') + path = std::string (THEMEDIR) + "/" + path; + + GError *error = 0; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path.c_str(), &error); + if (pixbuf) { + GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf); + gtk_button_set_image (button, image); + // disregard gtk-button-images setting for explicitly set icons + gtk_widget_show (image); + g_object_unref (G_OBJECT (pixbuf)); + } + else + yuiWarning() << "YGPushButton: Couldn't load icon image: " << path << endl + << "Reason: " << error->message << endl; + } + } + + virtual void setDefaultButton (bool isDefault) + { + struct inner { + static void realize_cb (GtkWidget *widget) + { + gtk_widget_grab_default (widget); + gtk_widget_grab_focus (widget); + } + }; + + YPushButton::setDefaultButton (isDefault); + if (isDefault) { + GtkWidget *button = getWidget(); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + if (GTK_WIDGET_REALIZED (button)) + inner::realize_cb (button); + g_signal_connect (G_OBJECT (button), "realize", + G_CALLBACK (inner::realize_cb), this); + } + } + + static bool hasIcon (YWidget *ywidget) + { + if (dynamic_cast <YPushButton *> (ywidget)) { + GtkWidget *button = YGWidget::get (ywidget)->getWidget(); + GtkWidget *icon = gtk_button_get_image (GTK_BUTTON (button)); + return icon && GTK_WIDGET_VISIBLE (icon); + } + return true; + } + +#if 0 + static gboolean treat_icon_cb (GtkWidget *widget, GdkEventExpose *event, + YGPushButton *pThis) + { + YLayoutBox *ybox = dynamic_cast <YLayoutBox *> (pThis->parent()); + if (ybox && !pThis->m_customIcon) { + if (ybox->primary() == YD_HORIZ) { + // only set stock icons if all to the left have them + YWidget *ylast = 0; + for (YWidgetListConstIterator it = ybox->childrenBegin(); + it != ybox->childrenEnd(); it++) { + if ((YWidget *) pThis == *it) { + if (ylast && !hasIcon (ylast)) + pThis->setIcon (""); + break; + } + ylast = *it; + if (!dynamic_cast <YPushButton *> (ylast)) + ylast = NULL; + } + } + else { // YD_VERT + // different strategy: set icons for all or none + bool disableIcons = false; + for (YWidgetListConstIterator it = ybox->childrenBegin(); + it != ybox->childrenEnd(); it++) + if (!hasIcon (*it)) + disableIcons = true; + if (disableIcons) + for (YWidgetListConstIterator it = ybox->childrenBegin(); + it != ybox->childrenEnd(); it++) + if (dynamic_cast <YPushButton *> (*it)) { + YGPushButton *button = (YGPushButton *) + YGWidget::get (*it); + if (!button->m_customIcon) + button->setIcon (""); + } + } + } + g_signal_handlers_disconnect_by_func (widget, (gpointer) treat_icon_cb, pThis); + return FALSE; + } +#endif + + // callbacks + static void clicked_cb (GtkButton *button, YGPushButton *pThis) + { pThis->emitEvent (YEvent::Activated, IGNORE_NOTIFY_EVENT); } + +// default values from gtkbbox.c; can vary from style to style, but no way to query those +#define DEFAULT_CHILD_MIN_WIDTH 85 +#define DEFAULT_CHILD_MIN_HEIGHT 27 + + static void size_request_cb (GtkWidget *widget, GtkRequisition *req, YGPushButton *pThis) + { // enlarge button if parent is ButtonBox + YWidget *yparent = pThis->m_ywidget->parent(); + if (yparent && !strcmp (yparent->widgetClass(), "YButtonBox")) { + req->width = MAX (req->width, DEFAULT_CHILD_MIN_WIDTH); + req->height = MAX (req->height, DEFAULT_CHILD_MIN_HEIGHT); + } + } + + YGWIDGET_IMPL_COMMON (YPushButton) +}; + +YPushButton *YGWidgetFactory::createPushButton (YWidget *parent, const string &label) +{ return new YGPushButton (parent, label); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGRadioButton.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGRadioButton.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGRadioButton.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,202 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGUtils.h" +#include "YGWidget.h" + +// Sub-class GtkRadioButton to get a widget that renders like +// a radio-button, but behaves like a check/toggle-button. +static GType getCheckRadioButtonType() +{ + static GType type = 0; + + if (type) + return type; + + static const GTypeInfo info = { + sizeof (GtkRadioButtonClass), NULL, NULL, + NULL, NULL, NULL, + sizeof (GtkRadioButton), 0, NULL + }; + type = g_type_register_static (GTK_TYPE_RADIO_BUTTON, "YGRadioButton", + &info, GTypeFlags(0)); + // save a class_init function + GtkButtonClass *klass_new = GTK_BUTTON_CLASS (g_type_class_ref (type)); + GtkButtonClass *klass_sane = + GTK_BUTTON_CLASS (g_type_class_ref (GTK_TYPE_TOGGLE_BUTTON)); + klass_new->clicked = klass_sane->clicked; + return type; +} + +#include "YLayoutBox.h" + +static bool is_horizontal_box (YWidget *widget) +{ + YLayoutBox *box = dynamic_cast <YLayoutBox *> (widget); + if (box) + return box->primary() == YD_HORIZ; + return false; +} + +#include "YRadioButton.h" +#include "YRadioButtonGroup.h" + +class YGRadioButton : public YRadioButton, public YGWidget +{ +public: + YGRadioButton (YWidget *parent, const std::string &label, bool isChecked) + : YRadioButton (NULL, label), + YGWidget (this, parent, getCheckRadioButtonType(), NULL) + { + if (!is_horizontal_box (parent)) + setStretchable (YD_HORIZ, true); + setLabel (label); + gtk_button_set_use_underline (GTK_BUTTON (getWidget()), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (getWidget()), FALSE); + + connect (getWidget(), "toggled", G_CALLBACK (toggled_cb), this); + } + + // YRadioButton + virtual void setLabel (const string &text) + { + // NOTE: we can't just set a gtk_widget_modify() at the initialization + // because each gtk_button_set_label() creates a new label + string str = YGUtils::mapKBAccel(text.c_str()); + gtk_button_set_label (GTK_BUTTON (getWidget()), str.c_str()); + YRadioButton::setLabel (text); + } + + virtual bool value() + { return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (getWidget())); } + + virtual void setValue (bool checked) + { + BlockEvents block (this); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (getWidget()), checked); + if (checked) { + YRadioButton *yradio = static_cast <YRadioButton *> (m_ywidget); + if (buttonGroup()) + buttonGroup()->uncheckOtherButtons (yradio); + } + } + + YGWIDGET_IMPL_COMMON (YRadioButton) + YGWIDGET_IMPL_USE_BOLD (YRadioButton) + + // callbacks + static void toggled_cb (GtkButton *button, YGRadioButton *pThis) + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) + pThis->emitEvent (YEvent::ValueChanged); + pThis->setValue (true); + } +}; + +YRadioButton *YGWidgetFactory::createRadioButton (YWidget *parent, const string &label, + bool isChecked) +{ + YRadioButton *button = new YGRadioButton (parent, label, isChecked); + + // libyui instructs us to do it here due to vfuncs craziness + YRadioButtonGroup *group = button->buttonGroup(); + if (group) + group->addRadioButton (button); + button->setValue (isChecked); + return button; +} + +class YGRadioButtonGroup : public YRadioButtonGroup, public YGWidget +{ +public: + YGRadioButtonGroup(YWidget *parent) + : YRadioButtonGroup (NULL), + YGWidget (this, parent, GTK_TYPE_EVENT_BOX, NULL) + { + setBorder (0); + } + + YGWIDGET_IMPL_CONTAINER (YRadioButtonGroup) +}; + +YRadioButtonGroup *YGWidgetFactory::createRadioButtonGroup (YWidget *parent) +{ + return new YGRadioButtonGroup (parent); +} + +#include "YCheckBox.h" + +class YGCheckBox : public YCheckBox, public YGWidget +{ + bool m_isBold; + +public: + YGCheckBox(YWidget *parent, const string &label, bool isChecked) + : YCheckBox (NULL, label), + YGWidget (this, parent, GTK_TYPE_CHECK_BUTTON, NULL) + { + if (!is_horizontal_box (parent)) + setStretchable (YD_HORIZ, true); + setLabel (label); + gtk_button_set_use_underline (GTK_BUTTON (getWidget()), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (getWidget()), isChecked); + + connect (getWidget(), "toggled", G_CALLBACK (toggled_cb), this); + } + + // YCheckButton + virtual void setLabel (const string &text) + { + string str = YGUtils::mapKBAccel(text); + gtk_button_set_label (GTK_BUTTON (getWidget()), str.c_str()); + YCheckBox::setLabel (text); + } + + virtual YCheckBoxState value() + { + GtkToggleButton *button = GTK_TOGGLE_BUTTON (getWidget()); + + if (gtk_toggle_button_get_inconsistent (button)) + return YCheckBox_dont_care; + return gtk_toggle_button_get_active (button) ? YCheckBox_on : YCheckBox_off; + } + + virtual void setValue (YCheckBoxState value) + { + BlockEvents block (this); + GtkToggleButton *button = GTK_TOGGLE_BUTTON (getWidget()); + switch (value) { + case YCheckBox_dont_care: + gtk_toggle_button_set_inconsistent (button, TRUE); + break; + case YCheckBox_on: + gtk_toggle_button_set_inconsistent (button, FALSE); + gtk_toggle_button_set_active (button, TRUE); + break; + case YCheckBox_off: + gtk_toggle_button_set_inconsistent (button, FALSE); + gtk_toggle_button_set_active (button, FALSE); + break; + } + } + + static void toggled_cb (GtkBox *box, YGCheckBox *pThis) + { + GtkToggleButton *button = GTK_TOGGLE_BUTTON (box); + if (gtk_toggle_button_get_inconsistent (button)) + pThis->setValue (YCheckBox_on); + pThis->emitEvent (YEvent::ValueChanged); + } + + YGWIDGET_IMPL_COMMON (YCheckBox) + YGWIDGET_IMPL_USE_BOLD (YCheckBox) +}; + +YCheckBox *YGWidgetFactory::createCheckBox (YWidget *parent, const string &label, + bool isChecked) +{ return new YGCheckBox (parent, label, isChecked); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,181 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include <gtk/gtk.h> +#include <YItem.h> +#include <YSelectionWidget.h> +#include "YGUtils.h" +#include "YGSelectionStore.h" + +static inline int getYItemCol (GtkTreeModel *model) +{ return gtk_tree_model_get_n_columns (model) - 2; } + +static inline int getRowIdCol (GtkTreeModel *model) +{ return gtk_tree_model_get_n_columns (model) - 1; } + +YGSelectionStore::YGSelectionStore (bool tree) +: isTree (tree), m_nextRowId (0) +{} + +YGSelectionStore::~YGSelectionStore() +{ g_object_unref (G_OBJECT (m_model)); } + +void YGSelectionStore::createStore (int cols, const GType types[]) +{ + int _cols = cols + 2; + GType _types[_cols]; + for (int i = 0; i < cols; i++) + _types[i] = types[i]; + _types[cols+0] = G_TYPE_POINTER; // pointer to YItem + _types[cols+1] = G_TYPE_POINTER; // some unique value identifying this row + + if (isTree) + m_model = GTK_TREE_MODEL (gtk_tree_store_newv (_cols, _types)); + else + m_model = GTK_TREE_MODEL (gtk_list_store_newv (_cols, _types)); +} + +void YGSelectionStore::addRow (YItem *item, GtkTreeIter *iter, GtkTreeIter *parent) +{ + if (isTree) { + GtkTreeStore *store = GTK_TREE_STORE (m_model); + gtk_tree_store_append (store, iter, parent); + gtk_tree_store_set (store, iter, getYItemCol (m_model), item, + getRowIdCol (m_model), m_nextRowId, -1); + } + else { + GtkListStore *store = getListStore(); + gtk_list_store_append (store, iter); + gtk_list_store_set (store, iter, getYItemCol (m_model), item, + getRowIdCol (m_model), m_nextRowId, -1); + } + item->setData (m_nextRowId); + m_nextRowId = GINT_TO_POINTER (GPOINTER_TO_INT (m_nextRowId) + 1); +} + +void YGSelectionStore::setRowText (GtkTreeIter *iter, int iconCol, const std::string &icon, int labelCol, const std::string &label, const YSelectionWidget *widget) +{ + GdkPixbuf *pixbuf = 0; + if (!icon.empty()) { + std::string path (widget->iconFullPath (icon)); + pixbuf = YGUtils::loadPixbuf (path); + } + + if (isTree) + gtk_tree_store_set (getTreeStore(), iter, iconCol, pixbuf, + labelCol, label.c_str(), -1); + else + gtk_list_store_set (getListStore(), iter, iconCol, pixbuf, + labelCol, label.c_str(), -1); +} + +void YGSelectionStore::setRowMark (GtkTreeIter *iter, int markCol, bool mark) +{ + if (isTree) + gtk_tree_store_set (getTreeStore(), iter, markCol, mark, -1); + else + gtk_list_store_set (getListStore(), iter, markCol, mark, -1); +} + +void YGSelectionStore::doDeleteAllItems() +{ + if (isTree) + gtk_tree_store_clear (getTreeStore()); + else + gtk_list_store_clear (getListStore()); + m_nextRowId = 0; +} + +YItem *YGSelectionStore::getYItem (GtkTreeIter *iter) +{ + gpointer ptr; + gtk_tree_model_get (m_model, iter, getYItemCol (m_model), &ptr, -1); + return (YItem *) ptr; +} + +static GtkTreeIter *found_iter; + +void YGSelectionStore::getTreeIter (const YItem *item, GtkTreeIter *iter) +{ + struct inner { + static gboolean foreach_find ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer find_id) + { + gpointer id; + gtk_tree_model_get (model, iter, getRowIdCol (model), &id, -1); + if (id == find_id) { + found_iter = gtk_tree_iter_copy (iter); + return TRUE; + } + return FALSE; + } + }; + + found_iter = NULL; + gtk_tree_model_foreach (m_model, inner::foreach_find, item->data()); + *iter = *found_iter; + gtk_tree_iter_free (found_iter); +} + +GtkListStore *YGSelectionStore::getListStore() +{ return GTK_LIST_STORE (m_model); } + +GtkTreeStore *YGSelectionStore::getTreeStore() +{ return GTK_TREE_STORE (m_model); } + +bool YGSelectionStore::isEmpty() +{ + GtkTreeIter iter; + return !gtk_tree_model_get_iter_first (m_model, &iter); +} + +int YGSelectionStore::getTreeDepth() +{ + struct inner { + static gboolean foreach_max_depth ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _depth) + { + int *depth = (int *) _depth; + *depth = MAX (*depth, gtk_tree_path_get_depth (path)); + return FALSE; + } + }; + + int depth = 0; + gtk_tree_model_foreach (m_model, inner::foreach_max_depth, &depth); + return depth; +} + +#include <string.h> +static int find_col; + +bool YGSelectionStore::findLabel (int labelCol, const std::string &label, GtkTreeIter *iter) +{ + struct inner { + static gboolean foreach_find ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _value) + { + gchar *value = (gchar *) _value; + gchar *v; + gtk_tree_model_get (model, iter, find_col, &v, -1); + if (!strcmp (v, value)) { + found_iter = gtk_tree_iter_copy (iter); + return TRUE; + } + return FALSE; + } + }; + + find_col = labelCol; + found_iter = NULL; + gtk_tree_model_foreach (m_model, inner::foreach_find, (gpointer) label.c_str()); + if (found_iter) { + *iter = *found_iter; + gtk_tree_iter_free (found_iter); + } + return found_iter != NULL; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGSelectionStore.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,65 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Provides a basic model common to YSelectedWidget widgets. */ + +#ifndef YGSELECTION_STORE_H +#define YGSELECTION_STORE_H + +#include <gtk/gtktreemodel.h> +struct YItem; +struct YSelectionWidget; + +struct YGSelectionStore +{ + YGSelectionStore (bool tree); + virtual ~YGSelectionStore(); + + GtkTreeModel *getModel() { return m_model; } + void createStore (int cols, const GType types[]); + + void addRow (YItem *item, GtkTreeIter *iter, GtkTreeIter *parent = 0); + void setRowText (GtkTreeIter *iter, int iconCol, const std::string &icon, + int labelCol, const std::string &label, const YSelectionWidget *widget); + void setRowMark (GtkTreeIter *iter, int markCol, bool mark); + void doDeleteAllItems(); + + YItem *getYItem (GtkTreeIter *iter); + void getTreeIter (const YItem *item, GtkTreeIter *iter); + + GtkListStore *getListStore(); + GtkTreeStore *getTreeStore(); + + bool isEmpty(); + int getTreeDepth(); + + bool findLabel (int labelCol, const std::string &label, GtkTreeIter *iter); + +protected: + GtkTreeModel *m_model; + bool isTree; + gpointer m_nextRowId; +}; + +#define YGSELECTION_WIDGET_IMPL(ParentClass) \ + virtual void addItem(YItem *item) { \ + ParentClass::addItem (item); \ + doAddItem (item); \ + } \ + virtual void deleteAllItems() { \ + ParentClass::deleteAllItems(); \ + blockSelected(); \ + doDeleteAllItems(); \ + } \ + virtual void selectItem (YItem *item, bool select) { \ + ParentClass::selectItem (item, select); \ + doSelectItem (item, select); \ + } \ + virtual void deselectAllItems() { \ + ParentClass::deselectAllItems(); \ + doDeselectAllItems(); \ + } + +#endif /*YGSELECTION_STORE_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGText.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGText.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGText.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,285 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include <string> +#include "YGUtils.h" +#include "YGWidget.h" +#include "ygtktextview.h" + +class YGTextView : public YGScrolledWidget +{ +int maxChars; + +public: + YGTextView (YWidget *ywidget, YWidget *parent, const string &label, bool editable) + : YGScrolledWidget (ywidget, parent, label, YD_VERT, + YGTK_TYPE_TEXT_VIEW, "wrap-mode", GTK_WRAP_WORD_CHAR, + "editable", editable, NULL) + { + setPolicy (GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + maxChars = -1; + connect (getBuffer(), "changed", G_CALLBACK (text_changed_cb), this); + } + + GtkTextBuffer* getBuffer() + { return gtk_text_view_get_buffer (GTK_TEXT_VIEW (getWidget())); } + + int getCharsNb() + { return gtk_text_buffer_get_char_count (getBuffer()); } + + void setCharsNb (int max_chars) + { + maxChars = max_chars; + if (maxChars != -1 && getCharsNb() > maxChars) + truncateText (maxChars); + } + + void truncateText(int pos) + { + BlockEvents block (this); + GtkTextIter start_it, end_it; + gtk_text_buffer_get_iter_at_offset (getBuffer(), &start_it, pos); + gtk_text_buffer_get_end_iter (getBuffer(), &end_it); + gtk_text_buffer_delete (getBuffer(), &start_it, &end_it); + } + + void setText (const std::string &text) + { + BlockEvents block (this); + gtk_text_buffer_set_text (getBuffer(), text.c_str(), -1); + } + + void appendText (const std::string &text) + { + GtkTextIter end_it; + gtk_text_buffer_get_end_iter (getBuffer(), &end_it); + gtk_text_buffer_insert (getBuffer(), &end_it, text.c_str(), -1); + } + + std::string getText() + { + GtkTextIter start_it, end_it; + gtk_text_buffer_get_bounds (getBuffer(), &start_it, &end_it); + + gchar* text = gtk_text_buffer_get_text (getBuffer(), &start_it, &end_it, FALSE); + std::string str (text); + g_free (text); + return str; + } + + void scrollToBottom() + { +#if 1 + YGUtils::scrollWidget (GTK_TEXT_VIEW (getWidget())->vadjustment, false); +#else + GtkTextBuffer *buffer = getBuffer(); + GtkTextIter iter; + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_iter_set_line_offset (&iter, 0); + + GtkTextMark *mark = gtk_text_buffer_get_mark (buffer, "scroll"); + if (mark) + gtk_text_buffer_move_mark (buffer, mark, &iter); + else + mark = gtk_text_buffer_create_mark (buffer, "scroll", &iter, TRUE); + + GtkTextView *view = GTK_TEXT_VIEW (getWidget()); + gtk_text_view_scroll_mark_onscreen (view, mark); +#endif + } + + // Event callbacks + static void text_changed_cb (GtkTextBuffer *buffer, YGTextView *pThis) + { + if (pThis->maxChars != -1 && pThis->getCharsNb() > pThis->maxChars) { + pThis->truncateText (pThis->maxChars); + gtk_widget_error_bell (pThis->getWidget()); + } + pThis->emitEvent (YEvent::ValueChanged); + } +}; + +#include "YMultiLineEdit.h" + +class YGMultiLineEdit : public YMultiLineEdit, public YGTextView +{ +public: + YGMultiLineEdit (YWidget *parent, const string &label) + : YMultiLineEdit (NULL, label) + , YGTextView (this, parent, label, true) + {} + + // YMultiLineEdit + virtual void setValue (const string &text) + { YGTextView::setText (text); } + + virtual string value() + { return YGTextView::getText(); } + + virtual void setInputMaxLength (int nb) + { + YGTextView::setCharsNb (nb); + YMultiLineEdit::setInputMaxLength (nb); + } + + // YGWidget + + virtual unsigned int getMinSize (YUIDimension dim) + { + if (dim == YD_VERT) { + int height = YGUtils::getCharsHeight (getWidget(), defaultVisibleLines()); + return MAX (10, height); + } + return 30; + } + + YGLABEL_WIDGET_IMPL (YMultiLineEdit) +}; + +YMultiLineEdit *YGWidgetFactory::createMultiLineEdit (YWidget *parent, const string &label) +{ + return new YGMultiLineEdit (parent, label); +} + +#include "YLogView.h" + +class YGLogView : public YLogView, public YGTextView +{ +std::string m_text; + +public: + YGLogView (YWidget *parent, const string &label, int visibleLines, int maxLines) + : YLogView (NULL, label, visibleLines, maxLines) + , YGTextView (this, parent, label, false) + {} + + // YLogView + virtual void displayLogText (const std::string &text) + { + // libyui calls clearText before setting it: let's ignore it + if (text.empty()) return; + + if (text.compare (0, m_text.size(), m_text) == 0) { + if (text.size() == m_text.size()) return; + + // appending text: avoid flickering and allow user to scroll freely + GtkAdjustment *vadj = GTK_TEXT_VIEW (getWidget())->vadjustment; + bool autoScroll = vadj->value >= vadj->upper - vadj->page_size; + + std::string diff (text.substr (m_text.size())); + YGTextView::appendText (diff); + if (autoScroll) + YGTextView::scrollToBottom(); + + } + else { + YGTextView::setText (text); + YGTextView::scrollToBottom(); + } + m_text = text; + } + + // YGWidget + virtual unsigned int getMinSize (YUIDimension dim) + { + if (dim == YD_VERT) { + int height = YGUtils::getCharsHeight (getWidget(), visibleLines()); + return MAX (80, height); + } + return 50; + } + + YGLABEL_WIDGET_IMPL (YLogView) +}; + +YLogView *YGWidgetFactory::createLogView (YWidget *parent, const string &label, + int visibleLines, int maxLines) +{ + return new YGLogView (parent, label, visibleLines, maxLines); +} + +#include "YRichText.h" +#include "ygtkhtmlwrap.h" + +class YGRichText : public YRichText, YGScrolledWidget +{ +public: + YGRichText (YWidget *parent, const string &text, bool plainText) + : YRichText (NULL, text, plainText) + , YGScrolledWidget (this, parent, ygtk_html_wrap_get_type(), NULL) + { + ygtk_html_wrap_init (getWidget()); + ygtk_html_wrap_connect_link_clicked (getWidget(), link_clicked_cb, this); + setText (text, plainText); + } + + void setPlainText (const string &text) + { + ygtk_html_wrap_set_text (getWidget(), text.c_str(), TRUE); + } + + void setRichText (const string &text) + { +#if 0 // current done at the XHTML treatment level, we may want to enable + // this code so that we replace the entity for all widgets + string text (_text); + std::string productName = YUI::app()->productName(); + YGUtils::replace (text, "&product;", 9, productName.c_str()); +#endif + ygtk_html_wrap_set_text (getWidget(), text.c_str(), FALSE); + } + + void scrollToBottom() + { + ygtk_html_wrap_scroll (getWidget(), FALSE); + } + + void setText (const string &text, bool plain_mode) + { + plain_mode ? setPlainText (text) : setRichText (text); + if (autoScrollDown()) + scrollToBottom(); + } + + // YRichText + virtual void setValue (const string &text) + { + YRichText::setValue (text); + setText (text, plainTextMode()); + } + + virtual void setAutoScrollDown (bool on) + { + YRichText::setAutoScrollDown (on); + if (on) scrollToBottom(); + } + + virtual void setPlainTextMode (bool plain_mode) + { + YRichText::setPlainTextMode (plain_mode); + if (plain_mode != plainTextMode()) + setText (value(), plain_mode); + } + + // callbacks + static void link_clicked_cb (GtkWidget *widget, const char *url, gpointer data) + { YGUI::ui()->sendEvent (new YMenuEvent (url)); } + + // YGWidget + virtual unsigned int getMinSize (YUIDimension dim) + { return shrinkable() ? 10 : 100; } + + YGWIDGET_IMPL_COMMON (YRichText) +}; + + +YRichText *YGWidgetFactory::createRichText (YWidget *parent, const string &text, + bool plainTextMode) +{ + return new YGRichText (parent, text, plainTextMode); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGTreeView.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGTreeView.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGTreeView.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,870 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGUtils.h" +#include "YGWidget.h" +#include "YSelectionWidget.h" +#include "YGSelectionStore.h" +#include "ygtktreeview.h" +#include <string.h> + +/* A generic widget for table related widgets. */ + +class YGTreeView : public YGScrolledWidget, public YGSelectionStore +{ +protected: + guint m_blockTimeout; + int markColumn; + GtkWidget *m_count; + +public: + YGTreeView (YWidget *ywidget, YWidget *parent, const string &label, bool tree) + : YGScrolledWidget (ywidget, parent, label, YD_VERT, YGTK_TYPE_TREE_VIEW, NULL), + YGSelectionStore (tree) + { + gtk_tree_view_set_headers_visible (getView(), FALSE); + + /* Yast tools expect the user to be unable to un-select the row. They + generally don't check to see if the returned value is -1. So, just + disallow un-selection. */ + gtk_tree_selection_set_mode (getSelection(), GTK_SELECTION_BROWSE); + + connect (getSelection(), "changed", G_CALLBACK (selection_changed_cb), this); + connect (getWidget(), "row-activated", G_CALLBACK (activated_cb), this); + connect (getWidget(), "right-click", G_CALLBACK (right_click_cb), this); + + m_blockTimeout = 0; // GtkTreeSelection idiotically fires when showing widget + markColumn = -1; m_count = NULL; + blockSelected(); + g_signal_connect (getWidget(), "map", G_CALLBACK (block_init_cb), this); + } + + virtual ~YGTreeView() + { if (m_blockTimeout) g_source_remove (m_blockTimeout); } + + inline GtkTreeView *getView() + { return GTK_TREE_VIEW (getWidget()); } + inline GtkTreeSelection *getSelection() + { return gtk_tree_view_get_selection (getView()); } + + void addTextColumn (int iconCol, int textCol) + { addTextColumn ("", YAlignUnchanged, iconCol, textCol); } + + void addTextColumn (const std::string &header, YAlignmentType align, int icon_col, int text_col) + { + gfloat xalign = -1; + switch (align) { + case YAlignBegin: xalign = 0.0; break; + case YAlignCenter: xalign = 0.5; break; + case YAlignEnd: xalign = 1.0; break; + case YAlignUnchanged: break; + } + + GtkTreeViewColumn *column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title (column, header.c_str()); + + GtkCellRenderer *renderer; + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, "pixbuf", icon_col, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, "text", text_col, NULL); + if (xalign != -1) + g_object_set (renderer, "xalign", xalign, NULL); + + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_append_column (getView(), column); + if (gtk_tree_view_get_search_column (getView()) == -1) + gtk_tree_view_set_search_column (getView(), text_col); + } + + void addCheckColumn (int check_col) + { + GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new(); + g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (check_col)); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes ( + NULL, renderer, "active", check_col, NULL); + gtk_tree_view_column_set_cell_data_func (column, renderer, inconsistent_mark_cb, this, NULL); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (toggled_cb), this); + + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_append_column (getView(), column); + if (markColumn == -1) + markColumn = check_col; + } + + void readModel() + { gtk_tree_view_set_model (getView(), getModel()); } + + void addCountWidget (YWidget *yparent) + { + bool mainWidget = !yparent || !strcmp (yparent->widgetClass(), "YVBox") || !strcmp (yparent->widgetClass(), "YReplacePoint"); + if (mainWidget) { + m_count = gtk_label_new ("0"); + GtkWidget *hbox = gtk_hbox_new (FALSE, 4); + GtkWidget *label = gtk_label_new (_("Total selected:")); + //gtk_box_pack_start (GTK_BOX (hbox), gtk_event_box_new(), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), m_count, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (YGWidget::getWidget()), hbox, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + } + } + + void syncCount() + { + if (!m_count) return; + + struct inner { + static gboolean foreach ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis) + { + YGTreeView *pThis = (YGTreeView *) _pThis; + gboolean mark; + gtk_tree_model_get (model, iter, pThis->markColumn, &mark, -1); + if (mark) { + int count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model), "count")); + g_object_set_data (G_OBJECT (model), "count", GINT_TO_POINTER (count+1)); + } + return FALSE; + } + }; + + GtkTreeModel *model = getModel(); + g_object_set_data (G_OBJECT (model), "count", 0); + gtk_tree_model_foreach (model, inner::foreach, this); + + int count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model), "count")); + gchar *str = g_strdup_printf ("%d", count); + gtk_label_set_text (GTK_LABEL (m_count), str); + g_free (str); + } + + void focusItem (YItem *item, bool select) + { + GtkTreeIter iter; + getTreeIter (item, &iter); + blockSelected(); + + if (select) { + GtkTreePath *path = gtk_tree_model_get_path (getModel(), &iter); + gtk_tree_view_expand_to_path (getView(), path); + + if (gtk_tree_selection_get_mode (getSelection()) != GTK_SELECTION_MULTIPLE) + gtk_tree_view_scroll_to_cell (getView(), path, NULL, TRUE, 0.5, 0); + gtk_tree_path_free (path); + + gtk_tree_selection_select_iter (getSelection(), &iter); + } + else + gtk_tree_selection_unselect_iter (getSelection(), &iter); + } + + void unfocusAllItems() + { + blockSelected(); + gtk_tree_selection_unselect_all (getSelection()); + } + + void unmarkAll() + { + struct inner { + static gboolean foreach_unmark ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis) + { + YGTreeView *pThis = (YGTreeView *) _pThis; + pThis->setRowMark (iter, pThis->markColumn, FALSE); + return FALSE; + } + }; + + gtk_tree_model_foreach (getModel(), inner::foreach_unmark, this); + } + + YItem *getFocusItem() + { + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (getSelection(), NULL, &iter)) + return getYItem (&iter); + return NULL; + } + + virtual bool _immediateMode() { return true; } + virtual bool _shrinkable() { return false; } + virtual bool _recursiveSelection() { return false; } + + void setMark (GtkTreeIter *iter, YItem *yitem, gint column, bool state, bool recursive) + { + setRowMark (iter, column, state); + yitem->setSelected (state); + + if (recursive) + for (YItemConstIterator it = yitem->childrenBegin(); + it != yitem->childrenEnd(); it++) { + GtkTreeIter _iter; + getTreeIter (*it, &_iter); + setMark (&_iter, *it, column, state, true); + } + } + + void toggleMark (GtkTreePath *path, gint column) + { + GtkTreeIter iter; + if (!gtk_tree_model_get_iter (getModel(), &iter, path)) + return; + gboolean state; + gtk_tree_model_get (getModel(), &iter, column, &state, -1); + state = !state; + + YItem *yitem = getYItem (&iter); + setMark (&iter, yitem, column, state, _recursiveSelection()); + syncCount(); + emitEvent (YEvent::ValueChanged); + } + + // YGWidget + + virtual unsigned int getMinSize (YUIDimension dim) + { + if (dim == YD_VERT) + return YGUtils::getCharsHeight (getWidget(), _shrinkable() ? 2 : 5); + return 80; + } + +protected: + static gboolean block_selected_timeout_cb (gpointer data) + { + YGTreeView *pThis = (YGTreeView *) data; + pThis->m_blockTimeout = 0; + return FALSE; + } + + void blockSelected() + { // GtkTreeSelection only fires when idle; so set a timeout + if (m_blockTimeout) g_source_remove (m_blockTimeout); + m_blockTimeout = g_timeout_add_full (G_PRIORITY_LOW, 50, block_selected_timeout_cb, this, NULL); + } + + static void block_init_cb (GtkWidget *widget, YGTreeView *pThis) + { pThis->blockSelected(); } + + // callbacks + + static bool all_marked (GtkTreeModel *model, GtkTreeIter *iter, int mark_col) + { + gboolean marked; + GtkTreeIter child_iter; + if (gtk_tree_model_iter_children (model, &child_iter, iter)) + do { + gtk_tree_model_get (model, &child_iter, mark_col, &marked, -1); + if (!marked) return false; + all_marked (model, &child_iter, mark_col); + } while (gtk_tree_model_iter_next (model, &child_iter)); + return true; + } + + static void inconsistent_mark_cb (GtkTreeViewColumn *column, + GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data) + { // used for trees -- show inconsistent if one node is check but another isn't + YGTreeView *pThis = (YGTreeView *) data; + gboolean marked; + gtk_tree_model_get (model, iter, pThis->markColumn, &marked, -1); + gboolean consistent = !marked || all_marked (model, iter, pThis->markColumn); + g_object_set (G_OBJECT (cell), "inconsistent", !consistent, NULL); + } + + static void selection_changed_cb (GtkTreeSelection *selection, YGTreeView *pThis) + { + struct inner { + static gboolean foreach_sync_select ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis) + { + YGTreeView *pThis = (YGTreeView *) _pThis; + GtkTreeSelection *selection = pThis->getSelection(); + bool sel = gtk_tree_selection_iter_is_selected (selection, iter); + pThis->getYItem (iter)->setSelected (sel); + return FALSE; + } + }; + + if (pThis->m_blockTimeout) return; + if (pThis->markColumn == -1) + gtk_tree_model_foreach (pThis->getModel(), inner::foreach_sync_select, pThis); + if (pThis->_immediateMode()) + pThis->emitEvent (YEvent::SelectionChanged, IF_NOT_PENDING_EVENT); + } + + static void activated_cb (GtkTreeView *tree_view, GtkTreePath *path, + GtkTreeViewColumn *column, YGTreeView* pThis) + { + if (pThis->markColumn >= 0) + pThis->toggleMark (path, pThis->markColumn); + else { + // for tree - expand/collpase double-clicked rows + if (gtk_tree_view_row_expanded (tree_view, path)) + gtk_tree_view_collapse_row (tree_view, path); + else + gtk_tree_view_expand_row (tree_view, path, FALSE); + + pThis->emitEvent (YEvent::Activated); + } + } + + static void toggled_cb (GtkCellRendererToggle *renderer, gchar *path_str, + YGTreeView *pThis) + { + GtkTreePath *path = gtk_tree_path_new_from_string (path_str); + gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (renderer), "column")); + pThis->toggleMark (path, column); + gtk_tree_path_free (path); + + // un/marking a sub-node can cause changes of "inconsistency" + if (gtk_tree_path_get_depth (path) >= 2) + gtk_widget_queue_draw (pThis->getWidget()); + } + + static void right_click_cb (YGtkTreeView *view, gboolean outreach, YGTreeView *pThis) + { pThis->emitEvent (YEvent::ContextMenuActivated); } +}; + +#include "YTable.h" +#include "YGDialog.h" +#include <gdk/gdkkeysyms.h> +#include <string.h> + +class YGTable : public YTable, public YGTreeView +{ +public: + YGTable (YWidget *parent, YTableHeader *headers, bool multiSelection) + : YTable (NULL, headers, multiSelection), + YGTreeView (this, parent, std::string(), false) + { + gtk_tree_view_set_headers_visible (getView(), TRUE); + gtk_tree_view_set_rules_hint (getView(), columns() > 1); + ygtk_tree_view_set_empty_text (YGTK_TREE_VIEW (getView()), _("No entries.")); + if (multiSelection) + gtk_tree_selection_set_mode (getSelection(), GTK_SELECTION_MULTIPLE); + + GType types [columns()*2]; + for (int i = 0; i < columns(); i++) { + int t = i*2; + types[t+0] = GDK_TYPE_PIXBUF; + types[t+1] = G_TYPE_STRING; + addTextColumn (header(i), alignment (i), t, t+1); + } + createStore (columns()*2, types); + readModel(); + if (!keepSorting()) + setSortable (true); + + // if last col is aligned: add some dummy so that it doesn't expand. + YAlignmentType lastAlign = alignment (columns()-1); + if (lastAlign == YAlignCenter || lastAlign == YAlignEnd) + gtk_tree_view_append_column (getView(), gtk_tree_view_column_new()); + + g_signal_connect (getWidget(), "key-press-event", G_CALLBACK (key_press_event_cb), this); + } + + void setSortable (bool sortable) + { + if (!sortable && !GTK_WIDGET_REALIZED (getWidget())) + return; + int n = 0; + GList *columns = gtk_tree_view_get_columns (getView()); + for (GList *i = columns; i; i = i->next, n++) { + GtkTreeViewColumn *column = (GtkTreeViewColumn *) i->data; + if (n >= YGTable::columns()) + break; + if (sortable) { + int index = (n*2)+1; + if (!sortable) + index = -1; + gtk_tree_sortable_set_sort_func ( + GTK_TREE_SORTABLE (getModel()), index, tree_sort_cb, + GINT_TO_POINTER (index), NULL); + gtk_tree_view_column_set_sort_column_id (column, index); + } + else + gtk_tree_view_column_set_sort_column_id (column, -1); + } + g_list_free (columns); + } + + void setCell (GtkTreeIter *iter, int column, const YTableCell *cell) + { + if (!cell) return; + std::string label (cell->label()); + if (label == "X") + label = YUI::app()->glyph (YUIGlyph_CheckMark); + + int index = column * 2; + setRowText (iter, index, cell->iconName(), index+1, label, this); + } + + // YGTreeView + + virtual bool _immediateMode() { return immediateMode(); } + + // YTable + + virtual void setKeepSorting (bool keepSorting) + { + YTable::setKeepSorting (keepSorting); + setSortable (!keepSorting); + if (!keepSorting) { + GtkTreeViewColumn *column = gtk_tree_view_get_column (getView(), 0); + if (column) + gtk_tree_view_column_clicked (column); + } + } + + virtual void cellChanged (const YTableCell *cell) + { + GtkTreeIter iter; + getTreeIter (cell->parent(), &iter); + setCell (&iter, cell->column(), cell); + } + + // YGSelectionStore + + void doAddItem (YItem *_item) + { + YTableItem *item = dynamic_cast <YTableItem *> (_item); + if (item) { + GtkTreeIter iter; + addRow (item, &iter); + int i = 0; + for (YTableCellIterator it = item->cellsBegin(); + it != item->cellsEnd(); it++) + setCell (&iter, i++, *it); + if (item->selected()) + focusItem (item, true); + } + else + yuiError() << "Can only add YTableItems to a YTable.\n"; + } + + void doSelectItem (YItem *item, bool select) + { focusItem (item, select); } + + void doDeselectAllItems() + { unfocusAllItems(); } + + // callbacks + + static void activateButton (YWidget *button) + { + YWidgetEvent *event = new YWidgetEvent (button, YEvent::Activated); + YGUI::ui()->sendEvent (event); + } + + static void hack_right_click_cb (YGtkTreeView *view, gboolean outreach, YGTable *pThis) + { + if (pThis->notifyContextMenu()) + return YGTreeView::right_click_cb (view, outreach, pThis); + + // If no context menu is specified, hack one ;-) + + struct inner { + static void key_activate_cb (GtkMenuItem *item, YWidget *button) + { activateButton (button); } + static void appendItem (GtkWidget *menu, const gchar *stock, int key) + { + YWidget *button = YGDialog::currentDialog()->getFunctionWidget (key); + if (button) { + GtkWidget *item; + item = gtk_image_menu_item_new_from_stock (stock, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (key_activate_cb), button); + } + } + }; + + GtkWidget *menu = gtk_menu_new(); + YGDialog *dialog = YGDialog::currentDialog(); + if (dialog->getClassWidgets ("YTable").size() == 1) { + // if more than one table exists, function keys would be ambiguous + if (outreach) { + if (dialog->getFunctionWidget(3)) + inner::appendItem (menu, GTK_STOCK_ADD, 3); + } + else { + if (dialog->getFunctionWidget(4)) + inner::appendItem (menu, GTK_STOCK_EDIT, 4); + if (dialog->getFunctionWidget(5)) + inner::appendItem (menu, GTK_STOCK_DELETE, 5); + } + } + + menu = ygtk_tree_view_append_show_columns_item (YGTK_TREE_VIEW (view), menu); + ygtk_tree_view_popup_menu (view, menu); + } + + static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, YGTable *pThis) + { + if (event->keyval == GDK_Delete) { + YWidget *button = YGDialog::currentDialog()->getFunctionWidget (5); + if (button) + activateButton (button); + else + gtk_widget_error_bell (widget); + return TRUE; + } + return FALSE; + } + + static gint tree_sort_cb ( + GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer _index) + { + int index = GPOINTER_TO_INT (_index); + gchar *str_a, *str_b; + gtk_tree_model_get (model, a, index, &str_a, -1); + gtk_tree_model_get (model, b, index, &str_b, -1); + if (!str_a) str_a = g_strdup (""); + if (!str_b) str_b = g_strdup (""); + int ret = strcmp (str_a, str_b); + g_free (str_a); g_free (str_b); + return ret; + } + + YGLABEL_WIDGET_IMPL (YTable) + YGSELECTION_WIDGET_IMPL (YTable) +}; + +YTable *YGWidgetFactory::createTable (YWidget *parent, YTableHeader *headers, + bool multiSelection) +{ + return new YGTable (parent, headers, multiSelection); +} + +#include "YSelectionBox.h" + +class YGSelectionBox : public YSelectionBox, public YGTreeView +{ +public: + YGSelectionBox (YWidget *parent, const string &label) + : YSelectionBox (NULL, label), + YGTreeView (this, parent, label, false) + { + GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING }; + addTextColumn (0, 1); + createStore (2, types); + readModel(); + } + + // YGTreeView + + virtual bool _shrinkable() { return shrinkable(); } + + // YGSelectionStore + + void doAddItem (YItem *item) + { + GtkTreeIter iter; + addRow (item, &iter); + setRowText (&iter, 0, item->iconName(), 1, item->label(), this); + if (item->selected()) + focusItem (item, true); + } + + void doSelectItem (YItem *item, bool select) + { focusItem (item, select); } + + void doDeselectAllItems() + { unfocusAllItems(); } + + YGLABEL_WIDGET_IMPL (YSelectionBox) + YGSELECTION_WIDGET_IMPL (YSelectionBox) +}; + +YSelectionBox *YGWidgetFactory::createSelectionBox (YWidget *parent, const string &label) +{ return new YGSelectionBox (parent, label); } + +#include "YMultiSelectionBox.h" + +class YGMultiSelectionBox : public YMultiSelectionBox, public YGTreeView +{ +public: + YGMultiSelectionBox (YWidget *parent, const string &label) + : YMultiSelectionBox (NULL, label), + YGTreeView (this, parent, label, false) + { + GType types [3] = { G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING }; + addCheckColumn (0); + addTextColumn (1, 2); + createStore (3, types); + readModel(); + addCountWidget (parent); + } + + // YGTreeView + + virtual bool _shrinkable() { return shrinkable(); } + + // YGSelectionStore + + void doAddItem (YItem *item) + { + GtkTreeIter iter; + addRow (item, &iter); + setRowMark (&iter, 0, item->selected()); + setRowText (&iter, 1, item->iconName(), 2, item->label(), this); + syncCount(); + } + + void doSelectItem (YItem *item, bool select) + { + GtkTreeIter iter; + getTreeIter (item, &iter); + setRowMark (&iter, 0, select); + syncCount(); + } + + void doDeselectAllItems() + { unmarkAll(); syncCount(); } + + // YMultiSelectionBox + + virtual YItem *currentItem() + { return getFocusItem(); } + + virtual void setCurrentItem (YItem *item) + { focusItem (item, true); } + + YGLABEL_WIDGET_IMPL (YMultiSelectionBox) + YGSELECTION_WIDGET_IMPL (YMultiSelectionBox) +}; + +YMultiSelectionBox *YGWidgetFactory::createMultiSelectionBox (YWidget *parent, const string &label) +{ return new YGMultiSelectionBox (parent, label); } + +#include "YTree.h" +#include "YTreeItem.h" + +class YGTree : public YTree, public YGTreeView +{ +public: +#if YAST2_VERSION >= 2020003 + YGTree (YWidget *parent, const string &label, bool multiselection, bool recursiveSelection) + : YTree (NULL, label, multiselection, recursiveSelection), +#else +#if YAST2_VERSION >= 2019002 + YGTree (YWidget *parent, const string &label, bool multiselection) + : YTree (NULL, label, multiselection), +#else + YGTree (YWidget *parent, const string &label) + : YTree (NULL, label), +#endif +#endif + YGTreeView (this, parent, label, true) + { +#if YAST2_VERSION >= 2019002 + if (multiselection) { + GType types [3] = { GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN }; + addCheckColumn (2); + addTextColumn (0, 1); + createStore (3, types); + addCountWidget (parent); + } + else +#endif + { + GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING }; + addTextColumn (0, 1); + createStore (2, types); + } + readModel(); + + g_signal_connect (getWidget(), "row-collapsed", G_CALLBACK (row_collapsed_cb), this); + g_signal_connect (getWidget(), "row-expanded", G_CALLBACK (row_expanded_cb), this); + } + +#if YAST2_VERSION >= 2020003 + virtual bool _recursiveSelection() { return recursiveSelection(); } +#endif + + void addNode (YItem *item, GtkTreeIter *parent) + { + GtkTreeIter iter; + addRow (item, &iter, parent); + setRowText (&iter, 0, item->iconName(), 1, item->label(), this); +#if 0 // yast2-qt ignores `selected flag + if (item->selected()) { +#if YAST2_VERSION >= 2019002 + if (hasMultiSelection()) + setRowMark (&iter, 2, item->selected()); + else +#endif + focusItem (item, true); + } +#endif +#if 0 + if (((YTreeItem *) item)->isOpen()) + expand (&iter); +#endif + for (YItemConstIterator it = item->childrenBegin(); + it != item->childrenEnd(); it++) + addNode (*it, &iter); + } + +#if 0 + void expand (GtkTreeIter *iter) + { + GtkTreePath *path = gtk_tree_model_get_path (getModel(), iter); + gtk_tree_view_expand_row (getView(), path, FALSE); + gtk_tree_path_free (path); + } + + bool isReallyOpen (YTreeItem *item) // are parents open as well? + { + for (YTreeItem *i = item; i; i = i->parent()) + if (!i->isOpen()) + return false; + return true; + } +#endif + + // YTree + + virtual void rebuildTree() + { + blockSelected(); + + doDeleteAllItems(); + for (YItemConstIterator it = YTree::itemsBegin(); it != YTree::itemsEnd(); it++) + addNode (*it, NULL); + + int depth = getTreeDepth(); + gtk_tree_view_set_show_expanders (getView(), depth > 1); + gtk_tree_view_set_enable_tree_lines (getView(), depth > 3); + + // for whatever reason, we need to expand nodes only after the model + // is fully initialized + struct inner { + static gboolean foreach_sync_open ( + GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis) + { + YGTree *pThis = (YGTree *) _pThis; + YTreeItem *item = (YTreeItem *) pThis->getYItem (iter); + if (item->isOpen()) + gtk_tree_view_expand_row (pThis->getView(), path, FALSE); + return FALSE; + } + }; + + g_signal_handlers_block_by_func (getWidget(), (gpointer) row_expanded_cb, this); + gtk_tree_model_foreach (getModel(), inner::foreach_sync_open, this); + g_signal_handlers_unblock_by_func (getWidget(), (gpointer) row_expanded_cb, this); + + syncCount(); + } + + virtual YTreeItem *currentItem() + { return (YTreeItem *) getFocusItem(); } + + void _markItem (YItem *item, bool select, bool recursive) { + GtkTreeIter iter; + getTreeIter (item, &iter); + setRowMark (&iter, 2, select); + + if (recursive) { + YTreeItem *_item = (YTreeItem *) item; + for (YItemConstIterator it = _item->childrenBegin(); + it != _item->childrenEnd(); it++) + _markItem (*it, select, true); + } + } + + // YGSelectionStore + + void doAddItem (YItem *item) {} // rebuild will be called anyway + + void doSelectItem (YItem *item, bool select) + { +#if YAST2_VERSION >= 2019002 + if (hasMultiSelection()) { +#if YAST2_VERSION >= 2020003 + _markItem (item, select, recursiveSelection()); +#else + _markItem (item, select, false); +#endif + syncCount(); + } + else +#endif + focusItem (item, select); + } + + void doDeselectAllItems() + { +#if YAST2_VERSION >= 2019002 + if (hasMultiSelection()) { + unmarkAll(); + syncCount(); + } + else +#endif + unfocusAllItems(); + } + + // callbacks + + void reportRowOpen (GtkTreeIter *iter, bool open) + { + YTreeItem *item = static_cast <YTreeItem *> (getYItem (iter)); + item->setOpen (open); + } + + static void row_collapsed_cb (GtkTreeView *view, GtkTreeIter *iter, + GtkTreePath *path, YGTree *pThis) + { pThis->reportRowOpen (iter, false); } + + static void row_expanded_cb (GtkTreeView *view, GtkTreeIter *iter, + GtkTreePath *path, YGTree *pThis) + { pThis->reportRowOpen (iter, true); } + +#if 0 + // we do a bit of a work-around here to mimic -qt behavior... A node can + // be initialized as open, yet its parent, or some grand-parent, be closed. + // We thus honor the open state when its parent gets open. + YTreeItem *item = static_cast <YTreeItem *> (pThis->getYItem (iter)); + for (YItemConstIterator it = item->childrenBegin(); + it != item->childrenEnd(); it++) { + const YTreeItem *child = static_cast <YTreeItem *> (*it); + if (child->isOpen()) { + GtkTreeIter iter; + if (pThis->getIter (child, &iter)) + pThis->expand (&iter); + } + } +#endif + + YGLABEL_WIDGET_IMPL (YTree) + YGSELECTION_WIDGET_IMPL (YTree) +}; + +#if YAST2_VERSION >= 2020003 +YTree *YGWidgetFactory::createTree (YWidget *parent, const string &label, bool multiselection, bool recursiveSelection) +{ return new YGTree (parent, label, multiselection, recursiveSelection); } +#else +#if YAST2_VERSION >= 2019002 +YTree *YGWidgetFactory::createTree (YWidget *parent, const string &label, bool multiselection) +{ return new YGTree (parent, label, multiselection); } +#else +YTree *YGWidgetFactory::createTree (YWidget *parent, const string &label) +{ return new YGTree (parent, label); } +#endif +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,652 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* + Textdomain "gtk" + */ + +#include <string.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <YEvent.h> +#include <YMacro.h> +#include <YCommandLine.h> +#include "YGUI.h" +#include "YGi18n.h" +#include "YGUtils.h" +#include "YGDialog.h" +#include <glib/gthread.h> + +static std::string askForFileOrDirectory (GtkFileChooserAction action, + const std::string &path, const std::string &filter, const std::string &title); + +static void errorMsg (const char *msg) +{ + GtkWidget* dialog = gtk_message_dialog_new (NULL, + GtkDialogFlags (0), GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Error")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), msg); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +#define DEFAULT_MACRO_FILE_NAME "macro.ycp" +#define BUSY_CURSOR_TIMEOUT 250 + +YUI *createUI( bool withThreads ) +{ + static YGUI *ui = 0; + if (!ui) + ui = new YGUI (withThreads); + return ui; +} + +YGUI::YGUI (bool with_threads) + : YUI (with_threads), m_done_init (false), busy_timeout (0) +{ + m_no_border = m_fullscreen = m_swsingle = false; + + YGUI::setTextdomain( TEXTDOMAIN ); + + // If we're running without threads, initialize Gtk stuff + // This enables standalone libyui use Gtk interface + if (!with_threads) + checkInit(); + + // without this none of the (default) threading action works ... + topmostConstructorHasFinished(); +} + +void YGUI::setTextdomain( const char * domain ) +{ + bindtextdomain( domain, LOCALEDIR ); + bind_textdomain_codeset( domain, "utf8" ); + textdomain( domain ); + + // Make change known. + { + extern int _nl_msg_cat_cntr; + ++_nl_msg_cat_cntr; + } +} + +static void print_log (const gchar *domain, GLogLevelFlags level, const gchar *message, void *pData) +{ + YUILogLevel_t ylevel = YUI_LOG_MILESTONE; + switch (level) { + case G_LOG_LEVEL_ERROR: + case G_LOG_LEVEL_CRITICAL: + ylevel = YUI_LOG_ERROR; + break; + case G_LOG_LEVEL_WARNING: + ylevel = YUI_LOG_WARNING; + break; + case G_LOG_LEVEL_DEBUG: + ylevel = YUI_LOG_DEBUG; + break; + case G_LOG_LEVEL_MESSAGE: + case G_LOG_LEVEL_INFO: + default: + break; + } + // YUILog.cc assumes 'logComponent' (etc.) are static strings, that it + // can just keep lying around for ever, and use later, so we have to + // intern the domain - that can be allocated (or belong to a transient + // plugin's address space). + const char *component = domain ? g_intern_string (domain) : "yast2-gtk"; + YUILog::instance()->log (ylevel, component, "yast2-gtk", 0, "") << message << std::endl; +#if 0 // uncomment to put a stop to gdb + static int bugStop = 0; + if (bugStop-- <= 0) + abort(); +#endif +} + +void YGUI::checkInit() +{ + if (m_done_init) + return; + m_done_init = true; + + // retrieve command line args from /proc/<pid>/cmdline + YCommandLine cmdLine; + int argc = cmdLine.argc(); + char **argv = cmdLine.argv(); + for (int i = 1; i < argc; i++) { + const char *argp = argv[i]; + if (argp[0] != '-') { + if (!strcmp (argp, "sw_single") || !strcmp (argp, "online_update")) + m_swsingle = true; + continue; + } + argp++; + if (argp[0] == '-') argp++; + + if (!strcmp (argp, "fullscreen")) + m_fullscreen = true; + else if (!strcmp (argp, "noborder")) + m_no_border = true; + else if (!strcmp (argp, "help")) { + printf ( + _("Command line options for the YaST2 UI (GTK plugin):\n\n" + "--noborder no window manager border for main dialogs\n" + "--fullscreen use full screen for main dialogs\n" + "--nothreads run without additional UI threads\n" + "--help prints this help text\n" + "\n" + )); + exit (0); + } + } + + gtk_init (&argc, &argv); + + g_log_set_default_handler (print_log, NULL); // send gtk logs to libyui system +#if 0 // to crash right away in order to get a stack trace + g_log_set_always_fatal (GLogLevelFlags (G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL| + G_LOG_LEVEL_WARNING| G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG)); +#endif + + GdkPixbuf *pixbuf = YGUtils::loadPixbuf (THEMEDIR "/icons/32x32/apps/yast.png"); + if (pixbuf) { // default window icon + gtk_window_set_default_icon (pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + } +} + +static gboolean ycp_wakeup_fn (GIOChannel *source, GIOCondition condition, + gpointer data) +{ + *(int *)data = TRUE; + return TRUE; +} + +void YGUI::idleLoop (int fd_ycp) +{ + // The rational for this is that we need somewhere to run + // the magic 'main' thread, that can process thread unsafe + // incoming CORBA messages for us + checkInit(); + + GIOChannel *wakeup; + wakeup = g_io_channel_unix_new (fd_ycp); + g_io_channel_set_encoding (wakeup, NULL, NULL); + g_io_channel_set_buffered (wakeup, FALSE); + + int woken = FALSE; + guint watch_tag = g_io_add_watch (wakeup, (GIOCondition)(G_IO_IN | G_IO_PRI), + ycp_wakeup_fn, &woken); + while (!woken) + g_main_context_iteration (NULL, TRUE); + + g_source_remove (watch_tag); + g_io_channel_unref (wakeup); +} + +static gboolean user_input_timeout_cb (YGUI *pThis) +{ + if (!pThis->pendingEvent()) + pThis->sendEvent (new YTimeoutEvent()); + return FALSE; +} + +// utility that implements both userInput() and pollInput() +YEvent *YGUI::waitInput (unsigned long timeout_ms, bool block) +{ + checkInit(); + if (!YDialog::currentDialog (false)) + return NULL; + + if (block) + normalCursor(); // waiting for input, so no more busy + + guint timeout = 0; + + if (timeout_ms > 0) + timeout = g_timeout_add (timeout_ms, + (GSourceFunc) user_input_timeout_cb, this); + + if (block) { + while (!pendingEvent()) + g_main_context_iteration (NULL, TRUE); + } + else + while (g_main_context_iteration (NULL, FALSE)) ; + + YEvent *event = NULL; + if (pendingEvent()) + event = m_event_handler.consumePendingEvent(); + + if (timeout) + g_source_remove (timeout); + + if (block) { // if YCP keeps working for more than X time, set busy cursor + if (busy_timeout) + g_source_remove (busy_timeout); + busy_timeout = g_timeout_add (BUSY_CURSOR_TIMEOUT, busy_timeout_cb, this); + } + return event; +} + +void YGUI::sendEvent (YEvent *event) +{ + m_event_handler.sendEvent (event); + g_main_context_wakeup (NULL); +} + +gboolean YGUI::busy_timeout_cb (gpointer data) +{ + YGUI *pThis = (YGUI *) data; + pThis->busyCursor(); + pThis->busy_timeout = 0; + return FALSE; +} + +void YGUI::busyCursor() +{ + YGDialog *dialog = YGDialog::currentDialog(); + if (dialog) + dialog->busyCursor(); +} + +void YGUI::normalCursor() +{ + if (busy_timeout) { + g_source_remove (busy_timeout); + busy_timeout = 0; + } + + YGDialog *dialog = YGDialog::currentDialog(); + if (dialog) + dialog->normalCursor(); +} + +void YGUI::makeScreenShot() +{ ((YGApplication *) app())->makeScreenShot (""); } + +YEvent *YGUI::runPkgSelection (YWidget *packageSelector) +{ + yuiMilestone() << "Running package selection...\n"; + YEvent *event = 0; + + try { + event = packageSelector->findDialog()->waitForEvent(); + } catch (const std::exception &e) { + yuiError() << "UI::RunPkgSelection() error: " << e.what() << endl; + yuiError() << "This is a libzypp problem. Do not file a bug against the UI!\n"; + } catch (...) { + yuiError() << "UI::RunPkgSelection() error (unspecified)\n"; + yuiError() << "This is a libzypp problem. Do not file a bug against the UI!\n"; + } + return event; +} + +void YGUI::askPlayMacro() +{ + string filename = askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_OPEN, + DEFAULT_MACRO_FILE_NAME, "*.ycp", _("Open Macro file")); + if (!filename.empty()) { + busyCursor(); + YMacro::play (filename); + sendEvent (new YEvent()); // flush + } +} + +void YGUI::toggleRecordMacro() +{ + if (YMacro::recording()) { + YMacro::endRecording(); + normalCursor(); + + GtkWidget* dialog = gtk_message_dialog_new (NULL, + GtkDialogFlags (0), GTK_MESSAGE_INFO, GTK_BUTTONS_OK, + _("Macro recording done.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + else { + string filename = askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SAVE, + DEFAULT_MACRO_FILE_NAME, "*.ycp", _("Save Macro")); + if (!filename.empty()) + YMacro::record (filename); + } +} + +void YGUI::askSaveLogs() +{ + string filename = askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SAVE, + "/tmp/y2logs.tgz", "*.tgz *.tar.gz", _("Save y2logs")); + if (!filename.empty()) { + std::string command = "/sbin/save_y2logs"; + command += " '" + filename + "'"; + yuiMilestone() << "Saving y2logs: " << command << endl; + int ret = system (command.c_str()); + if (ret == 0) + yuiMilestone() << "y2logs saved to " << filename << endl; + else { + char *error = g_strdup_printf ( + _("Could not run: '%s' (exit value: %d)"), + command.c_str(), ret); + yuiError() << error << endl; + errorMsg (error); + g_free (error); + } + } +} + +//** YGApplication + +#define ICONDIR THEMEDIR "/icons/22x22/apps/" + +YGApplication::YGApplication() +{ + setIconBasePath (ICONDIR); +} + +void YGApplication::makeScreenShot (const std::string &_filename) +{ + std::string filename (_filename); + bool interactive = filename.empty(); + + GtkWidget *widget = GTK_WIDGET (YGDialog::currentWindow()); + if (!widget) { + if (interactive) + errorMsg (_("No dialog to take screenshot of.")); + return; + } + + GError *error = 0; + GdkPixbuf *shot = + gdk_pixbuf_get_from_drawable (NULL, GDK_DRAWABLE (widget->window), + gdk_colormap_get_system(), 0, 0, 0, 0, widget->allocation.width, + widget->allocation.height); + + if (!shot) { + if (interactive) + errorMsg (_("Could not take screenshot.")); + return; + } + + if (interactive) { + //** ask user for filename + // calculate a default directory... + if (screenShotNameTemplate.empty()) { + string dir; + const char *homedir = getenv("HOME"); + const char *ssdir = getenv("Y2SCREENSHOTS"); + if (!homedir || !strcmp (homedir, "/")) { + // no homedir defined (installer) + dir = "/tmp/" + (ssdir ? (string(ssdir)) : (string(""))); + if (mkdir (dir.c_str(), 0700) == -1) + dir = ""; + } + else { + dir = homedir + (ssdir ? ("/" + string(ssdir)) : (string(""))); + mkdir (dir.c_str(), 0750); // create a dir for what to put the pics + } + + screenShotNameTemplate = dir + "/%s-%03d.png"; + } + + // calculate a default filename... + const char *baseName = "yast2-"; + + int nb; + map <string, int>::iterator it = screenShotNb.find (baseName); + if (it == screenShotNb.end()) + nb = 0; + + { + char *tmp_name = g_strdup_printf (screenShotNameTemplate.c_str(), baseName, nb); + filename = tmp_name; + g_free (tmp_name); + } + yuiDebug() << "screenshot: " << filename << endl; + + filename = askForFileOrDirectory ( + GTK_FILE_CHOOSER_ACTION_SAVE, "", "*.png", _("Save screenshot")); + if (filename.empty()) { // user dismissed the dialog + yuiDebug() << "Save screen shot canceled by user\n"; + goto makeScreenShot_ret; + } + + screenShotNb.erase (baseName); + screenShotNb[baseName] = nb + 1; + } + + yuiDebug() << "Saving screen shot to " << filename << endl; + if (!gdk_pixbuf_save (shot, filename.c_str(), "png", &error, NULL)) { + string msg = _("Could not save to:"); + msg += " "; msg += filename; + if (error) { + msg += "\n"; msg += "\n"; + msg += error->message; + } + yuiError() << msg << endl; + if (interactive) + errorMsg (msg.c_str()); + goto makeScreenShot_ret; + } + + makeScreenShot_ret: + g_object_unref (G_OBJECT (shot)); +} + +void YGApplication::beep() +{ + GtkWindow *window = YGDialog::currentWindow(); + if (window) { + gtk_window_present (window); + gtk_widget_error_bell (GTK_WIDGET (window)); + } + else + gdk_beep(); +} + +// File/directory dialogs +#include <sstream> + +std::string askForFileOrDirectory (GtkFileChooserAction action, + const std::string &path, const std::string &filter, const std::string &title) +{ + GtkWindow *parent = YGDialog::currentWindow(); + const char *button; + switch (action) { + case GTK_FILE_CHOOSER_ACTION_SAVE: + button = GTK_STOCK_SAVE; break; + case GTK_FILE_CHOOSER_ACTION_OPEN: + button = GTK_STOCK_OPEN; break; + case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER: + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + button = _("Select"); break; + } + GtkWidget *dialog; + dialog = gtk_file_chooser_dialog_new (title.c_str(), + parent, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + button, GTK_RESPONSE_ACCEPT, NULL); + GtkFileChooser *fileChooser = GTK_FILE_CHOOSER (dialog); + gtk_file_chooser_set_local_only (fileChooser, TRUE); + gtk_file_chooser_set_do_overwrite_confirmation (fileChooser, TRUE); + + // filepath can be a dir or a file path, split that up + string dirname, filename; + if (!path.empty()) { + if (path[0] != '/') + yuiWarning() << "FileDialog: Relative paths are not supported: '" << path << "'\n"; + else if (!g_file_test (path.c_str(), G_FILE_TEST_EXISTS)) + yuiWarning() << "FileDialog: Path doesn't exist: '" << path << "'\n"; + else if (g_file_test (path.c_str(), G_FILE_TEST_IS_DIR)) + dirname = path; + else { // its a file + string::size_type i = path.find_last_of ("/"); + if (i != string::npos) { + dirname = path.substr (0, i+1); + filename = path.substr (i+1); + } + } + } + + if (!dirname.empty()) + gtk_file_chooser_set_current_folder (fileChooser, dirname.c_str()); + if (!filename.empty()) + gtk_file_chooser_set_current_name (fileChooser, filename.c_str()); + + if (!filter.empty() && filter != "*") { + GtkFileFilter *gtk_filter = gtk_file_filter_new(); + gtk_file_filter_set_name (gtk_filter, filter.c_str()); + // cut filter_pattern into chuncks like GTK likes + std::istringstream stream (filter); + while (!stream.eof()) { + string str; + stream >> str; + if (!str.empty() && str [str.size()-1] == ',') + str.erase (str.size()-1); + gtk_file_filter_add_pattern (gtk_filter, str.c_str()); + } + gtk_file_chooser_add_filter (fileChooser, gtk_filter); + } + + // bug 335492: because "/" gets hidden as a an arrow at the top, make sure + // there is a root shortcut at the side pane (will not add if already exists) + gtk_file_chooser_add_shortcut_folder (fileChooser, "/", NULL); + + std::string ret; + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename (fileChooser); + if (filename) { + ret = filename; + g_free (filename); + } + } + gtk_widget_destroy (dialog); + return ret; +} + +std::string YGApplication::askForExistingDirectory ( + const std::string &path, const std::string &title) +{ return askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, path, "", title); } + +std::string YGApplication::askForExistingFile ( + const std::string &path, const std::string &filter, const std::string &title) +{ return askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_OPEN, path, filter, title); } + +std::string YGApplication::askForSaveFileName ( + const std::string &path, const std::string &filter, const std::string &title) +{ return askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SAVE, path, filter, title); } + +std::string YGApplication::glyph (const std::string &sym) +{ + bool reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + if (sym == YUIGlyph_ArrowLeft) + return reverse ? "\u25b6" : "\u25c0"; + if (sym == YUIGlyph_ArrowRight) + return reverse ? "\u25c0" : "\u25b6"; + if (sym == YUIGlyph_ArrowUp) + return "\u25b2"; + if (sym == YUIGlyph_ArrowDown) + return "\u25bc"; + if (sym == YUIGlyph_CheckMark) + return "\u2714"; + if (sym == YUIGlyph_BulletArrowRight) + return reverse ? "\u21e6" : "\u279c"; + if (sym == YUIGlyph_BulletCircle) + return "\u26ab"; + if (sym == YUIGlyph_BulletSquare) + return "\u25fe"; + return ""; +} + +// YWidget layout units -> pixels conversion. Same as yast-qt's. +int YGApplication::deviceUnits (YUIDimension dim, float size) +{ + if (dim == YD_HORIZ) + size *= 640.0 / 80; + else + size *= 480.0 / 25; + return size + 0.5; +} + +float YGApplication::layoutUnits (YUIDimension dim, int units) +{ + float size = (float) units; + if (dim == YD_HORIZ) return size * (80/640.0); + else return size * (25/480.0); +} + +static inline GdkScreen *getScreen () +{ return gdk_display_get_default_screen (gdk_display_get_default()); } +// GTK doesn't seem to have some desktopWidth/Height like Qt, so we to report +// a reduced display size to compensate for the panel, or the window frame +int YGApplication::displayWidth() +{ return gdk_screen_get_width (getScreen()) - 80; } +int YGApplication::displayHeight() +{ return gdk_screen_get_height (getScreen()) - 80; } + +int YGApplication::displayDepth() +{ return gdk_visual_get_best_depth(); } + +long YGApplication::displayColors() +{ return 1L << displayDepth(); /*from yast-qt*/ } + +// YCP uses defaultWidth/Height() to use their smaller YWizard impl; we +// want to use a smaller default size than qt though, so assume a bigger size +int YGApplication::defaultWidth() { return MIN (displayWidth(), 1024); } +int YGApplication::defaultHeight() { return MIN (displayHeight(), 768); } + +YWidgetFactory *YGUI::createWidgetFactory() +{ return new YGWidgetFactory; } +YOptionalWidgetFactory *YGUI::createOptionalWidgetFactory() +{ return new YGOptionalWidgetFactory; } +YApplication *YGUI::createApplication() +{ return new YGApplication(); } + +#include <YRichText.h> +#include "ygtktextview.h" + +void dumpYastHtml (YWidget *widget) +{ + struct inner { + static void dumpYastHtml (YWidget *widget, GtkBox *box) + { + YGWidget *ygwidget; + if (!widget || !(ygwidget = YGWidget::get (widget))) + return; + + YRichText *rtext = dynamic_cast <YRichText *> (widget); + if (rtext) { + std::string text = rtext->text(); + + GtkWidget *view = ygtk_text_view_new (FALSE); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD); + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + gtk_text_buffer_set_text (buffer, text.c_str(), -1); + + GtkWidget *scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (scroll), view); + gtk_box_pack_start (box, scroll, TRUE, TRUE, 6); + } + + for (YWidgetListConstIterator it = widget->childrenBegin(); + it != widget->childrenEnd(); it++) + dumpYastHtml (*it, box); + } + static void destroy_dialog (GtkDialog *dialog, gint arg) + { gtk_widget_destroy (GTK_WIDGET (dialog)); } + }; + + GtkWidget *dialog = gtk_dialog_new_with_buttons ("YWidgets HTML", NULL, + GtkDialogFlags (GTK_DIALOG_NO_SEPARATOR), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300); + + inner::dumpYastHtml (widget, GTK_BOX (GTK_DIALOG (dialog)->vbox)); + + gtk_widget_show_all (dialog); + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (inner::destroy_dialog), 0); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUI.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,240 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGUI_H +#define YGUI_H + +#include "config.h" +#include <YUI.h> +#define YUILogComponent "gtk" +#include <YUILog.h> +#include <YSimpleEventHandler.h> +#include <map> +#include <gtk/gtk.h> + +/* Comment the following line to disable debug messages */ +#define RET(a) { return (a); } + +class YGUI: public YUI +{ +public: + YGUI (bool with_threads); + void checkInit(); // called 1st time when execution thread kicks in + + static YGUI *ui() { return (YGUI *) YUI::ui(); } + +protected: + virtual YWidgetFactory *createWidgetFactory(); + virtual YOptionalWidgetFactory *createOptionalWidgetFactory(); + virtual YApplication *createApplication(); + +public: + static void setTextdomain (const char *domain); + + virtual void idleLoop (int fd_ycp); + // called by YDialog::waitInput() / pollEvent()... + YEvent *waitInput (unsigned long timeout_ms, bool block); + + virtual YEvent *runPkgSelection (YWidget *packageSelector); + + // used internally: for public use, see YApplication + void busyCursor(); + void normalCursor(); + void makeScreenShot(); + + // Plays a macro, opening a dialog first to ask for the filename + // activated by Ctrl-Shift-Alt-P + void askPlayMacro(); + void toggleRecordMacro(); + + // On Shift-F8, run save_logs + void askSaveLogs(); + + YSimpleEventHandler m_event_handler; + void sendEvent (YEvent *event); + YEvent *pendingEvent() const { return m_event_handler.pendingEvent(); } + bool eventPendingFor (YWidget *widget) const + { return m_event_handler.eventPendingFor (widget); } + +private: + bool m_done_init; + guint busy_timeout; // for busy cursor + static gboolean busy_timeout_cb (gpointer data); + + // window-related arguments + bool m_no_border, m_fullscreen, m_swsingle; + +public: + // Helpers for internal use [ visibility hidden ] + bool setFullscreen() const { return m_fullscreen; } + bool unsetBorder() const { return m_no_border; } + bool isSwsingle() const { return m_swsingle; } +}; + +// debug helpers. +void dumpTree (YWidget *widget); +void dumpYastHtml (YWidget *widget); + +#include <YWidgetFactory.h> + +class YGWidgetFactory : public YWidgetFactory +{ + virtual YDialog *createDialog (YDialogType dialogType, YDialogColorMode colorMode); + + virtual YPushButton *createPushButton (YWidget *parent, const string &label); + virtual YLabel *createLabel (YWidget *parent, const string &text, bool isHeading, bool isOutputField); + virtual YInputField *createInputField (YWidget *parent, const string &label, bool passwordMode); + virtual YCheckBox *createCheckBox (YWidget *parent, const string &label, bool isChecked); + virtual YRadioButton *createRadioButton (YWidget *parent, const string &label, bool isChecked); + virtual YComboBox *createComboBox (YWidget *parent, const string & label, bool editable); + virtual YSelectionBox *createSelectionBox (YWidget *parent, const string &label); +#if YAST2_VERSION >= 2020003 + virtual YTree *createTree (YWidget *parent, const string &label, bool multiselection, bool recursiveSelection); +#else +#if YAST2_VERSION >= 2019002 + virtual YTree *createTree (YWidget *parent, const string &label, bool multiselection); +#else + virtual YTree *createTree (YWidget *parent, const string &label); +#endif +#endif + virtual YTable *createTable (YWidget *parent, YTableHeader *headers, bool multiSelection); + virtual YProgressBar *createProgressBar (YWidget *parent, const string &label, int maxValue); + virtual YBusyIndicator *createBusyIndicator (YWidget *parent, const string &label, int timeout); + virtual YRichText *createRichText (YWidget *parent, const string &text, bool plainTextMode); + + virtual YIntField *createIntField (YWidget *parent, const string &label, int minVal, int maxVal, int initialVal); + virtual YMenuButton *createMenuButton (YWidget *parent, const string &label); + virtual YMultiLineEdit *createMultiLineEdit (YWidget *parent, const string &label); + virtual YImage *createImage (YWidget *parent, const string &imageFileName, bool animated); + virtual YLogView *createLogView (YWidget *parent, const string &label, int visibleLines, int storedLines); + virtual YMultiSelectionBox *createMultiSelectionBox (YWidget *parent, const string &label); + + virtual YPackageSelector *createPackageSelector (YWidget * parent, long ModeFlags); + virtual YWidget *createPkgSpecial (YWidget * parent, const string & subwidgetName) RET (NULL) // for ncurses + + virtual YLayoutBox *createLayoutBox (YWidget *parent, YUIDimension dimension); + virtual YButtonBox *createButtonBox (YWidget *parent); + + virtual YSpacing *createSpacing (YWidget *parent, YUIDimension dim, bool stretchable, YLayoutSize_t size); + virtual YEmpty *createEmpty (YWidget *parent); + virtual YAlignment *createAlignment (YWidget *parent, YAlignmentType horAlignment, YAlignmentType vertAlignment); + virtual YSquash *createSquash (YWidget *parent, bool horSquash, bool vertSquash); + + virtual YFrame *createFrame (YWidget *parent, const string &label); + virtual YCheckBoxFrame *createCheckBoxFrame (YWidget *parent, const string &label, bool checked); + + virtual YRadioButtonGroup *createRadioButtonGroup (YWidget *parent); + virtual YReplacePoint *createReplacePoint (YWidget *parent); +}; + +#include <YOptionalWidgetFactory.h> + +class YGOptionalWidgetFactory : public YOptionalWidgetFactory +{ +public: + virtual bool hasWizard() RET (true) + virtual YWizard *createWizard (YWidget *parent, const string &backButtonLabel, + const string &abortButtonLabel, const string &nextButtonLabel, + YWizardMode wizardMode); + + virtual bool hasDumbTab() RET (true) + virtual YDumbTab *createDumbTab (YWidget *parent); + + virtual bool hasSlider() RET (true) + virtual YSlider *createSlider (YWidget *parent, const string &label, int minVal, + int maxVal, int initialVal); + + virtual bool hasDateField() RET (true) + virtual YDateField *createDateField (YWidget *parent, const string &label); + + virtual bool hasTimeField() RET (true) + virtual YTimeField *createTimeField (YWidget *parent, const string &label); + + virtual bool hasTimezoneSelector() RET (true) + virtual YTimezoneSelector *createTimezoneSelector (YWidget *parent, + const string &pixmap, const map <string, string> &timezones); + + virtual bool hasBarGraph() RET (true) + virtual YBarGraph *createBarGraph (YWidget *parent); + + virtual bool hasMultiProgressMeter() RET (true) + virtual YMultiProgressMeter *createMultiProgressMeter (YWidget *parent, + YUIDimension dim, const vector<float> &maxValues); + + virtual bool hasPartitionSplitter() RET (true) + virtual YPartitionSplitter *createPartitionSplitter (YWidget *parent, + int usedSize, int totalFreeSize, int newPartSize, int minNewPartSize, + int minFreeSize, const string &usedLabel, const string &freeLabel, + const string &newPartLabel, const string &freeFieldLabel, + const string &newPartFieldLabel); + + virtual bool hasDownloadProgress() RET (true) + virtual YDownloadProgress *createDownloadProgress (YWidget *parent, + const string &label, const string & filename, YFileSize_t expectedFileSize); + + virtual bool hasContextMenu() RET (true) + + virtual bool hasSimplePatchSelector() RET (true) + virtual YWidget *createSimplePatchSelector (YWidget *parent, long modeFlags); + virtual bool hasPatternSelector() RET (true) + virtual YWidget *createPatternSelector (YWidget *parent, long modeFlags); +}; + +#include <YApplication.h> + +class YGApplication : public YApplication +{ +public: + YGApplication(); + + virtual std::string glyph (const std::string &symbolName); + + virtual std::string askForExistingDirectory (const std::string &startDir, + const std::string &headline); + virtual std::string askForExistingFile (const std::string &startWith, + const std::string &filter, const std::string &headline); + virtual std::string askForSaveFileName (const std::string &startWith, + const std::string &filter, const std::string &headline); + + virtual void busyCursor() { YGUI::ui()->busyCursor(); } + virtual void normalCursor() { YGUI::ui()->normalCursor(); } + + virtual void makeScreenShot (const std::string &filename); + virtual void beep(); + + virtual int deviceUnits (YUIDimension dim, float layout_units); + virtual float layoutUnits (YUIDimension dim, int device_units); + + virtual int displayWidth(); + virtual int displayHeight(); + virtual int displayDepth(); + virtual long displayColors(); + virtual int defaultWidth(); // internally, use _defaultWidth / Height() + virtual int defaultHeight(); + + virtual bool isTextMode() RET (false) + virtual bool leftHandedMouse() RET (false) + virtual bool hasImageSupport() RET (true) + virtual bool hasLocalImageSupport() RET (true) + virtual bool hasAnimationSupport() RET (true) + virtual bool hasIconSupport() RET (true) + virtual bool hasFullUtf8Support() RET (true) +#ifdef USE_WEBKIT + virtual bool richTextSupportsTable() RET (true) +#else + virtual bool richTextSupportsTable() RET (false) +#endif + + virtual bool openContextMenu (const YItemCollection &itemCollection); + +private: + // for screenshots: + std::map <std::string, int> screenShotNb; + std::string screenShotNameTemplate; +}; + +#undef RET + +#endif /*YGUI_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,869 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* + Textdomain "gtk" + */ + +#define YUILogComponent "gtk" +#include "config.h" +#include <string.h> +#include "YGUtils.h" +#include "YGUI.h" +#include "YGi18n.h" + +static inline void skipSpace (const char *instr, int *i) +{ while (g_ascii_isspace (instr[*i])) (*i)++; } + +typedef struct { + GString *tag; + int tag_len : 31; + unsigned int early_closer : 1; +} TagEntry; + +static TagEntry * +tag_entry_new (GString *tag, int tag_len) +{ + static const char *early_closers[] = { "p", "li" }; + TagEntry *entry = g_new (TagEntry, 1); + entry->tag = tag; + entry->tag_len = tag_len; + entry->early_closer = FALSE; + + unsigned int i; + for (i = 0; i < G_N_ELEMENTS (early_closers); i++) + if (!g_ascii_strncasecmp (tag->str, early_closers[i], tag_len)) + entry->early_closer = TRUE; + return entry; +} + +static void +tag_entry_free (TagEntry *entry) +{ + if (entry && entry->tag) + g_string_free (entry->tag, TRUE); + g_free (entry); +} + +static void +emit_unclosed_tags_for (GString *outp, GQueue *tag_queue, const char *tag_str, int tag_len) +{ + gboolean matched = FALSE; + + // top-level tag ... + if (g_queue_is_empty (tag_queue)) + return; + + do { + TagEntry *last_entry = (TagEntry *)g_queue_pop_tail (tag_queue); + if (!last_entry) + break; + + if (last_entry->tag_len != tag_len || + g_ascii_strncasecmp (last_entry->tag->str, tag_str, tag_len)) { + /* different tag - emit a close ... */ + g_string_append (outp, "</"); + g_string_append_len (outp, last_entry->tag->str, last_entry->tag_len); + g_string_append_c (outp, '>'); + } else + matched = TRUE; + + tag_entry_free (last_entry); + } while (!matched); +} + +static gboolean +check_early_close (GString *outp, GQueue *tag_queue, TagEntry *entry) +{ + TagEntry *last_tag; + + // Early closers: + if (!entry->early_closer) + return FALSE; + + last_tag = (TagEntry *) g_queue_peek_tail (tag_queue); + if (!last_tag || !last_tag->early_closer) + return FALSE; + + if (entry->tag_len != last_tag->tag_len || + g_ascii_strncasecmp (last_tag->tag->str, entry->tag->str, entry->tag_len)) + return FALSE; + + // Emit close & leave last tag on the stack + + g_string_append (outp, "</"); + g_string_append_len (outp, entry->tag->str, entry->tag_len); + g_string_append_c (outp, '>'); + + return TRUE; +} + +/* Some entities are translated by the xhtml parser, but not all... */ +typedef struct EntityMap { + const gchar *html, *text; +} EntityMap; + +static const EntityMap entities[] = { + { "nbsp", " " }, + { "product", 0 }, // dynamic +}; + +static const EntityMap *lookup_entity (const char *html) +{ + unsigned int i; + for (i = 0; i < sizeof (entities) / sizeof (EntityMap); i++) + if (!g_ascii_strncasecmp (html+1, entities[i].html, strlen (entities[i].html))) + return entities+i; + return NULL; +} + +// We have to: +// + rewrite <br> and <hr> tags +// + deal with <a attrib=noquotes> +gchar *ygutils_convert_to_xhtml (const char *instr) +{ + GString *outp = g_string_new (""); + GQueue *tag_queue = g_queue_new(); + int i = 0; + + gboolean allow_space = FALSE, pre_mode = FALSE; + skipSpace (instr, &i); + + // we must add an outer tag to make GMarkup happy + g_string_append (outp, "<body>"); + + for (; instr[i] != '\0'; i++) + { + // Tag foo + if (instr[i] == '<') { + // ignore comments + if (strncmp (&instr[i], "<!--", 4) == 0) { + for (i += 3; instr[i] != '\0'; i++) + if (strncmp (&instr[i], "-->", 3) == 0) { + i += 2; + break; + } + continue; + } + + gint j; + gboolean is_close = FALSE; + gboolean in_tag; + int tag_len; + GString *tag = g_string_sized_new (20); + + i++; + skipSpace (instr, &i); + + if (instr[i] == '/') { + i++; + is_close = TRUE; + } + + skipSpace (instr, &i); + + // find the tag name piece + in_tag = TRUE; + tag_len = 0; + for (; instr[i] != '>' && instr[i]; i++) { + if (in_tag) { + if (!g_ascii_isalnum(instr[i])) + in_tag = FALSE; + else + tag_len++; + } + g_string_append_c (tag, instr[i]); + } + + // Unmatched tags + if (!is_close && tag_len == 2 && + (!g_ascii_strncasecmp (tag->str, "hr", 2) || + !g_ascii_strncasecmp (tag->str, "br", 2)) && + tag->str[tag->len - 1] != '/') + g_string_append_c (tag, '/'); + + if (!g_ascii_strncasecmp (tag->str, "pre", 3)) + pre_mode = !is_close; + + // Add quoting for un-quoted attributes + unsigned int k; + for (k = 0; k < tag->len; k++) { + if (tag->str[k] == '=') { + gboolean unquote = tag->str[k+1] != '"'; + if (unquote) + g_string_insert_c (tag, k+1, '"'); + else + k++; + for (k++; tag->str[k]; k++) { + if (unquote && g_ascii_isspace (tag->str[k])) + break; + else if (!unquote && tag->str[k] == '"') + break; + } + if (unquote) + g_string_insert_c (tag, k, '"'); + } + else + tag->str[k] = g_ascii_tolower (tag->str[k]); + } + + // Is it an open or close ? + j = tag->len - 1; + + while (j > 0 && g_ascii_isspace (tag->str[j])) j--; + + gboolean is_open_close = (tag->str[j] == '/'); + if (is_open_close) + ; // ignore it + else if (is_close) + emit_unclosed_tags_for (outp, tag_queue, tag->str, tag_len); + else { + TagEntry *entry = tag_entry_new (tag, tag_len); + + entry->tag = tag; + entry->tag_len = tag_len; + + if (!check_early_close (outp, tag_queue, entry)) + g_queue_push_tail (tag_queue, entry); + else { + entry->tag = NULL; + tag_entry_free (entry); + } + } + + g_string_append_c (outp, '<'); + if (is_close) + g_string_append_c (outp, '/'); + g_string_append_len (outp, tag->str, tag->len); + g_string_append_c (outp, '>'); + + if (is_close || is_open_close) + g_string_free (tag, TRUE); + + allow_space = is_close; // don't allow space after opening a tag + } + + else if (instr[i] == '&') { // Entity + const EntityMap *entity = lookup_entity (instr+i); + if (entity) { + if (!strcmp (entity->html, "product")) + g_string_append (outp, YUI::app()->productName().c_str()); + else + g_string_append (outp, entity->text); + i += strlen (entity->html); + if (instr[i+1] == ';') i++; + } + else { + int j; + // check it is a valid entity - not a floating '&' in a <pre> tag eg. + for (j = i + 1; instr[j] != '\0'; j++) { + if (!g_ascii_isalnum (instr[j]) && instr[j] != '#') + break; + } + if (instr[j] != ';') // entity terminator + g_string_append (outp, "&"); + else + g_string_append_c (outp, instr[i]); + } + allow_space = TRUE; + } + + else { // Normal text + if (!pre_mode && g_ascii_isspace (instr[i])) { + if (allow_space) + g_string_append_c (outp, ' '); + allow_space = FALSE; // one space is enough + } + else { + allow_space = TRUE; + g_string_append_c (outp, instr[i]); + } + } + } + + emit_unclosed_tags_for (outp, tag_queue, "", 0); + g_queue_free (tag_queue); + g_string_append (outp, "</body>"); + + gchar *ret = g_string_free (outp, FALSE); + return ret; +} + +std::string YGUtils::mapKBAccel (const std::string &src) +{ + // conversion pairs: ('_', '__') ('&&', '&') ('&', '_') + std::string::size_type length = src.length(), i; + string str; + str.reserve (length); + for (i = 0; i < length; i++) { + if (src[i] == '_') + str += "__"; + else if (src[i] == '&') { + if (i+1 < length && src[i+1] == '&') { + str += '&'; // escaping + i++; + } + else + str += '_'; + } + else + str += src[i]; + } + return str; +} + +char *ygutils_mapKBAccel (const char *src) +{ + std::string ret (YGUtils::mapKBAccel (src)); + return strdup (ret.c_str()); +} + +void YGUtils::setFilter (GtkEntry *entry, const string &validChars) +{ + struct inner { + static void insert_text_cb (GtkEditable *editable, const gchar *new_text, + gint new_text_length, gint *pos) + { + const gchar *valid_chars = (gchar *) g_object_get_data (G_OBJECT (editable), + "valid-chars"); + if (valid_chars) { + const gchar *i, *j; + for (i = new_text; *i; i++) { + for (j = valid_chars; *j; j++) { + if (*i == *j) + break; + } + if (!*j) { + // not valid text + g_signal_stop_emission_by_name (editable, "insert_text"); + gtk_widget_error_bell (GTK_WIDGET (editable)); + return; + } + } + } + } + }; + + if (g_object_get_data (G_OBJECT (entry), "insert-text-set")) + g_object_disconnect (G_OBJECT (entry), "insert-text", + G_CALLBACK (inner::insert_text_cb), NULL); + + if (!validChars.empty()) { + gchar *chars = g_strdup (validChars.c_str()); + g_object_set_data_full (G_OBJECT (entry), "valid-chars", chars, g_free); + g_signal_connect (G_OBJECT (entry), "insert-text", + G_CALLBACK (inner::insert_text_cb), NULL); + g_object_set_data (G_OBJECT (entry), "insert-text-set", GINT_TO_POINTER (1)); + } + else + g_object_set_data (G_OBJECT (entry), "insert-text-set", GINT_TO_POINTER (0)); +} + +void ygutils_setFilter (GtkEntry *entry, const char *validChars) +{ YGUtils::setFilter (entry, validChars); } + +void YGUtils::replace (string &str, const char *mouth, int mouth_len, const char *food) +{ + if (mouth_len < 0) + mouth_len = strlen (mouth); + std::string::size_type i = 0; + while ((i = str.find (mouth, i)) != string::npos) { + str.erase (i, mouth_len); + str.insert (i, food); + } +} + +std::string YGUtils::truncate (const std::string &str, int length, int pos) +{ + std::string ret (str); + const char *pstr = ret.c_str(); char *pi; + int size = g_utf8_strlen (pstr, -1); + if (size > length) { + if (pos > 0) { + pi = g_utf8_offset_to_pointer (pstr, length-3); + ret.erase (pi-pstr); + ret.append ("..."); + } + else if (pos < 0) { + pi = g_utf8_offset_to_pointer (pstr, size-(length-3)); + ret.erase (0, pi-pstr); + ret.insert (0, "..."); + } + else /* (pos == 0) */ { + pi = g_utf8_offset_to_pointer (pstr, size/2); + int delta = size - (length-3); + gchar *pn = pi, *pp = pi; + for (int i = 0;;) { + if (i++ == delta) break; + pn = g_utf8_next_char (pn); + if (i++ == delta) break; + pp = g_utf8_prev_char (pp); + } + g_assert (pp != NULL && pn != NULL); + + ret.erase (pp-pstr, pn-pp); + ret.insert (pp-pstr, "..."); + } + } + return ret; +} + +static gboolean scroll_down_cb (void *pData) +{ + GtkAdjustment *vadj = (GtkAdjustment *) pData; + gtk_adjustment_set_value (vadj, vadj->upper - vadj->page_size); + return FALSE; +} + +void YGUtils::scrollWidget (GtkAdjustment *vadj, bool top) +{ + if (top) + gtk_adjustment_set_value (vadj, vadj->lower); + else + // since we usually want to call this together with a text change, we + // must wait till that gets in effect + g_idle_add_full (G_PRIORITY_LOW, scroll_down_cb, vadj, NULL); +} + +void ygutils_scrollAdj (GtkAdjustment *vadj, gboolean top) +{ YGUtils::scrollWidget (vadj, top); } + +std::string YGUtils::escapeMarkup (const std::string &ori) +{ + std::string::size_type length = ori.length(), i; + std::string ret; + ret.reserve (length * 1.5); + for (i = 0; i < length; i++) + switch (ori[i]) { + case '<': + ret += "<"; + break; + case '>': + ret += ">"; + break; + case '&': + ret += "&"; + break; + default: + ret += ori[i]; + break; + } + return ret; +} + +bool YGUtils::endsWith (const std::string &str, const std::string &key) +{ + if (str.size() < key.size()) + return false; + return str.compare (str.size()-key.size(), key.size(), key) == 0; +} + +int YGUtils::getCharsWidth (GtkWidget *widget, int chars_nb) +{ + PangoContext *context = gtk_widget_get_pango_context (widget); + PangoFontMetrics *metrics = pango_context_get_metrics (context, + widget->style->font_desc, NULL); + + int width = pango_font_metrics_get_approximate_char_width (metrics); + pango_font_metrics_unref (metrics); + + return PANGO_PIXELS (width) * chars_nb; +} + +int YGUtils::getCharsHeight (GtkWidget *widget, int chars_nb) +{ + PangoContext *context = gtk_widget_get_pango_context (widget); + PangoFontMetrics *metrics = pango_context_get_metrics (context, + widget->style->font_desc, NULL); + + int height = pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics); + pango_font_metrics_unref (metrics); + + return PANGO_PIXELS (height) * chars_nb; +} + +void YGUtils::setWidgetFont (GtkWidget *widget, PangoStyle style, PangoWeight weight, + double scale) +{ + PangoFontDescription *font_desc = widget->style->font_desc; + int size = pango_font_description_get_size (font_desc); + PangoFontDescription* font = pango_font_description_new(); + pango_font_description_set_weight (font, weight); + pango_font_description_set_size (font, (int)(size * scale)); + pango_font_description_set_style (font, style); + gtk_widget_modify_font (widget, font); + pango_font_description_free (font); +} + +void ygutils_setWidgetFont (GtkWidget *widget, PangoStyle style, PangoWeight weight, double scale) +{ YGUtils::setWidgetFont (widget, style, weight, scale); } + +static void paned_allocate_cb (GtkWidget *paned, GtkAllocation *alloc, gpointer _rel) +{ + if (!g_object_get_data (G_OBJECT (paned), "init")) { // only once + gdouble rel = GPOINTER_TO_INT (_rel) / 100.; + gint parent_size; + if (gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL) + parent_size = paned->allocation.width; + else + parent_size = paned->allocation.height; + int pos = parent_size * rel; + gtk_paned_set_position (GTK_PANED (paned), pos); + g_object_set_data (G_OBJECT (paned), "init", GINT_TO_POINTER (1)); + } +} + +void YGUtils::setPaneRelPosition (GtkWidget *paned, gdouble rel) +{ + gint _rel = rel * 100; + g_signal_connect_after (G_OBJECT (paned), "size-allocate", + G_CALLBACK (paned_allocate_cb), GINT_TO_POINTER (_rel)); +} + +void ygutils_setPaneRelPosition (GtkWidget *paned, gdouble rel) +{ YGUtils::setPaneRelPosition (paned, rel); } + +GdkPixbuf *YGUtils::loadPixbuf (const string &filename) +{ + GdkPixbuf *pixbuf = NULL; + if (!filename.empty()) { + GError *error = 0; + pixbuf = gdk_pixbuf_new_from_file (filename.c_str(), &error); + if (!pixbuf) + yuiWarning() << "Could not load icon: " << filename << "\n" + "Reason: " << error->message << "\n"; + } + return pixbuf; +} + +// Code from Banshee: shades a pixbuf a bit, used e.g. for hover effects +static inline guchar pixel_clamp (int val) +{ return MAX (0, MIN (255, val)); } +GdkPixbuf *YGUtils::setOpacity (const GdkPixbuf *src, int opacity, bool touchAlpha) +{ + if (!src) return NULL; + int shift = 255 - ((opacity * 255) / 100); + int rgb_shift = 0, alpha_shift = 0; + if (touchAlpha) + alpha_shift = shift; + else + rgb_shift = shift; + + int width = gdk_pixbuf_get_width (src), height = gdk_pixbuf_get_height (src); + gboolean has_alpha = gdk_pixbuf_get_has_alpha (src); + + GdkPixbuf *dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), + has_alpha, gdk_pixbuf_get_bits_per_sample (src), width, height); + + guchar *src_pixels_orig = gdk_pixbuf_get_pixels (src); + guchar *dest_pixels_orig = gdk_pixbuf_get_pixels (dest); + + int src_rowstride = gdk_pixbuf_get_rowstride (src); + int dest_rowstride = gdk_pixbuf_get_rowstride (dest); + int i, j; + for (i = 0; i < height; i++) { + guchar *src_pixels = src_pixels_orig + (i * src_rowstride); + guchar *dest_pixels = dest_pixels_orig + (i * dest_rowstride); + for (j = 0; j < width; j++) { + *(dest_pixels++) = pixel_clamp (*(src_pixels++) + rgb_shift); + *(dest_pixels++) = pixel_clamp (*(src_pixels++) + rgb_shift); + *(dest_pixels++) = pixel_clamp (*(src_pixels++) + rgb_shift); + if (has_alpha) + *(dest_pixels++) = pixel_clamp (*(src_pixels++) - alpha_shift); + } + } + return dest; +} + +GdkPixbuf *YGUtils::setGray (const GdkPixbuf *src) +{ + int width = gdk_pixbuf_get_width (src), height = gdk_pixbuf_get_height (src); + gboolean has_alpha = gdk_pixbuf_get_has_alpha (src); + + GdkPixbuf *dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), + has_alpha, gdk_pixbuf_get_bits_per_sample (src), width, height); + + guchar *src_pixels_orig = gdk_pixbuf_get_pixels (src); + guchar *dest_pixels_orig = gdk_pixbuf_get_pixels (dest); + + int src_rowstride = gdk_pixbuf_get_rowstride (src); + int dest_rowstride = gdk_pixbuf_get_rowstride (dest); + int i, j; + for (i = 0; i < height; i++) { + guchar *src_pixels = src_pixels_orig + (i * src_rowstride); + guchar *dest_pixels = dest_pixels_orig + (i * dest_rowstride); + for (j = 0; j < width; j++) { + int clr = (src_pixels[0] + src_pixels[1] + src_pixels[2]) / 3; + *(dest_pixels++) = clr; + *(dest_pixels++) = clr; + *(dest_pixels++) = clr; + src_pixels += 3; + if (has_alpha) + *(dest_pixels++) = *(src_pixels++); + } + } + return dest; +} + +GdkPixbuf *ygutils_setOpacity (const GdkPixbuf *src, int opacity, gboolean useAlpha) +{ return YGUtils::setOpacity (src, opacity, useAlpha); } + +static std::string cutUnderline (const std::string &str) +{ + std::string ret (str); + std::string::size_type i = 0; + if ((i = ret.find ('_', i)) != std::string::npos) + ret.erase (i, 1); + return ret; +} + +static void stripStart (std::string &str, char ch) +{ + while (!str.empty() && str[0] == ch) + str.erase (0, 1); +} +static void stripEnd (std::string &str, char ch) +{ + while (!str.empty() && str[str.size()-1] == ch) + str.erase (str.size()-1, 1); +} + +struct StockMap { + const char *english, *locale, *stock; +}; +static const StockMap stock_map[] = { + { "Apply", _("Apply"), GTK_STOCK_APPLY }, + { "Accept", _("Accept"), GTK_STOCK_APPLY }, + { "Install", _("Install"), GTK_STOCK_APPLY }, + { "OK", _("OK"), GTK_STOCK_OK }, + { "Cancel", _("Cancel"), GTK_STOCK_CANCEL }, + { "Abort", _("Abort"), GTK_STOCK_CANCEL }, + { "Close", _("Close"), GTK_STOCK_CLOSE }, + { "Yes", _("Yes"), GTK_STOCK_YES }, + { "No", _("No"), GTK_STOCK_NO }, + { "Add", _("Add"), GTK_STOCK_ADD }, + { "Edit", _("Edit"), GTK_STOCK_EDIT }, + { "Delete", _("Delete"), GTK_STOCK_DELETE }, + { "Up", _("Up"), GTK_STOCK_GO_UP }, + { "Down", _("Down"), GTK_STOCK_GO_DOWN }, + { "Enable", _("Enable"), GTK_STOCK_YES }, + { "Disable", _("Disable"), GTK_STOCK_NO }, + { "Exit", _("Exit"), GTK_STOCK_QUIT }, +}; +#define stock_map_length (sizeof (stock_map) / sizeof (StockMap)) + +const char *YGUtils::mapStockIcon (const std::string &label) +{ + static bool firstTime = true; static std::map <std::string, std::string> stockMap; + if (firstTime) { + firstTime = false; + + // match GTK stock labels to yast ones + GSList *list = gtk_stock_list_ids(); + for (GSList *i = list; i; i = i->next) { + gchar *id = (gchar *) i->data; + GtkStockItem item; + if (gtk_stock_lookup (id, &item)) { + const gchar *_id = id; + if (!strcmp (id, GTK_STOCK_MEDIA_NEXT) || !strcmp (id, GTK_STOCK_MEDIA_FORWARD)) + _id = GTK_STOCK_GO_FORWARD; + else if (!strcmp (id, GTK_STOCK_MEDIA_PREVIOUS) || !strcmp (id, GTK_STOCK_MEDIA_REWIND)) + _id = GTK_STOCK_GO_BACK; + else if (!strcmp (id, GTK_STOCK_MEDIA_RECORD)) + _id = GTK_STOCK_SAVE; + else if (!strcmp (id, GTK_STOCK_CLEAR)) + _id = GTK_STOCK_DELETE; + else if (!strcmp (id, GTK_STOCK_QUIT)) + _id = GTK_STOCK_APPLY; + else if (!strcmp (id, GTK_STOCK_JUMP_TO)) + _id = GTK_STOCK_OK; + else if (!strncmp (id, "gtk-dialog-", 11)) + _id = 0; + + if (_id) + stockMap[cutUnderline (item.label)] = _id; + } + // some may not have a stock item because they can't be set on a label + // e.g.: gtk-directory, gtk-missing-image, gtk-dnd + g_free (id); + } + g_slist_free (list); + + for (unsigned int j = 0; j < 2; j++) // add both current locale & english terms + for (unsigned int i = 0; i < stock_map_length; i++) + stockMap [stock_map[i].english+j] = stock_map[i].stock; + } + + std::string id = cutUnderline (label); + stripStart (id, ' '); + stripEnd (id, ' '); + stripEnd (id, '.'); + + std::map <std::string, std::string>::const_iterator it; + it = stockMap.find (id); + if (it != stockMap.end()) + return it->second.c_str(); + return NULL; +} + +const char *YGUtils::setStockIcon (GtkWidget *button, const std::string &label, + const char *fallbackIcon) +{ + const char *icon = mapStockIcon (label); + if (!icon && label.size() < 22) + icon = fallbackIcon; + if (icon) { + if (gtk_style_lookup_icon_set (button->style, icon)) { + // we want to use GtkImage stock mode so it honors sensitive + GtkWidget *image = gtk_image_new_from_stock (icon, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (button), image); + } + } + else { + GtkWidget *image = gtk_button_get_image (GTK_BUTTON (button)); + if (image) + gtk_widget_hide (image); + } + return icon; +} + +void YGUtils::shrinkWidget (GtkWidget *widget) +{ + static bool first_time = true; + if (first_time) { + first_time = false; + gtk_rc_parse_string ( + "style "small-widget-style"\n" + "{\n" + " GtkWidget::focus-padding = 0\n" + " GtkWidget::focus-line-width = 0\n" + " xthickness = 0\n" + " ythickness = 0\n" + "}\n" + "widget "*.small-widget" style "small-widget-style""); + } + gtk_widget_set_name (widget, "small-widget"); +} + +/* + * construct a help string by dropping the title, and mentioning + * the first sentence for a dialog sub-title + */ +gchar * +ygutils_headerize_help (const char *help_text, gboolean *cut) +{ + char *text = ygutils_convert_to_xhtml (help_text); + + GString *str = g_string_new (""); + int i; + gboolean copy_word = FALSE; + for (i = 0; text[i]; i++) { + if (text[i] == '<') { + int a = i; + for (; text[i]; i++) + if (text[i] == '>') + break; + + if (!strncasecmp (text+a, "<h", 2) || !strncasecmp (text+a, "<big>", 5) || + (!str->len && !strncasecmp (text+a, "<b>", 3))) { + for (i++; text[i]; i++) { + if (text[i] == '<') + a = i; + if (text[i] == '>') { + if (!strncasecmp (text+a, "</h", 3) || !strncasecmp (text+a, "</big>", 6) || + !strncasecmp (text+a, "</b>", 4)) + break; + } + } + } + } + else if (g_ascii_isspace (text[i])) { + if (copy_word) + g_string_append_c (str, ' '); + copy_word = FALSE; + } + else { + copy_word = TRUE; + g_string_append_c (str, text[i]); + if (text[i] == '.') { + if (g_ascii_isspace (text[i+1]) || text[i+1] == '<') { + i++; + break; + } + } + } + } + *cut = FALSE; + gboolean markup = FALSE; + for (; text[i]; i++) { + if (markup) { + if (text[i] == '>') + markup = FALSE; + } + else { + if (text[i] == '<') + markup = TRUE; + else if (!g_ascii_isspace (text[i])) { + *cut = TRUE; + break; + } + } + } + g_free (text); + return g_string_free (str, FALSE); +} + +const char *ygutils_mapStockIcon (const char *label) +{ return YGUtils::mapStockIcon (label); } + +const char *ygutils_setStockIcon (GtkWidget *button, const char *label, const char *fallback) +{ return YGUtils::setStockIcon (button, label, fallback); } + +/* interactive busy cursor */ +// half cursor, half clock cursor is not a Xlib theme icon, but there is +// a hack to load it like: (if we ever want to use it...) +#if 0 +__LEFT_PTR_WATCH = None +def set_busy_cursor (window): + global __LEFT_PTR_WATCH + if __LEFT_PTR_WATCH is None: + os.environ['XCURSOR_DISCOVER'] = '1' #Turn on logging in Xlib + # Busy cursor code from Padraig Brady P@draigBrady.com + # cursor_data hash is 08e8e1c95fe2fc01f976f1e063a24ccd + cursor_data = "\ +\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\ +\x0c\x00\x00\x00\x1c\x00\x00\x00\x3c\x00\x00\x00\ +\x7c\x00\x00\x00\xfc\x00\x00\x00\xfc\x01\x00\x00\ +\xfc\x3b\x00\x00\x7c\x38\x00\x00\x6c\x54\x00\x00\ +\xc4\xdc\x00\x00\xc0\x44\x00\x00\x80\x39\x00\x00\ +\x80\x39\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00" + + try: + pix = gtk.gdk.bitmap_create_from_data(None, cursor_data, 32, 32) + color = gtk.gdk.Color() + __LEFT_PTR_WATCH = gtk.gdk.Cursor(pix, pix, color, color, 2, 2) + except TypeError: + # old bug http://bugzilla.gnome.org/show_bug.cgi?id=103616 + # default "WATCH" cursor + __LEFT_PTR_WATCH = gtk.gdk.Cursor(gtk.gdk.WATCH) + window.set_cursor (__LEFT_PTR_WATCH) +#endif + + +gboolean YGUtils::empty_row_is_separator_cb ( + GtkTreeModel *model, GtkTreeIter *iter, gpointer _text_col) +{ + int text_col = GPOINTER_TO_INT (_text_col); + gchar *str; + gtk_tree_model_get (model, iter, text_col, &str, -1); + bool ret = !str || !(*str); + g_free (str); + return ret; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGUtils.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,96 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGUTILS_H +#define YGUTILS_H + +#include <string> +#include <list> +#include <gtk/gtktextview.h> +#include <gtk/gtkentry.h> + +/* YGUtils.h/cc have some functionality that is shared between different parts + of the code. */ + +namespace YGUtils +{ + /* Replaces Yast's '&' accelerator by Gnome's '_' (and proper escaping). */ + std::string mapKBAccel (const std::string &src); + + /* Adds filter support to a GtkEntry. */ + void setFilter (GtkEntry *entry, const std::string &validChars); + + /* Replaces every 'mouth' by 'food' in 'str'. */ + void replace (std::string &str, const char *mouth, int mouth_len, const char *food); + + /* Truncates the text with "..." past the given length. + pos: -1 = start, 0 = middle, 1 = end. Honors utf-8 characters. */ + std::string truncate (const std::string &str, int length, int pos); + + /* Escapes markup text (eg. changes '<' by '<'). */ + std::string escapeMarkup (const std::string &str); + + /* Check if 'str' ends with 'suffix'. */ + bool endsWith (const std::string &str, const std::string &suffix); + + /* Adds functionality to scroll widgets to top or bottom. */ + void scrollWidget (GtkAdjustment *vadj, bool top); + + /* Returns the average width of the given number of characters in pixels. */ + int getCharsWidth (GtkWidget *widget, int chars_nb); + int getCharsHeight (GtkWidget *widget, int chars_nb); + + /* Sets some widget font proprities. */ + void setWidgetFont (GtkWidget *widget, PangoStyle style, PangoWeight weight, double scale); + + /* Instead of setting GtkPaned::position in pixels, do so in percents. */ + void setPaneRelPosition (GtkWidget *paned, gdouble rel); + + /* Saves some code and standardizes the error. Returns NULL if failed. + Don't forget to g_object_unref it! */ + GdkPixbuf *loadPixbuf (const std::string &fileneme); + + /* Shifts colors in a GdkPixbuf. */ + GdkPixbuf *setOpacity (const GdkPixbuf *src, int opacity, bool touchAlpha); + + /* Gray out some pixbuf. */ + GdkPixbuf *setGray (const GdkPixbuf *src); + + /* Tries to make sense out of the string, applying some stock icon to the button. */ + const char *mapStockIcon (const std::string &label); + const char *setStockIcon (GtkWidget *button, const std::string &label, + const char *fallbackIcon); + + /* For empty model rows, render a separator (can be used for GtkTreeView and GtkComboBox */ + gboolean empty_row_is_separator_cb ( + GtkTreeModel *model, GtkTreeIter *iter, gpointer text_col); + + /* Shrink widget inner-border (thickness) */ + void shrinkWidget (GtkWidget *widget); +}; + +extern "C" { + char *ygutils_mapKBAccel (const char *src); + + void ygutils_setWidgetFont (GtkWidget *widget, PangoStyle style, PangoWeight weight, double scale); + void ygutils_setPaneRelPosition (GtkWidget *paned, gdouble rel); + + void ygutils_setFilter (GtkEntry *entry, const char *validChars); + + void ygutils_scrollAdj (GtkAdjustment *vadj, gboolean top); + + const char *ygutils_mapStockIcon (const char *label); + const char *ygutils_setStockIcon (GtkWidget *button, const char *label, + const char *fallbackIcon); + + GdkPixbuf *ygutils_setOpacity (const GdkPixbuf *src, int opacity, gboolean useAlpha); + + gchar *ygutils_headerize_help (const char *help_text, gboolean *cut); + + // convert liberal html to xhtml + gchar *ygutils_convert_to_xhtml (const char *instr); +}; + +#endif // YGUTILS_H +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,339 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include <stdarg.h> +#include "YGWidget.h" +#include "YGUtils.h" +#include "ygtkratiobox.h" + +// default widgets border -- may be overlapped with a setBorder(..) +#define DEFAULT_BORDER 6 +#define LABEL_WIDGET_SPACING 4 + +/* Utilities */ + +struct YGWidget::Signals +{ + typedef std::pair <GObject *, gulong> Handler; + std::list <Handler> m_handlers; + void connectSignal (GObject *object, const char *name, + GCallback callback, gpointer data, bool after) + { + gulong handler; + if (after) + handler = g_signal_connect_after (object, name, callback, data); + else + handler = g_signal_connect (object, name, callback, data); + + Handler h (object, handler); + m_handlers.push_back (h); + } + void block() + { + for (std::list <Handler>::const_iterator it = m_handlers.begin(); + it != m_handlers.end(); it++) { + const Handler &h = *it; + g_signal_handler_block (h.first, h.second); + } + } + void unblock() + { + for (std::list <Handler>::const_iterator it = m_handlers.begin(); + it != m_handlers.end(); it++) { + const Handler &h = *it; + g_signal_handler_unblock (h.first, h.second); + } + } +}; + +/* YGWidget follows */ + +static void min_size_cb (guint *min_width, guint *min_height, gpointer pData); + +YGWidget::YGWidget(YWidget *ywidget, YWidget *yparent, + GtkType type, const char *property_name, ...) + : m_ywidget (ywidget) +{ + va_list args; + va_start (args, property_name); + construct (ywidget, yparent, type, property_name, args); + va_end (args); +} + +void YGWidget::construct (YWidget *ywidget, YWidget *yparent, + GType type, const char *property_name, va_list args) +{ + m_widget = GTK_WIDGET (g_object_new_valist (type, property_name, args)); + + if (type == GTK_TYPE_WINDOW || type == GTK_TYPE_MENU) + m_adj_size = m_widget; + else { + m_adj_size = ygtk_adj_size_new(); + g_object_ref_sink (G_OBJECT (m_adj_size)); + gtk_widget_show (m_adj_size); + gtk_container_add (GTK_CONTAINER (m_adj_size), m_widget); + } + gtk_widget_show (m_widget); + + // Split by two so that with another widget it will have full border... + setBorder (DEFAULT_BORDER / 2); + ygtk_adj_size_set_min_cb (YGTK_ADJ_SIZE (m_adj_size), min_size_cb, this); + + ywidget->setWidgetRep ((void *) this); + if (yparent) { + ywidget->setParent (yparent); + yparent->addChild (ywidget); + } + m_signals = NULL; +} + +YGWidget::~YGWidget() +{ + delete m_signals; + m_signals = 0; + if (YGUI::ui()->eventPendingFor (m_ywidget)) + YGUI::ui()->m_event_handler.consumePendingEvent(); + // remove children if container? +#if 0 + struct inner { + static void foreach_child_cb (GtkWidget *child, GtkContainer *container) + { gtk_container_remove (container, child); } + }; + if (GTK_IS_CONTAINER (m_widget)) + gtk_container_foreach (GTK_CONTAINER (m_widget), + (GtkCallback) inner::foreach_child_cb, m_widget); +#endif + gtk_widget_destroy (m_adj_size); + g_object_unref (G_OBJECT (m_adj_size)); +} + +YGWidget *YGWidget::get (YWidget *ywidget) +{ + //g_assert (ywidget->widgetRep() != NULL); + return (YGWidget *) ywidget->widgetRep(); +} + +bool YGWidget::doSetKeyboardFocus() +{ + gtk_widget_grab_focus (getWidget()); + return gtk_widget_is_focus (getWidget()); +} + +void YGWidget::doSetEnabled (bool enabled) +{ + gtk_widget_set_sensitive (getLayout(), enabled); +} + +void YGWidget::doSetUseBoldFont (bool useBold) +{ + PangoWeight weight = useBold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL; + YGUtils::setWidgetFont (getWidget(), PANGO_STYLE_NORMAL, weight, PANGO_SCALE_MEDIUM); +} + +void YGWidget::doAddChild (YWidget *ychild, GtkWidget *container) +{ + GtkWidget *child = YGWidget::get (ychild)->getLayout(); + gtk_container_add (GTK_CONTAINER (container), child); +} + +void YGWidget::doRemoveChild (YWidget *ychild, GtkWidget *container) +{ + /* Note: removeChild() is generally a result of a widget being removed as it + will remove itself from the parent. But YGWidget deconstructor would run + before the YWidget one, as that's the order we have been using, so we + can't use it, we can't retrieve the GTK widget then. However, this is a + non-issue, as ~YGWidget will destroy the widget, and GTK will remove it + from the parent. */ + if (!ychild->beingDestroyed()) { + GtkWidget *child = YGWidget::get (ychild)->getLayout(); + gtk_container_remove (GTK_CONTAINER (container), child); + } +} + +int YGWidget::doPreferredSize (YUIDimension dimension) +{ + // We might want to do some caching here.. + GtkRequisition req; + gtk_widget_size_request (m_adj_size, &req); + return dimension == YD_HORIZ ? req.width : req.height; +} + +void min_size_cb (guint *min_width, guint *min_height, gpointer pData) +{ + YGWidget *pThis = (YGWidget *) pData; + *min_width = pThis->getMinSize (YD_HORIZ); + *min_height = pThis->getMinSize (YD_VERT); +} + +#include "ygtkfixed.h" + +void YGWidget::doSetSize (int width, int height) +{ + GtkWidget *parent = 0; + if (m_ywidget->parent()) + parent = YGWidget::get (m_ywidget->parent())->getWidget(); + + if (parent && YGTK_IS_FIXED (parent)) + ygtk_fixed_set_child_size (YGTK_FIXED (parent), m_adj_size, width, height); +} + +void YGWidget::emitEvent (YEvent::EventReason reason, EventFlags flags) +{ + struct inner + { + static gboolean dispatchEvent (gpointer data) + { + YWidgetEvent *event = (YWidgetEvent *) data; + if (!YGUI::ui()->eventPendingFor (event->widget())) + YGUI::ui()->sendEvent (event); + return FALSE; + } + }; + +#if YAST2_VERSION > 2018003 + if (reason == YEvent::ContextMenuActivated && !m_ywidget->notifyContextMenu()) + ; // cancel +#endif + if (flags & IGNORE_NOTIFY_EVENT || m_ywidget->notify()) { + YWidgetEvent *event = new YWidgetEvent (m_ywidget, reason); + if (flags & DELAY_EVENT) + g_timeout_add (250, inner::dispatchEvent, event); + else if (!(flags & IF_NOT_PENDING_EVENT) || !YGUI::ui()->eventPendingFor (m_ywidget)) + YGUI::ui()->sendEvent (event); + } +} + +void YGWidget::connect (gpointer object, const char *name, GCallback callback, gpointer data, + bool after) +{ + if (!m_signals) + m_signals = new YGWidget::Signals(); + m_signals->connectSignal (G_OBJECT (object), name, callback, data, after); +} + +void YGWidget::blockSignals() +{ if (m_signals) m_signals->block(); } +void YGWidget::unblockSignals() +{ if (m_signals) m_signals->unblock(); } + +void YGWidget::setBorder (unsigned int border) +{ gtk_container_set_border_width (GTK_CONTAINER (m_adj_size), border); } + +/* YGLabeledWidget follows */ + +YGLabeledWidget::YGLabeledWidget (YWidget *ywidget, YWidget *parent, + const std::string &label_text, YUIDimension label_ori, + GType type, const char *property_name, ...) + : YGWidget (ywidget, parent, +// label_ori == YD_VERT ? GTK_TYPE_VBOX : GTK_TYPE_HBOX, + GTK_TYPE_VBOX, "spacing", LABEL_WIDGET_SPACING, NULL) +{ + // Create the field widget + va_list args; + va_start (args, property_name); + m_field = GTK_WIDGET (g_object_new_valist (type, property_name, args)); + va_end (args); + + // Create the label + m_label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (m_label), 0.0, 0.5); +/* if (label_ori == YD_HORIZ) + gtk_label_set_line_wrap (GTK_LABEL (m_label), TRUE);*/ + gtk_widget_show (m_label); + gtk_widget_show (m_field); + + setBuddy (m_field); + doSetLabel (label_text); + + // Set the container and show widgets + gtk_box_pack_start (GTK_BOX (m_widget), m_label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (m_widget), m_field, TRUE, TRUE, 0); + m_orientation = label_ori; +} + +void YGLabeledWidget::setLabelVisible (bool show) +{ + if (show) + gtk_widget_show (m_label); + else + gtk_widget_hide (m_label); +} + +void YGLabeledWidget::setBuddy (GtkWidget *widget) +{ + gtk_label_set_mnemonic_widget (GTK_LABEL (m_label), widget); +} + +void YGLabeledWidget::doSetLabel (const std::string &label) +{ + if (!label.empty()) { + string str (YGUtils::mapKBAccel (label)); + + // add a ':' at the end of the label, if not set + if (!str.empty()) { + const gchar *last = g_utf8_find_prev_char (str.c_str(), str.c_str() + str.length()); + gunichar last_char = g_utf8_get_char (last); + if (g_unichar_isalpha (last_char)) { // append + bool reverse = false; + if (gtk_widget_get_direction (m_label) == GTK_TEXT_DIR_RTL && + pango_find_base_dir (str.c_str(), -1) == PANGO_DIRECTION_LTR) + reverse = true; + + int i = reverse ? 0 : str.length(); + str.insert (i, 1, ':'); + } + } + + gtk_label_set_text (GTK_LABEL (m_label), str.c_str()); + gtk_label_set_use_underline (GTK_LABEL (m_label), TRUE); + } + setLabelVisible (!label.empty()); +} + +/* YGScrolledWidget follows */ +#define MAX_SCROLL_WIDTH 120 + +YGScrolledWidget::YGScrolledWidget (YWidget *ywidget, YWidget *parent, + GType type, const char *property_name, ...) + : YGLabeledWidget (ywidget, parent, string(), YD_VERT, + GTK_TYPE_SCROLLED_WINDOW, "shadow-type", GTK_SHADOW_IN, NULL) +{ + va_list args; + va_start (args, property_name); + construct(type, property_name, args); + va_end (args); + setLabelVisible (false); +} + +YGScrolledWidget::YGScrolledWidget (YWidget *ywidget, YWidget *parent, + const std::string &label_text, YUIDimension label_ori, + GType type, const char *property_name, ...) + : YGLabeledWidget (ywidget, parent, label_text, label_ori, + GTK_TYPE_SCROLLED_WINDOW, "shadow-type", GTK_SHADOW_IN, NULL) +{ + va_list args; + va_start (args, property_name); + construct(type, property_name, args); + va_end (args); +} + +void YGScrolledWidget::construct (GType type, const char *property_name, + va_list args) +{ + m_widget = GTK_WIDGET (g_object_new_valist (type, property_name, args)); + setBuddy (m_widget); + + setPolicy (GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (YGLabeledWidget::getWidget()), m_widget); + gtk_widget_show (m_widget); +} + +void YGScrolledWidget::setPolicy (GtkPolicyType hpolicy, GtkPolicyType vpolicy) +{ + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (YGLabeledWidget::getWidget()), + hpolicy, vpolicy); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWidget.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,169 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGWIDGET_H +#define YGWIDGET_H + +#include <gtk/gtk.h> +#include <stdarg.h> +#include "YGUI.h" +#include "YEvent.h" + +class YGWidget +{ +public: + YGWidget (YWidget *ywidget, YWidget *yparent, + GType type, const char *property_name, ...); + virtual ~YGWidget(); + + // get the YGWidget associated with a YWidget + static YGWidget *get (YWidget *y_widget); + + virtual inline GtkWidget *getWidget() { return m_widget; } + GtkWidget *getLayout() { return m_adj_size; } // add this to a container + virtual GtkWidget *getContainer() { return m_widget; } // add children here + + // overload YWidget methods with these ones + virtual bool doSetKeyboardFocus(); + virtual void doSetEnabled (bool enabled); + virtual void doSetUseBoldFont (bool useBold); + virtual void doAddChild (YWidget *child, GtkWidget *container); + virtual void doRemoveChild (YWidget *child, GtkWidget *container); + + // layout + virtual int doPreferredSize (YUIDimension dimension); + virtual void doSetSize (int width, int height); + + // debug + const char *getWidgetName() const { return m_ywidget->widgetClass(); } + virtual string getDebugLabel() const // let YWidget::debugLabel() be overloaded + { if (m_ywidget->hasChildren()) return string(); return m_ywidget->debugLabel(); } + + // aesthetics + void setBorder (unsigned int border); // in pixels + virtual unsigned int getMinSize (YUIDimension dim) { return 0; } + +protected: + // event emission + enum EventFlags + { DELAY_EVENT = 2, IGNORE_NOTIFY_EVENT = 4, IF_NOT_PENDING_EVENT = 8 }; + void emitEvent (YEvent::EventReason reason, EventFlags flags = (EventFlags) 0); + + // signal registration; use "BlockEvents (this)" to temp-ly block all signals + friend struct BlockEvents; + void connect (gpointer object, const char *name, + GCallback callback, gpointer data, bool after = true); + void blockSignals(); + void unblockSignals(); + struct Signals; + friend struct Signals; + Signals *m_signals; + + void construct (YWidget *ywidget, YWidget *yparent, + GType type, const char *property_name, va_list args); + + // data + GtkWidget *m_widget, *m_adj_size; // associated GtkWidget, and adjustment for borders + YWidget *m_ywidget; // associated YWidget +}; + +struct BlockEvents +{ + BlockEvents (YGWidget *widget) : m_widget (widget) + { m_widget->blockSignals(); } + ~BlockEvents() + { m_widget->unblockSignals(); } + + private: YGWidget *m_widget; +}; + +/* + * Macros to help implement proxies between common YWidget virtual + * methods and the (multiply inherited) YGWidget base implementation + * for GTK+. + */ +#define YGWIDGET_IMPL_COMMON(ParentClass) \ + virtual bool setKeyboardFocus() { \ + return doSetKeyboardFocus(); } \ + virtual void setEnabled (bool enabled) { \ + ParentClass::setEnabled (enabled); \ + doSetEnabled (enabled); \ + } \ + virtual int preferredWidth() { return doPreferredSize (YD_HORIZ); } \ + virtual int preferredHeight() { return doPreferredSize (YD_VERT); } \ + virtual void setSize (int width, int height) { doSetSize (width, height); } + +#define YGWIDGET_IMPL_USE_BOLD(ParentClass) \ + virtual void setUseBoldFont (bool useBold) { \ + ParentClass::setUseBoldFont (useBold); \ + doSetUseBoldFont (useBold); \ + } + +#define YGWIDGET_IMPL_CONTAINER(ParentClass) \ + YGWIDGET_IMPL_COMMON (ParentClass) \ + virtual void addChild (YWidget *ychild) { \ + ParentClass::addChild (ychild); \ + doAddChild (ychild, getContainer()); \ + } \ + virtual void removeChild (YWidget *ychild) { \ + ParentClass::removeChild (ychild); \ + doRemoveChild (ychild, getContainer()); \ + } + +/* This is a convenience class that allows for a label next to the + intended widget. It should be used, in case you have the need for + such, as it gives an uniform API. */ +class YGLabeledWidget : public YGWidget +{ + public: + YGLabeledWidget(YWidget *ywidget, YWidget *yparent, + const std::string &label_text, YUIDimension label_ori, + GType type, const char *property_name, ...); + virtual ~YGLabeledWidget () {} + + virtual inline GtkWidget* getWidget() { return m_field; } + + void setLabelVisible (bool show); + void setBuddy (GtkWidget *widget); + virtual void doSetLabel (const std::string &label); + + YUIDimension orientation() { return m_orientation; } + GtkWidget *getLabelWidget() { return m_label; } + + protected: + GtkWidget *m_label, *m_field; + YUIDimension m_orientation; +}; + +#define YGLABEL_WIDGET_IMPL(ParentClass) \ + YGWIDGET_IMPL_COMMON (ParentClass) \ + virtual void setLabel (const std::string &label) { \ + ParentClass::setLabel (label); \ + doSetLabel (label); \ + } + +/* This is a convenience class for widgets that need scrollbars. */ +class YGScrolledWidget : public YGLabeledWidget +{ + public: + YGScrolledWidget(YWidget *ywidget, YWidget *yparent, + GType type, const char *property_name, ...); + // if you want a label, use: + YGScrolledWidget(YWidget *ywidget, YWidget *yparent, + const std::string &label_text, YUIDimension label_ori, + GType type, const char *property_name, ...); + virtual ~YGScrolledWidget () {} + + virtual inline GtkWidget *getWidget() { return m_widget; } + + // you should use this method, not gtk_scrolled_window_set... + void setPolicy (GtkPolicyType hpolicy, GtkPolicyType vpolicy); + + protected: + void construct(GType type, const char *property_name, va_list args); + GtkWidget *m_widget; +}; + +#endif /*YGWIDGET_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWizard.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWizard.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGWizard.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,305 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGUI.h" +#include "YGWidget.h" +#include "YGUtils.h" +#include "ygtkwizard.h" +#include "YWizard.h" +#include "YPushButton.h" +#include "YAlignment.h" +#include "YReplacePoint.h" +#include "YGDialog.h" + +class YGWizard : public YWizard, public YGWidget +{ + YReplacePoint *m_replacePoint; + + /* YCP requires us to allow people to use YPushButton API on the wizard buttons. + Wizard already has handlers for them; this seems like bad design. + + We could support wrapping right in our framework. One way, would be to subclass + YWidgetOpt to have a wrapping field where we would set a GtkWidget*. Then, + classes should pass opt to YGWidget and it would create a shallow instance + around it. However, this isn't really doable. The problem is that, like + in this case, the API isn't really exact; events must be sent as YWizard events. + */ + struct YGWButton : public YPushButton { + /* Thin class; just changes the associated button label and keeps track + of id change. */ + YGWButton (YGWizard *parent, GtkWidget *widget, const std::string &label) + : YPushButton (parent, label), m_widget (widget), m_wizard (parent) + { + setWidgetRep (NULL); + setLabel (label); + ygtk_wizard_set_button_ptr_id (getWizard(), widget, this); + } + + virtual void setLabel (const string &label) + { + YPushButton::setLabel (label); + + // notice: we can't use functionKey() to deduce the icon because yast + // tools code differ from the text-mode to the GUIs when setting buttons + // up, and the opt_f10 and so on will not be set in the GUI code + YGtkWizard *wizard = getWizard(); + std::string _label = YGUtils::mapKBAccel (label); + ygtk_wizard_set_button_label (wizard, getWidget(), _label.c_str(), NULL); + } + + virtual void setEnabled (bool enable) + { + YWidget::setEnabled (enable); + ygtk_wizard_enable_button (getWizard(), getWidget(), enable); + } + + virtual bool setKeyboardFocus() + { + gtk_widget_grab_focus (getWidget()); + return gtk_widget_is_focus (getWidget()); + } + + virtual int preferredWidth() { return 0; } + virtual int preferredHeight() { return 0; } + virtual void setSize (int w, int h) {} + + inline GtkWidget *getWidget() { return m_widget; } + inline YGtkWizard *getWizard() { return YGTK_WIZARD (m_wizard->getWidget()); } + + private: + GtkWidget *m_widget; + YGWizard *m_wizard; + }; + + YGWButton *m_back_button, *m_abort_button, *m_next_button, *m_notes_button; + // release notes button would be a little more hassle to support; yast-qt + // doesn't support it too anyway. + +public: + YGWizard (YWidget *parent, const string &backButtonLabel, + const string &abortButtonLabel, const string &nextButtonLabel, + YWizardMode wizardMode) + : YWizard (NULL, backButtonLabel, abortButtonLabel, nextButtonLabel, wizardMode) + , YGWidget (this, parent, YGTK_TYPE_WIZARD, NULL) + { + setBorder (0); + YGtkWizard *wizard = getWizard(); + + //** Application area + { + YAlignment *align = YUI::widgetFactory()->createAlignment (this, + YAlignCenter, YAlignCenter); + align->setStretchable (YD_HORIZ, true); + align->setStretchable (YD_VERT, true); + + m_replacePoint = YUI::widgetFactory()->createReplacePoint ((YWidget *) align); + YUI::widgetFactory()->createEmpty ((YWidget *) m_replacePoint); + m_replacePoint->showChild(); + } + + //** Steps/tree pane + bool steps_enabled = wizardMode == YWizardMode_Steps; + bool tree_enabled = wizardMode == YWizardMode_Tree; + if (steps_enabled && tree_enabled) { + yuiError() << "YGWizard doesn't support both steps and tree enabled at the " + "same time.\nDisabling the steps...\n"; + steps_enabled = false; + } + if (steps_enabled) + ygtk_wizard_enable_steps (wizard); + if (tree_enabled) + ygtk_wizard_enable_tree (wizard); + + //** Setting the bottom buttons + m_back_button = new YGWButton (this, wizard->back_button, backButtonLabel); + m_abort_button = new YGWButton (this, wizard->abort_button, abortButtonLabel); + m_next_button = new YGWButton (this, wizard->next_button, nextButtonLabel); + m_notes_button = new YGWButton (this, wizard->release_notes_button, string()); + ygtk_wizard_set_default_button (wizard, wizard->next_button); + + //** All event are sent through this signal together with an id + g_signal_connect (G_OBJECT (getWidget()), "action-triggered", + G_CALLBACK (action_triggered_cb), this); + } + + virtual ~YGWizard() + { + // m_back/abort/next_button are added as children and + // so will be freed by ~YContainerWidget + } + + inline YGtkWizard *getWizard() + { return YGTK_WIZARD (getWidget()); } + + virtual YReplacePoint *contentsReplacePoint() const + { return m_replacePoint; } + + virtual YPushButton *backButton() const + { return m_back_button; } + virtual YPushButton *abortButton() const + { return m_abort_button; } + virtual YPushButton *nextButton() const + { return m_next_button; } + + virtual void setButtonLabel (YPushButton *button, const string &label) + { + button->setLabel (label); + } + + virtual void setHelpText (const string &_text) + { + std::string productName = YUI::app()->productName(); + std::string text(_text); + YGUtils::replace (text, "&product;", 9, productName.c_str()); + ygtk_wizard_set_help_text (getWizard(), text.c_str()); + } + + virtual void setDialogIcon (const string &icon) + { + if (!ygtk_wizard_set_header_icon (getWizard(), icon.c_str())) + yuiWarning() << "YGWizard: could not load image: " << icon << endl; + YGDialog::currentDialog()->setIcon (icon); + } + + virtual void setDialogHeading (const string &heading) + { + ygtk_wizard_set_header_text (getWizard(), heading.c_str()); + YGDialog::currentDialog()->setTitle (heading, false); + } + + virtual void setDialogTitle (const string &title) + { + YGDialog::currentDialog()->setTitle (title, true); + } + + virtual void addStepHeading (const string &text) + { + ygtk_wizard_add_step_header (getWizard(), text.c_str()); + } + + virtual void addStep (const string &text, const string &id) + { + ygtk_wizard_add_step (getWizard(), text.c_str(), id.c_str()); + } + + virtual void setCurrentStep (const string &id) + { + if (!ygtk_wizard_set_current_step (getWizard(), id.c_str())) + yuiError() << "YGWizard: there is no step with id " << id << endl; + } + + virtual void deleteSteps() + { + ygtk_wizard_clear_steps (getWizard()); + } + + virtual void updateSteps() + {} + + virtual void addTreeItem (const string &parentID, const string &text, + const string &id) + { + if (!ygtk_wizard_add_tree_item (getWizard(), parentID.c_str(), + text.c_str(), id.c_str())) + yuiError() << "YGWizard: there is no tree item with id " << parentID << endl; + } + + virtual void selectTreeItem (const string &id) + { + if (!ygtk_wizard_select_tree_item (getWizard(), id.c_str())) + yuiError() << "YGWizard: there is no tree item with id " << id << endl; + } + + virtual string currentTreeSelection() + { + const char *selected = ygtk_wizard_get_tree_selection (getWizard()); + if (selected) + return selected; + return string(); + } + + virtual void deleteTreeItems() + { + ygtk_wizard_clear_tree (getWizard()); + } + + virtual void addMenu (const string &text, const string &id) + { + string str = YGUtils::mapKBAccel (text); + ygtk_wizard_add_menu (getWizard(), str.c_str(), id.c_str()); + } + + virtual void addSubMenu (const string &parentID, const string &text, + const string &id) + { + string str = YGUtils::mapKBAccel(text); + if (!ygtk_wizard_add_sub_menu (getWizard(), parentID.c_str(), str.c_str(), + id.c_str())) + yuiError() << "YGWizard: there is no menu item with id " << parentID << endl; + } + + virtual void addMenuEntry (const string &parentID, const string &text, + const string &id) + { + string str = YGUtils::mapKBAccel (text); + if (!ygtk_wizard_add_menu_entry (getWizard(), parentID.c_str(), + str.c_str(), id.c_str())) + yuiError() << "YGWizard: there is no menu item with id " << parentID << endl; + } + + virtual void addMenuSeparator (const string & parentID) + { + if (!ygtk_wizard_add_menu_separator (getWizard(), parentID.c_str())) + yuiError() << "YGWizard: there is no menu item with id " << parentID << endl; + } + + virtual void deleteMenus() + { + ygtk_wizard_clear_menu (getWizard()); + } + + virtual void showReleaseNotesButton (const string &label, const string &id) + { + string str = YGUtils::mapKBAccel (label.c_str()); + ygtk_wizard_set_button_label (getWizard(), m_notes_button->getWidget(), str.c_str(), NULL); + ygtk_wizard_set_button_str_id (getWizard(), m_notes_button->getWidget(), id.c_str()); + } + + virtual void hideReleaseNotesButton() + { + ygtk_wizard_set_button_label (getWizard(), m_notes_button->getWidget(), NULL, NULL); + } + + virtual void retranslateInternalButtons() + {} + + static void action_triggered_cb (YGtkWizard *wizard, gpointer id, + gint id_type, YGWizard *pThis) + { + if ((GType) id_type == G_TYPE_STRING) + YGUI::ui()->sendEvent (new YMenuEvent ((char *) id)); + else + YGUI::ui()->sendEvent (new YWidgetEvent ((YWidget *) id, YEvent::Activated)); + } + + // YGWidget + virtual void doAddChild (YWidget *ychild, GtkWidget *container) + { + if (ychild->widgetRep()) // don't actually add the button wrappers + ygtk_wizard_set_child (getWizard(), YGWidget::get (ychild)->getLayout()); + } + + YGWIDGET_IMPL_CONTAINER (YWizard) +}; + +YWizard *YGOptionalWidgetFactory::createWizard (YWidget *parent, + const string &backButtonLabel, const string &abortButtonLabel, + const string &nextButtonLabel, YWizardMode wizardMode) +{ + return new YGWizard (parent, backButtonLabel, abortButtonLabel, nextButtonLabel, + wizardMode); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/YGi18n.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/YGi18n.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/YGi18n.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,20 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGi18n_h +#define YGi18n_h + +#include <libintl.h> +#define TEXTDOMAIN "gtk" + +static inline const char *_(const char *msgid) +{ return dgettext (TEXTDOMAIN, msgid); } + +#ifndef YGI18N_C +static inline const char * _(const char * msgid1, const char *msgid2, unsigned long int n) +{ return dngettext(TEXTDOMAIN, msgid1, msgid2, n); } +#endif + +#endif /*YGi18n_h*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/dummy.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/dummy.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/dummy.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,13 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#include <config.h> +#include <YGUI.h> + +int main (int argc, char **argv) +{ + YGUI(false); + return 0; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/hr.xpm URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/hr.xpm (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/hr.xpm Mon Aug 1 09:44:45 2011 @@ -0,0 +1,173 @@ +/* XPM */ +static const char * hr_xpm[] = { +"200 1 169 2", +" c None", +". c #F0F8F0", +"+ c #EEF8EF", +"@ c #EEF7ED", +"# c #EDF6EC", +"$ c #EBF6EB", +"% c #E9F4E9", +"& c #E9F4E8", +"* c #E7F3E7", +"= c #E6F3E6", +"- c #E4F2E4", +"; c #E2F1E3", +"> c #E1F1E1", +", c #E0F0E0", +"' c #DFEFDF", +") c #DDEFDE", +"! c #DCEEDD", +"~ c #DBEEDB", +"{ c #DAEDDA", +"] c #D8EDD8", +"^ c #D7ECD7", +"/ c #D5EBD5", +"( c #D4EBD4", +"_ c #D3EAD3", +": c #D2EAD2", +"< c #D0E9D0", +"[ c #CFE8CF", +"} c #CEE8CE", +"| c #CCE7CC", +"1 c #CBE6CB", +"2 c #CAE5CA", +"3 c #C9E5C9", +"4 c #C7E4C7", +"5 c #C6E4C6", +"6 c #C4E3C4", +"7 c #C3E2C3", +"8 c #C2E2C2", +"9 c #C1E1C0", +"0 c #BFE1C0", +"a c #BEE0BE", +"b c #BDE0BC", +"c c #BBDFBB", +"d c #BADEBA", +"e c #B8DEB8", +"f c #B7DDB7", +"g c #B6DCB6", +"h c #B5DBB5", +"i c #B3DBB3", +"j c #B2DAB2", +"k c #B1DAB1", +"l c #AFD9AF", +"m c #AED8AE", +"n c #ADD8AD", +"o c #ABD7AC", +"p c #AAD6AA", +"q c #A8D6A9", +"r c #A7D5A7", +"s c #A6D5A6", +"t c #A4D4A4", +"u c #A3D3A3", +"v c #A2D3A2", +"w c #A0D2A0", +"x c #A0D1A0", +"y c #9ED19E", +"z c #9CD19D", +"A c #9BCF9B", +"B c #9ACF9A", +"C c #99CF99", +"D c #98CE97", +"E c #96CD97", +"F c #95CC94", +"G c #94CC94", +"H c #92CB92", +"I c #91CA91", +"J c #90CA8F", +"K c #8EC98E", +"L c #8DC88D", +"M c #8BC88C", +"N c #8AC88B", +"O c #89C689", +"P c #87C788", +"Q c #86C586", +"R c #85C585", +"S c #84C484", +"T c #82C483", +"U c #81C380", +"V c #7FC37F", +"W c #7EC27E", +"X c #7DC17D", +"Y c #7BC17C", +"Z c #7ABF7B", +"` c #79BF79", +" . c #78BF78", +".. c #76BE76", +"+. c #75BE75", +"@. c #74BD73", +"#. c #73BC72", +"$. c #71BB71", +"%. c #70BA70", +"&. c #6FBA6E", +"*. c #6EBA6E", +"=. c #EFF7EF", +"-. c #EEF7EE", +";. c #ECF6EC", +">. c #EAF5EA", +",. c #E9F5E9", +"'. c #E8F4E8", +"). c #E4F3E4", +"!. c #E3F1E3", +"~. c #E2F1E2", +"{. c #DFF0DF", +"]. c #DEEFDD", +"^. c #DCEFDC", +"/. c #DAEED9", +"(. c #D8ECD8", +"_. c #D7ECD6", +":. c #D6ECD6", +"<. c #D4EAD4", +"[. c #D2EAD1", +"}. c #D1E9D1", +"|. c #CDE7CD", +"1. c #CBE7CB", +"2. c #CAE6CA", +"3. c #C8E5C9", +"4. c #C7E5C7", +"5. c #C6E4C5", +"6. c #C5E3C4", +"7. c #C0E1C1", +"8. c #BFE1BF", +"9. c #BDE0BE", +"0. c #BDDFBC", +"a. c #BCDEBB", +"b. c #B4DBB5", +"c. c #B2DAB1", +"d. c #B0DAB0", +"e. c #B0D9B0", +"f. c #AED9AE", +"g. c #ACD8AC", +"h. c #ACD8AB", +"i. c #AAD6A9", +"j. c #A7D6A8", +"k. c #A6D4A6", +"l. c #A5D4A5", +"m. c #A3D3A4", +"n. c #A1D2A1", +"o. c #9FD29F", +"p. c #9CD09C", +"q. c #9BD09C", +"r. c #98CE99", +"s. c #97CE98", +"t. c #96CD96", +"u. c #95CD95", +"v. c #93CC93", +"w. c #8FCA8F", +"x. c #8DC88C", +"y. c #8CC88B", +"z. c #8AC78A", +"A. c #88C687", +"B. c #86C587", +"C. c #84C585", +"D. c #82C382", +"E. c #80C381", +"F. c #80C280", +"G. c #7EC27F", +"H. c #7CC07B", +"I. c #7AC07B", +"J. c #75BD75", +"K. c #74BD74", +"L. c #6EBB6E", +". =.-.;.>.,.'.* = ).!.~., {.].^.~ /.(._.:.<._ [.}.[ |.| 1.2.3.4.5.6.7 8 7.8.9.0.a.d e f g b.i c.d.e.f.g.h.i.q j.k.l.m.v n.o.y p.q.B r.s.t.u.v.H I w.K x.y.z.O A.B.C.S D.E.F.G.X H.I.` ...J.K.#.$.%.L.*.*.L.%.$.#.K.J... .` I.H.X G.F.E.D.S C.B.A.O z.y.x.K w.I H v.u.t.s.r.B q.p.y o.n.v m.l.k.j.q i.h.g.f.e.d.c.i b.g f e d a.0.9.8.7.8 7 6.5.4.3.2.1.| |.[ }.[._ <.:._.(./.~ ^.].{., ~.!.).= * '.,.>.;.-.=.. "};
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/CMakeLists.txt URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/CMakeLists.txt (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/CMakeLists.txt Mon Aug 1 09:44:45 2011 @@ -0,0 +1,55 @@ +## Makefile.am + +set (gtk_pkg_yast_plugin_SRCS + pkg-selector-help.cc + YGPackageSelector.cc + YGPackageSelectorPluginImpl.cc + ygtkpkgdetailview.cc + ygtkpkghistorydialog.cc + ygtkpkglistview.cc + ygtkpkgfilterview.cc + ygtkpkglanguageview.cc + ygtkpkgmenubar.cc + ygtkpkgpatternview.cc + ygtkpkgproductdialog.cc + ygtkpkgquerycombo.cc + ygtkpkgrpmgroupsview.cc + ygtkpkgsearchentry.cc + ygtkpkgstatusbar.cc + ygtkpkgundolist.cc + yzypptags.cc + yzyppwrapper.cc + #ygtkpkgvestigialdialog.cc + + ygtkcellrendererbutton.c + ygtkcellrenderersidebutton.c + ygtkcellrenderertext.c + ygtktreemodel.cc + ) + +## includes: + +include_directories (${ZYPP_INCLUDE_DIR}) +# we want libpy2gtk_pkg.so to link to libpy2gtk.so before install +# and then to find it when it is installed and on its own +link_directories (${CMAKE_BINARY_DIR}/src) +link_directories (${UI_PLUGIN_DIR}) + +## target: + +add_library (py2gtk_pkg SHARED ${gtk_pkg_yast_plugin_SRCS}) +add_dependencies (py2gtk_pkg py2gtk) + +## libraries: + +target_link_libraries (py2gtk_pkg libpy2gtk.so) +target_link_libraries (py2gtk_pkg ${GTK2_LIBRARIES}) +target_link_libraries (py2gtk_pkg ${LIBYUI_LIBRARY}) +target_link_libraries (py2gtk_pkg ${ZYPP_LIBRARY}) +set_target_properties (py2gtk_pkg PROPERTIES SOVERSION 2) +set_target_properties (py2gtk_pkg PROPERTIES LINK_FLAGS "--no-undefined -Wl,-rpath -Wl,${UI_PLUGIN_DIR}") + +## install: + +install (TARGETS py2gtk_pkg LIBRARY DESTINATION ${UI_PLUGIN_DIR}) +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,841 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include <string.h> +#include "YGUtils.h" +#include "YGDialog.h" +#include "YGUI.h" +#include "YGPackageSelector.h" +#include "yzyppwrapper.h" +#include "ygtkwizard.h" +#include "ygtkhtmlwrap.h" +#include "ygtkpkglistview.h" +#include "ygtkpkgsearchentry.h" +#include "ygtkpkgfilterview.h" +#include "ygtkpkgrpmgroupsview.h" +#include "ygtkpkgquerycombo.h" +#include "ygtkpkgpatternview.h" +#include "ygtkpkglanguageview.h" +#include "ygtkpkgundolist.h" +#include "ygtkpkgmenubar.h" +#include "ygtkpkgstatusbar.h" +#include "ygtkpkgdetailview.h" + +#include "ygtkpkghistorydialog.h" +#ifdef HAS_VESTIGIAL_DIALOG +#include "ygtkpkgvestigialdialog.h" +#endif + +//#define USE_LIST_BUTTONS + +YGPackageSelector *YGPackageSelector::singleton = 0; + +struct YGPackageSelector::Impl : + public Ypp::Interface, YGtkPkgListView::Listener, YGtkPkgQueryWidget::Listener, + YGtkPkgQueryCombo::Factory +{ +GtkWidget *m_widget, *m_toolbox; +YGtkPkgListView *m_list; +YGtkPkgQueryCombo *m_combo; +YGtkPkgSearchEntry *m_entry; +std::list <YGtkPkgQueryWidget *> m_queryWidgets; +YGtkPkgUndoList *m_undo; +YGtkPkgStatusBar *m_status; +YGtkPkgMenuBar *m_menu; +std::list std::string m_filterSuffices; +GtkWidget *m_overview; +YGtkPkgDetailView *m_details; +guint m_refresh_id; +Ypp::List m_refresh_list; +bool has_patterns_mode; + +struct SuffixFilter : public Ypp::Match { + SuffixFilter (Impl *pThis) : pThis (pThis) {} + + virtual bool match (Ypp::Selectable &sel) + { + if (!pThis->m_filterSuffices.empty()) { + std::string name (sel.name()); + for (std::list std::string::iterator it = pThis->m_filterSuffices.begin(); + it != pThis->m_filterSuffices.end(); it++) + if (YGUtils::endsWith (name, *it)) + return false; + } + return true; + } + + Impl *pThis; +}; + + GtkWidget *getWidget() { return m_widget; } + + GtkWidget *createMainArea() + { + bool onlineUpdate = YGPackageSelector::get()->onlineUpdateMode(); + + m_entry = new YGtkPkgSearchEntry(); + m_queryWidgets.push_back (m_entry); + + m_list = new YGtkPkgListView (false, Ypp::List::NAME_SORT, false, true); +#ifdef USE_LIST_BUTTONS + m_list->addImageColumn (NULL, STATUS_ICON_PROP); +#else + m_list->addCheckColumn (INSTALLED_CHECK_PROP); +#endif + m_list->addTextColumn (_("Name"), NAME_SUMMARY_PROP, true, -1); + m_list->addTextColumn (_("Version"), VERSION_PROP, true, 125); + if (!onlineUpdate) + m_list->addTextColumn (_("Size"), SIZE_PROP, false, 85); +#ifdef USE_LIST_BUTTONS + m_list->addButtonColumn (_("Action"), ACTION_BUTTON_PROP); +#endif + m_list->addTextColumn (_("Repository"), REPOSITORY_PROP, false, 180); + if (!onlineUpdate) + m_list->addTextColumn (_("Supportability"), SUPPORT_PROP, false, 120); + m_list->setListener (this); + m_entry->setActivateWidget (m_list->getView()); + + GtkWidget *hbox = gtk_hbox_new (FALSE, 2); + GtkWidget *label = gtk_label_new_with_mnemonic (_("Package _listing:")); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), m_list->getView()); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), gtk_event_box_new(), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), m_entry->getWidget(), FALSE, TRUE, 0); + m_toolbox = gtk_hbox_new (FALSE, 6); + + GtkWidget *overview = ygtk_html_wrap_new(); + if (onlineUpdate) + ygtk_html_wrap_set_text (overview, + "<h1><img src="gtk-dialog-info" />Overview</h1>" + "<p>Press a patch in the list to see more information about it.</p>" + "<p>To install a patch, just click on its "checkbox".</p>", FALSE); + else + ygtk_html_wrap_set_text (overview, + "<h1><img src="gtk-dialog-info" />Overview</h1>" + "<p>Browse packages using the groups list on the left.</p>" + "<p>Press a package in the list to see more information about it.</p>" + "<p>To install or remove a package, just click on its "checkbox".</p>", FALSE); + + m_overview = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_overview), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (m_overview), + GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (m_overview), overview); + GtkWidget *text = gtk_event_box_new(); + gtk_container_add (GTK_CONTAINER (text), m_overview); + + GtkWidget *list_vbox = gtk_vbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (list_vbox), m_list->getWidget(), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (list_vbox), m_toolbox, FALSE, TRUE, 0); + + GtkWidget *vpaned = gtk_vpaned_new(); + gtk_paned_pack1 (GTK_PANED (vpaned), list_vbox, TRUE, FALSE); + gtk_paned_pack2 (GTK_PANED (vpaned), text, FALSE, TRUE); + YGUtils::setPaneRelPosition (vpaned, .65); + + GtkWidget *_vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (_vbox), hbox, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (_vbox), vpaned, TRUE, TRUE, 0); + return _vbox; + } + + GtkWidget *createSidebar() + { + m_combo = new YGtkPkgQueryCombo (this); + if (YGPackageSelector::get()->onlineUpdateMode()) { + m_combo->add (_("Priorities")); + m_combo->add (_("Repositories")); + + int active = YGPackageSelector::get()->repoMode() ? 1 : 0; + m_combo->setActive (active); + } + else { + m_combo->add (_("Groups")); + m_combo->add (_("RPM Groups")); + m_combo->add (_("Repositories")); + m_combo->add (_("Support")); + m_combo->add (""); + has_patterns_mode = !isPatternsPoolEmpty(); + if (has_patterns_mode) + m_combo->add (_("Patterns")); + m_combo->add (_("Languages")); + + int active = 5; + if (YGPackageSelector::get()->repoMode()) + active = 2; + else if (YGPackageSelector::get()->searchMode()) + active = 0; + m_combo->setActive (active); + } + m_queryWidgets.push_back (m_combo); + + YGtkPkgFilterView *status = new YGtkPkgFilterView (new YGtkPkgStatusModel()); + if (YGPackageSelector::get()->updateMode()) + status->select (3); + m_queryWidgets.push_back (status); + + GtkWidget *vpaned = gtk_vpaned_new(); + gtk_paned_pack1 (GTK_PANED (vpaned), m_combo->getWidget(), TRUE, FALSE); + gtk_paned_pack2 (GTK_PANED (vpaned), status->getWidget(), FALSE, FALSE); + YGUtils::setPaneRelPosition (vpaned, .80); + return vpaned; + } + + Impl() : m_menu (NULL), m_details (NULL), m_refresh_id (0), m_refresh_list (0) + { + Ypp::init(); + m_undo = new YGtkPkgUndoList(); + m_status = 0; + + GtkWidget *hpaned = gtk_hpaned_new(); + gtk_paned_pack1 (GTK_PANED (hpaned), createSidebar(), FALSE, TRUE); + gtk_paned_pack2 (GTK_PANED (hpaned), createMainArea(), TRUE, FALSE); + YGUtils::setPaneRelPosition (hpaned, .28); + + m_widget = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (m_widget), hpaned, TRUE, TRUE, 0); + if (!YGPackageSelector::get()->onlineUpdateMode()) { + m_status = new YGtkPkgStatusBar (m_undo); + gtk_box_pack_start (GTK_BOX (m_widget), m_status->getWidget(), FALSE, TRUE, 0); + } + gtk_widget_show_all (m_widget); + gtk_widget_hide (m_toolbox); + + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) + (*it)->setListener (this); + } + + ~Impl() + { + if (m_refresh_id) + g_source_remove (m_refresh_id); + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) + delete *it; + delete m_list; + delete m_menu; + delete m_status; + delete m_details; + delete m_undo; + Ypp::finish(); + } + + void clearQueryWidgets() + { + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) + (*it)->clearSelection(); + } + + // Ypp::Interface + + static bool acceptText (Ypp::Selectable &selectable, const std::string &title, + const std::string &open, const std::string &text) + { + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + (GtkDialogFlags) 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, + "%s %s", selectable.name().c_str(), title.c_str()); + if (!open.empty()) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", open.c_str()); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES); + + GtkWidget *view = ygtk_html_wrap_new(), *scroll; + ygtk_html_wrap_set_text (view, text.c_str(), FALSE); + + scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type + (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (scroll), view); + + GtkBox *vbox = GTK_BOX (GTK_DIALOG(dialog)->vbox); + gtk_box_pack_start (vbox, scroll, TRUE, TRUE, 6); + + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 550, 500); + gtk_widget_show_all (dialog); + + gint ret = gtk_dialog_run (GTK_DIALOG (dialog)); + bool confirmed = (ret == GTK_RESPONSE_YES); + gtk_widget_destroy (dialog); + return confirmed; + } + + virtual bool showLicense (Ypp::Selectable &sel, const std::string &license) + { + return acceptText (sel, _("License Agreement"), + _("Do you accept the terms of this license?"), license); + } + + virtual bool showMessage (Ypp::Selectable &sel, const std::string &message) + { return acceptText (sel, _("Warning Message"), _("Install anyway?"), message); } + + virtual bool resolveProblems (const std::list <Ypp::Problem *> &problems) + { + // we can't use ordinary radio buttons, as gtk+ enforces that in a group + // one must be selected... + #define DETAILS_PAD 25 + enum ColumnAlias { + SHOW_TOGGLE_COL, ACTIVE_TOGGLE_COL, TEXT_COL, WEIGHT_TEXT_COL, + TEXT_PAD_COL, APPLY_PTR_COL, TOTAL_COLS + }; + + struct inner { + static void solution_toggled (GtkTreeModel *model, GtkTreePath *path) + { + GtkTreeStore *store = GTK_TREE_STORE (model); + GtkTreeIter iter, parent; + + gboolean enabled; + bool *apply; + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, ACTIVE_TOGGLE_COL, &enabled, + APPLY_PTR_COL, &apply, -1); + if (!apply) + return; + + // disable all the other radios on the group, setting current + gtk_tree_model_get_iter (model, &iter, path); + if (gtk_tree_model_iter_parent (model, &parent, &iter)) { + gtk_tree_model_iter_children (model, &iter, &parent); + do { + gtk_tree_store_set (store, &iter, ACTIVE_TOGGLE_COL, FALSE, -1); + bool *apply; + gtk_tree_model_get (model, &iter, APPLY_PTR_COL, &apply, -1); + if (apply) *apply = false; + } while (gtk_tree_model_iter_next (model, &iter)); + } + + enabled = !enabled; + *apply = enabled; + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_store_set (store, &iter, ACTIVE_TOGGLE_COL, enabled, -1); + } + static void cursor_changed_cb (GtkTreeView *view, GtkTreeModel *model) + { + GtkTreePath *path; + gtk_tree_view_get_cursor (view, &path, NULL); + solution_toggled (model, path); + gtk_tree_path_free (path); + } + }; + + // model + GtkTreeStore *store = gtk_tree_store_new (TOTAL_COLS, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_INT, + G_TYPE_INT, G_TYPE_POINTER); + for (std::list <Ypp::Problem *>::const_iterator it = problems.begin(); + it != problems.end(); it++) { + GtkTreeIter problem_iter; + gtk_tree_store_append (store, &problem_iter, NULL); + gtk_tree_store_set (store, &problem_iter, SHOW_TOGGLE_COL, FALSE, + TEXT_COL, (*it)->description.c_str(), WEIGHT_TEXT_COL, PANGO_WEIGHT_BOLD, -1); + if (!(*it)->details.empty()) { + GtkTreeIter details_iter; + gtk_tree_store_append (store, &details_iter, &problem_iter); + gtk_tree_store_set (store, &details_iter, SHOW_TOGGLE_COL, FALSE, + TEXT_COL, (*it)->details.c_str(), TEXT_PAD_COL, DETAILS_PAD, -1); + } + + for (int i = 0; (*it)->getSolution (i); i++) { + Ypp::Problem::Solution *solution = (*it)->getSolution (i); + GtkTreeIter solution_iter; + gtk_tree_store_append (store, &solution_iter, &problem_iter); + gtk_tree_store_set (store, &solution_iter, SHOW_TOGGLE_COL, TRUE, + WEIGHT_TEXT_COL, PANGO_WEIGHT_NORMAL, + ACTIVE_TOGGLE_COL, FALSE, TEXT_COL, solution->description.c_str(), + APPLY_PTR_COL, &solution->apply, -1); + if (!solution->details.empty()) { + gtk_tree_store_append (store, &solution_iter, &problem_iter); + gtk_tree_store_set (store, &solution_iter, SHOW_TOGGLE_COL, FALSE, + WEIGHT_TEXT_COL, PANGO_WEIGHT_NORMAL, + TEXT_COL, solution->details.c_str(), TEXT_PAD_COL, DETAILS_PAD, -1); + } + } + } + + // interface + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GtkDialogFlags (0), GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "%s", + _("There are some conflicts on the transaction that must be solved manually.")); + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY); + + GtkWidget *view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection ( + GTK_TREE_VIEW (view)), GTK_SELECTION_NONE); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (view), TEXT_COL); + + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + column = gtk_tree_view_column_new(); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "visible", SHOW_TOGGLE_COL, "active", ACTIVE_TOGGLE_COL, NULL); + gtk_cell_renderer_toggle_set_radio ( + GTK_CELL_RENDERER_TOGGLE (renderer), TRUE); + // we should not connect the actual toggle button, as we toggle on row press + g_signal_connect (G_OBJECT (view), "cursor-changed", + G_CALLBACK (inner::cursor_changed_cb), store); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", TEXT_COL, "weight", WEIGHT_TEXT_COL, "xpad", TEXT_PAD_COL, NULL); + g_object_set (G_OBJECT (renderer), "wrap-width", 400, + "wrap-mode", PANGO_WRAP_WORD_CHAR, NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (view), column); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + gtk_widget_set_has_tooltip (view, TRUE); + + GtkWidget *scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), + GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (scroll), view); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), scroll); + + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 500); + gtk_widget_show_all (dialog); + + bool apply = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY); + gtk_widget_destroy (dialog); + return apply; + } + + // YGtkPkgListView::Listener + + virtual void selectionChanged() + { + Ypp::List selected = m_list->getSelected(); + if (selected.size() == 0) return; + + if (m_overview) { + GtkWidget *parent = gtk_widget_get_parent (m_overview); + gtk_container_remove (GTK_CONTAINER (parent), m_overview); + m_overview = NULL; + + m_details = new YGtkPkgDetailView(); + gtk_container_add (GTK_CONTAINER (parent), m_details->getWidget()); + } + + m_details->setList (selected); + } + + // YGtkPkgQueryWidget::Listener + + void refreshQueryWidget (YGtkPkgQueryWidget *widget) + { + Ypp::Selectable::Type type = Ypp::Selectable::PACKAGE; + if (YGPackageSelector::get()->onlineUpdateMode()) + type = Ypp::Selectable::PATCH; + Ypp::PoolQuery query (type); + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) { + if (*it == widget) + continue; + (*it)->writeQuery (query); + } + query.addCriteria (new SuffixFilter (this)); + + Ypp::List list (query); + widget->updateList (list); + } + + void refreshFilters (Ypp::List list) + { + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) { + if ((*it)->begsUpdate()) { + if (YGPackageSelector::get()->yield()) return; + + if ((*it)->modified) + refreshQueryWidget (*it); + else + (*it)->updateList (list); + } + } + } + + void refreshToolbox() + { + // only present one toolbox widget as they may be quite large + GtkWidget *toolbox = 0; + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) + if ((toolbox = (*it)->createToolbox())) + break; + + GList *children = gtk_container_get_children (GTK_CONTAINER (m_toolbox)); + for (GList *i = children; i; i = i->next) + gtk_container_remove (GTK_CONTAINER (m_toolbox), (GtkWidget *) i->data); + g_list_free (children); + + if (toolbox) { + gtk_box_pack_end (GTK_BOX (m_toolbox), toolbox, FALSE, TRUE, 0); + gtk_widget_show (m_toolbox); + } + else + gtk_widget_hide (m_toolbox); + } + + static gboolean refresh_filters_timeout_cb (gpointer data) + { + YGUI::ui()->busyCursor(); + if (YGPackageSelector::get()->yield()) return FALSE; + + Impl *pThis = (Impl *) data; + pThis->refreshToolbox(); + pThis->refreshFilters (pThis->m_refresh_list); + pThis->m_refresh_id = 0; + + YGUI::ui()->normalCursor(); + return FALSE; + } + + virtual void refreshQuery() + { + if (m_refresh_id) { + g_source_remove (m_refresh_id); + m_refresh_id = 0; + } + + YGUI::ui()->busyCursor(); + if (YGPackageSelector::get()->yield()) return; + + std::list std::string keywords; + if (m_entry->getAttribute() == Ypp::PoolQuery::NAME) + keywords = m_entry->getText(); + + Ypp::Selectable::Type type = Ypp::Selectable::PACKAGE; + if (YGPackageSelector::get()->onlineUpdateMode()) + type = Ypp::Selectable::PATCH; + Ypp::PoolQuery query (type); + for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin(); + it != m_queryWidgets.end(); it++) + (*it)->modified = (*it)->writeQuery (query); + query.addCriteria (new SuffixFilter (this)); + + Ypp::List list (query); + m_list->setList (list); + m_list->setHighlight (keywords); + + YGUI::ui()->normalCursor(); + + m_refresh_list = list; + int wait = 800; + if (keywords.empty()) + wait = 500; + if (list.size() == 0) + wait = 200; + m_refresh_id = g_timeout_add_full (G_PRIORITY_LOW, wait, refresh_filters_timeout_cb, this, NULL); + } + + // YGtkPkgQueryCombo callback + + virtual YGtkPkgQueryWidget *createQueryWidget (YGtkPkgQueryCombo *combo, int index) + { + Ypp::Busy busy (0); + YGtkPkgFilterModel *model = 0; + if (YGPackageSelector::get()->onlineUpdateMode()) { + switch (index) { + case 0: model = new YGtkPkgPriorityModel(); break; + case 1: model = new YGtkPkgRepositoryModel(); break; + } + } + else { + switch (index) { + case 0: model = new YGtkPkgPKGroupModel(); break; + case 1: return new YGtkPkgRpmGroupsView(); + case 2: model = new YGtkPkgRepositoryModel(); break; + case 3: model = new YGtkPkgSupportModel(); break; + case 5: + if (has_patterns_mode) + return new YGtkPkgPatternView (Ypp::Selectable::PATTERN); + case 6: return new YGtkPkgLanguageView(); + } + } + return new YGtkPkgFilterView (model); + } + + // YGPackageSelector complementary methods + + static bool confirmCancel() + { + GtkWidget *dialog; + dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, + "%s", _("Changes not saved!")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", + _("Quit anyway?")); + gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_NO, + GTK_STOCK_QUIT, GTK_RESPONSE_YES, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES); + + bool yes = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES; + gtk_widget_destroy (dialog); + return yes; + } + + bool confirmUnsupported() + { + struct UnsupportedMatch : public Ypp::Match { + virtual bool match (Ypp::Selectable &sel) + { return Ypp::Package (sel).support() <= 1; } + }; + + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GtkDialogFlags (0), GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, + _("Unsupported packages")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Please realize that the following software is either unsupported " + "or requires an additional customer contract for support.")); + gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_NO, + GTK_STOCK_APPLY, GTK_RESPONSE_YES, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500); + + Ypp::PoolQuery query (Ypp::Selectable::PACKAGE); + query.addCriteria (new Ypp::StatusMatch (Ypp::StatusMatch::TO_MODIFY)); + query.addCriteria (new UnsupportedMatch()); + Ypp::List list (query); + + YGtkPkgListView view (true, Ypp::List::NAME_SORT, false, true); + view.addCheckColumn (INSTALLED_CHECK_PROP); + view.addTextColumn (_("Name"), NAME_SUMMARY_PROP, true, -1); + view.addTextColumn (_("Supportability"), SUPPORT_PROP, true, 140); + view.addTextColumn (_("Version"), VERSION_PROP, true, 110); + view.addTextColumn (_("Repository"), REPOSITORY_PROP, false, 160); + view.setListener (this); + view.setList (list); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), view.getWidget()); + gtk_widget_show_all (dialog); + int ret = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + return ret == GTK_RESPONSE_YES; + } +}; + +extern const char *pkg_help[], *patch_help[]; + +static bool confirm_cb (void *pThis) +{ + if (Ypp::isModified()) + return YGPackageSelector::Impl::confirmCancel(); + return true; +} + +static void wizard_action_cb (YGtkWizard *wizard, gpointer id, + gint id_type, YGPackageSelector *pThis) +{ + const gchar *action = (gchar *) id; + yuiMilestone() << "Closing PackageSelector with '" << action << "'\n"; + if (!strcmp (action, "accept")) + pThis->apply(); + else if (!strcmp (action, "cancel")) + pThis->cancel(); +} + +YGPackageSelector::YGPackageSelector (YWidget *parent, long mode) +: YPackageSelector (NULL, mode), YGWidget (this, parent, YGTK_TYPE_WIZARD, NULL), +m_historyDialog (NULL) +#ifdef HAS_VESTIGIAL_DIALOG +, m_vestigialDialog (NULL) +#endif +{ + singleton = this; + setBorder (0); + YGDialog *dialog = YGDialog::currentDialog(); + dialog->setCloseCallback (confirm_cb, this); + + const char *icon, *title, **help; + if (onlineUpdateMode()) { + icon = THEMEDIR "/icons/22x22/apps/yast-online_update.png"; + title = _("Online Update"); + help = patch_help; + } + else { + icon = THEMEDIR "/icons/22x22/apps/yast-software.png"; + title = _("Software Manager"); + help = pkg_help; + } + + YGtkWizard *wizard = YGTK_WIZARD (getWidget()); + ygtk_wizard_set_header_text (wizard, title); + ygtk_wizard_set_header_icon (wizard, icon); + dialog->setIcon (icon); + ygtk_wizard_set_help_text (wizard, _("Please wait...")); + + std::string cancel_str (YGUtils::mapKBAccel ("&Cancel")); + std::string apply_str (YGUtils::mapKBAccel ("&Apply")); + ygtk_wizard_set_button_label (wizard, wizard->abort_button, _(cancel_str.c_str()), GTK_STOCK_CANCEL); + ygtk_wizard_set_button_str_id (wizard, wizard->abort_button, "cancel"); + ygtk_wizard_set_button_label (wizard, wizard->back_button, NULL, NULL); + ygtk_wizard_set_button_label (wizard, wizard->next_button, _(apply_str.c_str()), GTK_STOCK_APPLY); + ygtk_wizard_set_button_str_id (wizard, wizard->next_button, "accept"); + g_signal_connect (G_OBJECT (wizard), "action-triggered", + G_CALLBACK (wizard_action_cb), this); + + impl = new Impl(); // can take a little + ygtk_wizard_set_child (wizard, impl->getWidget()); + impl->m_menu = new YGtkPkgMenuBar(); + ygtk_wizard_set_custom_menubar (wizard, impl->m_menu->getWidget(), FALSE); + //** TEMP: work-around global-menubar-applet: see bug 595560 + gtk_widget_show_all (impl->m_menu->getWidget()); + + std::string str; + str.reserve (6820); + for (int i = 0; help[i]; i++) + str.append (help [i]); + ygtk_wizard_set_help_text (wizard, str.c_str()); + dialog->setTitle (title); + + Ypp::setInterface (impl); + Ypp::runSolver(); // check dependencies at start + impl->refreshQuery(); + + if (summaryMode()) popupChanges(); +} + +YGPackageSelector::~YGPackageSelector() +{ + delete m_historyDialog; +#ifdef HAS_VESTIGIAL_DIALOG + delete m_vestigialDialog; +#endif + delete impl; + singleton = 0; +} + +void YGPackageSelector::cancel() +{ + if (Ypp::isModified()) + if (!Impl::confirmCancel()) + return; + YGUI::ui()->sendEvent (new YCancelEvent()); +} + +void YGPackageSelector::apply() +{ + if (!Ypp::runSolver()) return; // final dependencies check + if (onlineUpdateMode()) + if (!Ypp::showPendingLicenses (Ypp::Selectable::PATCH)) return; + if (!Ypp::showPendingLicenses (Ypp::Selectable::PACKAGE)) return; + + if (Ypp::isModified()) { // confirm + if (!onlineUpdateMode() && confirmUnsupported()) { + if (!impl->confirmUnsupported()) + return; + } + if (!impl->m_undo->popupDialog (true)) + return; + } + + YGUI::ui()->sendEvent (new YMenuEvent ("accept")); +} + +void YGPackageSelector::showFilterWidget (const char *filter) +{ + int index = -1; + if (!strcmp (filter, "patterns")) + index = 5; + assert (index != -1); + impl->m_combo->setActive (index); +} + +void YGPackageSelector::searchFor (Ypp::PoolQuery::StringAttribute attrb, const std::string &text) +{ + impl->clearQueryWidgets(); + impl->m_entry->setText (attrb, text); + impl->refreshQuery(); +} + +void YGPackageSelector::popupChanges() +{ impl->m_undo->popupDialog (false); } + +void YGPackageSelector::filterPkgSuffix (const std::string &suffix, bool enable) +{ + impl->m_filterSuffices.remove (suffix); + if (enable) + impl->m_filterSuffices.push_back (suffix); + if (Ypp::getInterface() == impl) // inited ? + impl->refreshQuery(); +} + +void YGPackageSelector::showRepoManager() +{ YGUI::ui()->sendEvent (new YMenuEvent ("repo_mgr")); } + +void YGPackageSelector::showHistoryDialog() +{ + if (!m_historyDialog) + m_historyDialog = new YGtkPkgHistoryDialog(); + m_historyDialog->popup(); +} + +#ifdef HAS_VESTIGIAL_DIALOG +void YGPackageSelector::showVestigialDialog() +{ + if (!m_vestigialDialog) + m_vestigialDialog = new YGtkPkgVestigialDialog(); + m_vestigialDialog->popup(); +} +#endif + +YGtkPkgUndoList *YGPackageSelector::undoList() +{ return impl->m_undo; } + +YGtkPkgSearchEntry *YGPackageSelector::getSearchEntry() +{ return impl->m_entry; } + +bool YGPackageSelector::yield() +{ + static int _id = 0; + int id = ++_id; + while (g_main_context_iteration (NULL, FALSE)) ; + return id != _id; +} + +#include "pkg/YGPackageSelectorPluginImpl.h" + +YPackageSelector * +YGPackageSelectorPluginImpl::createPackageSelector (YWidget *parent, long modeFlags) +{ + modeFlags |= YPkg_SearchMode; + return new YGPackageSelector (parent, modeFlags); +} + +YWidget * +YGPackageSelectorPluginImpl::createPatternSelector (YWidget *parent, long modeFlags) +{ + modeFlags &= YPkg_SearchMode; + return new YGPackageSelector (parent, modeFlags); +} + +YWidget * +YGPackageSelectorPluginImpl::createSimplePatchSelector (YWidget *parent, long modeFlags) +{ + modeFlags |= YPkg_OnlineUpdateMode; + return new YGPackageSelector (parent, modeFlags); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelector.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,68 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YPackageSelector is implemented as a singleton, allowing + intercommunication of the widgets that compound it. +*/ + +#ifndef YGTK_PACKAGE_SELECTOR_H +#define YGTK_PACKAGE_SELECTOR_H + +#include <YPackageSelector.h> +#include "YGWidget.h" +#include "yzyppwrapper.h" + +//#define HAS_VESTIGIAL_DIALOG + +struct YGtkPkgUndoList; +struct YGtkPkgSearchEntry; +struct YGtkPkgHistoryDialog; +struct YGtkPkgVestigialDialog; + +class YGPackageSelector : public YPackageSelector, YGWidget +{ +public: + YGPackageSelector (YWidget *parent, long mode); + virtual ~YGPackageSelector(); + + static YGPackageSelector *get() { return singleton; } + + void apply(); + void cancel(); + + void showFilterWidget (const char *filter); + void searchFor (Ypp::PoolQuery::StringAttribute attrb, const std::string &text); + void popupChanges(); + void filterPkgSuffix (const std::string &suffix, bool enable_filter); + void showRepoManager(); + + void showHistoryDialog(); +#ifdef HAS_VESTIGIAL_DIALOG + void showVestigialDialog(); +#endif + + YGtkPkgUndoList *undoList(); + YGtkPkgSearchEntry *getSearchEntry(); + + // let the UI function while processing something; returns 'true' if the + // function was called again while the UI was 'breathing' -- in which case + // you probably want to return. + bool yield(); + + YGWIDGET_IMPL_COMMON (YPackageSelector) + + struct Impl; + Impl *impl; + +private: + static YGPackageSelector *singleton; + + YGtkPkgHistoryDialog *m_historyDialog; +#ifdef HAS_VESTIGIAL_DIALOG + YGtkPkgVestigialDialog *m_vestigialDialog; +#endif +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,12 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#include "pkg/YGPackageSelectorPluginImpl.h" + +extern "C" +{ + // Important to locate this symbol - see YGPackageSelectorPluginStub.cc + YGPackageSelectorPluginImpl PSP; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/YGPackageSelectorPluginImpl.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,22 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#ifndef YGPACKAGE_SELECTOR_PLUGIN_IMPL_H +#define YGPACKAGE_SELECTOR_PLUGIN_IMPL_H + +struct YPackageSelector; +struct YWidget; + +#include "YGPackageSelectorPluginIf.h" + +class YGPackageSelectorPluginImpl : public YGPackageSelectorPluginIf +{ +public: + virtual YPackageSelector *createPackageSelector (YWidget *parent, long modeFlags); + virtual YWidget *createPatternSelector (YWidget *parent, long modeFlags); + virtual YWidget *createSimplePatchSelector (YWidget *parent, long modeFlags); +}; + +#endif /*YGPACKAGE_SELECTOR_PLUGIN_IMPL_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/pkg-selector-help.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/pkg-selector-help.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/pkg-selector-help.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,105 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" + +const char *pkg_help[] = { + _("<h1>Purpose</h1>" + "<p>This tool lets you install, remove, and update applications.</p>" + "<p>Software in &product; is broken down and distributed in the form of " + "packages. This way, if multiple applications require a common system file, " + "this system file is shipped in its own package and is installed only once " + "if needed. The user need not be concerned about such underlying <i>dependencies</i>. " + "Likewise, the plugins and other non-essential data of a given application may " + "be shipped in their own packages, so the user may install them only if needed.</p>"), + _("<p>Common suffixes for complementory packages:</p>" + "<ul>" + "<li><b>-plugin-</b>: extends the application with extra functionality.</li>" + "<li><b>-devel</b>: headers for software development.</li>" + "<li><b>-debuginfo</b>: debug symbols for software testing.</li>" + "<li><b>-fr</b>, <b>-pl</b> or other language siglas: translation files (your language " + "will be marked for installation automatically when needed).</li>" + "</ul>"), + _("<p>Both the packages that are installed on your system, and the packages " + "that are available from the <i>repositories</i> you have configured will be listed " + "together. <i>Status</i> filters are available in the right-bottom box.</p>"), + _("<blockquote>A repository is a packages media; it can either be local (such as the " + "installation CD), or a remote internet server. You can find an utility to setup " + "repositories on the YaST control center, which can also be accessed via the " + "<b>Configuration > Repositories</b> menu item.</blockquote>"), + _("<h1>Usage</h1>"), + _("<h2>Install, Upgrade, Remove, Undo tab pages</h2>" + "<p>All packages are listed together unless you have selected a <i>status</i> filter " + "from the right-bottom box. The check-box next to the package name indicates whether " + "the package is installed or not. If a more recent version of an installed " + "package is available, the version text will be highlighted in blue and an " + "upgrade button conveniently placed next to it. It is highlighted red if the " + "version installed is no longer being made available in any configured repository.</p>" + "<p>The context menu (right click on a package) provides extra options. " + "The <b>Undo</b> option can be used to revert any changes you have made. Multiple " + "packages may be selected (using the Control key) and modified together.</p>" + "<p>Use the <b>Version</b> list over the description box to select a specific version " + "of a package.</p>" + "<p>Press the <b>Apply</b> button when you want your changes to be performed.</p>"), + _("<h2>Lock software</h2>" + "<p>Packages can be locked against automatic changes via the context menu.</p>" + "<p>Locking is only useful in very unusual cases: for instance, you may not want " + "to install a given driver because it interferes with your system, yet you want " + "to install some collection that includes it. Locks can be applied whether the " + "package is installed or not.</p>"), + _("<h2>Filters</h2>"), + _("<h3>Search</h3>" + "<p>Enter free text into the search-field to match their names and descriptions. " + "(a search for 'office' will bring up the 'LibreOffice' packages as well as " + "'AbiWord' which carries the word 'office' in its description). You can search for " + "multiple keywords by separating the with a white space (e.g. 'spread sheet' " + "would return 'libreoffice-calc').You may use the " + "search combined with a filter, like searching for a package in a given repository. " + "Other search attributes are provided, such as to search for a given file.</p>"), + _("<h3>Groups</h3>" + "<p>Software for &product; is indexed so that you can find software for a specific " + "task when you are not aware of the software selection available. A more detailed, " + "hierarchical classification is provided by the <b>RPM Groups</b> filter.</p>"), + _("<h3>Patterns and Languages</h3>" + "<p><b>Patterns</b> are task-oriented collections of multiple packages that " + "install like one. The installation of the <i>File Server</i> pattern, for example, " + "will install various packages needed for running such a server.</p>" + "<p>If you want to install a particular language, you may want to do so via the " + "<b>Language</b> tool from the YaST control center.</p>"), + _("<h2>Software details in the box below</h2>" + "<p>Explore the available information about the package in the box below. Note " + "that more information is available for installed packages than for those only " + "available from a repository.</p>" + "<p>You can also pick a specific version of the package to install from this " + "box.</p>"), + "<hr/><p>http://en.opensuse.org/YaST2-GTK</p>", + 0 +}; + +const char *patch_help[] = { + _("<h1>Purpose</h1>" + "<p>This tool gives you control on overviewing and picking patches. You may also " + "reverse patches that have been applied to the system.</p>"), + _("<h1>Usage</h1>" + "<h2>Categories</h2>" + "<p>Patches are grouped as follows:</p>" + "<ul>" + "<li><b>Security</b>: patches a software flaw that could be exploited to gain " + "restricted privilege.</li>" + "<li><b>Recommended</b>: fixes non-security related flaws (e.g. data corruption, " + "performance slowdown)</li>" + "<li><b>Optional</b>: ones that only apply to few users.</li>" + "<li><b>Documentation</b>: fixes documentation errors.</li>" + "<li><b>YaST</b>: patches for the YaST control center tools.</li>" + "</ul>"), + _("<p>Only patches that apply to your system will be visible. You can be sure " + "that the decision to make a patch available is not done trivially.</p>" + "<p>If you are looking for applications enhancements, you should check for <i>upgrades</i> " + "on the <b>Software Manager</b>.</p>"), + 0 +}; +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,329 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkCellRendererButton widget */ +// check the header file for information about this widget + +#include "ygtkcellrendererbutton.h" +#include <gtk/gtk.h> + +#define INNER_BORDER 4 +#define OUTER_BORDER 1 +#define BORDER (INNER_BORDER+OUTER_BORDER) +#define DEPRESS_PAD 1 +#define PIXBUF_TEXT_SPACING 4 + +enum { + PROP_0, + PROP_ACTIVE, + PROP_PIXBUF, + PROP_ICON_NAME, + PROP_STOCK_ID, + PROP_ICON_SIZE, +}; + +static guint toggle_cell_signal = 0; + +G_DEFINE_TYPE (YGtkCellRendererButton, ygtk_cell_renderer_button, GTK_TYPE_CELL_RENDERER_TEXT) + +static void ygtk_cell_renderer_button_init (YGtkCellRendererButton *bcell) +{ + bcell->active = FALSE; + GtkCellRenderer *cell = GTK_CELL_RENDERER (bcell); + cell->xpad = BORDER; + cell->ypad = BORDER; + cell->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE; + bcell->icon_size = 16; +} + +static void unset_image_properties (YGtkCellRendererButton *cell) +{ + if (cell->icon_name) { + g_free (cell->icon_name); + cell->icon_name = NULL; + } + if (cell->pixbuf) { + g_object_unref (cell->pixbuf); + cell->pixbuf = NULL; + } +} + +static void ygtk_cell_renderer_button_finalize (GObject *object) +{ + YGtkCellRendererButton *ccell = YGTK_CELL_RENDERER_BUTTON (object); + unset_image_properties (ccell); + G_OBJECT_CLASS (ygtk_cell_renderer_button_parent_class)->finalize (object); +} + +static void ygtk_cell_renderer_button_get_property (GObject *object, + guint param_id, GValue *value, GParamSpec *pspec) +{ + if (pspec->owner_type == YGTK_TYPE_CELL_RENDERER_BUTTON) { + YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (object); + switch (param_id) { + case PROP_ACTIVE: + g_value_set_boolean (value, bcell->active); + break; + case PROP_PIXBUF: + g_value_set_object (value, G_OBJECT (bcell->pixbuf)); + break; + case PROP_STOCK_ID: + g_value_set_string (value, bcell->stock_id); + break; + case PROP_ICON_NAME: + g_value_set_string (value, bcell->icon_name); + break; + case PROP_ICON_SIZE: + g_value_set_uint (value, bcell->icon_size); + break; + } + } + else + G_OBJECT_CLASS (ygtk_cell_renderer_button_parent_class)->get_property ( + object, param_id, value, pspec); +} + +static void ygtk_cell_renderer_button_set_property (GObject *object, + guint param_id, const GValue *value, GParamSpec *pspec) +{ + if (pspec->owner_type == YGTK_TYPE_CELL_RENDERER_BUTTON) { + YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (object); + switch (param_id) { + case PROP_ACTIVE: + bcell->active = g_value_get_boolean (value); + break; + case PROP_PIXBUF: + unset_image_properties (bcell); + bcell->pixbuf = (GdkPixbuf *) g_value_dup_object (value); + break; + case PROP_STOCK_ID: + unset_image_properties (bcell); + bcell->stock_id = g_value_dup_string (value); + break; + case PROP_ICON_NAME: + unset_image_properties (bcell); + bcell->icon_name = g_value_dup_string (value); + break; + case PROP_ICON_SIZE: + bcell->icon_size = g_value_get_uint (value); + break; + } + } + else + G_OBJECT_CLASS (ygtk_cell_renderer_button_parent_class)->set_property ( + object, param_id, value, pspec); +} + +static PangoLayout *create_layout (YGtkCellRendererButton *bcell, GtkWidget *widget) +{ + GtkCellRendererText *tcell = GTK_CELL_RENDERER_TEXT (bcell); + if (tcell->text) + return gtk_widget_create_pango_layout (widget, tcell->text); + return NULL; +} + +static void ensure_pixbuf (YGtkCellRendererButton *cell, GtkWidget *widget) +{ + if ((cell->icon_name || cell->stock_id) && !cell->pixbuf) { + if (cell->icon_name) { + GtkIconTheme *theme = gtk_icon_theme_get_default(); + GError *error = 0; + cell->pixbuf = gtk_icon_theme_load_icon (theme, cell->icon_name, + cell->icon_size, GTK_ICON_LOOKUP_FORCE_SIZE, &error); + if (!cell->pixbuf) + g_warning ("Couldn't load ygtk-cell-renderer-button icon: %s\nGtk: %s\n", + cell->icon_name, error->message); + } + else // stock-id + cell->pixbuf = gtk_widget_render_icon (widget, cell->stock_id, + GTK_ICON_SIZE_BUTTON, "button"); + } +} + +static void ygtk_cell_renderer_button_get_size_full (GtkCellRenderer *cell, + GtkWidget *widget, GdkRectangle *cell_area, gint *_xoffset, gint *_yoffset, + gint *_width, gint *_height, gint *_pixbuf_xoffset, gint *_pixbuf_yoffset, + gint *_pixbuf_width, gint *_pixbuf_height, gint *_text_xoffset, gint *_text_yoffset) +{ + YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (cell); + ensure_pixbuf (bcell, widget); + + int pixbuf_width = 0, pixbuf_height = 0; + if (bcell->pixbuf) { + pixbuf_width = gdk_pixbuf_get_width (bcell->pixbuf); + pixbuf_height = gdk_pixbuf_get_height (bcell->pixbuf); + } + + PangoLayout *layout = create_layout (bcell, widget); + int text_width = 0, text_height = 0; + if (layout) { + PangoRectangle rect; + pango_layout_get_pixel_extents (layout, NULL, &rect); + text_width = rect.x + rect.width; + text_height = rect.y + rect.height; + } + + int width, height; + width = pixbuf_width + text_width; + if (pixbuf_width && text_width) + width += PIXBUF_TEXT_SPACING; + height = MAX (pixbuf_height, text_height); + + if (cell_area) { + gboolean reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + gfloat xalign = cell->xalign, yalign = cell->yalign; + if (reverse) + xalign = 1.0 - xalign; + + int cell_width = cell_area->width - cell->xpad*2, + cell_height = cell_area->height - cell->ypad*2; + int xoffset = (xalign * (cell_width - width)) + cell->xpad; + int yoffset = (yalign * (cell_height - height)) + cell->ypad; + if (_xoffset) *_xoffset = xoffset; + if (_yoffset) *_yoffset = yoffset; + + int text_x = xoffset, text_y; + if (pixbuf_width && !reverse) + text_x += (pixbuf_width + PIXBUF_TEXT_SPACING); + text_y = (yalign * (cell_height - text_height)) + cell->ypad; + + int pixbuf_x = xoffset, pixbuf_y; + if (text_width && reverse) + pixbuf_x += (text_width + PIXBUF_TEXT_SPACING); + pixbuf_y = (yalign * (cell_height - pixbuf_height)) + cell->ypad; + + if (_pixbuf_xoffset) *_pixbuf_xoffset = pixbuf_x; + if (_pixbuf_yoffset) *_pixbuf_yoffset = pixbuf_y; + if (_pixbuf_width) *_pixbuf_width = pixbuf_width; + if (_pixbuf_height) *_pixbuf_height = pixbuf_height; + if (_text_xoffset) *_text_xoffset = text_x; + if (_text_yoffset) *_text_yoffset = text_y; + } + + if (_width) *_width = width + (cell->xpad * 2); + if (_height) *_height = height + (cell->ypad * 2); +} + +static void ygtk_cell_renderer_button_get_size (GtkCellRenderer *cell, + GtkWidget *widget, GdkRectangle *cell_area, gint *xoffset, gint *yoffset, + gint *width, gint *height) +{ + ygtk_cell_renderer_button_get_size_full (cell, widget, cell_area, + xoffset, yoffset, width, height, NULL, NULL, NULL, NULL, NULL, NULL); +} + +static void ygtk_cell_renderer_button_render (GtkCellRenderer *cell, + GdkDrawable *window, GtkWidget *widget, GdkRectangle *background_area, + GdkRectangle *cell_area, GdkRectangle *expose_area, GtkCellRendererState flags) +{ + YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (cell); + + gboolean has_focus = FALSE; + if (flags & GTK_CELL_RENDERER_SELECTED) + has_focus = GTK_WIDGET_HAS_FOCUS (widget); + + GtkStateType state = GTK_STATE_NORMAL; + if (!cell->sensitive || GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) + state = GTK_STATE_INSENSITIVE; + else if ((flags & GTK_CELL_RENDERER_PRELIT)) + state = GTK_STATE_PRELIGHT; + + GtkShadowType shadow = GTK_SHADOW_OUT; + if (bcell->active) { + shadow = GTK_SHADOW_IN; + state = GTK_STATE_ACTIVE; + } + + int text_xoffset, text_yoffset, pixbuf_xoffset, pixbuf_yoffset, pixbuf_width, + pixbuf_height, width, height; + ygtk_cell_renderer_button_get_size_full (cell, widget, cell_area, + NULL, NULL, &width, &height, &pixbuf_xoffset, &pixbuf_yoffset, &pixbuf_width, + &pixbuf_height, &text_xoffset, &text_yoffset); + + int x = cell_area->x + OUTER_BORDER + 1; + int y = cell_area->y + (cell_area->height - height)/2 + OUTER_BORDER; + width -= OUTER_BORDER*2; height -= OUTER_BORDER*2; + + gtk_paint_box (widget->style, window, state, shadow, expose_area, widget, + "button", x, y, width, height); + + int cell_area_x = cell_area->x, cell_area_y = cell_area->y; + if (bcell->active) { + cell_area->x += DEPRESS_PAD; + cell_area->y += DEPRESS_PAD; + } + + // paint + ensure_pixbuf (bcell, widget); + if (bcell->pixbuf) { + int x = cell_area->x + pixbuf_xoffset, y = cell_area->y + pixbuf_yoffset; + cairo_t *cr = gdk_cairo_create (window); + gdk_cairo_set_source_pixbuf (cr, bcell->pixbuf, x, y); + cairo_rectangle (cr, x, y, pixbuf_width, pixbuf_height); + cairo_fill (cr); + cairo_destroy (cr); + } + + PangoLayout *layout = create_layout (bcell, widget); + if (layout) { + int x = cell_area->x + text_xoffset, y = cell_area->y + text_yoffset; + GtkStyle *style = gtk_widget_get_style (widget); + gtk_paint_layout (style, window, state, TRUE, expose_area, widget, + "cellrenderertext", x, y, layout); + g_object_unref (G_OBJECT (layout)); + } + + cell_area->x = cell_area_x; cell_area->y = cell_area_y; +} + +static gboolean ygtk_cell_renderer_button_activate (GtkCellRenderer *cell, + GdkEvent *event, GtkWidget *widget, const gchar *path, GdkRectangle *background_area, + GdkRectangle *cell_area, GtkCellRendererState flags) +{ + GdkEventButton *_event = &event->button; + if (_event->x >= cell_area->x && _event->x <= cell_area->x + cell_area->width) { + g_signal_emit (cell, toggle_cell_signal, 0, path); + return TRUE; + } + return FALSE; +} + +GtkCellRenderer *ygtk_cell_renderer_button_new (void) +{ return g_object_new (YGTK_TYPE_CELL_RENDERER_BUTTON, NULL); } + +gboolean ygtk_cell_renderer_button_get_active (YGtkCellRendererButton *cell) +{ return cell->active; } + +static void ygtk_cell_renderer_button_class_init (YGtkCellRendererButtonClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + object_class->get_property = ygtk_cell_renderer_button_get_property; + object_class->set_property = ygtk_cell_renderer_button_set_property; + object_class->finalize = ygtk_cell_renderer_button_finalize; + + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + cell_class->get_size = ygtk_cell_renderer_button_get_size; + cell_class->render = ygtk_cell_renderer_button_render; + cell_class->activate = ygtk_cell_renderer_button_activate; + + GParamFlags readwrite_flag = + G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB; + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", "Toggle state", "The toggle state of the button", + FALSE, readwrite_flag)); + g_object_class_install_property (object_class, PROP_PIXBUF, + g_param_spec_object ("pixbuf", "Image", "Side image", GDK_TYPE_PIXBUF, readwrite_flag)); + g_object_class_install_property (object_class, PROP_ICON_NAME, + g_param_spec_string ("icon-name", "Icon name", "Theme icon to render", NULL, readwrite_flag)); + g_object_class_install_property (object_class, PROP_STOCK_ID, + g_param_spec_string ("stock-id", "Stock id", "Stock icon to render", NULL, readwrite_flag)); + g_object_class_install_property (object_class, PROP_ICON_SIZE, + g_param_spec_uint ("icon-size", "Size", "Size of the icon to render", + 0, G_MAXUINT, 22, readwrite_flag)); + + toggle_cell_signal = g_signal_new ("toggled", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (YGtkCellRendererButtonClass, toggled), + NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrendererbutton.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,51 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* GtkButton meets GtkCellRenderer. +*/ + +#ifndef YGTK_CELL_RENDERER_BUTTON_H +#define YGTK_CELL_RENDERER_BUTTON_H + +#include <gtk/gtkcellrenderertext.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_CELL_RENDERER_BUTTON (ygtk_cell_renderer_button_get_type ()) +#define YGTK_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_CELL_RENDERER_BUTTON, YGtkCellRendererButton)) +#define YGTK_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_CELL_RENDERER_BUTTON, YGtkCellRendererButtonClass)) +#define YGTK_IS_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_CELL_RENDERER_BUTTON)) +#define YGTK_IS_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_CELL_RENDERER_BUTTON)) +#define YGTK_CELL_RENDERER_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_CELL_RENDERER_BUTTON, YGtkCellRendererButtonClass)) + +typedef struct _YGtkCellRendererButton +{ + GtkCellRendererText parent; + + // private: + GdkPixbuf *pixbuf; + gchar *icon_name, *stock_id; + gint icon_size; + guint active : 2; +} YGtkCellRendererButton; + +typedef struct _YGtkCellRendererButtonClass +{ + GtkCellRendererTextClass parent_class; + + void (* toggled) (YGtkCellRendererButton *renderer, const gchar *path); +} YGtkCellRendererButtonClass; + +GtkCellRenderer *ygtk_cell_renderer_button_new (void); +GType ygtk_cell_renderer_button_get_type (void) G_GNUC_CONST; + +gboolean ygtk_cell_renderer_button_get_active (YGtkCellRendererButton *cell); + +G_END_DECLS +#endif /*YGTK_CELL_RENDERER_BUTTON_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,221 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkCellRendererSideButton widget */ +// check the header file for information about this widget + +#include "ygtkcellrenderersidebutton.h" +#include <gtk/gtk.h> + +enum { + PROP_0, + PROP_ACTIVE, + PROP_STOCK_ID, + PROP_BUTTON_VISIBLE, +}; + +static guint toggle_cell_signal = 0; + +G_DEFINE_TYPE (YGtkCellRendererSideButton, ygtk_cell_renderer_side_button, GTK_TYPE_CELL_RENDERER_TEXT) + +static void ygtk_cell_renderer_side_button_init (YGtkCellRendererSideButton *bcell) +{ GTK_CELL_RENDERER (bcell)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE; } + +static void free_pixbuf (YGtkCellRendererSideButton *cell) +{ + if (cell->stock_id) { + g_free (cell->stock_id); + cell->stock_id = NULL; + } + if (cell->pixbuf) { + g_object_unref (cell->pixbuf); + cell->pixbuf = NULL; + } +} + +static void ensure_pixbuf (YGtkCellRendererSideButton *cell, GtkWidget *widget) +{ + if (!cell->pixbuf && cell->stock_id) + cell->pixbuf = gtk_widget_render_icon (widget, cell->stock_id, GTK_ICON_SIZE_BUTTON, NULL); +} + +static void ygtk_cell_renderer_side_button_finalize (GObject *object) +{ + YGtkCellRendererSideButton *bcell = YGTK_CELL_RENDERER_SIDE_BUTTON (object); + free_pixbuf (bcell); + G_OBJECT_CLASS (ygtk_cell_renderer_side_button_parent_class)->finalize (object); +} + +static void ygtk_cell_renderer_side_button_get_property ( + GObject *object, guint param_id, GValue *value, GParamSpec *pspec) +{ + if (pspec->owner_type == YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON) { + YGtkCellRendererSideButton *bcell = YGTK_CELL_RENDERER_SIDE_BUTTON (object); + switch (param_id) { + case PROP_ACTIVE: + g_value_set_boolean (value, bcell->active); + break; + case PROP_STOCK_ID: + g_value_set_string (value, bcell->stock_id); + break; + case PROP_BUTTON_VISIBLE: + g_value_set_boolean (value, bcell->button_visible); + break; + } + } + else + G_OBJECT_CLASS (ygtk_cell_renderer_side_button_parent_class)->get_property ( + object, param_id, value, pspec); +} + +static void ygtk_cell_renderer_side_button_set_property (GObject *object, + guint param_id, const GValue *value, GParamSpec *pspec) +{ + if (pspec->owner_type == YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON) { + YGtkCellRendererSideButton *bcell = YGTK_CELL_RENDERER_SIDE_BUTTON (object); + switch (param_id) { + case PROP_ACTIVE: + bcell->active = g_value_get_boolean (value); + break; + case PROP_STOCK_ID: + free_pixbuf (bcell); + bcell->stock_id = g_value_dup_string (value); + break; + case PROP_BUTTON_VISIBLE: + bcell->button_visible = g_value_get_boolean (value); + break; + } + } + else + G_OBJECT_CLASS (ygtk_cell_renderer_side_button_parent_class)->set_property ( + object, param_id, value, pspec); +} + +static void ygtk_cell_renderer_side_button_get_size ( + GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *cell_area, + gint *x_offset, gint *y_offset, gint *width, gint *height) +{ + GTK_CELL_RENDERER_CLASS (ygtk_cell_renderer_side_button_parent_class)->get_size ( + cell, widget, cell_area, x_offset, y_offset, width, height); + YGtkCellRendererSideButton *bcell = YGTK_CELL_RENDERER_SIDE_BUTTON (cell); + if (bcell->button_visible) { + gint icon_width, icon_height; + gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height); + *width += icon_width + 8 + 4; + } +} + +static void ygtk_cell_renderer_side_button_render ( + GtkCellRenderer *cell, GdkDrawable *window, GtkWidget *widget, + GdkRectangle *background_area, GdkRectangle *cell_area, + GdkRectangle *expose_area, GtkCellRendererState flags) +{ + YGtkCellRendererSideButton *bcell = YGTK_CELL_RENDERER_SIDE_BUTTON (cell); + GdkRectangle text_area = *cell_area; + gint icon_width, icon_height; + if (bcell->button_visible) { + gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height); + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + text_area.x += icon_width + 8; + text_area.width -= icon_width + 8 + 4; + } + GTK_CELL_RENDERER_CLASS (ygtk_cell_renderer_side_button_parent_class)->render ( + cell, window, widget, background_area, &text_area, expose_area, flags); + + if (bcell->button_visible) { + GtkStateType state = GTK_STATE_NORMAL; + GtkShadowType shadow = GTK_SHADOW_OUT; + if (!cell->sensitive || GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) + state = GTK_STATE_INSENSITIVE; + /* else if ((flags & GTK_CELL_RENDERER_PRELIT)) + state = GTK_STATE_PRELIGHT;*/ + if (bcell->active) { + shadow = GTK_SHADOW_IN; + state = GTK_STATE_ACTIVE; + } + + GdkRectangle button_area = *cell_area; + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + button_area.x = cell_area->x + 2; + else + button_area.x = cell_area->x + cell_area->width - icon_width - 8; + button_area.width = icon_width + 4; + button_area.height = icon_height + 4; + button_area.y = cell_area->y + ((cell_area->height - button_area.height) / 2); + gtk_paint_box (widget->style, window, state, shadow, expose_area, widget, + "button", button_area.x, button_area.y, button_area.width, button_area.height); + + GdkRectangle icon_area = button_area; + icon_area.x += 2; + icon_area.y += 2; + if (bcell->active) { + cell_area->x += 2; + cell_area->y += 2; + } + + ensure_pixbuf (bcell, widget); + cairo_t *cr = gdk_cairo_create (window); + gdk_cairo_set_source_pixbuf (cr, bcell->pixbuf, icon_area.x, icon_area.y); + cairo_rectangle (cr, icon_area.x, icon_area.y, icon_width, icon_height); + cairo_fill (cr); + cairo_destroy (cr); + } +} + +static gboolean ygtk_cell_renderer_side_button_activate ( + GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, + GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState flags) +{ + YGtkCellRendererSideButton *bcell = YGTK_CELL_RENDERER_SIDE_BUTTON (cell); + if (bcell->button_visible) { + GdkEventButton *_event = (GdkEventButton *) event; + gint icon_width, icon_height; + gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height); + int icon_x, x = _event->x - cell_area->x; + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + icon_x = 2; + else + icon_x = cell_area->width - (icon_width+12); + icon_width += 8; + if (x >= icon_x && x <= icon_x + icon_width) { + g_signal_emit (cell, toggle_cell_signal, 0, path); + return TRUE; + } + } + return FALSE; +} + +GtkCellRenderer *ygtk_cell_renderer_side_button_new (void) +{ return g_object_new (YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON, NULL); } + +gboolean ygtk_cell_renderer_side_button_get_active (YGtkCellRendererSideButton *cell) +{ return cell->active; } + +static void ygtk_cell_renderer_side_button_class_init (YGtkCellRendererSideButtonClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + object_class->get_property = ygtk_cell_renderer_side_button_get_property; + object_class->set_property = ygtk_cell_renderer_side_button_set_property; + object_class->finalize = ygtk_cell_renderer_side_button_finalize; + + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + cell_class->get_size = ygtk_cell_renderer_side_button_get_size; + cell_class->render = ygtk_cell_renderer_side_button_render; + cell_class->activate = ygtk_cell_renderer_side_button_activate; + + GParamFlags readwrite_flag = + G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB; + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", "Toggle state", "The toggle state of the button", + FALSE, readwrite_flag)); + g_object_class_install_property (object_class, PROP_STOCK_ID, + g_param_spec_string ("stock-id", "Stock ID", "Stock icon to render", NULL, readwrite_flag)); + g_object_class_install_property (object_class, PROP_BUTTON_VISIBLE, + g_param_spec_boolean ("button-visible", "Is Button Visible", "Whether to show side button", TRUE, readwrite_flag)); + + toggle_cell_signal = g_signal_new ("toggled", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (YGtkCellRendererSideButtonClass, toggled), + NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderersidebutton.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,50 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* A icon GtkButton next to a GtkLabel. +*/ + +#ifndef YGTK_CELL_RENDERER_SIDE_BUTTON_H +#define YGTK_CELL_RENDERER_SIDE_BUTTON_H + +#include <gtk/gtkcellrenderertext.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON (ygtk_cell_renderer_side_button_get_type()) +#define YGTK_CELL_RENDERER_SIDE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON, YGtkCellRendererSideButton)) +#define YGTK_CELL_RENDERER_SIDE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON, YGtkCellRendererSideButtonClass)) +#define YGTK_IS_CELL_RENDERER_SIDE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON)) +#define YGTK_IS_CELL_RENDERER_SIDE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON)) +#define YGTK_CELL_RENDERER_SIDE_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_CELL_RENDERER_SIDE_BUTTON, YGtkCellRendererSideButtonClass)) + +typedef struct YGtkCellRendererSideButton +{ + GtkCellRendererText parent; + + // private: + guint active : 2, button_visible : 2; + gchar *stock_id; + GdkPixbuf *pixbuf; +} YGtkCellRendererSideButton; + +typedef struct YGtkCellRendererSideButtonClass +{ + GtkCellRendererTextClass parent_class; + + void (* toggled) (YGtkCellRendererSideButton *renderer, const gchar *path); +} YGtkCellRendererSideButtonClass; + +GtkCellRenderer *ygtk_cell_renderer_side_button_new (void); +GType ygtk_cell_renderer_side_button_get_type (void) G_GNUC_CONST; + +gboolean ygtk_cell_renderer_side_button_get_active (YGtkCellRendererSideButton *cell); + +G_END_DECLS +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,56 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkCellRendererText cell renderer */ +// check the header file for information about this cell renderer + +#include "ygtkcellrenderertext.h" +#include <gtk/gtk.h> +#include <string.h> + +G_DEFINE_TYPE (YGtkCellRendererText, ygtk_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT) + +static void ygtk_cell_renderer_text_init (YGtkCellRendererText *bcell) +{} + +gboolean filter_color_cb (PangoAttribute *attrb, gpointer data) +{ + PangoAttrType type = attrb->klass->type; + return type == PANGO_ATTR_FOREGROUND || type == PANGO_ATTR_BACKGROUND; +} + +static void ygtk_cell_renderer_text_render ( + GtkCellRenderer *cell, GdkDrawable *window, GtkWidget *widget, + GdkRectangle *background_area, GdkRectangle *cell_area, + GdkRectangle *expose_area, GtkCellRendererState flags) +{ + GtkCellRendererText *tcell = GTK_CELL_RENDERER_TEXT (cell); + PangoAttrList *old_extra_attrs = 0, *new_extra_attrs = 0; + if (flags & (GTK_CELL_RENDERER_SELECTED | GTK_CELL_RENDERER_INSENSITIVE)) { + old_extra_attrs = pango_attr_list_copy (tcell->extra_attrs); + new_extra_attrs = tcell->extra_attrs; + + PangoAttrList *t = pango_attr_list_filter (tcell->extra_attrs, + filter_color_cb, NULL); + pango_attr_list_unref (t); + } + + GTK_CELL_RENDERER_CLASS (ygtk_cell_renderer_text_parent_class)->render ( + cell, window, widget, background_area, cell_area, expose_area, flags); + + if (old_extra_attrs) { + tcell->extra_attrs = old_extra_attrs; + pango_attr_list_unref (new_extra_attrs); + } +} + +GtkCellRenderer *ygtk_cell_renderer_text_new (void) +{ return g_object_new (YGTK_TYPE_CELL_RENDERER_TEXT, NULL); } + +static void ygtk_cell_renderer_text_class_init (YGtkCellRendererTextClass *class) +{ + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + cell_class->render = ygtk_cell_renderer_text_render; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkcellrenderertext.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,42 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkCellRendererText is a sub-class of GtkCellRendererText that + strips any markup in the cell when the row is selected. +*/ + +#ifndef YGTK_CELL_RENDERER_TEXT_H +#define YGTK_CELL_RENDERER_TEXT_H + +#include <gtk/gtkcellrenderertext.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_CELL_RENDERER_TEXT (ygtk_cell_renderer_text_get_type()) +#define YGTK_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_CELL_RENDERER_TEXT, YGtkCellRendererText)) +#define YGTK_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_CELL_RENDERER_TEXT, YGtkCellRendererTextClass)) +#define YGTK_IS_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_CELL_RENDERER_TEXT)) +#define YGTK_IS_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_CELL_RENDERER_TEXT)) +#define YGTK_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_CELL_TEXT, YGtkCellRendererTextClass)) + +typedef struct YGtkCellRendererText +{ + GtkCellRendererText parent; +} YGtkCellRendererText; + +typedef struct YGtkCellRendererTextClass +{ + GtkCellRendererTextClass parent_class; +} YGtkCellRendererTextClass; + +GtkCellRenderer *ygtk_cell_renderer_text_new (void); +GType ygtk_cell_renderer_text_get_type (void) G_GNUC_CONST; + +G_END_DECLS +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,1312 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgDetailView, selectable info-box */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGDialog.h" +#include "YGUtils.h" +#include "ygtkpkgdetailview.h" +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "YGPackageSelector.h" +#include "ygtkrichtext.h" +#include "ygtkpkgsearchentry.h" +#include "ygtkpkglistview.h" +#include <YStringTree.h> + +#define BROWSER_BIN "/usr/bin/firefox" +#define GNOME_OPEN_BIN "/usr/bin/gnome-open" + +static std::string onlyInstalledMsg() { + std::string s ("<i>"); + s += _("Information only available for installed packages."); + s += "</i>"; + return s; +} + +static const char *keywordOpenTag = "<keyword>", *keywordCloseTag = "</keyword>"; + +struct BusyOp { + BusyOp() { + YGDialog::currentDialog()->busyCursor(); + while (g_main_context_iteration (NULL, FALSE)) ; + } + ~BusyOp() { + YGDialog::currentDialog()->normalCursor(); + } +}; + +struct DetailWidget { + virtual ~DetailWidget() {} + virtual GtkWidget *getWidget() = 0; + virtual void refreshList (Ypp::List list) = 0; + virtual void setList (Ypp::List list) = 0; +}; + +struct DetailName : public DetailWidget { + GtkWidget *hbox, *icon, *text; + + DetailName() + { + text = ygtk_rich_text_new(); + icon = gtk_image_new(); + + hbox = gtk_hbox_new (FALSE, 2); + gtk_box_pack_start (GTK_BOX (hbox), text, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); + } + + virtual GtkWidget *getWidget() + { return hbox; } + + virtual void refreshList (Ypp::List list) + { + std::string str; + str.reserve (1024); + if (list.size() > 0) { + Ypp::ListProps props (list); + str = "<p bgcolor=""; + str += props.toModify() ? "#f4f4b7" : "#ededed"; + str += ""><font color="#000000">"; + if (list.size() == 1) { + Ypp::Selectable &sel = list.get (0); + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + str += sel.summary() + " - " + "<b>" + sel.name() + "</b>"; + else + str += "<b>" + sel.name() + "</b> - " + sel.summary(); + } + else { + str += "<b>"; + str += _("Several selected"); + str += "</b>"; + } + str += "</font></p>"; + } + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), str.c_str()); + } + + virtual void setList (Ypp::List list) + { + gtk_widget_hide (icon); + if (list.size() == 1) { + Ypp::Selectable &sel = list.get (0); + + gtk_image_clear (GTK_IMAGE (icon)); + GtkIconTheme *icons = gtk_icon_theme_get_default(); + GdkPixbuf *pixbuf = gtk_icon_theme_load_icon (icons, + sel.name().c_str(), 32, GtkIconLookupFlags (0), NULL); + if (pixbuf) { + gtk_image_set_from_pixbuf (GTK_IMAGE (icon), pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + gtk_widget_show (icon); + } + } + + refreshList (list); + } +}; + +struct DetailDescription : public DetailWidget { + GtkWidget *text, *popup; + std::string link_str; + + DetailDescription() : popup (NULL) + { + text = ygtk_rich_text_new(); + g_signal_connect (G_OBJECT (text), "link-clicked", + G_CALLBACK (link_clicked_cb), this); + } + + virtual ~DetailDescription() + { if (popup) gtk_widget_destroy (popup); } + + virtual GtkWidget *getWidget() + { return text; } + + virtual void refreshList (Ypp::List list) + { setList (list); } + + virtual void setList (Ypp::List list) + { + std::string str; + str.reserve (2048); + if (list.size() == 1) { + Ypp::Selectable &sel = list.get (0); + str = sel.description (true); + + YGtkPkgSearchEntry *search = YGPackageSelector::get()->getSearchEntry(); + if (search->getAttribute() == Ypp::PoolQuery::DESCRIPTION) { + std::list std::string keywords; + keywords = YGPackageSelector::get()->getSearchEntry()->getText(); + highlightMarkup (str, keywords, keywordOpenTag, keywordCloseTag, + strlen (keywordOpenTag), strlen (keywordCloseTag)); + } + + if (sel.type() == Ypp::Selectable::PACKAGE) { + Ypp::Package pkg (sel); + std::string url (pkg.url()); + if (!url.empty()) { + str += "<p><i>"; str += _("Web site:"); + str += "</i> <a href=""; str += url; str += "">"; + str += url; str += "</a></p>"; + } + if (pkg.isCandidatePatch()) { + Ypp::Selectable _patch = pkg.getCandidatePatch(); + Ypp::Patch patch (_patch); + str += "<p><i>"; str += _("Patch issued:"); + str += "</i> "; str += _patch.summary(); + str += " <b>("; str += Ypp::Patch::prioritySummary (patch.priority()); + str += ")</b>"; str += "</p>"; + + } + } + } + else { + if (list.size() > 0) { + str += "<ul>"; + for (int i = 0; i < list.size(); i++) + str += "<li>" + list.get (i).name() + "</li>"; + str += "</ul>"; + } + } + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), str.c_str()); + } + + static void link_clicked_cb (YGtkRichText *text, const gchar *link, DetailDescription *pThis) + { + if (!pThis->popup) { + GtkWidget *menu = pThis->popup = gtk_menu_new(); + gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (text), NULL); + + GtkWidget *item; + if (g_file_test (BROWSER_BIN, G_FILE_TEST_IS_EXECUTABLE)) { + std::string label (YGUtils::mapKBAccel ("&Open")); + if (getuid() == 0) { + const char *username = getenv ("USERNAME"); + if (!username || !(*username)) + username = "root"; + label += " ("; label += _("as"); + label += " "; label += username; label += ")"; + } + item = gtk_image_menu_item_new_with_mnemonic (label.c_str()); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), + gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU)); + gtk_widget_show (item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (open_link_cb), pThis); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + } + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL); + gtk_widget_show (item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (copy_link_cb), pThis); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + } + gtk_menu_popup (GTK_MENU (pThis->popup), NULL, NULL, NULL, NULL, + 0, gtk_get_current_event_time()); + pThis->link_str = link; + } + + static void open_link_cb (GtkMenuItem *item, DetailDescription *pThis) + { + const std::string &link = pThis->link_str; + std::string command; + command.reserve (256); + + const char *username = 0; + if (getuid() == 0) { + username = getenv ("USERNAME"); + if (username && !(*username)) + username = 0; + } + + if (username) { + command += "gnomesu -u "; + command += username; + command += " -c "" BROWSER_BIN " --new-window "; + command += link; + command += """; + } + else { + command += BROWSER_BIN " --new-window "; + command += link; + } + command += " &"; + (void) system (command.c_str()); + } + + static void copy_link_cb (GtkMenuItem *item, DetailDescription *pThis) + { + const std::string &link = pThis->link_str; + GtkClipboard *clipboard = + gtk_widget_get_clipboard (pThis->text, GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text (clipboard, link.c_str(), -1); + } +}; + +struct DetailExpander : public DetailWidget { + GtkWidget *expander; + Ypp::List list; + bool dirty; + + DetailExpander (const std::string &label, bool default_expanded) + : list (0), dirty (false) + { + std::string str = "<b>" + label + "</b>"; + expander = gtk_expander_new (str.c_str()); + gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE); + gtk_expander_set_expanded (GTK_EXPANDER (expander), default_expanded); + g_signal_connect_after (G_OBJECT (expander), "notify::expanded", + G_CALLBACK (expanded_cb), this); + } + + virtual GtkWidget *getWidget() { return expander; } + + void updateList() + { + if (dirty) + if (gtk_expander_get_expanded (GTK_EXPANDER (expander))) + if (visible()) { + showList (list); + dirty = false; + } + } + + bool visible() + { + if (onlySingleList()) + return list.size() == 1; + return list.size() > 0; + } + + virtual void setList (Ypp::List list) + { + this->list = list; + dirty = true; + visible() ? gtk_widget_show (expander) : gtk_widget_hide (expander); + updateList(); + } + + virtual void refreshList (Ypp::List list) + { + if (gtk_expander_get_expanded (GTK_EXPANDER (expander))) + showRefreshList (list); + else + dirty = true; + } + + static void expanded_cb (GObject *object, GParamSpec *param_spec, DetailExpander *pThis) + { pThis->updateList(); } + + void setChild (GtkWidget *child) + { gtk_container_add (GTK_CONTAINER (expander), child); } + + // use this method to only request data when/if necessary; so to speed up expander children + virtual void showList (Ypp::List list) = 0; + virtual void showRefreshList (Ypp::List list) = 0; + virtual bool onlySingleList() = 0; // show only when list.size()==1 +}; + +struct DetailsExpander : public DetailExpander { + GtkWidget *text; + + DetailsExpander() + : DetailExpander (_("Details"), true) + { + text = ygtk_rich_text_new(); + gtk_widget_set_size_request (text, 160, -1); + setChild (text); + } + + virtual bool onlySingleList() { return true; } + + virtual void showList (Ypp::List list) + { + Ypp::Selectable sel = list.get (0); + ZyppSelectable zsel = sel.zyppSel(); + ZyppResObject zobj = zsel->theObj(); + ZyppPackage zpkg = castZyppPackage (zobj); + + std::string str; + std::string b ("<i>"), _b ("</i> "), i (""), _i(""), br ("<br/>"); + str.reserve (2048); + str += b + _("Size:") + _b + i + zobj->installSize().asString() + _i; + str += br + b + _("License:") + _b + i + zpkg->license() + _i; + if (zsel->hasInstalledObj()) + str += br + b + ("Installed at:") + _b + i + + zsel->installedObj()->installtime().form ("%x") + _i; + if (zsel->hasCandidateObj()) + str += br + b + ("Latest build:") + _b + i + + zsel->candidateObj()->buildtime().form ("%x") + _i; + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), str.c_str()); + } + + virtual void showRefreshList (Ypp::List list) {} +}; + +typedef zypp::ResPoolProxy::const_iterator ZyppPoolIterator; +inline ZyppPoolIterator zyppSrcPkgBegin() +{ return zyppPool().byKindBeginzypp::SrcPackage(); } +inline ZyppPoolIterator zyppSrcPkgEnd() +{ return zyppPool().byKindEndzypp::SrcPackage(); } + +struct VersionExpander : public DetailExpander { + GtkWidget *versions_box; + std::list Ypp::Version versions; + + VersionExpander() + : DetailExpander (_("Versions"), false) + { + versions_box = gtk_vbox_new (FALSE, 2); + + // draw border + GtkWidget *frame = gtk_frame_new (NULL); + gtk_container_set_border_width (GTK_CONTAINER (frame), 2); + gtk_container_add (GTK_CONTAINER (frame), versions_box); + setChild (frame); + } + + GtkWidget *addVersion (Ypp::Selectable &sel, Ypp::Version version, GtkWidget *group) + { + GtkWidget *button = gtk_toggle_button_new(); + gtk_container_add (GTK_CONTAINER (button), gtk_image_new()); + + GtkWidget *label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 2); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + + g_object_set_data (G_OBJECT (button), "nb", GINT_TO_POINTER (versions.size())); + updateVersionWidget (hbox, sel, version); + g_signal_connect (G_OBJECT (button), "toggled", + G_CALLBACK (button_clicked_cb), this); + + GtkWidget *widget = hbox; + if ((versions.size() % 2) == 1) + g_signal_connect (G_OBJECT (widget), "expose-event", + G_CALLBACK (draw_gray_cb), NULL); + versions.push_back (version); + gtk_box_pack_start (GTK_BOX (versions_box), widget, FALSE, TRUE, 0); + return widget; + } + + void updateVersionLabel (GtkWidget *label, Ypp::Selectable &sel, Ypp::Version &version) + { + std::string repo; bool modified; + if (version.isInstalled()) { + repo = _("Installed"); + modified = sel.toRemove(); + } + else { + repo = version.repository().name(); + modified = sel.toInstall(); + } + modified = modified && version.toModify(); + + std::string number (version.number()), arch (version.arch()); + char *tooltip = g_strdup_printf ("%s <small>(%s)</small>\n<small>%s</small>", + number.c_str(), arch.c_str(), repo.c_str()); + number = YGUtils::truncate (number, 20, 0); + char *str = g_strdup_printf ("%s%s <small>(%s)</small>\n<small>%s</small>%s", + modified ? "<i>" : "", number.c_str(), arch.c_str(), repo.c_str(), + modified ? "</i>" : ""); + + gtk_label_set_markup (GTK_LABEL (label), str); + if (number.size() > 20) + gtk_widget_set_tooltip_markup (label, tooltip); + g_free (tooltip); g_free (str); + } + + void updateVersionButton (GtkWidget *button, Ypp::Selectable &sel, Ypp::Version &version) + { + const char *stock, *tooltip; + bool modified, can_modify = true; + if (version.isInstalled()) { + tooltip = _("Remove"); + stock = GTK_STOCK_DELETE; + can_modify = sel.canRemove(); + modified = sel.toRemove(); + } + else { + if (sel.hasInstalledVersion()) { + Ypp::Version installed = sel.installed(); + if (installed < version) { + tooltip = _("Upgrade"); + stock = GTK_STOCK_GO_UP; + } + else if (installed > version) { + tooltip = _("Downgrade"); + stock = GTK_STOCK_GO_DOWN; + } + else { + tooltip = _("Re-install"); + stock = GTK_STOCK_REFRESH; + } + } + else { + tooltip = _("Install"); + stock = GTK_STOCK_SAVE; + } + modified = sel.toInstall(); + } + modified = modified && version.toModify(); + if (modified) + tooltip = _("Undo"); + + g_signal_handlers_block_by_func (button, (gpointer) button_clicked_cb, this); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), modified); + g_signal_handlers_unblock_by_func (button, (gpointer) button_clicked_cb, this); + can_modify ? gtk_widget_show (button) : gtk_widget_hide (button); + gtk_widget_set_tooltip_text (button, tooltip); + gtk_image_set_from_stock (GTK_IMAGE (gtk_bin_get_child (GTK_BIN(button))), + stock, GTK_ICON_SIZE_BUTTON); + } + + void updateVersionWidget (GtkWidget *widget, Ypp::Selectable &sel, Ypp::Version &version) + { + GList *children = gtk_container_get_children (GTK_CONTAINER (widget)); + GtkWidget *label = (GtkWidget *) children->data; + GtkWidget *button = (GtkWidget *) children->next->data; + g_list_free (children); + + updateVersionLabel (label, sel, version); + updateVersionButton (button, sel, version); + } + + void updateMultiselectionButton (GtkWidget *button) + { + Ypp::ListProps props (list); + + const char *stock, *tooltip; + bool modified, hide = false; + if (props.isInstalled()) { + tooltip = _("Remove"); + stock = GTK_STOCK_DELETE; + } + else if (props.isNotInstalled()) { + tooltip = _("Install"); + stock = GTK_STOCK_SAVE; + } + else + hide = true; + modified = props.toModify(); + if (modified) + tooltip = _("Undo"); + + g_signal_handlers_block_by_func (button, (gpointer) button_clicked_cb, this); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), modified); + g_signal_handlers_unblock_by_func (button, (gpointer) button_clicked_cb, this); + hide ? gtk_widget_hide (button) : gtk_widget_show (button); + gtk_widget_set_tooltip_text (button, tooltip); + gtk_image_set_from_stock (GTK_IMAGE (gtk_bin_get_child (GTK_BIN(button))), + stock, GTK_ICON_SIZE_BUTTON); + } + + void clearVersions() + { + GList *children = gtk_container_get_children (GTK_CONTAINER (versions_box)); + for (GList *i = children; i; i = i->next) + gtk_container_remove (GTK_CONTAINER (versions_box), (GtkWidget *) i->data); + g_list_free (children); + versions.clear(); + } + + Ypp::Version &getVersion (GtkToggleButton *button) + { + int nb = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "nb")); + std::list Ypp::Version::iterator it = versions.begin(); + for (int i = 0; i < nb; i++) it++; + return *it; + } + + static void button_clicked_cb (GtkToggleButton *button, VersionExpander *pThis) + { + BusyOp op; + if (gtk_toggle_button_get_active (button)) { // was un-pressed + if (pThis->list.size() > 1) { + Ypp::ListProps props (pThis->list); + if (props.hasUpgrade()) + pThis->list.install(); + else if (props.isInstalled()) + pThis->list.remove(); + else if (props.isNotInstalled()) + pThis->list.install(); + else if (props.toModify()) + pThis->list.undo(); + } + else { + Ypp::Selectable sel = pThis->list.get (0); + Ypp::Version &version = pThis->getVersion (button); + + if (sel.toModify()) + sel.undo(); + if (version.isInstalled()) + sel.remove(); + else { + sel.setCandidate (version); + sel.install(); + } + } + } + else // button was pressed + pThis->list.undo(); + } + + virtual bool onlySingleList() { return false; } + + virtual void showList (Ypp::List list) + { + Ypp::ListProps props (list); + clearVersions(); + + if (list.size() == 1) { + Ypp::Selectable sel = list.get (0); + GtkWidget *radio = 0; + for (int i = 0; i < sel.totalVersions(); i++) + radio = addVersion (sel, sel.version (i), radio); + } + else { + GtkWidget *button = gtk_toggle_button_new(); + gtk_container_add (GTK_CONTAINER (button), gtk_image_new()); + + GtkWidget *label = gtk_label_new (_("Several selected")); + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (versions_box), hbox, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + + updateMultiselectionButton (button); + g_signal_connect (G_OBJECT (button), "toggled", + G_CALLBACK (button_clicked_cb), this); + } + + gtk_widget_set_sensitive (versions_box, !props.isLocked()); + } + + virtual void showRefreshList (Ypp::List list) + { + // update radios + GList *children = gtk_container_get_children (GTK_CONTAINER (versions_box)); + if (list.size() == 1) { + Ypp::Selectable &sel = list.get (0); + std::list Ypp::Version::iterator it = versions.begin(); + for (GList *i = children; i; i = i->next, it++) + updateVersionWidget ((GtkWidget *) i->data, sel, *it); + } + else { + GtkWidget *hbox = (GtkWidget *) children->data; + GList *l = gtk_container_get_children (GTK_CONTAINER (hbox)); + GtkWidget *button = (GtkWidget *) l->next->data; + g_list_free (l); + updateMultiselectionButton (button); + } + g_list_free (children); + } + + static gboolean draw_gray_cb (GtkWidget *widget, GdkEventExpose *event) + { + GtkAllocation *alloc = &widget->allocation; + int x = alloc->x, y = alloc->y, w = alloc->width, h = alloc->height; + + cairo_t *cr = gdk_cairo_create (widget->window); + cairo_rectangle (cr, x, y, w, h); + // use alpha to cope with styles who might not have a white background + cairo_set_source_rgba (cr, 0, 0, 0, .060); + cairo_fill (cr); + cairo_destroy (cr); + return FALSE; + } +}; + +struct SupportExpander : public DetailWidget { + // not derived from DetailExpander as this one behaves a little differently + GtkWidget *text, *expander; + + SupportExpander() + { + text = ygtk_rich_text_new(); + expander = gtk_expander_new (""); + gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE); + gtk_container_add (GTK_CONTAINER (expander), text); + } + + virtual GtkWidget *getWidget() { return expander; } + + virtual void refreshList (Ypp::List list) {} + + virtual void setList (Ypp::List list) + { + if (list.size() == 1) { + gtk_widget_show (expander); + + Ypp::Selectable sel = list.get (0); + Ypp::Package pkg (sel); + + std::string label ("<b>" + std::string (_("Supportability:")) + "</b> "); + label += Ypp::Package::supportSummary (pkg.support()); + gtk_expander_set_label (GTK_EXPANDER (expander), label.c_str()); + + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), + Ypp::Package::supportDescription (pkg.support()).c_str()); + } + else + gtk_widget_hide (expander); + } +}; + +struct DependenciesExpander : public DetailExpander { + GtkWidget *vbox; + + DependenciesExpander() + : DetailExpander (_("Dependencies"), false) + { + vbox = gtk_vbox_new (FALSE, 6); + setChild (vbox); + } + + void clear() + { + GList *children = gtk_container_get_children (GTK_CONTAINER (vbox)); + for (GList *i = children; i; i = i->next) + gtk_container_remove (GTK_CONTAINER (vbox), (GtkWidget *) i->data); + g_list_free (children); + } + + void addLine (const std::string &col1, const std::string &col2, + const std::string &col3, int dep) + { +// if (dep >= 0) +// gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new(), FALSE, TRUE, 0); + + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + GtkWidget *col; + col = ygtk_rich_text_new(); + gtk_widget_set_size_request (col, 100, -1); + ygtk_rich_text_set_text (YGTK_RICH_TEXT (col), + ("<i>" + col1 + "</i>").c_str()); + gtk_box_pack_start (GTK_BOX (hbox), col, FALSE, TRUE, 0); + // by settings both the two following "col" text widgets with the same + // fixed width, and expanded=True, we ensure all rows have the same size + col = ygtk_rich_text_new(); + gtk_widget_set_size_request (col, 80, -1); + ygtk_rich_text_set_text (YGTK_RICH_TEXT (col), col2.c_str()); + if (dep == 0) + g_signal_connect (G_OBJECT (col), "link-clicked", + G_CALLBACK (requires_link_cb), NULL); + else if (dep == 2) + g_signal_connect (G_OBJECT (col), "link-clicked", + G_CALLBACK (provides_link_cb), NULL); + gtk_box_pack_start (GTK_BOX (hbox), col, TRUE, TRUE, 0); + col = ygtk_rich_text_new(); + gtk_widget_set_size_request (col, 80, -1); + ygtk_rich_text_set_text (YGTK_RICH_TEXT (col), col3.c_str()); + if (dep == 0) + g_signal_connect (G_OBJECT (col), "link-clicked", + G_CALLBACK (requires_link_cb), NULL); + else if (dep == 2) + g_signal_connect (G_OBJECT (col), "link-clicked", + G_CALLBACK (provides_link_cb), NULL); + gtk_box_pack_start (GTK_BOX (hbox), col, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + } + + static void requires_link_cb (GtkWidget *widget, const gchar *link) + { YGPackageSelector::get()->searchFor (Ypp::PoolQuery::REQUIRES, link); } + + static void provides_link_cb (GtkWidget *widget, const gchar *link) + { YGPackageSelector::get()->searchFor (Ypp::PoolQuery::PROVIDES, link); } + + + virtual bool onlySingleList() { return true; } + + virtual void showList (Ypp::List list) + { + Ypp::Selectable sel = list.get (0); + + clear(); + std::string i ("<i>"), _i ("</i>"), b ("<b>"), _b ("</b>"); + std::string installed_str (i + b + _("Installed Version:") + _b + _i); + std::string candidate_str (i + b + _("Available Version:") + _b + _i); + bool hasInstalled = sel.hasInstalledVersion(), + hasCandidate = sel.hasCandidateVersion(); + if (hasInstalled) + installed_str += "<br>" + sel.installed().number(); + if (hasCandidate) + candidate_str += "<br>" + sel.candidate().number(); + addLine ("", installed_str, candidate_str, -1); + for (int dep = 0; dep < VersionDependencies::total(); dep++) { + std::string inst, cand; + if (hasInstalled) + inst = VersionDependencies (sel.installed()).getText (dep); + if (hasCandidate) + cand = VersionDependencies (sel.candidate()).getText (dep); + if (!inst.empty() || !cand.empty()) + addLine (VersionDependencies::getLabel (dep), inst, cand, dep); + } + gtk_widget_show_all (vbox); + } + + virtual void showRefreshList (Ypp::List list) + { showList (list); } + + struct VersionDependencies { + VersionDependencies (Ypp::Version version) + : m_version (version) {} + + static int total() + { return ((int) zypp::Dep::SUPPLEMENTS_e) + 1; } + + static const char *getLabel (int dep) + { + + switch ((zypp::Dep::for_use_in_switch) dep) { + case zypp::Dep::PROVIDES_e: return "Provides:"; + case zypp::Dep::PREREQUIRES_e: return "Pre-requires:"; + case zypp::Dep::REQUIRES_e: return "Requires:"; + case zypp::Dep::CONFLICTS_e: return "Conflicts:"; + case zypp::Dep::OBSOLETES_e: return "Obsoletes:"; + case zypp::Dep::RECOMMENDS_e: return "Recommends:"; + case zypp::Dep::SUGGESTS_e: return "Suggests:"; + case zypp::Dep::ENHANCES_e: return "Enhances:"; + case zypp::Dep::SUPPLEMENTS_e: return "Supplements:"; + } + return 0; + } + + // zypp::Dep(zypp::Dep::for_use_in_switch) construtor is private + static zypp::Dep getDep (int dep) + { + switch (dep) { + case 0: return zypp::Dep::PROVIDES; + case 1: return zypp::Dep::PREREQUIRES; + case 2: return zypp::Dep::REQUIRES; + case 3: return zypp::Dep::CONFLICTS; + case 4: return zypp::Dep::OBSOLETES; + case 5: return zypp::Dep::RECOMMENDS; + case 6: return zypp::Dep::SUGGESTS; + case 7: return zypp::Dep::ENHANCES; + case 8: return zypp::Dep::SUPPLEMENTS; + } + return zypp::Dep::PROVIDES; + } + + + std::string getText (int dep) + { + std::string keyword; + YGtkPkgSearchEntry *search = YGPackageSelector::get()->getSearchEntry(); + if ((dep == 0 && search->getAttribute() == Ypp::PoolQuery::PROVIDES) || + (dep == 2 && search->getAttribute() == Ypp::PoolQuery::REQUIRES)) + keyword = search->getTextStr(); + + ZyppResObject zobj = m_version.zyppObj(); + zypp::Capabilities caps = zobj->dep (getDep (dep)); + std::string ret; + ret.reserve (4092); + for (zypp::Capabilities::const_iterator it = caps.begin(); + it != caps.end(); it++) { + if (!ret.empty()) + ret += ", "; + + std::string str (it->asString()); + bool highlight = (str == keyword); + + + if (dep == 0 || dep == 2) { + std::string::size_type i; + i = MIN (str.find (' '), str.find ('(')); + + std::string name (str, 0, i); + ret += "<a href="" + name + "">" + YGUtils::escapeMarkup (name) + "</a>"; + if (i != std::string::npos) { + std::string rest (str, i); + ret += YGUtils::escapeMarkup (rest); + } + } + else + ret += YGUtils::escapeMarkup (str); + + if (highlight) + ret += keywordCloseTag; + } + return ret; + } + + Ypp::Version m_version; + }; +}; + +struct FilelistExpander : public DetailExpander { + GtkWidget *text; + + FilelistExpander() + : DetailExpander (_("File list"), false) + { + text = ygtk_rich_text_new(); + g_signal_connect (G_OBJECT (text), "link-clicked", + G_CALLBACK (dirname_pressed_cb), this); + setChild (text); + } + + virtual bool onlySingleList() { return true; } + + virtual void showList (Ypp::List list) + { + Ypp::Selectable sel = list.get (0); + if (sel.isInstalled()) + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), filelist (sel).c_str()); + else + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), onlyInstalledMsg().c_str()); + } + + virtual void showRefreshList (Ypp::List list) {} + + static void dirname_pressed_cb (GtkWidget *text, const gchar *link, FilelistExpander *pThis) + { + gchar *cmd = g_strdup_printf (GNOME_OPEN_BIN " %s &", link); + (void) system (cmd); + g_free (cmd); + } + + static std::string filelist (Ypp::Selectable &sel) + { + std::string text; + text.reserve (4096); + + ZyppResObject zobj = sel.installed().zyppObj(); + ZyppPackage zpkg = castZyppPackage (zobj); + if (zpkg) { + YStringTree tree (""); + + zypp::Package::FileList files = zpkg->filelist(); + for (zypp::Package::FileList::iterator it = files.begin(); + it != files.end(); it++) + tree.addBranch (*it, '/'); + + struct inner { + static std::string path (YStringTreeItem *item) + { + std::string str; + str.reserve (1024); + for (YStringTreeItem *i = item->parent(); i->parent(); i = i->parent()) { + std::string val (i->value().orig()); + std::string prefix ("/"); + str = prefix + val + str; + } + return str; + } + + static void traverse (YStringTreeItem *item, std::string &text) + { + if (!item) return; + + // traverse nodes first + bool has_leaves = false; + for (YStringTreeItem *i = item; i; i = i->next()) { + YStringTreeItem *child = i->firstChild(); + if (child) + traverse (child, text); + else + has_leaves = true; + } + + // traverse leaves now + if (has_leaves) { + std::string dirname (path (item)); + text += "<a href="" + dirname + "">" + dirname + "</a>"; + text += "<blockquote>"; + + std::string keyword; + YGtkPkgSearchEntry *search = YGPackageSelector::get()->getSearchEntry(); + if (search->getAttribute() == Ypp::PoolQuery::FILELIST) + keyword = search->getTextStr(); + if (!keyword.empty() && keyword[0] == '/') { + std::string _dirname (keyword, 0, keyword.find_last_of ('/')); + if (dirname == _dirname) + keyword = std::string (keyword, _dirname.size()+1); + else + keyword.clear(); + } + + for (YStringTreeItem *i = item; i; i = i->next()) + if (!i->firstChild()) { + if (i != item) // not first + text += ", "; + std::string basename (i->value().orig()); + + bool highlight = (basename == keyword); + if (highlight) + text += keywordOpenTag; + text += basename; + if (highlight) + text += keywordCloseTag; + } + + text += "</blockquote>"; + } + } + }; + + inner::traverse (tree.root(), text); + } + return text; + } +}; + +struct ChangelogExpander : public DetailExpander { + GtkWidget *text; + + ChangelogExpander() + : DetailExpander (_("Changelog"), false) + { + text = ygtk_rich_text_new(); + setChild (text); + } + + virtual bool onlySingleList() { return true; } + + virtual void showList (Ypp::List list) + { + Ypp::Selectable sel = list.get (0); + if (sel.isInstalled()) + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), changelog (sel).c_str()); + else + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), onlyInstalledMsg().c_str()); + } + + virtual void showRefreshList (Ypp::List list) {} + + static std::string changelog (Ypp::Selectable &sel) + { + std::string text; + text.reserve (32768); + text += "<p><i>"; + text += _("Changelog applies only to the installed version."); + text += "</i></p>"; + ZyppResObject zobj = sel.installed().zyppObj(); + ZyppPackage zpkg = castZyppPackage (zobj); + if (zpkg) { + const std::list zypp::ChangelogEntry &logs = zpkg->changelog(); + for (std::list zypp::ChangelogEntry::const_iterator it = logs.begin(); + it != logs.end(); it++) { + std::string date (it->date().form ("%d %B %Y")), author (it->author()), + changes (it->text()); + author = YGUtils::escapeMarkup (author); + changes = YGUtils::escapeMarkup (changes); + YGUtils::replace (changes, "\n", 1, "<br>"); + if (author.compare (0, 2, "- ", 2) == 0) // zypp returns a lot of author strings as + author.erase (0, 2); // "- author". wtf? + text += "<i>" + date + " (" + author + "):</i><br><blockquote>" + changes + "</blockquote>"; + } + } + return text; + } +}; + +struct AuthorsExpander : public DetailExpander { + GtkWidget *text; + + AuthorsExpander() + : DetailExpander (_("Authors"), false) + { + text = ygtk_rich_text_new(); + setChild (text); + } + + virtual bool onlySingleList() { return true; } + + virtual void showList (Ypp::List list) + { + Ypp::Selectable sel = list.get (0); + std::string str (authors (sel)); + if (str.empty()) { + str = "<i>"; str += _("Attribute not-specified."); str += "</i>"; + } + ygtk_rich_text_set_text (YGTK_RICH_TEXT (text), str.c_str()); + } + + virtual void showRefreshList (Ypp::List list) {} + + static std::string authors (Ypp::Selectable &sel) + { + std::string text; + ZyppResObject object = sel.zyppSel()->theObj(); + ZyppPackage package = castZyppPackage (object); + if (package) { + std::string packager = package->packager(), vendor = package->vendor(), authors; + packager = YGUtils::escapeMarkup (packager); + vendor = YGUtils::escapeMarkup (vendor); + const std::list std::string &authorsList = package->authors(); + for (std::list std::string::const_iterator it = authorsList.begin(); + it != authorsList.end(); it++) { + std::string author (*it); + author = YGUtils::escapeMarkup (author); + if (!authors.empty()) + authors += "<br>"; + authors += author; + } + // look for Authors line in description + std::string description = package->description(); + std::string::size_type i = description.find ("\nAuthors:\n-----", 0); + if (i != std::string::npos) { + i += sizeof ("\nAuthors:\n----"); + i = description.find ("\n", i); + if (i != std::string::npos) + i++; + } + else { + i = description.find ("\nAuthor:", 0); + if (i == std::string::npos) { + i = description.find ("\nAuthors:", 0); + if (i != std::string::npos) + i++; + } + if (i != std::string::npos) + i += sizeof ("\nAuthor:"); + } + if (i != std::string::npos) { + std::string str = description.substr (i); + str = YGUtils::escapeMarkup (str); + YGUtils::replace (str, "\n", 1, "<br>"); + authors += str; + } + + if (!authors.empty()) { + text = "<i>"; text += _("Developed by:"); text += "</i>"; + text += ("<blockquote>" + authors) + "</blockquote>"; + if (!packager.empty() || !vendor.empty()) { + text += "<i>"; text += _("Packaged by:"); text += "</i>"; + text += "<blockquote>"; + if (!packager.empty()) text += packager + " "; + if (!vendor.empty()) text += "(" + vendor + ")"; + text += "</blockquote>"; + } + } + } + return text; + } +}; + +struct ContentsExpander : public DetailExpander { + YGtkPkgListView *view; + + ContentsExpander() + : DetailExpander (_("Applies to"), false) + { + view = new YGtkPkgListView (true, Ypp::List::NAME_SORT, false, false); + view->addCheckColumn (INSTALLED_CHECK_PROP); + view->addTextColumn (_("Name"), NAME_SUMMARY_PROP, true, -1); + view->addTextColumn (_("Version"), VERSION_PROP, true, 125); + view->addTextColumn (_("Size"), SIZE_PROP, false, 85); + view->addTextColumn (_("Repository"), REPOSITORY_PROP, false, 180); + view->addTextColumn (_("Supportability"), SUPPORT_PROP, false, 120); + gtk_widget_set_size_request (view->getWidget(), -1, 150); + + setChild (view->getWidget()); + } + + virtual ~ContentsExpander() + { delete view; } + + virtual bool onlySingleList() { return true; } + + virtual void showList (Ypp::List list) + { + Ypp::Selectable sel = list.get (0); + Ypp::Collection col (sel); + + Ypp::PoolQuery query (Ypp::Selectable::PACKAGE); + query.addCriteria (new Ypp::FromCollectionMatch (col)); + view->setQuery (query); + } + + virtual void showRefreshList (Ypp::List list) {} +}; + +struct YGtkPkgDetailView::Impl : public Ypp::SelListener +{ +std::list <DetailWidget *> m_widgets; +GtkWidget *m_scroll; +Ypp::List m_list; + + Impl() : m_list (0) + { + DetailWidget *widget; + + GtkWidget *side_vbox = gtk_vbox_new (FALSE, 0); + + if (!YGPackageSelector::get()->onlineUpdateMode()) { + widget = new DetailsExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (side_vbox), widget->getWidget(), FALSE, TRUE, 0); + } + widget = new VersionExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (side_vbox), widget->getWidget(), FALSE, TRUE, 0); + + GtkWidget *main_vbox = gtk_vbox_new (FALSE, 0); + + widget = new DetailName(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + + widget = new DetailDescription(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + GtkWidget *detail_description = widget->getWidget(); + + if (YGPackageSelector::get()->onlineUpdateMode()) { + widget = new ContentsExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + } + else { + widget = new FilelistExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + widget = new ChangelogExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + widget = new AuthorsExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + widget = new DependenciesExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + widget = new SupportExpander(); + m_widgets.push_back (widget); + gtk_box_pack_start (GTK_BOX (main_vbox), widget->getWidget(), FALSE, TRUE, 0); + } + + GtkWidget *hbox = gtk_hbox_new (FALSE, 2); + gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), side_vbox, FALSE, TRUE, 0); + + GtkWidget *child = gtk_event_box_new(); + gtk_container_add (GTK_CONTAINER (child), hbox); + + GdkColor *color = &detail_description->style->base [GTK_STATE_NORMAL]; + gtk_widget_modify_bg (child, GTK_STATE_NORMAL, color); + + m_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (m_scroll), child); + + gtk_widget_show_all (m_scroll); + g_signal_connect (G_OBJECT (m_scroll), "realize", + G_CALLBACK (scroll_realize_cb), this); + Ypp::addSelListener (this); + } + + ~Impl() + { + for (std::list <DetailWidget *>::iterator it = m_widgets.begin(); + it != m_widgets.end(); it++) + delete *it; + Ypp::removeSelListener (this); + } + + void refreshList (Ypp::List list) + { + for (std::list <DetailWidget *>::iterator it = m_widgets.begin(); + it != m_widgets.end(); it++) + (*it)->refreshList (list); + } + + void setList (Ypp::List list) + { + for (std::list <DetailWidget *>::iterator it = m_widgets.begin(); + it != m_widgets.end(); it++) + (*it)->setList (list); + + m_list = list; + scrollTop(); + } + + virtual void selectableModified() + { refreshList (m_list); } + + void scrollTop() + { + GtkScrolledWindow *scroll = GTK_SCROLLED_WINDOW (m_scroll); + GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment (scroll); + YGUtils::scrollWidget (vadj, true); + } + + // fix cursor keys support + static void move_cursor_cb (GtkTextView *view, GtkMovementStep step, gint count, + gboolean extend_selection, GtkWidget *scroll) + { + GtkScrolledWindow *_scroll = GTK_SCROLLED_WINDOW (scroll); + GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment (_scroll); + int height = scroll->allocation.height; + gdouble increment; + switch (step) { + case GTK_MOVEMENT_DISPLAY_LINES: + increment = height / 10.0; + break; + case GTK_MOVEMENT_PAGES: + increment = height * 0.9; + break; + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + increment = adj->upper - adj->lower; + break; + default: + increment = 0.0; + break; + } + + gdouble value = adj->value + (count * increment); + value = MIN (value, adj->upper - adj->page_size); + value = MAX (value, adj->lower); + if (value != adj->value) + gtk_adjustment_set_value (adj, value); + } + + static void fix_keys (GtkWidget *widget, void *_scroll) + { + GtkWidget *scroll = (GtkWidget *) _scroll; + if (GTK_IS_TEXT_VIEW (widget)) + g_signal_connect (G_OBJECT (widget), "move-cursor", + G_CALLBACK (move_cursor_cb), scroll); + else if (GTK_IS_CONTAINER (widget)) + gtk_container_foreach (GTK_CONTAINER (widget), fix_keys, _scroll); + } + + static void scroll_realize_cb (GtkWidget *widget, Impl *pThis) + { fix_keys (widget, widget); } +}; + +YGtkPkgDetailView::YGtkPkgDetailView() +: impl (new Impl()) {} + +YGtkPkgDetailView::~YGtkPkgDetailView() +{ delete impl; } + +GtkWidget *YGtkPkgDetailView::getWidget() +{ return impl->m_scroll; } + +void YGtkPkgDetailView::setSelectable (Ypp::Selectable &sel) +{ + Ypp::List list (1); + list.append (sel); + impl->setList (list); +} + +void YGtkPkgDetailView::setList (Ypp::List list) +{ impl->setList (list); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgdetailview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,29 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Text box information on a given selectable. +*/ + +#ifndef YGTK_PKG_DETAIL_VIEW_H +#define YGTK_PKG_DETAIL_VIEW_H + +#include <gtk/gtkwidget.h> +#include "yzyppwrapper.h" + +struct YGtkPkgDetailView +{ + YGtkPkgDetailView(); + ~YGtkPkgDetailView(); + + GtkWidget *getWidget(); + + void setSelectable (Ypp::Selectable &sel); + void setList (Ypp::List list); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,759 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgFilterView, several zypp attribute query criteria implementations */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "YGUI.h" +#include "config.h" +#include "YGUtils.h" +#include "ygtkpkgfilterview.h" +#include "ygtkpkglistview.h" +#include "YGPackageSelector.h" +#include <gtk/gtk.h> +#include "ygtktreeview.h" +#include "ygtkcellrenderertext.h" + +// List store abstract + +struct YGtkPkgFilterModel::Impl { + GtkTreeModel *filter, *model; + + int convertlIterToStoreRow (GtkTreeIter *iter) + { + GtkTreeIter _iter; + gtk_tree_model_filter_convert_iter_to_child_iter ( + GTK_TREE_MODEL_FILTER (filter), &_iter, iter); + + GtkTreePath *path = gtk_tree_model_get_path (model, &_iter); + int row = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + return row; + } +}; + +YGtkPkgFilterModel::YGtkPkgFilterModel() +: impl (new Impl()) +{ + GtkListStore *store = gtk_list_store_new (TOTAL_COLUMNS, GDK_TYPE_PIXBUF, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT, + G_TYPE_POINTER); + impl->model = GTK_TREE_MODEL (store); + + impl->filter = gtk_tree_model_filter_new (impl->model, NULL); + gtk_tree_model_filter_set_visible_column ( + GTK_TREE_MODEL_FILTER (impl->filter), VISIBLE_COLUMN); + g_object_unref (G_OBJECT (impl->model)); +} + +YGtkPkgFilterModel::~YGtkPkgFilterModel() +{ g_object_unref (G_OBJECT (impl->filter)); } + +GtkTreeModel *YGtkPkgFilterModel::getModel() +{ return impl->filter; } + +struct UpdateData { + YGtkPkgFilterModel *pThis; + Ypp::List *list; + UpdateData (YGtkPkgFilterModel *pThis, Ypp::List *list) + : pThis (pThis), list (list) {} +}; + +static gboolean update_list_cb (GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, gpointer _data) +{ + gchar *text; + UpdateData *data = (UpdateData *) _data; + gpointer mdata; + gtk_tree_model_get (model, iter, YGtkPkgFilterModel::TEXT_COLUMN, &text, + YGtkPkgFilterModel::DATA_COLUMN, &mdata, -1); + + bool separator = !(*text); + g_free (text); + if (separator) return FALSE; + + int row = gtk_tree_path_get_indices (path)[0]; + if (row == 0 && data->pThis->firstRowIsAll()) + data->pThis->setRowCount (0, data->list->size()); + else + data->pThis->updateRow (*data->list, row, mdata); + return FALSE; +} + +void YGtkPkgFilterModel::updateList (Ypp::List list) +{ + if (!begsUpdate()) return; + UpdateData data (this, &list); + gtk_tree_model_foreach (impl->model, update_list_cb, &data); +} + +bool YGtkPkgFilterModel::writeQuery (Ypp::PoolQuery &query, GtkTreeIter *iter) +{ + gpointer data; + gtk_tree_model_get (impl->filter, iter, DATA_COLUMN, &data, -1); + + int row = impl->convertlIterToStoreRow (iter); + if (row == 0 && firstRowIsAll()) + return false; + return writeRowQuery (query, row, data); +} + +GtkWidget *YGtkPkgFilterModel::createToolbox (GtkTreeIter *iter) +{ + int row = impl->convertlIterToStoreRow (iter); + return createToolboxRow (row); +} + +void YGtkPkgFilterModel::addRow (const char *icon, const char *text, + bool enabled, gpointer data, bool defaultVisible) +{ + // we use cell-render-pixbuf's "pixbuf" rather than "icon-name" so we + // can use a fixed size + GdkPixbuf *pixbuf = 0; + if (icon) { + if (!strcmp (icon, "empty")) { + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); + gdk_pixbuf_fill (pixbuf, 0xffffff00); + } + else + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(), + icon, 32, GtkIconLookupFlags (0), NULL); + } + + int weight = PANGO_WEIGHT_NORMAL; + if (firstRowIsAll() && gtk_tree_model_iter_n_children (impl->model, NULL) == 0) + weight = PANGO_WEIGHT_BOLD; + + GtkTreeIter iter; + GtkListStore *store = GTK_LIST_STORE (impl->model); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, ICON_COLUMN, pixbuf, TEXT_COLUMN, text, + COUNT_NUMBER_COLUMN, "", ENABLED_COLUMN, enabled, VISIBLE_COLUMN, defaultVisible, + DATA_COLUMN, data, WEIGHT_COLUMN, weight, -1); + + if (pixbuf) g_object_unref (pixbuf); +} + +void YGtkPkgFilterModel::addSeparator() +{ addRow (NULL, "", true, NULL); } + +void YGtkPkgFilterModel::setRowCount (int row, int count) +{ + GtkListStore *store = GTK_LIST_STORE (impl->model); + GtkTreeIter iter; + gtk_tree_model_iter_nth_child (impl->model, &iter, NULL, row); + + gchar *str = g_strdup_printf ("%d", count); + gtk_list_store_set (store, &iter, COUNT_NUMBER_COLUMN, str, + VISIBLE_COLUMN, row == 0 || count > 0, -1); + g_free (str); +} + +// Status + +struct YGtkPkgStatusModel::Impl : public Ypp::SelListener { + Impl (YGtkPkgStatusModel *parent) + : parent (parent), list (0) + { Ypp::addSelListener (this); } + + ~Impl() + { Ypp::removeSelListener (this); } + + virtual void selectableModified() + { // make sure to update the "Modified" row on packages change + parent->updateRow (list, modifiedRow(), NULL); + } + + YGtkPkgStatusModel *parent; + + static int modifiedRow() + { return YGPackageSelector::get()->onlineUpdateMode() ? 2 : 5; } + + Ypp::List list; +}; + +static Ypp::StatusMatch::Status rowToStatus (int row) +{ + if (YGPackageSelector::get()->onlineUpdateMode()) + switch (row) { + case 0: return Ypp::StatusMatch::NOT_INSTALLED; + case 1: return Ypp::StatusMatch::IS_INSTALLED; + case 2: return Ypp::StatusMatch::TO_MODIFY; + } + else + switch (row) { + case 1: return Ypp::StatusMatch::NOT_INSTALLED; + case 2: return Ypp::StatusMatch::IS_INSTALLED; + case 3: return Ypp::StatusMatch::HAS_UPGRADE; + case 4: return Ypp::StatusMatch::IS_LOCKED; + case 5: return Ypp::StatusMatch::TO_MODIFY; + } + return (Ypp::StatusMatch::Status) 0; +} + +YGtkPkgStatusModel::YGtkPkgStatusModel() +: impl (new Impl (this)) +{ + if (YGPackageSelector::get()->onlineUpdateMode()) { + // Translators: this refers to the package status + addRow (NULL, _("Available"), true, 0); + // Translators: this refers to the package status + addRow (NULL, _("Installed"), true, 0); + // Translators: this refers to the package status + addRow (NULL, _("Modified"), true, 0, false); + } + else { + // Translators: "Any status" may be translated as "All statuses" (whichever's smaller) + addRow (NULL, _("All packages"), true, 0); + // Translators: this refers to the package status + addRow (NULL, _("Not installed"), true, 0); + addRow (NULL, _("Installed"), true, 0); + // Translators: refers to package status: may be translated as "Upgrade" + addRow (NULL, _("Upgrades"), true, 0, false); + // Translators: this refers to the package status + addRow (NULL, _("Locked"), true, 0, false); + addRow (NULL, _("Modified"), true, 0, false); + } +} + +YGtkPkgStatusModel::~YGtkPkgStatusModel() +{ delete impl; } + +bool YGtkPkgStatusModel::firstRowIsAll() +{ return !YGPackageSelector::get()->onlineUpdateMode(); } + +void YGtkPkgStatusModel::updateRow (Ypp::List list, int row, gpointer data) +{ + impl->list = list; + Ypp::StatusMatch match (rowToStatus (row)); + setRowCount (row, list.count (&match)); +} + +bool YGtkPkgStatusModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data) +{ + query.addCriteria (new Ypp::StatusMatch (rowToStatus (row))); + return true; +} + +static void upgrade_patches_clicked_cb (GtkButton *button, YGtkPkgStatusModel *pThis) +{ + Ypp::startTransactions(); + for (int i = 0; i < pThis->impl->list.size(); i++) { + Ypp::Selectable sel = pThis->impl->list.get (i); + Ypp::Package pkg (sel); + if (sel.hasUpgrade() && pkg.isCandidatePatch()) + sel.install(); + } + Ypp::finishTransactions(); +} + +static void upgrade_all_clicked_cb (GtkButton *button, YGtkPkgStatusModel *pThis) +{ + Ypp::startTransactions(); + for (int i = 0; i < pThis->impl->list.size(); i++) { + Ypp::Selectable sel = pThis->impl->list.get (i); + if (sel.hasUpgrade()) + sel.install(); + } + Ypp::finishTransactions(); +} + +struct PkgHasPatchMatch : public Ypp::Match { + virtual bool match (Ypp::Selectable &sel) + { + Ypp::Package pkg (sel); + return pkg.isCandidatePatch(); + } +}; + +GtkWidget *YGtkPkgStatusModel::createToolboxRow (int row) +{ // "Upgrade All" button + if (row == 3 && !YGPackageSelector::get()->onlineUpdateMode()) { + PkgHasPatchMatch match; + bool hasPatches = impl->list.count (&match); + + GtkWidget *hbox = gtk_hbox_new (FALSE, 6), *button, *icon; + + // Translators: this is shown next to the "Upgrade All" button + button = gtk_button_new_with_label (_("Only Upgrade Patches")); + gtk_widget_set_sensitive (button, hasPatches); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (upgrade_patches_clicked_cb), this); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + + button = gtk_button_new_with_label (_("Upgrade All")); + icon = gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (button), icon); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (upgrade_all_clicked_cb), this); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + + gtk_widget_show_all (hbox); + return hbox; + } + return NULL; +} + +// PackageKit-based Group + +struct PKGroup { + const char *name, *icon; + YPkgGroupEnum id; + + PKGroup (YPkgGroupEnum id) : id (id) { + name = zypp_tag_group_enum_to_localised_text (id); + icon = zypp_tag_enum_to_icon (id); + } + bool operator < (const PKGroup &other) const + { return strcmp (this->name, other.name) < 0; } +}; + +YGtkPkgPKGroupModel::YGtkPkgPKGroupModel() +{ + std::set <PKGroup> groups; + for (int i = 0; i < YPKG_GROUP_UNKNOWN; i++) + groups.insert (PKGroup ((YPkgGroupEnum) i)); + + addRow ("empty", _("All packages"), true, 0); + for (std::set <PKGroup>::const_iterator it = groups.begin(); + it != groups.end(); it++) + addRow (it->icon, it->name, true, GINT_TO_POINTER (((int)it->id)+1)); + + for (int i = YPKG_GROUP_UNKNOWN; i < YPKG_GROUP_TOTAL; i++) { +#if ZYPP_VERSION >= 6030004 + if (i == YPKG_GROUP_MULTIVERSION && zypp::sat::Pool::instance().multiversionEmpty()) +#else + if (i == YPKG_GROUP_MULTIVERSION) +#endif + continue; + if (i == YPKG_GROUP_ORPHANED) continue; + std::string name = zypp_tag_group_enum_to_localised_text ((YPkgGroupEnum) i); + const char *icon = zypp_tag_enum_to_icon ((YPkgGroupEnum) i); + if (i == YPKG_GROUP_RECENT) + // Translators: when "upload" isn't easy to translate, translate this as "7 days old" + name += std::string ("\n<small>") + _("(uploaded last 7 days)") + "</small>"; + addRow (icon, name.c_str(), true, GINT_TO_POINTER (i+1)); + if (i == YPKG_GROUP_UNKNOWN) + addSeparator(); + } +} + +void YGtkPkgPKGroupModel::updateRow (Ypp::List list, int row, gpointer data) +{ + YPkgGroupEnum id = (YPkgGroupEnum) (GPOINTER_TO_INT (data)-1); + Ypp::PKGroupMatch match (id); + setRowCount (row, list.count (&match)); +} + +bool YGtkPkgPKGroupModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data) +{ + YPkgGroupEnum id = (YPkgGroupEnum) (GPOINTER_TO_INT (data)-1); + query.addCriteria (new Ypp::PKGroupMatch (id)); + return true; +} + +// Repository + +struct YGtkPkgRepositoryModel::Impl { + // cannot store as point at the list-store + std::vector Ypp::Repository repos; + Ypp::Repository *selected; + + Impl() : selected (NULL) + { repos.reserve (zyppPool().knownRepositoriesSize()); } +}; + +YGtkPkgRepositoryModel::YGtkPkgRepositoryModel() +: impl (new Impl()) +{ + addRow (NULL, _("All repositories"), true, 0); + + for (zypp::ResPoolProxy::repository_iterator it = zyppPool().knownRepositoriesBegin(); + it != zyppPool().knownRepositoriesEnd(); it++) { + Ypp::Repository repo (*it); + const char *icon = getRepositoryStockIcon (repo); + std::string label (getRepositoryLabel (repo)); + addRow (icon, label.c_str(), true, GINT_TO_POINTER (1)); + impl->repos.push_back (repo); + } + + zypp::RepoManager manager; + std::list zypp::RepoInfo known_repos = manager.knownRepositories(); + for (std::list zypp::RepoInfo::const_iterator it = known_repos.begin(); + it != known_repos.end(); it++) { + Ypp::Repository repo (*it); + if (!repo.enabled()) { + const char *icon = getRepositoryStockIcon (repo); + std::string label (getRepositoryLabel (repo)); + addRow (icon, label.c_str(), false, GINT_TO_POINTER (0)); + } + } + + addRow (GTK_STOCK_MISSING_IMAGE, _("None"), true, GINT_TO_POINTER (2)); +} + +YGtkPkgRepositoryModel::~YGtkPkgRepositoryModel() +{ delete impl; } + +void YGtkPkgRepositoryModel::updateRow (Ypp::List list, int row, gpointer data) +{ + if (GPOINTER_TO_INT (data) == 1) { // normal repo + Ypp::Repository &repo = impl->repos[row-1]; + bool isSystem = repo.isSystem(); + int count = 0; + for (int i = 0; i < list.size(); i++) { + Ypp::Selectable sel = list.get (i); + if (isSystem) { + if (sel.isInstalled()) + count++; + } + else for (int i = 0; i < sel.totalVersions(); i++) { + Ypp::Version version = sel.version (i); + if (!version.isInstalled()) { + Ypp::Repository pkg_repo = version.repository(); + if (repo == pkg_repo) { + count++; + break; + } + } + } + } + setRowCount (row, count); + } + else if (GPOINTER_TO_INT (data) == 2) { // 'none' + Ypp::PKGroupMatch match (YPKG_GROUP_ORPHANED); + setRowCount (row, list.count (&match)); + } +} + +bool YGtkPkgRepositoryModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data) +{ + impl->selected = 0; + if (GPOINTER_TO_INT (data) == 1) { + Ypp::Repository &repo = impl->repos[row-1]; + query.addRepository (repo); + impl->selected = &repo; + } + else if (GPOINTER_TO_INT (data) == 2) + query.addCriteria (new Ypp::PKGroupMatch (YPKG_GROUP_ORPHANED)); + return true; +} + +static void sync_toolbox_buttons (Ypp::Repository *repo, GtkWidget *box) +{ + GtkWidget *button, *undo; + GList *children = gtk_container_get_children (GTK_CONTAINER (box)); + button = (GtkWidget *) g_list_nth_data (children, 0); + undo = (GtkWidget *) g_list_nth_data (children, 1); + g_list_free (children); + + if (zypp::getZYpp()->resolver()->upgradingRepo (repo->zyppRepo())) { + gtk_widget_set_sensitive (button, FALSE); + gtk_widget_show (undo); + } + else { + gtk_widget_set_sensitive (button, TRUE); + gtk_widget_hide (undo); + } +} + +static void switch_repo_status (Ypp::Repository *repo) +{ + ZyppRepository zrepo = repo->zyppRepo(); + if (zypp::getZYpp()->resolver()->upgradingRepo (zrepo)) + zypp::getZYpp()->resolver()->removeUpgradeRepo (zrepo); + else + zypp::getZYpp()->resolver()->addUpgradeRepo (zrepo); +} + +static void switch_clicked_cb (GtkButton *button, YGtkPkgRepositoryModel *pThis) +{ + Ypp::Repository *repo = pThis->impl->selected; + switch_repo_status (repo); + if (!Ypp::runSolver()) // on solver cancel -- switch back + switch_repo_status (repo); + sync_toolbox_buttons (repo, gtk_widget_get_parent (GTK_WIDGET (button))); +} + +GtkWidget *YGtkPkgRepositoryModel::createToolboxRow (int row) +{ + if (row > 0 && impl->selected) { + if (impl->selected->isOutdated()) { + GtkWidget *label = gtk_label_new ( + _("Repository not refreshed in a long time.")); + gtk_misc_set_alignment (GTK_MISC (label), 0, .5); + YGUtils::setWidgetFont (label, + PANGO_STYLE_ITALIC, PANGO_WEIGHT_NORMAL, PANGO_SCALE_MEDIUM); + GtkWidget *icon = gtk_image_new_from_stock ( + GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON); + GtkWidget *hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show_all (hbox); + return hbox; + } + else if (!impl->selected->isSystem()) { + GtkWidget *hbox = gtk_hbox_new (FALSE, 6), *button; + button = gtk_button_new_from_stock (GTK_STOCK_UNDO); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (switch_clicked_cb), this); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, TRUE, 0); + button = gtk_button_new_with_label ( + _("Switch installed packages to the versions in this repository")); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (switch_clicked_cb), this); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + sync_toolbox_buttons (impl->selected, hbox); + return hbox; + } + } + return NULL; +} + +static void edit_clicked_cb (GtkWidget *button, YGtkPkgRepositoryModel *pThis) +{ YGPackageSelector::get()->showRepoManager(); } + +GtkWidget *YGtkPkgRepositoryModel::createInternalToolbox() +{ + if (!YGPackageSelector::get()->repoMgrEnabled()) return NULL; + GtkWidget *button = gtk_button_new_with_label (_("Edit Repositories")); + GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (button), icon); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (edit_clicked_cb), this); + + GtkWidget *align = gtk_alignment_new (0, 0, 0, 1); + gtk_container_add (GTK_CONTAINER (align), button); + return align; +} + +GtkWidget *YGtkPkgRepositoryModel::createInternalPopup() +{ + if (!YGPackageSelector::get()->repoMgrEnabled()) return NULL; + GtkWidget *menu = gtk_menu_new(); + GtkWidget *item = gtk_image_menu_item_new_with_mnemonic (_("Edit Repositories")); + GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (edit_clicked_cb), this); + return menu; +} + +// Support + +YGtkPkgSupportModel::YGtkPkgSupportModel() +{ + addRow (NULL, _("All packages"), true, 0); + for (int i = 0; i < Ypp::Package::supportTotal(); i++) + addRow (NULL, Ypp::Package::supportSummary (i).c_str(), true, 0, false); +} + +void YGtkPkgSupportModel::updateRow (Ypp::List list, int row, gpointer data) +{ + Ypp::SupportMatch match (row-1); + setRowCount (row, list.count (&match)); +} + +bool YGtkPkgSupportModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data) +{ + query.addCriteria (new Ypp::SupportMatch (row-1)); + return true; +} + +// Priority + +YGtkPkgPriorityModel::YGtkPkgPriorityModel() +{ + // Translators: "Any priority" may be translated as "All priorities" (whichever's smaller) + addRow (NULL, _("All patches"), true, 0); + for (int i = 0; i < Ypp::Patch::priorityTotal(); i++) + addRow (NULL, Ypp::Patch::prioritySummary (i), true, 0, false); +} + +void YGtkPkgPriorityModel::updateRow (Ypp::List list, int row, gpointer data) +{ + Ypp::PriorityMatch match (row-1); + setRowCount (row, list.count (&match)); +} + +bool YGtkPkgPriorityModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data) +{ + query.addCriteria (new Ypp::PriorityMatch (row-1)); + return true; +} + +// View widget + +struct YGtkPkgFilterView::Impl { + GtkWidget *box, *scroll, *view; + YGtkPkgFilterModel *model; + + Impl (YGtkPkgFilterModel *model) : model (model) {} + ~Impl() { delete model; } +}; + +static gboolean tree_selection_possible_cb (GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, gboolean is_selected, gpointer data) +{ + GtkTreeIter iter; + gtk_tree_model_get_iter (model, &iter, path); + int col = GPOINTER_TO_INT (data); + gboolean val; + gtk_tree_model_get (model, &iter, col, &val, -1); + return val; +} + +static void selection_changed_cb (GtkTreeSelection *selection, YGtkPkgFilterView *pThis) +{ + if (gtk_tree_selection_get_selected (selection, NULL, NULL)) + pThis->notify(); +} + +static void right_click_cb (YGtkTreeView *view, gboolean outreach, YGtkPkgFilterView *pThis) +{ + GtkWidget *menu = pThis->impl->model->createInternalPopup(); + if (menu) ygtk_tree_view_popup_menu (view, menu); +} + +YGtkPkgFilterView::YGtkPkgFilterView (YGtkPkgFilterModel *model) +: YGtkPkgQueryWidget(), impl (new Impl (model)) +{ + bool updates = model->begsUpdate(); + impl->view = ygtk_tree_view_new (NULL); + gtk_tree_view_set_model (GTK_TREE_VIEW (impl->view), model->getModel()); + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + gtk_tree_view_set_headers_visible (view, FALSE); + gtk_tree_view_set_search_column (view, YGtkPkgFilterModel::TEXT_COLUMN); + if (updates) + gtk_tree_view_set_tooltip_column (view, YGtkPkgFilterModel::TEXT_COLUMN); + gtk_tree_view_set_enable_tree_lines (view, TRUE); + gtk_tree_view_set_row_separator_func (view, YGUtils::empty_row_is_separator_cb, + GINT_TO_POINTER (YGtkPkgFilterModel::TEXT_COLUMN), NULL); + gtk_tree_view_expand_all (view); + g_signal_connect (G_OBJECT (view), "right-click", + G_CALLBACK (right_click_cb), this); + + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + if (model->hasIconCol()) { + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, "pixbuf", YGtkPkgFilterModel::ICON_COLUMN, + "sensitive", YGtkPkgFilterModel::ENABLED_COLUMN, NULL); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column); + } + renderer = ygtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ( + NULL, renderer, "markup", YGtkPkgFilterModel::TEXT_COLUMN, + "sensitive", YGtkPkgFilterModel::ENABLED_COLUMN, + "weight", YGtkPkgFilterModel::WEIGHT_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_set_expand (column, TRUE); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ( + NULL, renderer, "text", YGtkPkgFilterModel::COUNT_NUMBER_COLUMN, + "sensitive", YGtkPkgFilterModel::ENABLED_COLUMN, + "weight", YGtkPkgFilterModel::WEIGHT_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "xalign", 1.0, + "scale", PANGO_SCALE_SMALL, "foreground", "#8c8c8c", NULL); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + gtk_tree_selection_set_select_function (selection, tree_selection_possible_cb, + GINT_TO_POINTER (YGtkPkgFilterModel::ENABLED_COLUMN), NULL); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_changed_cb), this); + clearSelection(); + + impl->scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (impl->scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (impl->scroll), impl->view); + + GtkWidget *toolbox = model->createInternalToolbox(); + if (toolbox) { + impl->box = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (impl->box), impl->scroll, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (impl->box), toolbox, FALSE, TRUE, 0); + } + else + impl->box = impl->scroll; + gtk_widget_show_all (impl->box); +} + +YGtkPkgFilterView::~YGtkPkgFilterView() +{ delete impl; } + +GtkWidget *YGtkPkgFilterView::getWidget() +{ return impl->box; } + +bool YGtkPkgFilterView::begsUpdate() +{ return impl->model->begsUpdate(); } + +void YGtkPkgFilterView::updateList (Ypp::List list) +{ + impl->model->updateList (list); + + // always ensure some row is selected + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + GtkTreeModel *model; + if (!gtk_tree_selection_get_selected (selection, &model, NULL)) { + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first (model, &iter)) { + g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this); + gtk_tree_selection_select_iter (selection, &iter); + g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this); + } + } +} + +void YGtkPkgFilterView::clearSelection() +{ select (0); /* select 1st row, so some row is selected */ } + +bool YGtkPkgFilterView::writeQuery (Ypp::PoolQuery &query) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + return impl->model->writeQuery (query, &iter); + return false; +} + +GtkWidget *YGtkPkgFilterView::createToolbox() +{ + GtkTreeIter iter; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + return impl->model->createToolbox (&iter); + return NULL; +} + +void YGtkPkgFilterView::select (int row) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this); + GtkTreeIter iter; + if (row >= 0) { + gtk_tree_model_iter_nth_child (impl->model->getModel(), &iter, NULL, row); + gtk_tree_selection_select_iter (selection, &iter); + } + else + gtk_tree_selection_unselect_all (selection); + g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgfilterview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,140 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Several user interfaces for zypp query attributes. + + Create a List-model and pass it to FilterView in order to + use it. Example: + QueryWidget *query_widget = new YGtkPkgFilterView (new YGtkPkgStatusModel()); + GtkWidget *gtk_widget = query_widget->getWidget(); + + One can very easily re-implement YGtkPkgFilterView to use the GtkComboBox. +*/ + +#ifndef YGTK_PKG_FILTER_VIEW_H +#define YGTK_PKG_FILTER_VIEW_H + +#include "ygtkpkgquerywidget.h" +#include <gtk/gtktreestore.h> + +struct YGtkPkgFilterModel // abstract +{ + enum Column { ICON_COLUMN, TEXT_COLUMN, COUNT_NUMBER_COLUMN, + VISIBLE_COLUMN, ENABLED_COLUMN, WEIGHT_COLUMN, DATA_COLUMN, TOTAL_COLUMNS }; + + YGtkPkgFilterModel(); + virtual ~YGtkPkgFilterModel(); + GtkTreeModel *getModel(); + + virtual void updateList (Ypp::List list); + virtual bool writeQuery (Ypp::PoolQuery &query, GtkTreeIter *iter); + virtual GtkWidget *createToolbox (GtkTreeIter *iter); + + virtual bool hasIconCol() = 0; + virtual bool firstRowIsAll() = 0; + + virtual bool begsUpdate() = 0; + virtual void updateRow (Ypp::List list, int row, gpointer data) = 0; + virtual bool writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data) = 0; + + virtual GtkWidget *createToolboxRow (int selectedRow) { return NULL; } + virtual GtkWidget *createInternalToolbox() { return NULL; } + virtual GtkWidget *createInternalPopup() { return NULL; } + + void addRow (const char *icon, const char *text, bool enabled, gpointer data, bool defaultVisible = true); + void addSeparator(); + void setRowCount (int row, int count); + + struct Impl; + Impl *impl; +}; + +// implementations + +struct YGtkPkgStatusModel : public YGtkPkgFilterModel +{ + YGtkPkgStatusModel(); + virtual ~YGtkPkgStatusModel(); + virtual bool hasIconCol() { return false; } + virtual bool firstRowIsAll(); + virtual bool begsUpdate() { return true; } + virtual void updateRow (Ypp::List list, int row, gpointer data); + virtual bool writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data); + virtual GtkWidget *createToolboxRow (int selectedRow); + + struct Impl; + Impl *impl; +}; + +struct YGtkPkgPKGroupModel : public YGtkPkgFilterModel +{ + YGtkPkgPKGroupModel(); + virtual bool hasIconCol() { return true; } + virtual bool firstRowIsAll() { return true; } + virtual bool begsUpdate() { return true; } + virtual void updateRow (Ypp::List list, int row, gpointer data); + virtual bool writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data); +}; + +struct YGtkPkgRepositoryModel : public YGtkPkgFilterModel +{ + YGtkPkgRepositoryModel(); + virtual ~YGtkPkgRepositoryModel(); + virtual bool hasIconCol() { return true; } + virtual bool firstRowIsAll() { return true; } + virtual bool begsUpdate() { return true; } + virtual void updateRow (Ypp::List list, int row, gpointer data); + virtual bool writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data); + virtual GtkWidget *createToolboxRow (int selectedRow); + virtual GtkWidget *createInternalToolbox(); + virtual GtkWidget *createInternalPopup(); + + struct Impl; + Impl *impl; +}; + +struct YGtkPkgSupportModel : public YGtkPkgFilterModel +{ + YGtkPkgSupportModel(); + virtual bool hasIconCol() { return false; } + virtual bool firstRowIsAll() { return true; } + virtual bool begsUpdate() { return true; } + virtual void updateRow (Ypp::List list, int row, gpointer data); + virtual bool writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data); +}; + +struct YGtkPkgPriorityModel : public YGtkPkgFilterModel +{ + YGtkPkgPriorityModel(); + virtual bool hasIconCol() { return false; } + virtual bool firstRowIsAll() { return true; } + virtual bool begsUpdate() { return true; } + virtual void updateRow (Ypp::List list, int row, gpointer data); + virtual bool writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data); +}; + +// widget + +struct YGtkPkgFilterView : public YGtkPkgQueryWidget +{ + YGtkPkgFilterView (YGtkPkgFilterModel *model); + virtual ~YGtkPkgFilterView(); + virtual GtkWidget *getWidget(); + + virtual bool begsUpdate(); + virtual void updateList (Ypp::List list); + + virtual void clearSelection(); + virtual bool writeQuery (Ypp::PoolQuery &query); + + virtual GtkWidget *createToolbox(); + + void select (int row); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,670 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgHistoryDialog, dialog */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "ygtkpkghistorydialog.h" +#include "YGDialog.h" +#include "YGUtils.h" +#include "YGPackageSelector.h" +#include "ygtkpkglistview.h" +#include <gtk/gtk.h> +#include "ygtktreeview.h" +#include "ygtkcellrenderertext.h" + +#include <zypp/parser/HistoryLogReader.h> +#define FILENAME "/var/log/zypp/history" + +static std::string reqbyTreatment (const std::string &reqby) +{ + if (reqby.empty()) + return _("automatic"); + if (reqby.compare (0, 4, "root", 4) == 0) { + std::string str (_("user:")); + str += " root"; + return str; + } + return reqby; +} + +struct Handler +{ + virtual void date (const std::string &str, bool first) = 0; + virtual void item (const std::string &action, const std::string &name, + const std::string &description, const std::string &repositoryName, + const std::string &repositoryUrl, const std::string &reqby, bool autoReq) = 0; +}; + +struct LogListHandler +{ + GtkListStore *store; + + enum Column { ICON_COLUMN, NAME_COLUMN, VERSION_URL_COLUMN, REPOSITORY_COLUMN, + REQBY_COLUMN, REPOSITORY_ICON_COLUMN, REPOSITORY_URL_COLUMN, SHORTCUT_COLUMN, + COLOR_COLUMN, XPAD_COLUMN, TOTAL_COLUMNS }; + + LogListHandler() + { + store = gtk_list_store_new (TOTAL_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); + } + + ~LogListHandler() + { g_object_unref (G_OBJECT (store)); } + + GtkTreeModel *getModel() + { return GTK_TREE_MODEL (store); } + + void addRow (GtkTreeIter *iter) + { + gtk_list_store_append (store, iter); + gtk_list_store_set (store, iter, ICON_COLUMN, NULL, NAME_COLUMN, NULL, + VERSION_URL_COLUMN, NULL, REPOSITORY_COLUMN, NULL, REQBY_COLUMN, NULL, + REPOSITORY_ICON_COLUMN, NULL, REPOSITORY_URL_COLUMN, NULL, + SHORTCUT_COLUMN, NULL, COLOR_COLUMN, NULL, XPAD_COLUMN, 0, -1); + } + + int date (const std::string &str, bool first) + { + GtkTreeIter iter; + if (!first) addRow (&iter); // white space + std::string _date = std::string ("<b>\u26ab ") + str + "</b>"; + addRow (&iter); + gtk_list_store_set (store, &iter, NAME_COLUMN, _date.c_str(), -1); + + GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); + int row = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + return row; + } + + void item (const std::string &action, const std::string &name, + const std::string &description, const std::string &repositoryName, + const std::string &repositoryUrl, const std::string &reqby, bool autoReq) + { + GtkTreeIter iter; + const char *icon = 0; + std::string shortcut (name); + if (action == _("install")) + icon = GTK_STOCK_ADD; + else if (action == _("upgrade")) + icon = GTK_STOCK_GO_UP; + else if (action == _("remove")) + icon = GTK_STOCK_REMOVE; + else if (action == _("downgrade")) + icon = GTK_STOCK_GO_DOWN; + else if (action == _("re-install")) + icon = GTK_STOCK_REFRESH; + else { + icon = getRepositoryStockIcon (name); + shortcut = "_repo"; + } + + std::string _name; + _name.reserve (action.size() + name.size() + 64); + _name = "<b>"; + _name += action + "</b> " + name; + int xpad = 0; // autoReq ? 25 : 0; + + const char *repo_icon = 0, *color = 0; + bool is_patch = false; + if (action == _("upgrade") && !repositoryUrl.empty()) { + // if 'upgrade' and from '*update*' server then mark as patch + repo_icon = getRepositoryStockIcon (repositoryUrl); + if (repositoryUrl.find ("update") != std::string::npos) { + //color = "red"; + std::string tag; + tag.reserve (64); + tag = "<small><span color="#999999">"; tag += _("patch"); tag += "</span></small>"; + _name += " "; _name += tag; + is_patch = true; + } + } + if (autoReq && !is_patch) { // dependency + std::string tag; + tag.reserve (64); + tag = "<small><span color="#999999">"; tag += _("auto"); tag += "</span></small>"; + _name += " "; _name += tag; + } + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, ICON_COLUMN, icon, NAME_COLUMN, _name.c_str(), + VERSION_URL_COLUMN, description.c_str(), REPOSITORY_COLUMN, repositoryName.c_str(), + REPOSITORY_ICON_COLUMN, repo_icon, REPOSITORY_URL_COLUMN, repositoryUrl.c_str(), + REQBY_COLUMN, reqby.c_str(), SHORTCUT_COLUMN, shortcut.c_str(), + COLOR_COLUMN, color, XPAD_COLUMN, xpad, -1); + } +}; + +struct DateListHandler +{ + GtkListStore *store; + + enum Column { TEXT_COLUMN, LOG_ROW_COLUMN, TOTAL_COLUMNS }; + + DateListHandler() + { store = gtk_list_store_new (TOTAL_COLUMNS, G_TYPE_STRING, G_TYPE_INT); } + + ~DateListHandler() + { g_object_unref (G_OBJECT (store)); } + + GtkTreeModel *getModel() + { return GTK_TREE_MODEL (store); } + + void date (const std::string &str, bool first, int log_row) + { + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, TEXT_COLUMN, str.c_str(), + LOG_ROW_COLUMN, log_row, -1); + } +}; + +struct ListHandler : public Handler +{ + LogListHandler *log_handler; + DateListHandler *date_handler; + + ListHandler() + { + log_handler = new LogListHandler(); + date_handler = new DateListHandler(); + } + + ~ListHandler() + { delete log_handler; delete date_handler; } + + virtual void date (const std::string &str, bool first) + { + int row = log_handler->date (str, first); + date_handler->date (str, first, row); + } + + virtual void item (const std::string &a, const std::string &n, + const std::string &d, const std::string &rn, + const std::string &ru, const std::string &rb, bool ar) + { log_handler->item (a, n, d, rn, ru, rb, ar); } +}; + +struct FileHandler : public Handler +{ + FILE *file; + + FileHandler (const char *filename) + { file = fopen (filename, "w"); } + + ~FileHandler() + { fclose (file); } + + void addSpace() + { (void) fwrite ("\n", sizeof (char), 1, file); } + + virtual void date (const std::string &str, bool first) + { + if (!first) + addSpace(); + (void) fwrite (str.c_str(), sizeof (char), str.size(), file); + addSpace(); addSpace(); + } + + virtual void item (const std::string &action, const std::string &name, + const std::string &description, const std::string &repositoryName, + const std::string &repositoryUrl, const std::string &reqby, bool autoReq) + { + std::string str; + str.reserve (action.size() + name.size() + description.size() + 4); + str = std::string ("\t") + action + " " + name + " " + description + "\n"; + (void) fwrite (str.c_str(), sizeof (char), str.size(), file); + } +}; + +struct ZyppHistoryParser +{ + Handler *handler; + std::string _date; + std::map <std::string, zypp::Edition> installed; + + ZyppHistoryParser (Handler *handler) + : handler (handler) + { + zypp::parser::HistoryLogReader parser (FILENAME, boost::ref (*this)); + try { + parser.readAll(); + } + catch (const zypp::Exception &ex) { + yuiWarning () << "Error: Could not load log file" << FILENAME << ": " + << ex.asUserHistory() << std::endl; + } + } + + bool operator() (const zypp::HistoryItem::Ptr &item) + { + std::string date (item->date.form ("%d %B %Y")); + if (_date != date) { + handler->date (date, _date.empty()); + _date = date; + } + + std::string action, name, descrpt, repoName, repoUrl, reqby, t; + bool autoreq = false; + switch (item->action.toEnum()) { + case zypp::HistoryActionID::NONE_e: + break; + case zypp::HistoryActionID::INSTALL_e: { + zypp::HistoryItemInstall *_item = + static_cast <zypp::HistoryItemInstall *> (item.get()); + name = _item->name; + descrpt = _item->edition.version(); + Ypp::getRepositoryFromAlias (_item->repoalias, repoName, repoUrl); + reqby = _item->reqby; autoreq = reqby.empty(); + reqby = reqbyTreatment (reqby); + zypp::Edition edition = _item->edition; + std::map <std::string, zypp::Edition>::iterator it; + it = installed.find (name); + if (it == installed.end()) + action = _("install"); + else { + zypp::Edition prev_edition = it->second; + if (edition > prev_edition) + action = _("upgrade"); + else if (edition < prev_edition) + action = _("downgrade"); + else // (edition == prev_edition) + action = _("re-install"); + } + installed[name] = edition; + break; + } + case zypp::HistoryActionID::REMOVE_e: { + zypp::HistoryItemRemove *_item = + static_cast <zypp::HistoryItemRemove *> (item.get()); + action = _("remove"); + name = _item->name; + descrpt = _item->edition.version(); + reqby = _item->reqby; autoreq = reqby.empty(); + reqby = reqbyTreatment (reqby); + std::map <std::string, zypp::Edition>::iterator it; + it = installed.find (name); + if (it != installed.end()) + installed.erase (it); + break; + } + case zypp::HistoryActionID::REPO_ADD_e: { + zypp::HistoryItemRepoAdd *_item = + static_cast <zypp::HistoryItemRepoAdd *> (item.get()); + action = _("add repository"); + Ypp::getRepositoryFromAlias (_item->alias, name, t); + descrpt = _item->url.asString(); + break; + } + case zypp::HistoryActionID::REPO_REMOVE_e: { + zypp::HistoryItemRepoRemove *_item = + static_cast <zypp::HistoryItemRepoRemove *> (item.get()); + action = _("remove repository"); + name = _item->alias; + break; + } + case zypp::HistoryActionID::REPO_CHANGE_ALIAS_e: { + zypp::HistoryItemRepoAliasChange *_item = + static_cast <zypp::HistoryItemRepoAliasChange *> (item.get()); + action = _("change repository alias"); + name = _item->oldalias + " -> " + _item->newalias; + break; + } + case zypp::HistoryActionID::REPO_CHANGE_URL_e: { + zypp::HistoryItemRepoUrlChange *_item = + static_cast <zypp::HistoryItemRepoUrlChange *> (item.get()); + action = _("change repository url"); + Ypp::getRepositoryFromAlias (_item->alias, name, t); + descrpt = _item->newurl.asString(); + break; + } + } + if (!action.empty()) + handler->item (action, name, descrpt, repoName, repoUrl, reqby, autoreq); + return true; + } +}; + +static void goto_clicked (GtkTreeView *log_view) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection (log_view); + GtkTreeModel *model; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + char *shortcut; + gtk_tree_model_get (model, &iter, LogListHandler::SHORTCUT_COLUMN, &shortcut, -1); + if (!strcmp (shortcut, "_repo")) + YGPackageSelector::get()->showRepoManager(); + else + YGPackageSelector::get()->searchFor (Ypp::PoolQuery::NAME, shortcut); + g_free (shortcut); + + GtkWidget *dialog = gtk_widget_get_toplevel (GTK_WIDGET (log_view)); + gtk_widget_hide (dialog); + } +} + +static void log_selection_changed_cb (GtkTreeSelection *selection, GtkDialog *dialog) +{ + bool selected = gtk_tree_selection_count_selected_rows (selection) > 0; + gtk_dialog_set_response_sensitive (dialog, 1, selected); +} + +static void log_row_activated_cb (GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *column) +{ goto_clicked (view); } + +static gboolean log_can_select_cb (GtkTreeSelection *selection, GtkTreeModel *model, + GtkTreePath *path, gboolean path_currently_selected, gpointer data) +{ + GtkTreeIter iter; + gtk_tree_model_get_iter (model, &iter, path); + gchar *shortcut; + gtk_tree_model_get (model, &iter, LogListHandler::SHORTCUT_COLUMN, &shortcut, -1); + bool can_select = shortcut != NULL; + if (shortcut) g_free (shortcut); + return can_select; +} + +static void date_selection_changed_cb (GtkTreeSelection *selection, GtkTreeView *log_view) +{ + GtkTreeModel *model; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + int log_row; + gtk_tree_model_get (model, &iter, 1, &log_row, -1); + + GtkTreePath *path = gtk_tree_path_new_from_indices (log_row, -1); + gtk_tree_view_scroll_to_cell (log_view, path, NULL, TRUE, 0, 0); + gtk_tree_path_free (path); + } +} + +static void save_to_file (GtkWindow *parent) +{ + GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save to"), parent, + GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + FileHandler handler (filename); + ZyppHistoryParser parser (&handler); + g_free (filename); + } + gtk_widget_destroy (dialog); +} + +static void response_cb (GtkDialog *dialog, gint response, GtkTreeView *log_view) +{ + switch (response) { + case 1: goto_clicked (log_view); break; + case 2: save_to_file (GTK_WINDOW (dialog)); break; + default: gtk_widget_hide (GTK_WIDGET (dialog)); break; + } +} + +static gboolean query_tooltip_cb (GtkWidget *widget, gint x, gint y, + gboolean keyboard_mode, GtkTooltip *tooltip, YGtkPkgListView *pThis) +{ + GtkTreeView *view = GTK_TREE_VIEW (widget); + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + if (gtk_tree_view_get_tooltip_context (view, + &x, &y, keyboard_mode, &model, &path, &iter)) { + gtk_tree_view_set_tooltip_row (view, tooltip, path); + gtk_tree_path_free (path); + + GtkTreeViewColumn *column; + int bx, by; + gtk_tree_view_convert_widget_to_bin_window_coords ( + view, x, y, &bx, &by); + gtk_tree_view_get_path_at_pos ( + view, x, y, NULL, &column, NULL, NULL); + + std::string text; + text.reserve (254); + const char *icon = 0; + + if (column == ygtk_tree_view_get_column (YGTK_TREE_VIEW (view), 2)) { // repository + char *name, *url; + gtk_tree_model_get (model, &iter, LogListHandler::REPOSITORY_COLUMN, &name, + LogListHandler::REPOSITORY_URL_COLUMN, &url, -1); + if (name && *name) { + text = name; + if (url && *url) { + text += "\n<small>"; text += url; text += "</small>"; + icon = getRepositoryStockIcon (url); + } + if (url) + g_free (url); + } + if (name) + g_free (name); + } + + if (!text.empty()) { + gtk_tooltip_set_markup (tooltip, text.c_str()); + gtk_tooltip_set_icon_from_icon_name (tooltip, + icon, GTK_ICON_SIZE_BUTTON); + return TRUE; + } + } + return FALSE; +} + +static void goto_activate_cb (GtkMenuItem *item, GtkTreeView *view) +{ goto_clicked (view); } + +static void right_click_cb (YGtkTreeView *view, gboolean outreach) +{ + GtkWidget *menu = gtk_menu_new(); + if (!outreach) { + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + bool selected = gtk_tree_selection_get_selected (selection, NULL, NULL); + + GtkWidget *item = gtk_image_menu_item_new_from_stock (GTK_STOCK_JUMP_TO, NULL); + gtk_widget_set_sensitive (item, selected); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (goto_activate_cb), view); + } + + ygtk_tree_view_append_show_columns_item (view, menu); + ygtk_tree_view_popup_menu (view, menu); +} + +static gboolean read_logs_idle_cb (void *data) +{ + GtkWidget **views = (GtkWidget **) data; + GtkWidget *dialog = views[0]; + GtkTreeView *log_view = GTK_TREE_VIEW (views[1]); + GtkTreeView *date_view = GTK_TREE_VIEW (views[2]); + + ListHandler handler; + ZyppHistoryParser parser (&handler); + + gtk_tree_view_set_model (date_view, handler.date_handler->getModel()); + gtk_tree_view_set_model (log_view, handler.log_handler->getModel()); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (date_view); + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first ( + gtk_tree_view_get_model (date_view), &iter)) + gtk_tree_selection_select_iter (selection, &iter); + + gdk_window_set_cursor (dialog->window, NULL); + return FALSE; +} + +YGtkPkgHistoryDialog::YGtkPkgHistoryDialog() +{ + GtkCellRenderer *renderer, *pix_renderer; + GtkTreeViewColumn *column; + + GtkWidget *log_view = ygtk_tree_view_new (_("No entries.")); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (log_view), LogListHandler::SHORTCUT_COLUMN); + gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (log_view), TRUE); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (log_view), TRUE); + gtk_widget_set_has_tooltip (log_view, TRUE); + g_signal_connect (G_OBJECT (log_view), "query-tooltip", + G_CALLBACK (query_tooltip_cb), this); + g_signal_connect (G_OBJECT (log_view), "right-click", + G_CALLBACK (right_click_cb), this); + + bool reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title (column, _("Action")); + gtk_tree_view_column_set_spacing (column, 6); + + pix_renderer = gtk_cell_renderer_pixbuf_new(); + if (!reverse) + gtk_tree_view_column_pack_start (column, pix_renderer, FALSE); + + renderer = ygtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "markup", LogListHandler::NAME_COLUMN, "xpad", LogListHandler::XPAD_COLUMN, + "foreground", LogListHandler::COLOR_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL); + + if (reverse) + gtk_tree_view_column_pack_start (column, pix_renderer, FALSE); + gtk_tree_view_column_set_attributes (column, pix_renderer, + "icon-name", LogListHandler::ICON_COLUMN, NULL); + + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand (column, TRUE); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes (_("Version / URL"), renderer, + "text", LogListHandler::VERSION_URL_COLUMN, "xpad", LogListHandler::XPAD_COLUMN, + "foreground", LogListHandler::COLOR_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, 120); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title (column, _("Repository")); + gtk_tree_view_column_set_spacing (column, 2); + + pix_renderer = gtk_cell_renderer_pixbuf_new(); + if (!reverse) + gtk_tree_view_column_pack_start (column, pix_renderer, FALSE); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", LogListHandler::REPOSITORY_COLUMN, NULL); + + if (reverse) + gtk_tree_view_column_pack_start (column, pix_renderer, FALSE); + gtk_tree_view_column_set_attributes (column, pix_renderer, + "icon-name", LogListHandler::REPOSITORY_ICON_COLUMN, NULL); + + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, 140); + gtk_tree_view_column_set_visible (column, FALSE); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes (_("Requested by"), renderer, + "text", LogListHandler::REQBY_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, 100); + gtk_tree_view_column_set_visible (column, FALSE); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column); + + GtkWidget *log_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (log_scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (log_scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (log_scroll), log_view); + + GtkWidget *date_view = gtk_tree_view_new(); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (date_view), DateListHandler::TEXT_COLUMN); + gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (date_view), TRUE); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes (_("Date"), renderer, + "text", DateListHandler::TEXT_COLUMN, NULL); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_append_column (GTK_TREE_VIEW (date_view), column); + + GtkWidget *date_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (date_scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (date_scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (date_scroll), date_view); + + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE, + _("Show History (%s)"), FILENAME); + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_JUMP_TO, 1); + gtk_dialog_add_button (GTK_DIALOG (dialog), _("Save to File"), 2); + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), 1, FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 650, 600); + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (response_cb), log_view); + g_signal_connect (G_OBJECT (dialog), "delete-event", + G_CALLBACK (gtk_true), log_view); + + GtkWidget *hpaned = gtk_hpaned_new(); + gtk_paned_pack1 (GTK_PANED (hpaned), date_scroll, FALSE, FALSE); + gtk_paned_pack2 (GTK_PANED (hpaned), log_scroll, TRUE, FALSE); + YGUtils::setPaneRelPosition (hpaned, .30); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hpaned); + + gtk_widget_show_all (dialog); + m_dialog = dialog; + + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (log_view)); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (log_selection_changed_cb), dialog); + g_signal_connect (G_OBJECT (log_view), "row-activated", + G_CALLBACK (log_row_activated_cb), NULL); + gtk_tree_selection_set_select_function (selection, log_can_select_cb, NULL, NULL); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (date_view)); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (date_selection_changed_cb), log_view); + + GdkCursor *cursor = gdk_cursor_new (GDK_WATCH); + gdk_window_set_cursor (dialog->window, cursor); + gdk_cursor_unref (cursor); + + GtkWidget **views = g_new (GtkWidget *, 3); + views[0] = dialog; views[1] = log_view; views[2] = date_view; + g_idle_add_full (G_PRIORITY_LOW, read_logs_idle_cb, views, g_free); +} + +YGtkPkgHistoryDialog::~YGtkPkgHistoryDialog() +{ gtk_widget_destroy (m_dialog); } + +void YGtkPkgHistoryDialog::popup() +{ gtk_window_present (GTK_WINDOW (m_dialog)); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkghistorydialog.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,29 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Show /var/log/zypp/history. + Uses zypp::parser::HistoryLogReader. +*/ + +#ifndef YGTK_PKG_HISTORY_DIALOG_H +#define YGTK_PKG_HISTORY_DIALOG_H + +#include <gtk/gtkwidget.h> + +struct YGtkPkgHistoryDialog +{ + YGtkPkgHistoryDialog(); + ~YGtkPkgHistoryDialog(); + + void popup(); + +private: + GtkWidget *m_dialog; +}; + +// you do not want to use this class directly: +// use YGPackageSelector::get()->showHistoryDialog() + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,41 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* Textdomain "yast2-gtk" */ +/* YGtkPkgLanguageView, language list implementation */ +// check the header file for information about this widget + +#include "YGi18n.h" +#include "config.h" +#include "ygtkpkglanguageview.h" +#include <gtk/gtk.h> + +YGtkPkgLanguageView::YGtkPkgLanguageView() +: YGtkPkgListView (true, Ypp::List::NAME_SORT, false, true) +{ + addCheckColumn (INSTALLED_CHECK_PROP); + addTextColumn (NULL, NAME_SUMMARY_PROP, true, -1); + + Ypp::LangQuery query; + YGtkPkgListView::setList (query); + + YGtkPkgListView::setListener (this); +} + +GtkWidget *YGtkPkgLanguageView::getWidget() +{ return YGtkPkgListView::getWidget(); } + +bool YGtkPkgLanguageView::writeQuery (Ypp::PoolQuery &query) +{ + Ypp::List list (getSelected()); + if (list.size() > 0) { + Ypp::Collection col (list.get (0)); + query.addCriteria (new Ypp::FromCollectionMatch (col)); + return true; + } + return false; +} + +void YGtkPkgLanguageView::selectionChanged() +{ notify(); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglanguageview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,33 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Unlike the pattern view, this one is a simple widget derived from + the packages one. +*/ + +#ifndef YGTK_PKG_LANGUAGE_LIST_H +#define YGTK_PKG_LANGUAGE_LIST_H + +#include "ygtkpkglistview.h" +#include "ygtkpkgquerywidget.h" + +struct YGtkPkgLanguageView : public YGtkPkgListView, YGtkPkgListView::Listener, YGtkPkgQueryWidget +{ + YGtkPkgLanguageView(); + virtual GtkWidget *getWidget(); + + virtual bool begsUpdate() { return false; } + virtual void updateList (Ypp::List list) {} + + virtual void clearSelection() {} + virtual bool writeQuery (Ypp::PoolQuery &query); + + virtual void selectionChanged(); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,1094 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgListView, Zypp GtkTreeView implementation */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#define YUILogComponent "gtk" +#include "config.h" +#include "YGi18n.h" +#include "YGUtils.h" +#include "YGUI.h" +#include "YGPackageSelector.h" +#include "ygtkpkglistview.h" +#include "ygtktreeview.h" +#include "ygtktreemodel.h" +#include "ygtkcellrenderertext.h" +#include "ygtkcellrendererbutton.h" +#include "ygtkcellrenderersidebutton.h" +#include <gtk/gtk.h> +#include <string.h> + +#define GRAY_COLOR "#727272" + +//** Model + +enum ImplProperty { + // booleans + HAS_UPGRADE_PROP = TOTAL_PROPS, TO_UPGRADE_PROP, CAN_TOGGLE_INSTALL_PROP, + MANUAL_MODIFY_PROP, IS_LOCKED_PROP, + // integer + XPAD_PROP, + // string + FOREGROUND_PROP, BACKGROUND_PROP, REPOSITORY_STOCK_PROP, + ACTION_ICON_PROP, + // pointer + PTR_PROP, + TOTAL_IMPL_PROPS +}; + +static GType _columnType (int col) +{ + switch (col) { + case NAME_PROP: case ACTION_NAME_PROP: case NAME_SUMMARY_PROP: + case VERSION_PROP: case SINGLE_VERSION_PROP: case REPOSITORY_PROP: + case SUPPORT_PROP: case SIZE_PROP: case STATUS_ICON_PROP: + case ACTION_BUTTON_PROP: case ACTION_ICON_PROP: case FOREGROUND_PROP: + case BACKGROUND_PROP: case REPOSITORY_STOCK_PROP: + return G_TYPE_STRING; + case INSTALLED_CHECK_PROP: + case HAS_UPGRADE_PROP: case TO_UPGRADE_PROP: case CAN_TOGGLE_INSTALL_PROP: + case MANUAL_MODIFY_PROP: case IS_LOCKED_PROP: + return G_TYPE_BOOLEAN; + case XPAD_PROP: + return G_TYPE_INT; + case PTR_PROP: + return G_TYPE_POINTER; + } + return 0; +} + +struct YGtkZyppModel : public YGtkTreeModel, Ypp::SelListener +{ + // we pass GtkTreeView to the model, so we can test for selected rows + // and modify text markup appropriely (trashing the data-view model, heh) + + YGtkZyppModel (Ypp::List list) : m_list (list.clone()) + { addSelListener (this); } + + ~YGtkZyppModel() + { removeSelListener (this); } + + void setHighlight (std::list std::string keywords) + { m_keywords = keywords; } + +protected: + Ypp::List m_list; + std::list std::string m_keywords; + + virtual int rowsNb() { return m_list.size(); } + virtual int columnsNb() const { return TOTAL_IMPL_PROPS; } + virtual bool showEmptyEntry() const { return false; } + + virtual GType columnType (int col) const + { return _columnType (col); } + + virtual void getValue (int row, int col, GValue *value) + { + Ypp::Selectable &sel = m_list.get (row); + switch (col) { + case NAME_PROP: { + std::string str (sel.name()); + highlightMarkupSpan (str, m_keywords); + g_value_set_string (value, str.c_str()); + break; + } + case ACTION_NAME_PROP: { + std::string str ("<b>"), name (sel.name()); + str.reserve (name.size() + 35); + str += getStatusAction (&sel); + str += "</b> "; + str += name; + g_value_set_string (value, str.c_str()); + break; + } + case NAME_SUMMARY_PROP: { + std::string name (sel.name()), summary (sel.summary()), str; + summary = YGUtils::escapeMarkup (summary); + highlightMarkupSpan (name, m_keywords); + highlightMarkupSpan (summary, m_keywords); + str.reserve (name.size() + summary.size() + 64); + str = name; + if (!summary.empty()) { + str += "\n"; + str += "<span color="" GRAY_COLOR "">"; + str += "<small>" + summary + "</small>"; + str += "</span>"; + } + g_value_set_string (value, str.c_str()); + break; + } + case REPOSITORY_PROP: { + std::string str; + if (sel.hasCandidateVersion()) { + Ypp::Repository repo (sel.candidate().repository()); + str = getRepositoryLabel (repo); + } +#if 0 + else { + Ypp::Repository repo (sel.installed().repository()); + str = getRepositoryLabel (repo); + } +#endif + g_value_set_string (value, str.c_str()); + break; + } + case REPOSITORY_STOCK_PROP: { + const char *stock = 0; + if (sel.hasCandidateVersion()) { + Ypp::Repository repo (sel.candidate().repository()); + stock = getRepositoryStockIcon (repo); + } +#if 0 + else { + Ypp::Repository repo (sel.installed().repository()); + stock = getRepositoryStockIcon (repo); + } +#endif + g_value_set_string (value, stock); + break; + } + case SUPPORT_PROP: { + if (sel.type() == Ypp::Selectable::PACKAGE) { + Ypp::Package pkg (sel); + std::string str (Ypp::Package::supportSummary (pkg.support())); + g_value_set_string (value, str.c_str()); + } + break; + } + case SIZE_PROP: { + Size_t size = sel.anyVersion().size(); + g_value_set_string (value, size.asString().c_str()); + break; + } + case VERSION_PROP: { + std::string str; + str.reserve (128); + int cmp = 0; + bool hasCandidate = sel.hasCandidateVersion(); + bool patch = false; + if (hasCandidate) { + Ypp::Version candidate = sel.candidate(); + if (sel.isInstalled()) { + Ypp::Version installed = sel.installed(); + if (candidate > installed) + cmp = 1; + if (candidate < installed) + cmp = -1; + } + } + if (cmp > 0) { + str += "<span color="blue">"; + + if (sel.type() == Ypp::Selectable::PACKAGE) { + Ypp::Package pkg (sel); + patch = pkg.isCandidatePatch(); + } + } + else if (cmp < 0) + str += "<span color="red">"; + + if (cmp == 0) { + if (sel.isInstalled()) { + if (!hasCandidate) // red for orphan too + str += "<span color="red">"; + str += sel.installed().number(); + if (!hasCandidate) + str += "</span>"; + } + else + str += sel.candidate().number(); + } + else { + str += sel.candidate().number(); + if (patch) + { str += " <small>"; str += _("patch"); str += "</small>"; } + str += "</span>\n<small>"; + str += sel.installed().number(); + str += "</small>"; + } + g_value_set_string (value, str.c_str()); + break; + } + case SINGLE_VERSION_PROP: { + std::string str; + str.reserve (128); + if (sel.hasCandidateVersion() && !sel.toRemove()) + str = sel.candidate().number(); + else + str = sel.installed().number(); + g_value_set_string (value, str.c_str()); + break; + } + case INSTALLED_CHECK_PROP: { + bool installed; // whether it is installed or will be at apply + if (sel.toInstall()) + installed = true; + else if (sel.toRemove()) + installed = false; + else + installed = sel.isInstalled(); + g_value_set_boolean (value, installed); + break; + } + case HAS_UPGRADE_PROP: + g_value_set_boolean (value, sel.hasUpgrade()); + break; + case TO_UPGRADE_PROP: + g_value_set_boolean (value, sel.hasUpgrade() && sel.toInstall()); + break; + case CAN_TOGGLE_INSTALL_PROP: + g_value_set_boolean (value, !sel.isInstalled() || sel.canRemove()); + break; + case MANUAL_MODIFY_PROP: + g_value_set_boolean (value, sel.toModify() && !sel.toModifyAuto()); + break; + case IS_LOCKED_PROP: + g_value_set_boolean (value, !sel.isLocked()); + break; + case BACKGROUND_PROP: { + const char *color = 0; + if (sel.toModify()) + color = "#f4f4b7"; + g_value_set_string (value, color); + break; + } + case FOREGROUND_PROP: { + const char *color = 0; + if (sel.toModifyAuto()) + color = "#6f6f6f"; + g_value_set_string (value, color); + break; + } + case XPAD_PROP: { + int xpad = sel.toModifyAuto() ? 20 : 0; + g_value_set_int (value, xpad); + break; + } + case STATUS_ICON_PROP: + g_value_set_string (value, getStatusStockIcon (sel)); + break; + case ACTION_ICON_PROP: { + const char *stock; + if (sel.toModify()) + stock = GTK_STOCK_UNDO; + else if (sel.isInstalled()) + stock = GTK_STOCK_REMOVE; + else + stock = GTK_STOCK_ADD; + g_value_set_string (value, stock); + break; + } + case ACTION_BUTTON_PROP: { + const char *text; + if (sel.toModify()) + text = _("Undo"); + else if (sel.isInstalled()) + text = _("Remove"); + else + text = _("Install"); + g_value_set_string (value, text); + break; + } + case PTR_PROP: + g_value_set_pointer (value, (void *) &sel); + break; + } + } + + virtual void selectableModified() + { + for (int i = 0; i < rowsNb(); i++) + listener->rowChanged (i); + } +}; + +static Ypp::Selectable *ygtk_zypp_model_get_sel (GtkTreeModel *model, gchar *path_str) +{ + GtkTreeIter iter; + gtk_tree_model_get_iter_from_string (model, &iter, path_str); + Ypp::Selectable *sel; + gtk_tree_model_get (model, &iter, PTR_PROP, &sel, -1); + return sel; +} + +static Ypp::Selectable *ygtk_zypp_model_get_sel (GtkTreeModel *model, GtkTreePath *path) +{ + GtkTreeIter iter; + gtk_tree_model_get_iter (model, &iter, path); + Ypp::Selectable *sel; + gtk_tree_model_get (model, &iter, PTR_PROP, &sel, -1); + return sel; +} + +//** View + +struct YGtkPkgListView::Impl { + GtkWidget *scroll, *view; + YGtkPkgListView::Listener *listener; + Ypp::List list; + bool descriptiveTooltip; + int sort_attrb, ascendent : 2; + bool userModified; + std::list std::string m_keywords; + bool indentAuto, colorModified; + + Impl (bool descriptiveTooltip, int default_sort_attrb, bool indentAuto, bool colorModified) + : listener (NULL), list (0), descriptiveTooltip (descriptiveTooltip), + sort_attrb (default_sort_attrb), ascendent (true), userModified (false), + indentAuto (indentAuto), colorModified (colorModified) {} + + void setList (Ypp::List _list, int _attrb, bool _ascendent, bool userSorted, const std::list std::string &keywords) + { + if (userSorted) userModified = true; + if (_list != list || sort_attrb != _attrb || ascendent != _ascendent) { + if (_attrb != -1) { + if (_list == list && sort_attrb == _attrb) + _list.reverse(); + else + _list.sort ((Ypp::List::SortAttribute) _attrb, _ascendent); + } + list = _list; + sort_attrb = _attrb; + ascendent = _ascendent; + } + + GtkTreeModel *model = ygtk_tree_model_new (new YGtkZyppModel (list)); + gtk_tree_view_set_model (GTK_TREE_VIEW (view), model); + g_object_unref (G_OBJECT (model)); + setHighlight (keywords); + + if (userModified) { + GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (view)); + for (GList *i = columns; i; i = i->next) { + GtkTreeViewColumn *column = (GtkTreeViewColumn *) i->data; + bool v = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (column), "attrb")) == _attrb; + gtk_tree_view_column_set_sort_indicator (column, v); + if (v) + gtk_tree_view_column_set_sort_order (column, + ascendent ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING); + } + g_list_free (columns); + } + + // search_column is one prop (among others) that gets reset on new model + gtk_tree_view_set_search_column (GTK_TREE_VIEW (view), NAME_PROP); + } + + void setHighlight (const std::list std::string &keywords) + { + if (m_keywords.empty() && keywords.empty()) return; + m_keywords = keywords; + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + YGtkZyppModel *zmodel = (YGtkZyppModel *) ygtk_tree_model_get_model (model); + zmodel->setHighlight (keywords); + gtk_widget_queue_draw (view); + } +}; + +static void right_click_cb (YGtkTreeView *view, gboolean outreach, YGtkPkgListView *pThis) +{ + struct inner { + static void appendItem (GtkWidget *menu, const char *_label, + const char *tooltip, const char *stock, bool sensitive, + void (& callback) (GtkMenuItem *item, YGtkPkgListView *pThis), YGtkPkgListView *pThis) + { + GtkWidget *item; + std::string label; + if (_label) + label = YGUtils::mapKBAccel (_label); + if (stock) { + if (_label) { + item = gtk_image_menu_item_new_with_mnemonic (label.c_str()); + GtkWidget *image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + } + else + item = gtk_image_menu_item_new_from_stock (stock, NULL); + } + else + item = gtk_menu_item_new_with_mnemonic (label.c_str()); + if (tooltip) + gtk_widget_set_tooltip_markup (item, tooltip); + gtk_widget_set_sensitive (item, sensitive); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (callback), pThis); + } + static void install_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { pThis->getSelected().install(); } + static void reinstall_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { reinstall (pThis->getSelected().get(0)); } + static void remove_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { pThis->getSelected().remove(); } + static void undo_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { pThis->getSelected().undo(); } + static void lock_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { pThis->getSelected().lock (true); } + static void unlock_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { pThis->getSelected().lock (false); } + static void select_all_cb (GtkMenuItem *item, YGtkPkgListView *pThis) + { pThis->selectAll(); } + + static bool hasReinstall (Ypp::Selectable sel) + { + if (sel.hasInstalledVersion()) { + Ypp::Version installedVersion = sel.installed(); + for (int i = 0; i < sel.totalVersions(); i++) { + Ypp::Version version = sel.version (i); + if (!version.isInstalled() && version == installedVersion) + return true; + } + } + return false; + } + static void reinstall (Ypp::Selectable sel) + { + Ypp::Version installedVersion = sel.installed(); + for (int i = 0; i < sel.totalVersions(); i++) { + Ypp::Version version = sel.version (i); + if (!version.isInstalled() && version == installedVersion) { + sel.setCandidate (version); + sel.install(); + break; + } + } + } + }; + + GtkWidget *menu = gtk_menu_new(); + Ypp::List list = pThis->getSelected(); + Ypp::Selectable::Type type = Ypp::Selectable::PACKAGE; + if (list.size() > 0) + type = list.get(0).type(); + + if (!outreach) { + Ypp::ListProps props (list); + + bool canLock = props.canLock(), unlocked = props.isUnlocked(); + bool modified = props.toModify(); + bool locked = !unlocked && canLock; + if (props.isNotInstalled() && !modified) + inner::appendItem (menu, _("&Install"), 0, GTK_STOCK_SAVE, + !locked, inner::install_cb, pThis); + if (props.hasUpgrade() && !modified) + inner::appendItem (menu, _("&Upgrade"), 0, GTK_STOCK_GO_UP, + !locked, inner::install_cb, pThis); + if (type == Ypp::Selectable::PACKAGE && list.size() == 1 && inner::hasReinstall (list.get(0)) && !modified) + inner::appendItem (menu, _("&Re-install"), 0, GTK_STOCK_REFRESH, + !locked, inner::reinstall_cb, pThis); + if (props.isInstalled() && !modified) + inner::appendItem (menu, _("&Remove"), 0, GTK_STOCK_DELETE, + !locked && props.canRemove(), inner::remove_cb, pThis); + if (modified) + inner::appendItem (menu, _("&Undo"), 0, GTK_STOCK_UNDO, + true, inner::undo_cb, pThis); + if (canLock) { + static const char *lock_tooltip = + "<b>Package lock:</b> prevents the package status from being modified by " + "the dependencies resolver."; + if (props.isLocked()) + inner::appendItem (menu, _("&Unlock"), _(lock_tooltip), + GTK_STOCK_DIALOG_AUTHENTICATION, true, inner::unlock_cb, pThis); + if (unlocked) + inner::appendItem (menu, _("&Lock"), _(lock_tooltip), + GTK_STOCK_DIALOG_AUTHENTICATION, !modified, + inner::lock_cb, pThis); + } + } + + if (type == Ypp::Selectable::PACKAGE || type == Ypp::Selectable::PATCH) { + GList *items = gtk_container_get_children (GTK_CONTAINER (menu)); + g_list_free (items); + + if (items != NULL) /* add separator if there are other items */ + gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new()); + inner::appendItem (menu, NULL, NULL, GTK_STOCK_SELECT_ALL, + true, inner::select_all_cb, pThis); + ygtk_tree_view_append_show_columns_item (YGTK_TREE_VIEW (pThis->impl->view), menu); + } + ygtk_tree_view_popup_menu (YGTK_TREE_VIEW (pThis->impl->view), menu); +} + +static void selection_changed_cb (GtkTreeSelection *selection, YGtkPkgListView *pThis) +{ + if (GTK_WIDGET_REALIZED (pThis->impl->view) && pThis->impl->listener) + pThis->impl->listener->selectionChanged(); +} + +static void row_activated_cb (GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *column, YGtkPkgListView *pThis) +{ + YGUI::ui()->busyCursor(); + if (YGPackageSelector::get()->yield()) return; + + GtkTreeModel *model = gtk_tree_view_get_model (view); + Ypp::Selectable *sel = ygtk_zypp_model_get_sel (model, path); + if (sel->toModify()) + sel->undo(); + else if (sel->isInstalled()) + sel->remove(); + else + sel->install(); + + YGUI::ui()->normalCursor(); +} + +static void check_toggled_cb (GtkCellRendererToggle *renderer, gchar *path_str, + YGtkPkgListView *pThis) +{ + YGUI::ui()->busyCursor(); + if (YGPackageSelector::get()->yield()) return; + + GtkTreeView *view = GTK_TREE_VIEW (pThis->impl->view); + GtkTreeModel *model = gtk_tree_view_get_model (view); + Ypp::Selectable *sel = ygtk_zypp_model_get_sel (model, path_str); + + gboolean active = gtk_cell_renderer_toggle_get_active (renderer); + if (sel->toModify()) + sel->undo(); + else + active ? sel->remove() : sel->install(); + + YGUI::ui()->normalCursor(); +} + +static void upgrade_toggled_cb (YGtkCellRendererButton *renderer, gchar *path_str, + YGtkPkgListView *pThis) +{ + YGUI::ui()->busyCursor(); + if (YGPackageSelector::get()->yield()) return; + + GtkTreeView *view = GTK_TREE_VIEW (pThis->impl->view); + GtkTreeModel *model = gtk_tree_view_get_model (view); + Ypp::Selectable *sel = ygtk_zypp_model_get_sel (model, path_str); + sel->toInstall() ? sel->undo() : sel->install(); + + YGUI::ui()->normalCursor(); +} + +static void undo_toggled_cb (YGtkCellRendererButton *renderer, gchar *path_str, + YGtkPkgListView *pThis) +{ + GtkTreeView *view = GTK_TREE_VIEW (pThis->impl->view); + GtkTreeModel *model = gtk_tree_view_get_model (view); + Ypp::Selectable *sel = ygtk_zypp_model_get_sel (model, path_str); + sel->undo(); +} + +static void action_button_toggled_cb (YGtkCellRendererButton *renderer, gchar *path_str, + YGtkPkgListView *pThis) +{ + GtkTreeView *view = GTK_TREE_VIEW (pThis->impl->view); + GtkTreeModel *model = gtk_tree_view_get_model (view); + Ypp::Selectable *sel = ygtk_zypp_model_get_sel (model, path_str); + if (sel->toModify()) + sel->undo(); + else if (sel->isInstalled()) + sel->remove(); + else + sel->install(); +} + +static gboolean query_tooltip_cb (GtkWidget *widget, gint x, gint y, + gboolean keyboard_mode, GtkTooltip *tooltip, YGtkPkgListView *pThis) +{ + GtkTreeView *view = GTK_TREE_VIEW (widget); + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + if (gtk_tree_view_get_tooltip_context (view, + &x, &y, keyboard_mode, &model, &path, &iter)) { + gtk_tree_view_set_tooltip_row (view, tooltip, path); + + Ypp::Selectable *sel = ygtk_zypp_model_get_sel (model, path); + gtk_tree_path_free (path); + std::string text; + text.reserve (256); + const char *icon = 0; + + GtkTreeViewColumn *column; + int bx, by; + gtk_tree_view_convert_widget_to_bin_window_coords ( + view, x, y, &bx, &by); + gtk_tree_view_get_path_at_pos ( + view, x, y, NULL, &column, NULL, NULL); + + GtkIconSize icon_size = GTK_ICON_SIZE_MENU; + if (column == ygtk_tree_view_get_column (YGTK_TREE_VIEW (view), 0)) { + text = getStatusSummary (*sel); + icon = getStatusStockIcon (*sel); + } + else if (pThis->impl->descriptiveTooltip) { + text = std::string ("<b>") + sel->name() + "</b>\n\n"; + text += sel->description (false); + switch (sel->type()) { + case Ypp::Selectable::PATTERN: { + ZyppPattern pattern = castZyppPattern (sel->zyppSel()->theObj()); + icon = pattern->icon().asString().c_str(); + if (!(*icon)) + icon = "pattern-generic"; + break; + } + case Ypp::Selectable::PACKAGE: + icon = getStatusStockIcon (*sel); + break; + default: break; + } + icon_size = GTK_ICON_SIZE_DIALOG; + } + + if (text.empty()) + return FALSE; + gtk_tooltip_set_markup (tooltip, text.c_str()); + if (icon) + gtk_tooltip_set_icon_from_icon_name (tooltip, icon, icon_size); + return TRUE; + } + return FALSE; +} + +static void column_clicked_cb (GtkTreeViewColumn *column, YGtkPkgListView *pThis) +{ + int attrb = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (column), "attrb")); + bool ascendent = true; + if (gtk_tree_view_column_get_sort_indicator (column)) + ascendent = gtk_tree_view_column_get_sort_order (column) == GTK_SORT_DESCENDING; + pThis->impl->setList (pThis->impl->list, attrb, ascendent, true, pThis->impl->m_keywords); +} + +static void set_sort_column (YGtkPkgListView *pThis, GtkTreeViewColumn *column, int property) +{ + int attrb = -1; + switch (property) { + case INSTALLED_CHECK_PROP: attrb = Ypp::List::IS_INSTALLED_SORT; break; + case NAME_PROP: case ACTION_NAME_PROP: case NAME_SUMMARY_PROP: + attrb = Ypp::List::NAME_SORT; break; + case REPOSITORY_PROP: attrb = Ypp::List::REPOSITORY_SORT; break; + case SUPPORT_PROP: attrb = Ypp::List::SUPPORT_SORT; break; + case SIZE_PROP: attrb = Ypp::List::SIZE_SORT; break; + } + + gtk_tree_view_column_set_clickable (column, true); + g_object_set_data (G_OBJECT (column), "attrb", GINT_TO_POINTER (attrb)); + if (attrb != -1) + g_signal_connect (G_OBJECT (column), "clicked", + G_CALLBACK (column_clicked_cb), pThis); +} + +YGtkPkgListView::YGtkPkgListView (bool descriptiveTooltip, int default_sort, bool indentAuto, bool colorModified, bool variableHeight) +: impl (new Impl (descriptiveTooltip, default_sort, indentAuto, colorModified)) +{ + impl->scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->scroll), + GTK_SHADOW_IN); + + GtkTreeView *view = GTK_TREE_VIEW (impl->view = ygtk_tree_view_new (_("No matches."))); + if (!variableHeight) + gtk_tree_view_set_fixed_height_mode (view, TRUE); + gtk_tree_view_set_headers_visible (view, FALSE); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_changed_cb), this); + + g_signal_connect (G_OBJECT (view), "row-activated", + G_CALLBACK (row_activated_cb), this); + g_signal_connect (G_OBJECT (view), "right-click", + G_CALLBACK (right_click_cb), this); + gtk_widget_set_has_tooltip (impl->view, TRUE); + g_signal_connect (G_OBJECT (view), "query-tooltip", + G_CALLBACK (query_tooltip_cb), this); + + gtk_container_add (GTK_CONTAINER (impl->scroll), impl->view); + gtk_widget_show_all (impl->scroll); +} + +YGtkPkgListView::~YGtkPkgListView() +{ delete impl; } + +GtkWidget *YGtkPkgListView::getWidget() +{ return impl->scroll; } + +GtkWidget *YGtkPkgListView::getView() +{ return impl->view; } + +void YGtkPkgListView::setQuery (Ypp::Query &query) +{ setList (Ypp::List (query)); } + +void YGtkPkgListView::setList (Ypp::List list) +{ + std::list std::string keywords; + impl->setList (list, impl->sort_attrb, impl->ascendent, false, keywords); +} + +void YGtkPkgListView::setHighlight (const std::list std::string &keywords) +{ + impl->setHighlight (keywords); + + int index = keywords.size() == 1 ? impl->list.find (keywords.front()) : -1; + if (index != -1) { + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + GtkTreePath *path = gtk_tree_path_new_from_indices (index, -1); + gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, .5, 0); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_select_path (selection, path); + gtk_tree_path_free (path); + } + else if (GTK_WIDGET_REALIZED (impl->view)) + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (impl->view), -1, 0); +} + +void YGtkPkgListView::addTextColumn (const char *header, int property, bool visible, int size) +{ + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + if (header) + gtk_tree_view_set_headers_visible (view, TRUE); + GtkTreeViewColumn *column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title (column, header); + + GtkCellRenderer *renderer; + if (property == REPOSITORY_PROP) { + renderer = gtk_cell_renderer_pixbuf_new(); + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + gtk_tree_view_column_pack_end (column, renderer, FALSE); + else + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "icon-name", REPOSITORY_STOCK_PROP, NULL); + } + + if (property == VERSION_PROP) { + renderer = ygtk_cell_renderer_side_button_new(); + g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_GO_UP, NULL); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (upgrade_toggled_cb), this); + } + else + renderer = ygtk_cell_renderer_text_new(); + + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "markup", property, "sensitive", IS_LOCKED_PROP, NULL); + + if (impl->colorModified) + gtk_tree_view_column_add_attribute (column, renderer, + "cell-background", BACKGROUND_PROP); + if (impl->indentAuto) { + gtk_tree_view_column_add_attribute (column, renderer, "xpad", XPAD_PROP); + gtk_tree_view_column_add_attribute (column, renderer, "foreground", FOREGROUND_PROP); + } + + PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_END; + if (size >= 0 && property != NAME_SUMMARY_PROP) + ellipsize = PANGO_ELLIPSIZE_MIDDLE; + g_object_set (G_OBJECT (renderer), "ellipsize", ellipsize, NULL); + + if (property == VERSION_PROP) { + gtk_tree_view_column_add_attribute (column, renderer, + "button-visible", HAS_UPGRADE_PROP); + gtk_tree_view_column_add_attribute (column, renderer, + "active", TO_UPGRADE_PROP); + } + + if (size != -1) // on several columns + gtk_tree_view_set_rules_hint (view, TRUE); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_resizable (column, TRUE); + if (size >= 0) + gtk_tree_view_column_set_fixed_width (column, size); + else + gtk_tree_view_column_set_expand (column, TRUE); + gtk_tree_view_column_set_visible (column, visible); + set_sort_column (this, column, property); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column); +} + +void YGtkPkgListView::addCheckColumn (int property) +{ + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new(); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, "active", property, "sensitive", IS_LOCKED_PROP, NULL); + if (impl->colorModified) + gtk_tree_view_column_add_attribute (column, renderer, + "cell-background", BACKGROUND_PROP); + if (property == INSTALLED_CHECK_PROP) + gtk_tree_view_column_add_attribute (column, renderer, + "activatable", CAN_TOGGLE_INSTALL_PROP); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (check_toggled_cb), this); + + // it seems like GtkCellRendererToggle has no width at start, so fixed doesn't work + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, 25); + set_sort_column (this, column, property); + gtk_tree_view_append_column (view, column); +} + +void YGtkPkgListView::addImageColumn (const char *header, int property, bool onlyManualModified) +{ + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new(); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes ( + header, renderer, "icon-name", property, NULL); + if (impl->colorModified) + gtk_tree_view_column_add_attribute (column, renderer, + "cell-background", BACKGROUND_PROP); + if (onlyManualModified) + gtk_tree_view_column_add_attribute (column, renderer, + "visible", MANUAL_MODIFY_PROP); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + int height = MAX (32, YGUtils::getCharsHeight (impl->view, 1)); + gtk_cell_renderer_set_fixed_size (renderer, -1, height); + gtk_tree_view_column_set_fixed_width (column, 38); + gtk_tree_view_append_column (view, column); +} + +void YGtkPkgListView::addButtonColumn (const char *header, int property) +{ + GtkCellRenderer *renderer = ygtk_cell_renderer_button_new(); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes ( + header, renderer, "sensitive", IS_LOCKED_PROP, NULL); + if (impl->colorModified) + gtk_tree_view_column_add_attribute (column, renderer, + "cell-background", BACKGROUND_PROP); + + gboolean show_icon; + g_object_get (G_OBJECT (gtk_settings_get_default()), "gtk-button-images", &show_icon, NULL); + + const char *text; + if (property == UNDO_BUTTON_PROP) { // static property (always "Undo") + text = _("Undo"); + g_object_set (G_OBJECT (renderer), "text", text, NULL); + if (show_icon) + g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_UNDO, NULL); + gtk_tree_view_column_add_attribute (column, renderer, + "visible", MANUAL_MODIFY_PROP); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (undo_toggled_cb), this); + } + else { + text = "xxxxxxxxxx"; + gtk_tree_view_column_add_attribute (column, renderer, + "text", property); + if (show_icon) + gtk_tree_view_column_add_attribute (column, renderer, + "stock-id", ACTION_ICON_PROP); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (action_button_toggled_cb), this); + } + + PangoRectangle rect; + int width = 0; + PangoLayout *layout = gtk_widget_create_pango_layout (impl->view, + strlen (header) > strlen (text) ? header : text); + pango_layout_get_pixel_extents (layout, NULL, &rect); + width = MAX (width, rect.width); + g_object_unref (G_OBJECT (layout)); + width += 18; + if (show_icon) { + int icon_width; + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (impl->view), + GTK_ICON_SIZE_MENU, &icon_width, NULL); + width += icon_width; + } + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, width); + gtk_tree_view_append_column (GTK_TREE_VIEW (impl->view), column); +} + +void YGtkPkgListView::setListener (YGtkPkgListView::Listener *listener) +{ impl->listener = listener; } + +Ypp::List YGtkPkgListView::getList() +{ return impl->list; } + +Ypp::List YGtkPkgListView::getSelected() +{ + GtkTreeModel *model; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + GList *rows = gtk_tree_selection_get_selected_rows (selection, &model); + + Ypp::List list (g_list_length (rows)); + for (GList *i = rows; i; i = i->next) { + GtkTreeIter iter; + GtkTreePath *path = (GtkTreePath *) i->data; + gtk_tree_model_get_iter (model, &iter, path); + + Ypp::Selectable *sel; + gtk_tree_model_get (model, &iter, PTR_PROP, &sel, -1); + gtk_tree_path_free (path); + list.append (*sel); + } + g_list_free (rows); + return list; +} + +void YGtkPkgListView::selectAll() +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + gtk_tree_selection_select_all (selection); +} + +// utilities + +const char *getStatusAction (Ypp::Selectable *sel) +{ + const char *action = 0; + if (sel->toInstall()) { + action = _("install"); + if (sel->type() == Ypp::Selectable::PACKAGE) { + if (sel->isInstalled()) { + Ypp::Version candidate = sel->candidate(), installed = sel->installed(); + if (candidate > installed) + action = _("upgrade"); + else if (candidate < installed) + action = _("downgrade"); + else + action = _("re-install"); + } + } + } + else if (sel->toRemove()) + action = _("remove"); + else //if (sel->toModify()) + action = _("modify"); // generic for locked and so on + return action; +} + +std::string getStatusSummary (Ypp::Selectable &sel) +{ + std::string text; + if (sel.isLocked()) + text = _("locked: right-click to unlock"); + else if (sel.toInstall()) { + Ypp::Version candidate = sel.candidate(); + text = _("To install") + std::string (" ") + candidate.number(); + } + else if (sel.toRemove()) + text = _("To remove"); + else if (sel.isInstalled()) { + text = _("Installed"); + if (sel.hasUpgrade()) { + text += " "; + text += _("(upgrade available)"); + } + } + else + text = _("Not installed"); + if (sel.toModifyAuto()) { + text += "\n<i>"; + text += _("status changed by the dependencies resolver"); + text += "</i>"; + } + return text; +} + +const char *getStatusStockIcon (Ypp::Selectable &sel) +{ + const char *icon; + if (sel.isLocked()) + icon = GTK_STOCK_DIALOG_AUTHENTICATION; + else if (sel.toInstall()) { + icon = GTK_STOCK_ADD; + if (sel.type() == Ypp::Selectable::PACKAGE) { + Ypp::Version candidate = sel.candidate(); + if (sel.isInstalled()) { + Ypp::Version installed = sel.installed(); + if (candidate > installed) + icon = GTK_STOCK_GO_UP; + else if (candidate < installed) + icon = GTK_STOCK_GO_DOWN; + else // if (candidate == installed) + icon = GTK_STOCK_REFRESH; + } + } + } + else if (sel.toRemove()) + icon = GTK_STOCK_REMOVE; + else if (sel.isInstalled()) + icon = GTK_STOCK_HARDDISK; + else + icon = "package"; + return icon; +} + +std::string getRepositoryLabel (Ypp::Repository &repo) +{ + std::string name (repo.name()), url, str; + url = repo.isSystem() ? _("Installed packages") : repo.url(); + str.reserve (name.size() + url.size() + 64); + str = name + "\n"; + str += "<span color="" GRAY_COLOR "">"; + str += "<small>" + url + "</small>"; + str += "</span>"; + return str; +} + +const char *getRepositoryStockIcon (const std::string &url) +{ + if (url.empty()) + return GTK_STOCK_MISSING_IMAGE; + if (url.compare (0, 2, "cd", 2) == 0 || url.compare (0, 3, "dvd", 3) == 0) + return GTK_STOCK_CDROM; + if (url.compare (0, 3, "iso", 3) == 0) + return GTK_STOCK_FILE; + if (url.find ("KDE") != std::string::npos) + return "pattern-kde"; + if (url.find ("GNOME") != std::string::npos) + return "pattern-gnome"; + if (url.find ("update") != std::string::npos) + return "yast-update"; + if (url.find ("home") != std::string::npos) + return "yast-users"; + return GTK_STOCK_NETWORK; +} + +const char *getRepositoryStockIcon (Ypp::Repository &repo) +{ + if (repo.isSystem()) + return "yast-host"; + return getRepositoryStockIcon (repo.url()); +} + +void highlightMarkup (std::string &text, const std::list std::string &keywords, + const char *openTag, const char *closeTag, int openTagLen, int closeTagLen) +{ + if (keywords.empty()) return; + text.reserve ((openTagLen + closeTagLen + 2) * 6); + const char *i = text.c_str(); + while (*i) { + std::list std::string::const_iterator it; + for (it = keywords.begin(); it != keywords.end(); it++) { + const std::string &keyword = *it; + int len = keyword.size(); + if (strncasecmp (i, keyword.c_str(), len) == 0) { + int pos = i - text.c_str(); + text.insert (pos+len, closeTag); + text.insert (pos, openTag); + i = text.c_str() + pos + len + openTagLen + closeTagLen - 2; + break; + } + } + if (it == keywords.end()) + i++; + } +} + +void highlightMarkupSpan (std::string &text, const std::list std::string &keywords) +{ + static const char openTag[] = "<span fgcolor="#000000" bgcolor="#ffff00">"; + static const char closeTag[] = "</span>"; + highlightMarkup (text, keywords, openTag, closeTag, sizeof (openTag), sizeof (closeTag)); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkglistview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,68 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* A view for Ypp's query results. +*/ + +#ifndef YGTK_PKG_LIST_VIEW_H +#define YGTK_PKG_LIST_VIEW_H + +#include "yzyppwrapper.h" +#include <gtk/gtkwidget.h> + +enum Property { + INSTALLED_CHECK_PROP, NAME_PROP, ACTION_NAME_PROP, NAME_SUMMARY_PROP, + VERSION_PROP, SINGLE_VERSION_PROP, REPOSITORY_PROP, SUPPORT_PROP, + SIZE_PROP, STATUS_ICON_PROP, ACTION_BUTTON_PROP, UNDO_BUTTON_PROP, TOTAL_PROPS +}; + +struct YGtkPkgListView { + YGtkPkgListView (bool descriptiveTooltip, int default_sort /* -1 to disable */, + bool indentAuto, bool colorModified, bool variableHeight = false); + ~YGtkPkgListView(); + + GtkWidget *getWidget(); + GtkWidget *getView(); + + void setQuery (Ypp::Query &query); + void setList (Ypp::List list); + + void setHighlight (const std::list std::string &keywords); + + void addTextColumn (const char *header, int property, bool visible, int size); + void addCheckColumn (int checkProperty); + void addImageColumn (const char *header, int property, bool onlyManualModified = false); + void addButtonColumn (const char *header, int property); + + Ypp::List getList(); + + struct Listener { + virtual void selectionChanged() = 0; + }; + void setListener (Listener *listener); + + Ypp::List getSelected(); + void selectAll(); + + struct Impl; + Impl *impl; +}; + +// some utilities: + +const char *getStatusAction (Ypp::Selectable *sel); +std::string getStatusSummary (Ypp::Selectable &sel); +const char *getStatusStockIcon (Ypp::Selectable &sel); + +std::string getRepositoryLabel (Ypp::Repository &repo); +const char *getRepositoryStockIcon (Ypp::Repository &repo); +const char *getRepositoryStockIcon (const std::string &url); + +void highlightMarkup (std::string &text, const std::list std::string &keywords, + const char *openTag, const char *closeTag, int openTagLen, int closeTagLen); + +void highlightMarkupSpan (std::string &text, const std::list std::string &keywords); + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,766 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgMenuBar, menu bar */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGDialog.h" +#include "YGUtils.h" +#include "ygtkpkgmenubar.h" +#include <gtk/gtk.h> +#include <glib/gstdio.h> +#include <stdio.h> +#include <gdk/gdkkeysyms.h> +#include "yzyppwrapper.h" +#include "YGPackageSelector.h" + +// flags handling + +#define YAST_GTK_SYSCONFIG "/etc/sysconfig/yast2-gtk" + +struct Flags { + Flags() { + keys = g_key_file_new(); + g_key_file_load_from_file (keys, YAST_GTK_SYSCONFIG, G_KEY_FILE_NONE, NULL); + modified = false; + } + + ~Flags() { + if (modified) + writeFile(); + g_key_file_free (keys); + } + + bool hasKey (const char *variable) { + return g_key_file_has_key (keys, "zypp", variable, NULL); + } + + bool getBool (const char *variable) { + return g_key_file_get_boolean (keys, "zypp", variable, NULL); + } + + void setBool (const char *variable, bool value) { + g_key_file_set_boolean (keys, "zypp", variable, value); + modified = true; + } + + private: + void writeFile() { + FILE *out = fopen (YAST_GTK_SYSCONFIG, "w"); + if (out) { + gsize size; + gchar *data = g_key_file_to_data (keys, &size, NULL); + fwrite (data, sizeof (char), size, out); + g_free (data); + fclose (out); + } + } + + GKeyFile *keys; + bool modified; +}; + +// utilities + +static GtkWidget *append_menu_item (GtkWidget *menu, const char *_text, + const char *stock, GCallback callback, gpointer callback_data) +{ + std::string text; + if (_text) + text = YGUtils::mapKBAccel (_text); + GtkWidget *item; + if (stock && _text) { + GtkWidget *icon = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU); + item = gtk_image_menu_item_new_with_mnemonic (text.c_str()); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon); + } + else if (_text) + item = gtk_menu_item_new_with_mnemonic (text.c_str()); + else if (stock) + item = gtk_image_menu_item_new_from_stock (stock, NULL); + else + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + if (callback) + g_signal_connect (G_OBJECT (item), "activate", callback, callback_data); + return item; +} + +static void errorMsg (const std::string &message) +{ + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Error")); + gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), + "%s", message.c_str()); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +// callback implementations +// code from yast2-qt + +#include <zypp/SysContent.h> +#include <zypp/ui/Status.h> +#include <zypp/ui/Selectable.h> +#include <zypp/ResPoolProxy.h> +#include <zypp/ZYppFactory.h> +#include <fstream> + +using zypp::ui::S_Protected; +using zypp::ui::S_Taboo; +using zypp::ui::S_Del; +using zypp::ui::S_Update; +using zypp::ui::S_Install; +using zypp::ui::S_AutoDel; +using zypp::ui::S_AutoUpdate; +using zypp::ui::S_AutoInstall; +using zypp::ui::S_KeepInstalled; +using zypp::ui::S_NoInst; +typedef zypp::ui::Selectable::Ptr ZyppSel; +typedef zypp::ui::Status ZyppStatus; +typedef zypp::ResPoolProxy::const_iterator ZyppPoolIterator; +typedef zypp::ResPoolProxy ZyppPool; +//inline ZyppPool zyppPool() { return zypp::getZYpp()->poolProxy(); } +template<class T> ZyppPoolIterator zyppBegin() { return zyppPool().byKindBegin<T>(); } +template<class T> ZyppPoolIterator zyppEnd() { return zyppPool().byKindEnd<T>(); } +inline ZyppPoolIterator zyppPkgBegin() { return zyppBeginzypp::Package(); } +inline ZyppPoolIterator zyppPkgEnd() { return zyppEndzypp::Package(); } +inline ZyppPoolIterator zyppPatternsBegin() { return zyppBeginzypp::Pattern(); } +inline ZyppPoolIterator zyppPatternsEnd() { return zyppEndzypp::Pattern(); } + +#define DEFAULT_EXPORT_FILE_NAME "user-packages.xml" + +static void +importSelectable( ZyppSel selectable, + bool isWanted, + const char * kind ) +{ + ZyppStatus oldStatus = selectable->status(); + ZyppStatus newStatus = oldStatus; + + if ( isWanted ) + { + // + // Make sure this selectable does not get installed + // + + switch ( oldStatus ) + { + case S_Install: + case S_AutoInstall: + case S_KeepInstalled: + case S_Protected: + case S_Update: + case S_AutoUpdate: + newStatus = oldStatus; + break; + + case S_Del: + case S_AutoDel: + newStatus = S_KeepInstalled; + yuiDebug() << "Keeping " << kind << " " << selectable->name() << endl; + break; + + case S_NoInst: + case S_Taboo: + + if ( selectable->hasCandidateObj() ) + { + newStatus = S_Install; + yuiDebug() << "Adding " << kind << " " << selectable->name() << endl; + } + else + { + yuiDebug() << "Can't add " << kind << " " << selectable->name() + << ": No candidate" << endl; + } + break; + } + } + else // ! isWanted + { + // + // Make sure this selectable does not get installed + // + + switch ( oldStatus ) + { + case S_Install: + case S_AutoInstall: + case S_KeepInstalled: + case S_Protected: + case S_Update: + case S_AutoUpdate: + newStatus = S_Del; + yuiDebug() << "Deleting " << kind << " " << selectable->name() << endl; + break; + + case S_Del: + case S_AutoDel: + case S_NoInst: + case S_Taboo: + newStatus = oldStatus; + break; + } + } + + if ( oldStatus != newStatus ) + selectable->setStatus( newStatus ); +} + +static void import_file_cb (GtkMenuItem *item) +{ + GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import from"), + YGDialog::currentWindow(), GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name (filter, "*.xml"); + gtk_file_filter_add_pattern (filter, "*.xml"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + filter = gtk_file_filter_new(); + gtk_file_filter_set_name (filter, "*"); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), DEFAULT_EXPORT_FILE_NAME); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); + + int ret = gtk_dialog_run (GTK_DIALOG (dialog)); + if ( ret == GTK_RESPONSE_ACCEPT ) + { + char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + yuiMilestone() << "Importing package list from " << filename << endl; + + try + { + std::ifstream importFile( filename ); + zypp::syscontent::Reader reader( importFile ); + + // + // Put reader contents into maps + // + + typedef zypp::syscontent::Reader::Entry ZyppReaderEntry; + typedef std::pair<string, ZyppReaderEntry> ImportMapPair; + + map<string, ZyppReaderEntry> importPkg; + map<string, ZyppReaderEntry> importPatterns; + + for ( zypp::syscontent::Reader::const_iterator it = reader.begin(); + it != reader.end(); + ++ it ) + { + string kind = it->kind(); + + if ( kind == "package" ) importPkg.insert ( ImportMapPair( it->name(), *it ) ); + else if ( kind == "pattern" ) importPatterns.insert( ImportMapPair( it->name(), *it ) ); + } + + yuiDebug() << "Found " << importPkg.size() + <<" packages and " << importPatterns.size() + << " patterns in " << filename + << endl; + + + // + // Set status of all patterns and packages according to import map + // + + for ( ZyppPoolIterator it = zyppPatternsBegin(); + it != zyppPatternsEnd(); + ++it ) + { + ZyppSel selectable = *it; + importSelectable( *it, importPatterns.find( selectable->name() ) != importPatterns.end(), "pattern" ); + } + + for ( ZyppPoolIterator it = zyppPkgBegin(); + it != zyppPkgEnd(); + ++it ) + { + ZyppSel selectable = *it; + importSelectable( *it, importPkg.find( selectable->name() ) != importPkg.end(), "package" ); + } + + YGPackageSelector::get()->popupChanges(); + } + catch (const zypp::Exception & exception) + { + yuiWarning() << "Error reading package list from " << filename << endl; + std::string str (_("Could not open:")); + str += " "; str += filename; + errorMsg (str); + } + + g_free (filename); + Ypp::runSolver(); + } + + gtk_widget_destroy (dialog); +} + +static void export_file_cb (GtkMenuItem *item) +{ + GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export to"), + YGDialog::currentWindow(), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + + GtkFileFilter *filter = gtk_file_filter_new(); + gtk_file_filter_set_name (filter, "*.xml"); + gtk_file_filter_add_pattern (filter, "*.xml"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + filter = gtk_file_filter_new(); + gtk_file_filter_set_name (filter, "*"); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), DEFAULT_EXPORT_FILE_NAME); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + + int ret = gtk_dialog_run (GTK_DIALOG (dialog)); + if (ret == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + + zypp::syscontent::Writer writer; + const zypp::ResPool & pool = zypp::getZYpp()->pool(); + + // The ZYPP obfuscated C++ contest proudly presents: + + for_each( pool.begin(), pool.end(), + boost::bind( &zypp::syscontent::Writer::addIf, + boost::ref( writer ), + _1 ) ); + // Yuck. What a mess. + // + // Does anybody seriously believe this kind of thing is easier to read, + // let alone use? Get real. This is an argument in favour of all C++ + // haters. And it's one that is really hard to counter. + // + // -sh 2006-12-13 + + try + { + std::ofstream exportFile( filename ); + exportFile.exceptions( std::ios_base::badbit | std::ios_base::failbit ); + exportFile << writer; + + yuiMilestone() << "Package list exported to " << filename << endl; + } + catch ( std::exception & exception ) + { + yuiWarning() << "Error exporting package list to " << filename << endl; + + // The export might have left over a partially written file. + // Try to delete it. Don't care if it doesn't exist and unlink() fails. + g_remove (filename); + + // Post error popup + std::string str (_("Could not save to:")); + str += " "; str += filename; + errorMsg (str); + } + + g_free (filename); + } + + gtk_widget_destroy (dialog); +} + +static void create_solver_testcase_cb (GtkMenuItem *item) +{ + const char *dirname = "/var/log/YaST2/solverTestcase"; + std::string msg (_("Use this to generate extensive logs to help tracking " + "down bugs in the dependencies resolver.")); + msg += "\n"; msg += _("The logs will be saved to the directory:"); + msg += " "; msg += dirname; + + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, + // Translators: if there is no direct translation to Dependencies Resolver, then translate it to e.g. Dependencies Manager + "%s", _("Generate Dependencies Resolver Testcase")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", msg.c_str()); + int ret = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + if (ret == GTK_RESPONSE_OK) { + bool success; + yuiMilestone() << "Generating solver test case START" << endl; + success = zypp::getZYpp()->resolver()->createSolverTestcase (dirname); + yuiMilestone() << "Generating solver test case END" << endl; + + if (success) { + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, "%s", _("Success")); + msg = _("Dependencies resolver test case written to:"); + msg += " <tt>"; + msg += dirname; + msg += "</tt>\n"; + msg += _("Also create a <tt>y2logs.tgz</tt> tar archive to attach to bugzilla?"); + gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), + "%s", msg.c_str()); + ret = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + if (ret == GTK_RESPONSE_YES) + YGUI::ui()->askSaveLogs(); + } + else { + msg = _("Failed to create dependencies resolver testcase.\n" + "Please check disk space and permissions for:"); + msg += " <tt>"; msg += dirname; msg += "</tt>"; + errorMsg (msg.c_str()); + } + } +} + +static void repoManager() +{ + yuiMilestone() << "Closing PackageSelector with "RepoManager"" << endl; + YGUI::ui()->sendEvent( new YMenuEvent( "repo_mgr" ) ); +} + +static void onlineUpdateConfiguration() +{ + yuiMilestone() << "Closing PackageSelector with "OnlineUpdateConfiguration"" << endl; + YGUI::ui()->sendEvent( new YMenuEvent( "online_update_configuration" ) ); +} + +static void webpinSearch() +{ + yuiMilestone() << "Closing PackageSelector with "webpin"" << endl; + YGUI::ui()->sendEvent( new YMenuEvent( "webpin" ) ); +} + +static void manualResolvePackageDependencies() +{ Ypp::runSolver (true); } + +// check menu item flags + +struct CheckMenuFlag { + CheckMenuFlag (GtkWidget *menu, const char *text) { + m_item = gtk_check_menu_item_new_with_mnemonic (text); + g_object_set_data_full (G_OBJECT (m_item), "this", this, destructor); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), m_item); + } + + void init (Flags *flags) { + bool check = getZyppValue(); + + const char *var = variable(); + if (flags->hasKey (var)) { + bool c = flags->getBool (var); + if (c != check) + setZyppValue (c); + check = c; + } + + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (m_item), check); + g_signal_connect_after (G_OBJECT (m_item), "toggled", G_CALLBACK (toggled_cb), this); + } + + virtual const char *variable() = 0; + virtual bool getZyppValue() = 0; + virtual void setZyppValue (bool on) = 0; + virtual void runtimeSync() {} + + void writeFile (bool on) { + Flags flags; + flags.setBool (variable(), on); + } + + static void toggled_cb (GtkCheckMenuItem *item, CheckMenuFlag *pThis) { + bool on = gtk_check_menu_item_get_active (item); + pThis->setZyppValue (on); + pThis->runtimeSync(); + pThis->writeFile (on); + } + + static void destructor (gpointer data) { + delete ((CheckMenuFlag *) data); + } + + GtkWidget *m_item; +}; + +struct AutoCheckItem : public CheckMenuFlag { + AutoCheckItem (GtkWidget *menu, const char *text, Flags *flags) + : CheckMenuFlag (menu, text) { init (flags); } + virtual const char *variable() { return "auto-check"; } + virtual bool getZyppValue() { return Ypp::isSolverEnabled(); } + virtual void setZyppValue (bool on) { Ypp::setEnableSolver (on); } +}; + +struct ShowDevelCheckItem : public CheckMenuFlag { + ShowDevelCheckItem (GtkWidget *menu, const char *text, Flags *flags) + : CheckMenuFlag (menu, text) { init (flags); } + virtual const char *variable() { return "show-devel"; } + virtual bool getZyppValue() { return true; } + virtual void setZyppValue (bool on) + { YGPackageSelector::get()->filterPkgSuffix ("-devel", !on); } +}; + +struct ShowDebugCheckItem : public CheckMenuFlag { + ShowDebugCheckItem (GtkWidget *menu, const char *text, Flags *flags) + : CheckMenuFlag (menu, text) { init (flags); } + virtual const char *variable() { return "show-debug"; } + virtual bool getZyppValue() { return true; } + virtual void setZyppValue (bool on) { + YGPackageSelector::get()->filterPkgSuffix ("-debuginfo", !on); + YGPackageSelector::get()->filterPkgSuffix ("-debugsource", !on); + } +}; + +struct SystemVerificationCheckItem : public CheckMenuFlag { + SystemVerificationCheckItem (GtkWidget *menu, const char *text, Flags *flags) + : CheckMenuFlag (menu, text) { init (flags); } + virtual const char *variable() { return "system-verification"; } + virtual bool getZyppValue() { + return zypp::getZYpp()->resolver()->systemVerification(); + } + virtual void setZyppValue (bool on) { + zypp::getZYpp()->resolver()->setSystemVerification (on); + } + virtual void runtimeSync() {} +}; + +#if ZYPP_VERSION > 6031004 + +struct CleanupDepsCheckItem : public CheckMenuFlag { + CleanupDepsCheckItem (GtkWidget *menu, const char *text, Flags *flags) + : CheckMenuFlag (menu, text) { init (flags); } + virtual const char *variable() { return "cleanup-deps"; } + virtual bool getZyppValue() { + return zypp::getZYpp()->resolver()->cleandepsOnRemove(); + } + virtual void setZyppValue (bool on) { + zypp::getZYpp()->resolver()->setCleandepsOnRemove( on ); + } + virtual void runtimeSync() { Ypp::runSolver(); } +}; + +struct AllowVendorChangeCheckItem : public CheckMenuFlag { + AllowVendorChangeCheckItem (GtkWidget *menu, const char *text, Flags *flags) + : CheckMenuFlag (menu, text) { init (flags); } + virtual const char *variable() { return "allow-vendor-change"; } + virtual bool getZyppValue() { + return zypp::getZYpp()->resolver()->allowVendorChange(); + } + virtual void setZyppValue (bool on) { + zypp::getZYpp()->resolver()->setAllowVendorChange( on ); + } + virtual void runtimeSync() { Ypp::runSolver(); } +}; + +#endif + +static void installSubPkgs (std::string suffix) +{ + // Find all matching packages and put them into a QMap + + std::map<std::string, ZyppSel> subPkgs; + + for ( ZyppPoolIterator it = zyppPkgBegin(); it != zyppPkgEnd(); ++it ) + { + std::string name = (*it)->name(); + + if (YGUtils::endsWith (name, suffix)) + { + subPkgs[ name ] = *it; + yuiDebug() << "Found subpackage: " << name << endl; + } + } + + + // Now go through all packages and look if there is a corresponding subpackage in the QMap + + for ( ZyppPoolIterator it = zyppPkgBegin(); it != zyppPkgEnd(); ++it ) + { + std::string name = (*it)->name(); + + std::string subPkgName( name + suffix ); + if ( subPkgs.find( subPkgName ) != subPkgs.end() ) + { + ZyppSel subPkg = subPkgs[ subPkgName ]; + + switch ( (*it)->status() ) + { + case S_AutoDel: + case S_NoInst: + case S_Protected: + case S_Taboo: + case S_Del: + // Don't install the subpackage + yuiMilestone() << "Ignoring unwanted subpackage " << subPkgName << endl; + break; + + case S_AutoInstall: + case S_Install: + case S_KeepInstalled: + + // Install the subpackage, but don't try to update it + + if ( ! subPkg->installedObj() ) + { + subPkg->setStatus( S_Install ); + yuiMilestone() << "Installing subpackage " << subPkgName << endl; + } + break; + + + case S_Update: + case S_AutoUpdate: + + // Install or update the subpackage + + if ( ! subPkg->installedObj() ) + { + subPkg->setStatus( S_Install ); + yuiMilestone() << "Installing subpackage " << subPkgName << endl; + } + else + { + subPkg->setStatus( S_Update ); + yuiMilestone() << "Updating subpackage " << subPkgName << endl; + } + break; + + // Intentionally omitting 'default' branch so the compiler can + // catch unhandled enum states + } + } + } + + Ypp::runSolver(); + YGPackageSelector::get()->popupChanges(); +} + +static void install_all_devel_pkgs_cb() +{ installSubPkgs ("-devel"); } + +static void install_all_debug_info_pkgs_cb() +{ installSubPkgs ("-debuginfo"); } + +static void install_all_debug_source_pkgs_cb() +{ installSubPkgs ("-debugsource"); } + +static void show_pkg_changes_cb() +{ YGPackageSelector::get()->popupChanges(); } + +static void show_log_changes_cb() +{ YGPackageSelector::get()->showHistoryDialog(); } + +#ifdef HAS_VESTIGIAL_DIALOG +static void show_vestigial_packages_cb() +{ YGPackageSelector::get()->showVestigialDialog(); } +#endif + +static void reset_ignored_dependency_conflicts_cb() +{ zypp::getZYpp()->resolver()->undo(); } + +#include "ygtkpkgproductdialog.h" + +static void show_products_cb() +{ + YGtkPkgProductDialog dialog; + dialog.popup(); +} + +static void accept_item_cb (GtkMenuItem *item, YGPackageSelector *selector) +{ selector->apply(); } + +static void reject_item_cb (GtkMenuItem *item, YGPackageSelector *selector) +{ selector->cancel(); } + +YGtkPkgMenuBar::YGtkPkgMenuBar() +{ + YGPackageSelector *selector = YGPackageSelector::get(); + m_menu = gtk_menu_bar_new(); + Flags flags; + + GtkWidget *menu_bar = m_menu, *item, *submenu; + item = append_menu_item (menu_bar, _("F&ile"), NULL, NULL, NULL); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), (submenu = gtk_menu_new())); + append_menu_item (submenu, _("&Import..."), NULL, + G_CALLBACK (import_file_cb), this); + append_menu_item (submenu, _("&Export..."), NULL, + G_CALLBACK (export_file_cb), this); + append_menu_item (submenu, NULL, NULL, NULL, NULL); + append_menu_item (submenu, NULL, GTK_STOCK_APPLY, G_CALLBACK (accept_item_cb), selector); + append_menu_item (submenu, NULL, GTK_STOCK_QUIT, G_CALLBACK (reject_item_cb), selector); + if (selector->repoMgrEnabled()) { + item = append_menu_item (menu_bar, _("&Configuration"), NULL, NULL, NULL); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), (submenu = gtk_menu_new())); + append_menu_item (submenu, _("&Repositories..."), NULL, + G_CALLBACK (repoManager), this); + if (selector->onlineUpdateMode()) + append_menu_item (submenu, _("&Online Update..."), NULL, + G_CALLBACK (onlineUpdateConfiguration), this); + else + append_menu_item (submenu, _("Search Packages on &Web..."), NULL, + G_CALLBACK (webpinSearch), this); + } + item = append_menu_item (menu_bar, _("&Dependencies"), NULL, NULL, NULL); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), (submenu = gtk_menu_new())); + append_menu_item (submenu, _("&Check Now"), NULL, + G_CALLBACK (manualResolvePackageDependencies), this); + new AutoCheckItem (submenu, _("&Autocheck"), &flags); + + if (!selector->onlineUpdateMode()) { + item = append_menu_item (menu_bar, _("&Options"), NULL, NULL, NULL); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), (submenu = gtk_menu_new())); + // Translators: don't translate the "-devel" + new ShowDevelCheckItem (submenu, _("Show -de&vel Packages"), &flags); + // Translators: don't translate the "-debuginfo/-debugsource" part + new ShowDebugCheckItem (submenu, _("Show -&debuginfo/-debugsource Packages"), &flags); + new SystemVerificationCheckItem (submenu, _("&System Verification Mode"), &flags); + new CleanupDepsCheckItem (submenu, _("&Cleanup when deleting packages"), &flags); + new AllowVendorChangeCheckItem (submenu, _("&Allow vendor change"), &flags); + } + + item = append_menu_item (menu_bar, _("E&xtras"), NULL, NULL, NULL); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), (submenu = gtk_menu_new())); + append_menu_item (submenu, _("Show &Products"), NULL, + G_CALLBACK (show_products_cb), this); + append_menu_item (submenu, _("Show &Changes"), NULL, + G_CALLBACK (show_pkg_changes_cb), this); + if (!selector->onlineUpdateMode()) { + append_menu_item (submenu, _("Show &History"), NULL, + G_CALLBACK (show_log_changes_cb), this); +#ifdef HAS_VESTIGIAL_DIALOG + append_menu_item (submenu, _("Show &Unneeded Dependencies"), NULL, + G_CALLBACK (show_vestigial_packages_cb), this); +#endif + } + append_menu_item (submenu, NULL, NULL, NULL, NULL); + // Translators: keep "-_devel" untranslated + append_menu_item (submenu, _("Install All Matching -&devel Packages"), NULL, + G_CALLBACK (install_all_devel_pkgs_cb), this); + // Translators: keep "-debug-_info" untranslated + append_menu_item (submenu, _("Install All Matching -debug-&sinfo Packages"), NULL, + G_CALLBACK (install_all_debug_info_pkgs_cb), this); + // Translators: keep "-debug-_source" untranslated + append_menu_item (submenu, _("Install All Matching -debug-&source Packages"), NULL, + G_CALLBACK (install_all_debug_source_pkgs_cb), this); + append_menu_item (submenu, NULL, NULL, NULL, NULL); + append_menu_item (submenu, _("Generate Dependencies Resolver &Testcase"), NULL, + G_CALLBACK (create_solver_testcase_cb), this); + append_menu_item (submenu, _("Reset &Ignored Dependencies Conflicts"), NULL, + G_CALLBACK (reset_ignored_dependency_conflicts_cb), this); + + //** TEMP: work-around global-menubar-applet: see bug 595560 + //** will call show_all() at YGPackageSelector.cc + //gtk_widget_show_all (m_menu); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgmenubar.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,24 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* A menu bar identical to that from yast2-qt. +*/ + +#ifndef YGTK_PKG_MENU_BAR_H +#define YGTK_PKG_MENU_BAR_H + +#include <gtk/gtkwidget.h> + +struct YGtkPkgMenuBar +{ + YGtkPkgMenuBar(); + + GtkWidget *getWidget() { return m_menu; } + + private: + GtkWidget *m_menu; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,540 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgPatternList, pattern and language list implementations */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGUtils.h" +#include "ygtkpkgpatternview.h" +#include "ygtktreeview.h" +#include "ygtkcellrenderertext.h" +#include <gtk/gtk.h> + +#define GRAY_COLOR "#727272" + +enum Column { + HAS_CHECK_COLUMN, CHECK_COLUMN, HAS_ICON_COLUMN, ICON_COLUMN, + TEXT_COLUMN, ORDER_COLUMN, POINTER_COLUMN, TOTAL_COLUMNS +}; + +struct YGtkPkgPatternView::Impl : public Ypp::SelListener { + GtkWidget *scroll, *view; + + Impl() + { Ypp::addSelListener (this); } + ~Impl() + { + Ypp::removeSelListener (this); + if (!g_object_get_data (G_OBJECT (getModel()), "patterns")) + gtk_tree_model_foreach (getModel(), free_foreach_cb, this); + } + + GtkTreeModel *getModel() + { return gtk_tree_view_get_model (GTK_TREE_VIEW (view)); } + + static gboolean free_foreach_cb (GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, gpointer data) + { + gpointer _data; + gtk_tree_model_get (model, iter, POINTER_COLUMN, &_data, -1); + g_free (_data); + return FALSE; + } + + static gboolean update_foreach_cb (GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, gpointer data) + { + gpointer _data; + gtk_tree_model_get (model, iter, POINTER_COLUMN, &_data, -1); + if (_data) { + ZyppSelectablePtr zsel = (ZyppSelectablePtr) _data; + Ypp::Selectable sel (zsel); + + GtkTreeStore *store = GTK_TREE_STORE (model); + gtk_tree_store_set (store, iter, CHECK_COLUMN, + sel.isInstalled() || sel.toInstall(), -1); + } + return FALSE; + } + + virtual void selectableModified() + { gtk_tree_model_foreach (getModel(), update_foreach_cb, this); } +}; + +static Ypp::Selectable get_iter_selectable (GtkTreeModel *model, GtkTreeIter *iter) +{ + if (g_object_get_data (G_OBJECT (model), "patterns")) { + ZyppSelectablePtr zsel; + gtk_tree_model_get (model, iter, POINTER_COLUMN, &zsel, -1); + return Ypp::Selectable (zsel); + } + else { + gchar *code; + gtk_tree_model_get (model, iter, POINTER_COLUMN, &code, -1); + zypp::Locale locale (code); + g_free (code); + return Ypp::Selectable (locale); + } +} + +static void set_row ( + GtkTreeStore *store, GtkTreeIter *iter, Ypp::Selectable &sel, ZyppPattern pattern) +{ + std::string order; + GdkPixbuf *pixbuf = 0; + gpointer ptr; + if (pattern) { + std::string filename (pattern->icon().asString()); + if (filename == zypp::Pathname("yast-system").asString() || filename.empty()) + filename = "pattern-generic"; + if (filename.compare (0, 2, "./", 2) == 0) + filename.erase (0, 2); + filename = zypp::str::form ("%s/icons/%dx%d/apps/%s.png", + THEMEDIR, 32, 32, filename.c_str()); + pixbuf = YGUtils::loadPixbuf (filename); +/* + if (pixbuf && !sel.isInstalled()) { + GdkPixbuf *_pixbuf = pixbuf; + pixbuf = YGUtils::setOpacity (_pixbuf, 50, true); + g_object_unref (_pixbuf); + } +*/ + order = pattern->order(); + ptr = get_pointer (sel.zyppSel()); + } + else + ptr = g_strdup (sel.zyppLocale().code().c_str()); + + gtk_tree_store_set (store, iter, HAS_CHECK_COLUMN, TRUE, + CHECK_COLUMN, sel.isInstalled() || sel.toInstall(), HAS_ICON_COLUMN, TRUE, + ICON_COLUMN, pixbuf, TEXT_COLUMN, (sel.name() + '\n').c_str(), + ORDER_COLUMN, order.c_str(), + POINTER_COLUMN, ptr, -1); + + if (pixbuf) + g_object_unref (G_OBJECT (pixbuf)); +} + +static gboolean set_rows_cb (GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + gpointer _data; + gtk_tree_model_get (model, iter, POINTER_COLUMN, &_data, -1); + if (!_data) return FALSE; + + Ypp::Selectable sel (get_iter_selectable (model, iter)); + + int installed, total; + Ypp::Collection (sel).stats (&installed, &total); + gchar *text = g_strdup_printf ("%s\n<span color="" GRAY_COLOR + ""><small>Installed %d of %d</small></span>", + sel.name().c_str(), installed, total); + + GtkTreeStore *store = GTK_TREE_STORE (model); + gtk_tree_store_set (store, iter, TEXT_COLUMN, text, -1); + g_free (text); + return FALSE; +} + +static gboolean set_rows_idle (gpointer data) +{ + gtk_tree_model_foreach (GTK_TREE_MODEL (data), set_rows_cb, NULL); + return FALSE; +} + +static bool find_name (GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, const std::string &name) +{ + GtkTreeModel *model = GTK_TREE_MODEL (store); + bool done = false; + if (gtk_tree_model_iter_children (model, iter, parent)) + do { + gchar *value; + gtk_tree_model_get (model, iter, TEXT_COLUMN, &value, -1); + if (name == value) + done = true; + g_free (value); + if (done) + return true; + } while (gtk_tree_model_iter_next (model, iter)); + return false; +} + +static bool find_place (GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, const std::string &order) +{ + GtkTreeModel *model = GTK_TREE_MODEL (store); + bool done = false; + if (gtk_tree_model_iter_children (model, iter, parent)) + do { + gchar *value; + gtk_tree_model_get (model, iter, ORDER_COLUMN, &value, -1); + if (order < value) + done = true; + g_free (value); + if (done) return true; + } while (gtk_tree_model_iter_next (model, iter)); + return false; +} + +static void insert_node (GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, const std::string &order) +{ + if (find_place (store, parent, iter, order)) { + GtkTreeIter sibling = *iter; + gtk_tree_store_insert_before (store, iter, parent, &sibling); + } + else + gtk_tree_store_append (store, iter, parent); +} + +static void insert_pattern ( + GtkTreeStore *store, GtkTreeIter *parent, Ypp::Selectable &sel, ZyppPattern pattern) +{ + GtkTreeIter iter; + insert_node (store, parent, &iter, pattern->order()); + set_row (store, &iter, sel, pattern); +} + +static void insert_category (GtkTreeStore *store, Ypp::Selectable &sel, ZyppPattern pattern) +{ + GtkTreeIter iter; + std::string text ("<big><b>" + pattern->category() + "</b></big>"); + + if (!find_name (store, NULL, &iter, text)) { + insert_node (store, NULL, &iter, pattern->order()); + gtk_tree_store_set (store, &iter, HAS_CHECK_COLUMN, FALSE, + CHECK_COLUMN, FALSE, HAS_ICON_COLUMN, FALSE, ICON_COLUMN, NULL, + TEXT_COLUMN, text.c_str(), + ORDER_COLUMN, pattern->order().c_str(), POINTER_COLUMN, NULL, -1); + } + insert_pattern (store, &iter, sel, pattern); +} + +static void insert_language (GtkTreeStore *store, Ypp::Selectable &sel) +{ + std::string name (sel.name()); + bool done = false; + GtkTreeModel *model = GTK_TREE_MODEL (store); + GtkTreeIter iter; + if (gtk_tree_model_iter_children (model, &iter, NULL)) + do { + gchar *value; + gtk_tree_model_get (model, &iter, TEXT_COLUMN, &value, -1); + if (g_utf8_collate (name.c_str(), value) < 0) + done = true; + g_free (value); + if (done) break; + } while (gtk_tree_model_iter_next (model, &iter)); + + if (done) { + GtkTreeIter sibling = iter; + gtk_tree_store_insert_before (store, &iter, NULL, &sibling); + } + else + gtk_tree_store_append (store, &iter, NULL); + + set_row (store, &iter, sel, NULL); +} + +static void install_cb (GtkWidget *widget, ZyppSelectablePtr zsel) +{ Ypp::Selectable (zsel).install(); } +static void undo_cb (GtkWidget *widget, ZyppSelectablePtr zsel) +{ Ypp::Selectable (zsel).undo(); } +static void remove_cb (GtkWidget *widget, ZyppSelectablePtr zsel) +{ Ypp::Selectable (zsel).remove(); } + +static GtkWidget *menu_item_append (GtkWidget *menu, const char *_label, const char *stock, bool sensitive) +{ + std::string label; + if (_label) + label = YGUtils::mapKBAccel (_label); + GtkWidget *item; + if (_label) { + item = gtk_image_menu_item_new_with_mnemonic (label.c_str()); + if (stock) { + GtkWidget *icon = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon); + } + } + else + item = gtk_image_menu_item_new_from_stock (stock, NULL); + gtk_widget_set_sensitive (item, sensitive); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + return item; +} + +static void right_click_cb (YGtkTreeView *yview, gboolean outreach, YGtkPkgPatternView *pThis) +{ + if (!outreach) { + GtkTreeView *view = GTK_TREE_VIEW (yview); + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + GtkTreeModel *model; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + ZyppSelectablePtr zsel; + gtk_tree_model_get (model, &iter, POINTER_COLUMN, &zsel, -1); + Ypp::Selectable sel (zsel); + + GtkWidget *menu = gtk_menu_new(), *item; + if (sel.toModify()) { + item = menu_item_append (menu, NULL, GTK_STOCK_UNDO, true); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (undo_cb), zsel); + } + else { + if (sel.isInstalled()) { + if (sel.canRemove()) { + item = menu_item_append (menu, _("&Remove"), GTK_STOCK_REMOVE, true); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (remove_cb), zsel); + } + else + menu_item_append (menu, _("Remove: cannot remove patterns"), NULL, false); + } + else { + item = menu_item_append (menu, _("&Install"), GTK_STOCK_ADD, true); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (install_cb), zsel); + } + } + + ygtk_tree_view_popup_menu (yview, menu); + } + } +} + +static void toggle_iter (GtkTreeModel *model, GtkTreeIter *iter) +{ + Ypp::Selectable sel (get_iter_selectable (model, iter)); + sel.toModify() ? sel.undo() : sel.install(); +} + +static void row_activated_cb (GtkTreeView *view, + GtkTreePath *path, GtkTreeViewColumn *column, YGtkPkgPatternView *pThis) +{ + GtkTreeModel *model = pThis->impl->getModel(); + GtkTreeIter iter; + if (gtk_tree_model_get_iter (model, &iter, path)) + toggle_iter (model, &iter); +} + +static void toggled_cb (GtkCellRendererToggle *renderer, gchar *path, YGtkPkgPatternView *pThis) +{ + GtkTreeModel *model = pThis->impl->getModel(); + GtkTreeIter iter; + if (gtk_tree_model_get_iter_from_string (model, &iter, path)) + toggle_iter (model, &iter); +} + +static gboolean can_tree_select_cb (GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, gboolean path_selected, gpointer data) +{ + GtkTreeIter iter; + gtk_tree_model_get_iter (model, &iter, path); + gpointer _data; + gtk_tree_model_get (model, &iter, POINTER_COLUMN, &_data, -1); + return _data != NULL; +} + +static void selection_changed_cb (GtkTreeSelection *selection, YGtkPkgPatternView *pThis) +{ pThis->notify(); } + +static gboolean query_tooltip_cb (GtkWidget *widget, gint x, gint y, + gboolean keyboard_mode, GtkTooltip *tooltip, YGtkPkgPatternView *pThis) +{ + GtkTreeView *view = GTK_TREE_VIEW (widget); + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + if (gtk_tree_view_get_tooltip_context (view, + &x, &y, keyboard_mode, &model, &path, &iter)) { + gtk_tree_view_set_tooltip_row (view, tooltip, path); + gtk_tree_path_free (path); + + GtkTreeViewColumn *column; + int bx, by; + gtk_tree_view_convert_widget_to_bin_window_coords ( + view, x, y, &bx, &by); + gtk_tree_view_get_path_at_pos ( + view, x, y, NULL, &column, NULL, NULL); + + ZyppSelectablePtr zsel; + gtk_tree_model_get (model, &iter, POINTER_COLUMN, &zsel, -1); + if (zsel) { + Ypp::Selectable sel (zsel); + + if (column == ygtk_tree_view_get_column (YGTK_TREE_VIEW (view), 0)) { // check-marks + if (sel.isInstalled()) + gtk_tooltip_set_text (tooltip, + _("Installed: cannot remove a pattern.\n\n" + "You must manually remove the individual packages you no " + "longer want to keep.")); + else + gtk_tooltip_set_text (tooltip, _("Not installed")); + } + else { + std::string text (YGUtils::escapeMarkup (sel.description (false))); + gtk_tooltip_set_text (tooltip, text.c_str()); + } + return TRUE; + } + } + return FALSE; +} + +YGtkPkgPatternView::YGtkPkgPatternView (Ypp::Selectable::Type type) +: impl (new Impl()) +{ + assert (type == Ypp::Selectable::LANGUAGE || type == Ypp::Selectable::PATTERN); + + GtkTreeStore *store = gtk_tree_store_new (TOTAL_COLUMNS, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER); + + if (type == Ypp::Selectable::LANGUAGE) { + Ypp::LangQuery query; + Ypp::List list (query); + list.sort (Ypp::List::NAME_SORT, true); + for (int i = 0; i < list.size(); i++) + insert_language (store, list.get(i)); + } + else { + // HACK: in opensuse 11.2, zypp crashes when re-iterating patterns +#if 1 // zypp::PoolQuery is crashing on me when using Patterns + Ypp::PoolQuery query (type); + while (query.hasNext()) { + Ypp::Selectable sel = query.next(); + ZyppPattern pattern = castZyppPattern (sel.zyppSel()->theObj()); + if (pattern->userVisible()) + insert_category (store, sel, pattern); + } +/* static Ypp::List list (0); + if (list.size() == 0) { + Ypp::PoolQuery query (type); + list = Ypp::List (query); + } + for (int i = 0; i < list.size(); i++) { + Ypp::Selectable &sel = list.get (i); + ZyppPattern pattern = castZyppPattern (sel.zyppSel()->theObj()); + if (pattern->userVisible()) + insert_category (store, sel, pattern); + }*/ +#else + for (zypp::ResPoolProxy::const_iterator it = + zypp::getZYpp()->poolProxy().byKindBegin zypp::Pattern(); + it != zypp::getZYpp()->poolProxy().byKindEnd zypp::Pattern(); + it++) { + Ypp::Selectable sel (*it); + ZyppPattern pattern = castZyppPattern (sel.zyppSel()->theObj()); + if (pattern->userVisible()) + insert_category (store, sel, pattern); + } +#endif + g_object_set_data (G_OBJECT (store), "patterns", GINT_TO_POINTER (1)); + } + + impl->view = ygtk_tree_view_new (NULL); + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + gtk_tree_view_set_model (view, GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + gtk_tree_view_set_headers_visible (view, FALSE); + gtk_tree_view_set_search_column (view, TEXT_COLUMN); + gtk_tree_view_expand_all (view); + gtk_tree_view_set_show_expanders (view, FALSE); + gtk_widget_set_has_tooltip (impl->view, TRUE); + g_signal_connect (G_OBJECT (view), "query-tooltip", + G_CALLBACK (query_tooltip_cb), this); + g_signal_connect (G_OBJECT (view), "right-click", + G_CALLBACK (right_click_cb), this); + g_signal_connect (G_OBJECT (view), "row-activated", + G_CALLBACK (row_activated_cb), this); + + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + renderer = gtk_cell_renderer_toggle_new(); + column = gtk_tree_view_column_new_with_attributes ( + NULL, renderer, "active", CHECK_COLUMN, "visible", HAS_CHECK_COLUMN, NULL); + g_signal_connect (G_OBJECT (renderer), "toggled", + G_CALLBACK (toggled_cb), this); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column); + + bool reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title (column, NULL); + gtk_tree_view_column_set_spacing (column, 4); + + GtkCellRenderer *pix_renderer = gtk_cell_renderer_pixbuf_new(); + if (!reverse) + gtk_tree_view_column_pack_start (column, pix_renderer, FALSE); + + renderer = ygtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "markup", TEXT_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + + if (reverse) + gtk_tree_view_column_pack_start (column, pix_renderer, FALSE); + gtk_tree_view_column_set_attributes (column, pix_renderer, + "pixbuf", ICON_COLUMN, "visible", HAS_ICON_COLUMN, NULL); + + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand (column, TRUE); + ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_select_function (selection, can_tree_select_cb, NULL, NULL); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_changed_cb), this); + clearSelection(); + + impl->scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (impl->scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (impl->scroll), impl->view); + gtk_widget_show_all (impl->scroll); + + g_idle_add_full (G_PRIORITY_LOW, set_rows_idle, store, NULL); +} + +YGtkPkgPatternView::~YGtkPkgPatternView() +{ delete impl; } + +GtkWidget *YGtkPkgPatternView::getWidget() +{ return impl->scroll; } + +void YGtkPkgPatternView::clearSelection() +{ + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this); + gtk_tree_selection_unselect_all (selection); + g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this); +} + +bool YGtkPkgPatternView::writeQuery (Ypp::PoolQuery &query) +{ + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + GtkTreeModel *model; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + Ypp::Selectable sel (get_iter_selectable (model, &iter)); + Ypp::Collection collection (sel); + query.addCriteria (new Ypp::FromCollectionMatch (collection)); + return true; + } + return false; +} + +bool isPatternsPoolEmpty() +{ return zyppPool().empty zypp::Pattern(); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgpatternview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,33 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Pattern and language list boxes. +*/ + +#ifndef YGTK_PKG_PATTERN_LIST_H +#define YGTK_PKG_PATTERN_LIST_H + +#include "ygtkpkgquerywidget.h" +#include <gtk/gtkwidget.h> + +struct YGtkPkgPatternView : public YGtkPkgQueryWidget +{ + YGtkPkgPatternView (Ypp::Selectable::Type type); + virtual ~YGtkPkgPatternView(); + virtual GtkWidget *getWidget(); + + virtual bool begsUpdate() { return false; } + virtual void updateList (Ypp::List list) {} + + virtual void clearSelection(); + virtual bool writeQuery (Ypp::PoolQuery &query); + + struct Impl; + Impl *impl; +}; + +bool isPatternsPoolEmpty(); + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,181 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgMenuBar, menu bar */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGDialog.h" +#include "YGUtils.h" +#include "ygtkpkgproductdialog.h" +#include <gtk/gtk.h> +#include "ygtkrichtext.h" + +#include <zypp/ui/Status.h> +#include <zypp/ui/Selectable.h> +#include <zypp/ResObject.h> +#include <zypp/Package.h> +#include <zypp/Pattern.h> +#include <zypp/Patch.h> +#include <zypp/Product.h> +#include <zypp/ZYppFactory.h> +#include <zypp/ResPoolProxy.h> + +typedef zypp::ResPoolProxy ZyppPool; +typedef zypp::ResPoolProxy::const_iterator ZyppPoolIterator; +inline ZyppPool zyppPool() { return zypp::getZYpp()->poolProxy(); } +template<class T> ZyppPoolIterator zyppBegin() { return zyppPool().byKindBegin<T>(); } +template<class T> ZyppPoolIterator zyppEnd() { return zyppPool().byKindEnd<T>(); } +inline ZyppPoolIterator zyppProductsBegin() { return zyppBeginzypp::Product(); } +inline ZyppPoolIterator zyppProductsEnd() { return zyppEndzypp::Product(); } +typedef zypp::ui::Selectable::Ptr ZyppSel; +typedef zypp::ResObject::constPtr ZyppObj; +typedef zypp::Product::constPtr ZyppProduct; +inline ZyppProduct tryCastToZyppProduct( ZyppObj zyppObj ) +{ return zypp::dynamic_pointer_cast<const zypp::Product>( zyppObj ); } + +enum { + INSTALLED_COLUMN, TEXT_COLUMN, VERSION_COLUMN, VENDOR_COLUMN, DESCRIPTION_COLUMN, TOTAL_COLUMNS +}; + +static void selection_changed_cb (GtkTreeSelection *selection, YGtkRichText *description) +{ + GtkTreeModel *model; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + char *text; + gtk_tree_model_get (model, &iter, DESCRIPTION_COLUMN, &text, -1); + ygtk_rich_text_set_text (description, text); + g_free (text); + } + else + ygtk_rich_text_set_plain_text (description, ""); +} + +YGtkPkgProductDialog::YGtkPkgProductDialog() +{ + GtkListStore *store = gtk_list_store_new (TOTAL_COLUMNS, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + for (ZyppPoolIterator it = zyppProductsBegin(); it != zyppProductsEnd(); it++) { + ZyppSel sel = *it; + ZyppProduct prod = tryCastToZyppProduct( sel->theObj() ); + + std::string text (sel->name() + "\n<small>" + prod->summary() + "</small>"); + + ZyppObj available = sel->candidateObj(); + ZyppObj installed = sel->installedObj(); + + std::string version; + if (!!available && !!installed && (available->edition() != installed->edition())) { + version = available->edition().asString() + "\n<small>"; + version += installed->edition().asString() + "</small>"; + } + else if (!!available) + version = available->edition().asString(); + else if (!!installed) + version = installed->edition().asString(); + + std::string description; + if (!!available) { + description += std::string ("<p><b>") + _("Candidate provides:") + " </b>"; + description += available->dep (zypp::Dep::PROVIDES).begin()->asString(); + } + if (!!installed) { + description += std::string ("<p><b>") + _("Installed provides:") + " </b>"; + description += installed->dep (zypp::Dep::PROVIDES).begin()->asString(); + } + + + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, INSTALLED_COLUMN, + sel->candidateObj().isSatisfied() || sel->hasInstalledObj(), + TEXT_COLUMN, text.c_str(), VERSION_COLUMN, version.c_str(), + VENDOR_COLUMN, prod->vendor().c_str(), DESCRIPTION_COLUMN, + description.c_str(), -1); + } + + GtkWidget *description = ygtk_rich_text_new(); + GtkWidget *description_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (description_scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (description_scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (description_scroll), description); + + GtkWidget *view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + GtkTreeView *tview = GTK_TREE_VIEW (view); + g_object_unref (G_OBJECT (store)); + gtk_tree_view_set_search_column (tview, TEXT_COLUMN); + gtk_tree_view_set_rules_hint (tview, TRUE); + + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + renderer = gtk_cell_renderer_toggle_new(); + column = gtk_tree_view_column_new_with_attributes ( + NULL, renderer, "active", INSTALLED_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "sensitive", FALSE, NULL); + gtk_tree_view_append_column (tview, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ( + _("Name"), renderer, "markup", TEXT_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_expand (column, TRUE); + gtk_tree_view_append_column (tview, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ( + _("Version"), renderer, "markup", VERSION_COLUMN, NULL); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_append_column (tview, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ( + _("Vendor"), renderer, "text", VENDOR_COLUMN, NULL); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_append_column (tview, column); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (tview); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_changed_cb), description); + + GtkWidget *scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (scroll), view); + + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + // Translators: same as "Listing of Products" + GtkDialogFlags (0), GTK_MESSAGE_OTHER, GTK_BUTTONS_CLOSE, _("Products Listing")); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500); + + GtkWidget *vpaned = gtk_vpaned_new(); + gtk_paned_pack1 (GTK_PANED (vpaned), scroll, TRUE, FALSE); + gtk_paned_pack2 (GTK_PANED (vpaned), description_scroll, FALSE, TRUE); + YGUtils::setPaneRelPosition (vpaned, .70); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vpaned); + + gtk_widget_show_all (dialog); + m_dialog = dialog; +} + +YGtkPkgProductDialog::~YGtkPkgProductDialog() +{ gtk_widget_destroy (m_dialog); } + +void YGtkPkgProductDialog::popup() +{ gtk_dialog_run (GTK_DIALOG (m_dialog)); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgproductdialog.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,25 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Show zypp::Product list. +*/ + +#ifndef YGTK_PKG_PRODUCT_DIALOG_H +#define YGTK_PKG_PRODUCT_DIALOG_H + +#include <gtk/gtkwidget.h> + +struct YGtkPkgProductDialog +{ + YGtkPkgProductDialog(); + ~YGtkPkgProductDialog(); + + void popup(); + +private: + GtkWidget *m_dialog; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,109 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgQueryCombo, an umbrella for any group of QueryWidget widgets */ +// check the header file for information about this widget + +/* + Textdomain "yast2-gtk" + */ + +#define YUILogComponent "gtk-pkg" +#include "YGi18n.h" +#include "config.h" +#include "YGUtils.h" +#include "ygtkpkgquerycombo.h" +#include <gtk/gtk.h> + +struct YGtkPkgQueryCombo::Impl { + GtkWidget *vbox, *combo, *content; + Factory *factory; + YGtkPkgQueryWidget *child; + + Impl (Factory *factory) : factory (factory), child (NULL) {} + + ~Impl() + { delete child; } +}; + +static void set_child (YGtkPkgQueryCombo *pThis, int index) +{ + if (pThis->impl->child) + delete pThis->impl->child; + YGtkPkgQueryWidget *child = pThis->impl->factory->createQueryWidget (pThis, index); + pThis->impl->child = child; + child->setListener (pThis->listener); + + GtkWidget *cur_child = gtk_bin_get_child (GTK_BIN (pThis->impl->content)); + if (cur_child) + gtk_container_remove (GTK_CONTAINER (pThis->impl->content), cur_child); + gtk_container_add (GTK_CONTAINER (pThis->impl->content), child->getWidget()); + gtk_widget_grab_focus (child->getWidget()); +} + +static void combo_changed_cb (GtkComboBox *combo, YGtkPkgQueryCombo *pThis) +{ + Ypp::Busy busy (0); + set_child (pThis, gtk_combo_box_get_active (combo)); + pThis->notify(); +} + +YGtkPkgQueryCombo::YGtkPkgQueryCombo (Factory *factory) +: impl (new Impl (factory)) +{ + impl->combo = gtk_combo_box_new_text(); + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (impl->combo), + YGUtils::empty_row_is_separator_cb, GINT_TO_POINTER (0), NULL); + g_signal_connect_after (G_OBJECT (impl->combo), "changed", + G_CALLBACK (combo_changed_cb), this); + impl->content = gtk_event_box_new(); + impl->vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (impl->vbox), impl->combo, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (impl->vbox), impl->content, TRUE, TRUE, 0); +} + +YGtkPkgQueryCombo::~YGtkPkgQueryCombo() +{ delete impl; } + +GtkWidget *YGtkPkgQueryCombo::getWidget() +{ return impl->vbox; } + +void YGtkPkgQueryCombo::add (const char *title) +{ + GtkComboBox *combo = GTK_COMBO_BOX (impl->combo); + gtk_combo_box_append_text (combo, title); +} + +void YGtkPkgQueryCombo::setActive (int index) +{ + GtkComboBox *combo = GTK_COMBO_BOX (impl->combo); + if (gtk_combo_box_get_active (combo) != index) { + g_signal_handlers_block_by_func (combo, (gpointer) combo_changed_cb, this); + gtk_combo_box_set_active (combo, index); + g_signal_handlers_unblock_by_func (combo, (gpointer) combo_changed_cb, this); + set_child (this, index); + } +} + +bool YGtkPkgQueryCombo::begsUpdate() +{ return impl->child->begsUpdate(); } + +void YGtkPkgQueryCombo::updateList (Ypp::List list) +{ return impl->child->updateList (list); } + +void YGtkPkgQueryCombo::clearSelection() +{ return impl->child->clearSelection(); } + +bool YGtkPkgQueryCombo::writeQuery (Ypp::PoolQuery &query) +{ return impl->child->writeQuery (query); } + +void YGtkPkgQueryCombo::setListener (Listener *listener) +{ + YGtkPkgQueryWidget::setListener (listener); + if (impl->child) + impl->child->setListener (listener); +} + +GtkWidget *YGtkPkgQueryCombo::createToolbox() +{ return impl->child->createToolbox(); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerycombo.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,44 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Comprises several QueryWidget widgets. + You probably want to use this in cooperation with YGtkPkgFilerBox. +*/ + +#ifndef YGTK_PKG_QUERY_COMBO_H +#define YGTK_PKG_QUERY_COMBO_H + +#include "ygtkpkgquerywidget.h" +#include <gtk/gtkwidget.h> + +struct YGtkPkgQueryCombo : public YGtkPkgQueryWidget +{ + struct Factory { + virtual YGtkPkgQueryWidget *createQueryWidget ( + YGtkPkgQueryCombo *combo, int index) = 0; + }; + + YGtkPkgQueryCombo (Factory *factory); + virtual ~YGtkPkgQueryCombo(); + virtual GtkWidget *getWidget(); + + void add (const char *title); + void setActive (int index); + + virtual bool begsUpdate(); + virtual void updateList (Ypp::List list); + + virtual void clearSelection(); + virtual bool writeQuery (Ypp::PoolQuery &query); + + virtual GtkWidget *createToolbox(); + + virtual void setListener (Listener *listener); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerywidget.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerywidget.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgquerywidget.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,64 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Abstract for widgets that support query. +*/ + +#ifndef YGTK_PKG_QUERY_WIDGET_H +#define YGTK_PKG_QUERY_WIDGET_H + +#include <gtk/gtkwidget.h> +#include "yzyppwrapper.h" + +struct YGtkPkgQueryWidget +{ + virtual ~YGtkPkgQueryWidget() {} + virtual GtkWidget *getWidget() = 0; + + // if begsUpdate is true, the current List without this filter applied + // will be passed to updateList() so you can e.g. avoid presenting + // entries for which there is no match. + virtual bool begsUpdate() = 0; + virtual void updateList (Ypp::List list) = 0; + + virtual void clearSelection() = 0; + virtual bool writeQuery (Ypp::PoolQuery &query) = 0; + + virtual GtkWidget *createToolbox() { return NULL; } + + struct Listener { + virtual void refreshQuery() = 0; + }; + + virtual void setListener (Listener *listener) { this->listener = listener; } + Listener *listener; + + bool modified; // flag for internal use + + //protected: + void notify() { if (listener) listener->refreshQuery(); } + + void notifyDelay (int delay) + { + static guint timeout_id = 0; + struct inner { + static gboolean timeout_cb (gpointer data) + { + YGtkPkgQueryWidget *pThis = (YGtkPkgQueryWidget *) data; + timeout_id = 0; + pThis->notify(); + return FALSE; + } + }; + if (timeout_id) g_source_remove (timeout_id); + timeout_id = g_timeout_add_full ( + G_PRIORITY_LOW, delay, inner::timeout_cb, this, NULL); + } + +protected: + YGtkPkgQueryWidget() : listener (NULL) {} +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,165 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgRpmGroupsView, rpm-groups tree-view */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "YGUI.h" +#include "config.h" +#include "YGUtils.h" +#include "ygtkpkgrpmgroupsview.h" +#include "ygtkpkglistview.h" +#include <YRpmGroupsTree.h> + +enum { + TEXT_COLUMN, DATA_COLUMN, TOTAL_COLUMNS +}; + +static void addNode (GtkTreeStore *store, GtkTreeIter *parent, const YStringTreeItem *item) +{ + if (!item) return; + + GtkTreeIter iter; + gtk_tree_store_append (store, &iter, parent); + gtk_tree_store_set (store, &iter, + TEXT_COLUMN, item->value().translation().c_str(), DATA_COLUMN, item, -1); + + addNode (store, &iter, item->firstChild()); + addNode (store, parent, item->next()); +} + +struct YGtkPkgRpmGroupsModel { + GtkTreeStore *store; + YRpmGroupsTree *tree; + + GtkTreeModel *getModel() + { return GTK_TREE_MODEL (store); } + + YGtkPkgRpmGroupsModel() + { + store = gtk_tree_store_new (TOTAL_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); + tree = new YRpmGroupsTree(); + + zypp::sat::LookupAttr rpmgroups (zypp::sat::SolvAttr::group); + for (zypp::sat::LookupAttr::iterator it = rpmgroups.begin(); + it != rpmgroups.end(); it++) + tree->addRpmGroup (it.asString()); + + GtkTreeIter iter; + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + TEXT_COLUMN, _("All packages"), DATA_COLUMN, NULL, -1); + + addNode (store, NULL, tree->root()->firstChild()); + } + + ~YGtkPkgRpmGroupsModel() + { + delete tree; + g_object_unref (G_OBJECT (store)); + } + + bool writeQuery (Ypp::PoolQuery &query, GtkTreeIter *iter) + { + gpointer data; + gtk_tree_model_get (getModel(), iter, DATA_COLUMN, &data, -1); + if (data) { + YStringTreeItem *group = (YStringTreeItem *) data; + std::string rpm_group (tree->rpmGroup (group)); + query.addCriteria (new Ypp::RpmGroupMatch (rpm_group)); + return true; + } + return false; + } +}; + + +struct YGtkPkgRpmGroupsView::Impl { + GtkWidget *scroll, *view; + YGtkPkgRpmGroupsModel *model; + + Impl (YGtkPkgRpmGroupsModel *model) : model (model) {} + ~Impl() { delete model; } +}; + +static void selection_changed_cb (GtkTreeSelection *selection, YGtkPkgRpmGroupsView *pThis) +{ + if (gtk_tree_selection_get_selected (selection, NULL, NULL)) + pThis->notify(); +} + +YGtkPkgRpmGroupsView::YGtkPkgRpmGroupsView() +: YGtkPkgQueryWidget(), impl (new Impl (new YGtkPkgRpmGroupsModel())) +{ + impl->view = gtk_tree_view_new_with_model (impl->model->getModel()); + + GtkTreeView *view = GTK_TREE_VIEW (impl->view); + gtk_tree_view_set_headers_visible (view, FALSE); + gtk_tree_view_set_search_column (view, TEXT_COLUMN); + gtk_tree_view_set_enable_tree_lines (view, TRUE); + gtk_tree_view_expand_all (view); + + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes ( + NULL, renderer, "text", TEXT_COLUMN, NULL); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_append_column (view, column); + + GtkTreeSelection *selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_changed_cb), this); + clearSelection(); + + impl->scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->scroll), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (impl->scroll), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (impl->scroll), impl->view); + gtk_widget_show_all (impl->scroll); +} + +YGtkPkgRpmGroupsView::~YGtkPkgRpmGroupsView() +{ delete impl; } + +GtkWidget *YGtkPkgRpmGroupsView::getWidget() +{ return impl->scroll; } + +void YGtkPkgRpmGroupsView::clearSelection() +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this); + { + GtkTreeIter iter; + gtk_tree_model_get_iter_first (impl->model->getModel(), &iter); + gtk_tree_selection_select_iter (selection, &iter); + } + g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this); +} + +bool YGtkPkgRpmGroupsView::writeQuery (Ypp::PoolQuery &query) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + return impl->model->writeQuery (query, &iter); + else { + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first (model, &iter)) { + g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this); + gtk_tree_selection_select_iter (selection, &iter); + g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this); + } + } + return false; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgrpmgroupsview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,31 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Good-old RPM-groups tree-view. +*/ + +#ifndef YGTK_PKG_RPM_GROUP_VIEW_H +#define YGTK_PKG_RPM_GROUP_VIEW_H + +#include "ygtkpkgquerywidget.h" +#include <gtk/gtkwidget.h> + +struct YGtkPkgRpmGroupsView : public YGtkPkgQueryWidget +{ + YGtkPkgRpmGroupsView(); + virtual ~YGtkPkgRpmGroupsView(); + virtual GtkWidget *getWidget(); + + virtual bool begsUpdate() { return false; } + virtual void updateList (Ypp::List list) {} + + virtual void clearSelection(); + virtual bool writeQuery (Ypp::PoolQuery &query); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,310 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgSearchEntry, zypp query string attributes */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGUtils.h" +#include "ygtkpkgsearchentry.h" +#include "YGPackageSelector.h" +#include <gtk/gtk.h> + +struct YGtkPkgSearchEntry::Impl { + GtkWidget *box, *entry, *combo; +}; + +static void entry_icons_sync (GtkWidget *widget) +{ + static GdkColor yellow = { 0, 0xf7f7, 0xf7f7, 0xbdbd }; + static GdkColor black = { 0, 0, 0, 0 }; + + GtkEntry *entry = GTK_ENTRY (widget); // show clear icon if text + const gchar *name = gtk_entry_get_text (entry); + bool showIcon = *name; + if (showIcon != gtk_entry_get_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY)) { + gtk_entry_set_icon_activatable (entry, + GTK_ENTRY_ICON_SECONDARY, showIcon); + gtk_entry_set_icon_from_stock (entry, + GTK_ENTRY_ICON_SECONDARY, showIcon ? GTK_STOCK_CLEAR : NULL); + + if (showIcon) { + gtk_entry_set_icon_tooltip_text (entry, + GTK_ENTRY_ICON_SECONDARY, _("Clear")); + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, &yellow); + gtk_widget_modify_text (widget, GTK_STATE_NORMAL, &black); + } + else { // revert + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, NULL); + gtk_widget_modify_text (widget, GTK_STATE_NORMAL, NULL); + } + } +} + +static void entry_changed_cb (GtkEditable *editable, YGtkPkgSearchEntry *pThis) +{ + int item = gtk_combo_box_get_active (GTK_COMBO_BOX (pThis->impl->combo)); + pThis->notifyDelay (item == 0 ? 150 : 500); + entry_icons_sync (GTK_WIDGET (editable)); +} + +static void combo_changed_cb (GtkComboBox *combo, YGtkPkgSearchEntry *pThis) +{ + const gchar *name = gtk_entry_get_text (GTK_ENTRY (pThis->impl->entry)); + if (*name) // unless entry has text, no need to refresh query + pThis->notify(); + gtk_editable_select_region (GTK_EDITABLE (pThis->impl->entry), 0, -1); + gtk_widget_grab_focus (pThis->impl->entry); +} + +static void icon_press_cb (GtkEntry *entry, GtkEntryIconPosition pos, + GdkEvent *event, YGtkPkgSearchEntry *pThis) +{ + if (pos == GTK_ENTRY_ICON_PRIMARY) + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + else + gtk_entry_set_text (entry, ""); + gtk_widget_grab_focus (GTK_WIDGET (entry)); +} + +static void activate_cb (GtkEntry *entry, GtkWidget *widget) +{ gtk_widget_grab_focus (widget); } + +YGtkPkgSearchEntry::YGtkPkgSearchEntry() +: YGtkPkgQueryWidget(), impl (new Impl()) +{ + impl->entry = gtk_entry_new(); + gtk_widget_set_size_request (impl->entry, 180, -1); + g_signal_connect (G_OBJECT (impl->entry), "realize", // grab focus at start + G_CALLBACK (gtk_widget_grab_focus), NULL); + g_signal_connect (G_OBJECT (impl->entry), "changed", + G_CALLBACK (entry_changed_cb), this); + g_signal_connect (G_OBJECT (impl->entry), "icon-press", + G_CALLBACK (icon_press_cb), this); + gtk_widget_set_tooltip_markup (impl->entry, + _("<b>Package search:</b> Use spaces to separate your keywords.\n" + "(usage example: a name search for "yast dhcp" would match yast dhcpd tool)")); + + gtk_entry_set_icon_from_stock (GTK_ENTRY (impl->entry), + GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND); + gtk_entry_set_icon_activatable (GTK_ENTRY (impl->entry), GTK_ENTRY_ICON_PRIMARY, TRUE); + + std::string label_str (YGUtils::mapKBAccel (_("&Find:"))); + GtkWidget *label = gtk_label_new_with_mnemonic (label_str.c_str()); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->entry); + + impl->combo = gtk_combo_box_new_text(); + gtk_combo_box_append_text (GTK_COMBO_BOX (impl->combo), _("Name & Summary")); + gtk_combo_box_append_text (GTK_COMBO_BOX (impl->combo), _("Description")); + if (!YGPackageSelector::get()->onlineUpdateMode()) { + gtk_combo_box_append_text (GTK_COMBO_BOX (impl->combo), _("File name")); + gtk_combo_box_append_text (GTK_COMBO_BOX (impl->combo), "RPM Provides"); + gtk_combo_box_append_text (GTK_COMBO_BOX (impl->combo), "RPM Requires"); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (impl->combo), 0); + YGUtils::shrinkWidget (impl->combo); + g_signal_connect (G_OBJECT (impl->combo), "changed", + G_CALLBACK (combo_changed_cb), this); + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (impl->combo), + YGUtils::empty_row_is_separator_cb, GINT_TO_POINTER (0), NULL); + + impl->box = gtk_hbox_new (FALSE, 3); + gtk_box_pack_start (GTK_BOX (impl->box), label, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (impl->box), impl->entry, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (impl->box), gtk_label_new (_("by")), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (impl->box), impl->combo, TRUE, TRUE, 0); + gtk_widget_show_all (impl->box); +} + +YGtkPkgSearchEntry::~YGtkPkgSearchEntry() +{ delete impl; } + +GtkWidget *YGtkPkgSearchEntry::getWidget() +{ return impl->box; } + +void YGtkPkgSearchEntry::clearSelection() +{ setText (Ypp::PoolQuery::NAME, ""); } + +bool YGtkPkgSearchEntry::writeQuery (Ypp::PoolQuery &query) +{ + const gchar *text = gtk_entry_get_text (GTK_ENTRY (impl->entry)); + if (*text) { + int item = gtk_combo_box_get_active (GTK_COMBO_BOX (impl->combo)); + if (item >= 0 && item <= 1) { + int attrbs; + switch (item) { + case 0: // name & summary + attrbs = Ypp::StrMatch::NAME | Ypp::StrMatch::SUMMARY; + break; + case 1: // description + attrbs = Ypp::StrMatch::DESCRIPTION; + break; + } + + Ypp::StrMatch *match = new Ypp::StrMatch (attrbs); + const gchar delimiter[2] = { ' ', '\0' }; + gchar **keywords = g_strsplit (text, delimiter, -1); + for (gchar **i = keywords; *i; i++) + match->add (*i); + g_strfreev (keywords); + + query.addCriteria (match); + } + else { + query.setStringMode (false, Ypp::PoolQuery::CONTAINS); + + switch (item) { + case 2: // filelist + query.addStringAttribute (Ypp::PoolQuery::PROVIDES); + query.addStringAttribute (Ypp::PoolQuery::FILELIST); + break; + case 3: // provides + query.addStringAttribute (Ypp::PoolQuery::PROVIDES); + break; + case 4: // requires + query.addStringAttribute (Ypp::PoolQuery::REQUIRES); + break; + } + + query.addStringOr (text); + } + return true; + } + return false; +} + +static gboolean patterns_link_cb (GtkLabel *label, gchar *uri, YGtkPkgSearchEntry *pThis) +{ YGPackageSelector::get()->showFilterWidget (uri); return TRUE; } + +static bool any_pattern_contains (const char *name) +{ + if (strstr (name, " ")) return false; + + // roll on some criteria to find whole-word match (the zypp one doesnt cut it) + struct WholeWordMatch : public Ypp::Match { + WholeWordMatch (const char *word) + : word (word), len (strlen (word)) {} + + virtual bool match (Ypp::Selectable &sel) { + std::string name (sel.name()); + const char *i = name.c_str(); + while ((i = strcasestr (i, word))) { + bool starts = (i == word) || (i[-1] != ' '); + bool ends = (i[len+1] == '\0') || (i[len+1] != ' '); + if (starts && ends) + return true; + i += len; + } + return false; + } + private: const char *word; int len; + }; + + Ypp::PoolQuery query (Ypp::Selectable::PATTERN); + query.addCriteria (new WholeWordMatch (name)); + //query.addStringOr (name); + return query.hasNext(); +} + +GtkWidget *YGtkPkgSearchEntry::createToolbox() +{ + const gchar *text = gtk_entry_get_text (GTK_ENTRY (impl->entry)); + int item = gtk_combo_box_get_active (GTK_COMBO_BOX (impl->combo)); + if (*text) + switch (item) { + case 0: + if (any_pattern_contains (text)) { + char *_text = g_strdup_printf (_("%sPatterns are available%s" + " that correspond to your search criteria."), + "<a href="patterns">", "</a>"); + GtkWidget *label = gtk_label_new (_text); + g_free (_text); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0, .5); + g_signal_connect (G_OBJECT (label), "activate-link", + G_CALLBACK (patterns_link_cb), this); + GtkWidget *icon = gtk_image_new_from_stock ( + GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON); + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show_all (hbox); + return hbox; + } + break; + case 2: { + GtkWidget *label = gtk_label_new ( + _("Search by file name only reliable for installed packages.")); + gtk_misc_set_alignment (GTK_MISC (label), 0, .5); + GtkWidget *icon = gtk_image_new_from_stock ( + GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON); + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + gtk_widget_show_all (hbox); + return hbox; + } + } + return NULL; +} + +void YGtkPkgSearchEntry::setText (Ypp::PoolQuery::StringAttribute attrb, const std::string &text) +{ + int index = 0; + switch (attrb) { + case Ypp::PoolQuery::NAME: case Ypp::PoolQuery::SUMMARY: break; + case Ypp::PoolQuery::DESCRIPTION: index = 1; break; + case Ypp::PoolQuery::FILELIST: index = 2; break; + case Ypp::PoolQuery::PROVIDES: index = 3; break; + case Ypp::PoolQuery::REQUIRES: index = 4; break; + } + + g_signal_handlers_block_by_func (impl->entry, (gpointer) entry_changed_cb, this); + g_signal_handlers_block_by_func (impl->combo, (gpointer) combo_changed_cb, this); + gtk_combo_box_set_active (GTK_COMBO_BOX (impl->combo), index); + gtk_entry_set_text (GTK_ENTRY (impl->entry), text.c_str()); + entry_icons_sync (impl->entry); + g_signal_handlers_unblock_by_func (impl->entry, (gpointer) entry_changed_cb, this); + g_signal_handlers_unblock_by_func (impl->combo, (gpointer) combo_changed_cb, this); +} + +void YGtkPkgSearchEntry::setActivateWidget (GtkWidget *widget) +{ + g_signal_connect (G_OBJECT (impl->entry), "activate", + G_CALLBACK (activate_cb), widget); +} + +Ypp::PoolQuery::StringAttribute YGtkPkgSearchEntry::getAttribute() +{ + int item = gtk_combo_box_get_active (GTK_COMBO_BOX (impl->combo)); + switch (item) { + case 0: return Ypp::PoolQuery::NAME; break; + case 1: return Ypp::PoolQuery::DESCRIPTION; break; + case 2: return Ypp::PoolQuery::FILELIST; break; + case 3: return Ypp::PoolQuery::PROVIDES; break; + case 4: return Ypp::PoolQuery::REQUIRES; break; + } + return (Ypp::PoolQuery::StringAttribute) 0; +} + +std::list std::string YGtkPkgSearchEntry::getText() +{ + std::list std::string keywords; + const char *text = gtk_entry_get_text (GTK_ENTRY (impl->entry)); + const gchar delimiter[2] = { ' ', '\0' }; + gchar **_keywords = g_strsplit (text, delimiter, -1); + for (gchar **i = _keywords; *i; i++) + if (**i) + keywords.push_back (*i); + g_strfreev (_keywords); + return keywords; +} + +std::string YGtkPkgSearchEntry::getTextStr() +{ return gtk_entry_get_text (GTK_ENTRY (impl->entry)); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgsearchentry.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,41 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Zypp search query interface.. +*/ + +#ifndef YGTK_PKG_SEARCH_ENTRY_H +#define YGTK_PKG_SEARCH_ENTRY_H + +#include "ygtkpkgquerywidget.h" +#include "ygtkpkglistview.h" + +struct YGtkPkgSearchEntry : public YGtkPkgQueryWidget +{ + YGtkPkgSearchEntry(); + virtual ~YGtkPkgSearchEntry(); + + virtual GtkWidget *getWidget(); + + virtual bool begsUpdate() { return false; } + virtual void updateList (Ypp::List list) {} + + virtual void clearSelection(); + virtual bool writeQuery (Ypp::PoolQuery &query); + + virtual GtkWidget *createToolbox(); + + void setText (Ypp::PoolQuery::StringAttribute attribute, const std::string &text); + void setActivateWidget (GtkWidget *widget); + + Ypp::PoolQuery::StringAttribute getAttribute(); + std::list std::string getText(); + std::string getTextStr(); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,309 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgStatusBar, a status-bar */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGUI.h" +#include "ygtkpkgstatusbar.h" +#include "YGPackageSelector.h" +#include "YGUtils.h" +#include "ygtkpkglistview.h" +#include "ygtkpkgundolist.h" +#include "yzyppwrapper.h" +#include <gtk/gtk.h> + +static void enlarge_width_size_request_cb (GtkWidget *widget, GtkRequisition *req) +{ req->width += 4; } + +struct LastChange { + GtkWidget *hbox, *icon, *text, *undo_button; + + GtkWidget *getWidget() { return hbox; } + + LastChange() + { + icon = gtk_image_new(); + gtk_widget_set_size_request (icon, 16, 16); + + text = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (text), 0, .5); + undo_button = gtk_button_new_from_stock (GTK_STOCK_UNDO); + YGUtils::shrinkWidget (undo_button); + g_signal_connect (G_OBJECT (undo_button), "size-request", + G_CALLBACK (enlarge_width_size_request_cb), NULL); + g_signal_connect (G_OBJECT (undo_button), "clicked", + G_CALLBACK (undo_clicked_cb), this); + gchar *str = g_strdup_printf ("(<a href="more">%s</a>)", _("view all changes")); + GtkWidget *more = gtk_label_new (str); + g_free (str); + gtk_label_set_use_markup (GTK_LABEL (more), TRUE); + gtk_label_set_track_visited_links (GTK_LABEL (more), FALSE); + g_signal_connect (G_OBJECT (more), "activate-link", + G_CALLBACK (more_link_cb), this); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), text, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), undo_button, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), more, FALSE, TRUE, 0); + } + + void undoChanged (YGtkPkgUndoList *list) + { + int auto_count; + Ypp::Selectable *sel = list->front (&auto_count); + if (sel) { + const char *stock = getStatusStockIcon (*sel); + GdkPixbuf *pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(), + stock, 16, GtkIconLookupFlags (0), NULL); + //GdkPixbuf *_pixbuf = YGUtils::setGray (pixbuf); + gtk_image_set_from_pixbuf (GTK_IMAGE (icon), pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + + const char *action = getStatusAction (sel); + gchar *str; + if (sel->toModifyAuto()) + str = g_strdup_printf (_("<b>%s</b> %d preselected packages"), action, auto_count); + else { + if (auto_count == 0) + str = g_strdup_printf ("<b>%s</b> %s", action, sel->name().c_str()); + else { + char *deps; + if (auto_count == 1) + deps = g_strdup (_("plus 1 dependency")); + else + deps = g_strdup_printf (_("plus %d dependencies"), auto_count); + str = g_strdup_printf ("<b>%s</b> %s, %s", action, sel->name().c_str(), deps); + g_free (deps); + } + } + + gtk_label_set_markup (GTK_LABEL (text), str); + gtk_label_set_attributes (GTK_LABEL (text), NULL); + gtk_widget_set_sensitive (undo_button, TRUE); + g_free (str); + } + else { + gtk_image_clear (GTK_IMAGE (icon)); + gtk_label_set_text (GTK_LABEL (text), _("No changes to perform")); + + PangoAttrList *attrs = pango_attr_list_new(); + pango_attr_list_insert (attrs, pango_attr_foreground_new (110<<8, 110<<8, 110<<8)); + pango_attr_list_insert (attrs, pango_attr_style_new (PANGO_STYLE_ITALIC)); + gtk_label_set_attributes (GTK_LABEL (text), attrs); + pango_attr_list_unref (attrs); + + gtk_widget_set_sensitive (undo_button, FALSE); + } + set_ellipsize (text); + } + + static gboolean more_link_cb (GtkLabel *label, gchar *uri, LastChange *pThis) + { YGPackageSelector::get()->popupChanges(); return TRUE; } + + static void undo_clicked_cb (GtkButton *button, LastChange *pThis) + { + Ypp::Selectable *sel = YGPackageSelector::get()->undoList()->front (NULL); + if (sel) sel->undo(); + } + + void set_ellipsize (GtkWidget *label) + { + GtkWidget *hbox = gtk_widget_get_parent (this->hbox); + if (GTK_WIDGET_REALIZED (hbox)) { + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE); + + GtkRequisition req; + gtk_widget_size_request (hbox, &req); + GtkWidget *window = gtk_widget_get_toplevel (hbox); + bool ellipsize = req.width > window->allocation.width - 10; + + PangoEllipsizeMode mode = ellipsize ? PANGO_ELLIPSIZE_MIDDLE : PANGO_ELLIPSIZE_NONE; + gtk_label_set_ellipsize (GTK_LABEL (label), mode); + gtk_box_set_child_packing (GTK_BOX (this->hbox), label, ellipsize, TRUE, 0, GTK_PACK_START); + } + } +}; + +#if 0 +struct StatChange { + GtkWidget *hbox, *to_install, *to_upgrade, *to_remove; + + GtkWidget *getWidget() { return hbox; } + + StatChange() + { + to_install = gtk_label_new (""); + gtk_label_set_selectable (GTK_LABEL (to_install), TRUE); + to_upgrade = gtk_label_new (""); + gtk_label_set_selectable (GTK_LABEL (to_upgrade), TRUE); + to_remove = gtk_label_new (""); + gtk_label_set_selectable (GTK_LABEL (to_remove), TRUE); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("To install:")), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), to_install, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new(), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("To upgrade:")), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), to_upgrade, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new(), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("To remove:")), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), to_remove, FALSE, TRUE, 0); + } + + void undoChanged (YGtkPkgUndoList *undo) + { + int toInstall = 0, toUpgrade = 0, toRemove = 0; + Ypp::List list (undo->getList()); + for (int i = 0; i < list.size(); i++) { + Ypp::Selectable &sel = list.get (i); + if (sel.toInstall()) { + if (sel.isInstalled()) + toUpgrade++; + else + toInstall++; + } + else if (sel.toRemove()) + toInstall++; + } + + label_set_int (to_install, toInstall); + label_set_int (to_upgrade, toUpgrade); + label_set_int (to_remove, toRemove); + } + + static void label_set_int (GtkWidget *label, int value) + { + gchar *str = g_strdup_printf ("%d", value); + gtk_label_set_text (GTK_LABEL (label), str); + g_free (str); + } +}; +#endif + +#define MIN_FREE_MB_WARN 400 +#define MIN_PERCENT_WARN 90 + +struct DiskChange { + GtkWidget *hbox, *combo, *text; + + GtkWidget *getWidget() { return hbox; } + + DiskChange() + { + GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); + std::vector std::string partitions = Ypp::getPartitionList(); + int active = -1; + for (unsigned int i = 0; i < partitions.size(); i++) { + const std::string &part = partitions[i]; + if (part == "/usr" || part == "/usr/") + active = i; + else if (active == -1 && part == "/") + active = i; + + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, part.c_str(), -1); + } + + combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + YGUtils::shrinkWidget (combo); + gtk_widget_set_name (combo, "small-widget"); + gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (combo), FALSE); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", 0, NULL); + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), active); + g_signal_connect (G_OBJECT (combo), "changed", + G_CALLBACK (combo_changed_cb), this); + + text = gtk_label_new (""); + YGUtils::setWidgetFont (text, PANGO_STYLE_ITALIC, + PANGO_WEIGHT_NORMAL, PANGO_SCALE_MEDIUM); + gtk_label_set_selectable (GTK_LABEL (text), TRUE); + + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new (_("Space available:")), FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), text, FALSE, TRUE, 0); + } + + void undoChanged (YGtkPkgUndoList *undo) + { + GtkTreeIter iter; + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter); + GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); + gchar *mount_point; + gtk_tree_model_get (model, &iter, 0, &mount_point, -1); + const ZyppDu part = Ypp::getPartition (mount_point); + g_free (mount_point); + + int percent = part.total_size ? ((100 * part.pkg_size) / part.total_size) : 0; + long long free = (part.total_size - part.pkg_size) * 1024 * 1024; + + const char *format = "%s"; + if (percent > MIN_PERCENT_WARN && free < MIN_FREE_MB_WARN) + format = "<b><span foreground="red"><b>%s<b></span></b>"; + char *str = g_strdup_printf (format, part.freeAfterCommit().asString().c_str()); + gtk_label_set_markup (GTK_LABEL (text), str); + g_free (str); + } + + static void combo_changed_cb (GtkComboBox *combo, DiskChange *pThis) + { pThis->undoChanged (YGPackageSelector::get()->undoList()); } +}; + +struct YGtkPkgStatusBar::Impl : public YGtkPkgUndoList::Listener { + GtkWidget *box; + LastChange *last; + DiskChange *disk; + + GtkWidget *getWidget() { return box; } + + Impl (YGtkPkgUndoList *undo) + { + last = new LastChange(); + disk = new DiskChange(); + + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), last->getWidget(), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), disk->getWidget(), FALSE, TRUE, 0); + box = hbox; + gtk_widget_show_all (box); + + undoChanged (undo); + undo->addListener (this); + } + + ~Impl() + { + delete last; + delete disk; + YGPackageSelector::get()->undoList()->removeListener (this); + } + + virtual void undoChanged (YGtkPkgUndoList *list) + { + last->undoChanged (list); + disk->undoChanged (list); + } +}; + +YGtkPkgStatusBar::YGtkPkgStatusBar (YGtkPkgUndoList *undo) +: impl (new Impl (undo)) +{} + +YGtkPkgStatusBar::~YGtkPkgStatusBar() +{ delete impl; } + +GtkWidget *YGtkPkgStatusBar::getWidget() +{ return impl->getWidget(); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgstatusbar.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,27 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Status bar, showing the changes to apply. +*/ + +#ifndef YGTK_PKG_STATUS_BAR_H +#define YGTK_PKG_STATUS_BAR_H + +#include <gtk/gtkwidget.h> + +struct YGtkPkgUndoList; + +struct YGtkPkgStatusBar +{ + YGtkPkgStatusBar (YGtkPkgUndoList *undo); + ~YGtkPkgStatusBar(); + + GtkWidget *getWidget(); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,318 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgUndoList, list of changes */ +// check the header file for information about this utility + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGDialog.h" +#include "YGUtils.h" +#include "ygtkpkgundolist.h" +#include <gtk/gtk.h> + +struct YGtkPkgUndoList::Impl : public Ypp::SelListener +{ + YGtkPkgUndoList *parent; + Ypp::List changes; + std::list <YGtkPkgUndoList::Listener *> listeners; + + Impl (YGtkPkgUndoList *parent) : parent (parent), changes (0) + { Ypp::addSelListener (this); } + + ~Impl() + { Ypp::removeSelListener (this); } + + virtual void selectableModified() + { + // remove any previously changed selectable now undone + Ypp::List _changes (changes.size()); + for (int i = 0; i < changes.size(); i++) { + Ypp::Selectable &sel = changes.get(i); + if (sel.toModify()) + _changes.append (sel); + } + changes = _changes; + + // check for any new changed selectable + // first, check for user-modified, then for auto ones, to keep them ordered + for (int step = 0; step < 2; step++) { + Ypp::PoolQuery query (Ypp::Selectable::ALL); + query.addCriteria (new Ypp::StatusMatch (Ypp::StatusMatch::TO_MODIFY)); + while (query.hasNext()) { + Ypp::Selectable sel = query.next(); + if ((step == 0 && sel.toModifyAuto()) || (step == 1 && !sel.toModifyAuto())) + continue; + if (changes.find (sel) == -1) + changes.append (sel); + } + } + + for (std::list <YGtkPkgUndoList::Listener *>::iterator it = listeners.begin(); + it != listeners.end(); it++) + (*it)->undoChanged (parent); + } +}; + +YGtkPkgUndoList::YGtkPkgUndoList() +: impl (new Impl (this)) {} + +YGtkPkgUndoList::~YGtkPkgUndoList() +{ delete impl; } + +Ypp::Selectable *YGtkPkgUndoList::front (int *autoCount) +{ + Ypp::Selectable *sel = 0; + if (autoCount) + *autoCount = 0; + for (int i = impl->changes.size()-1; i >= 0; i--) { + sel = &impl->changes.get (i); + if (sel->toModifyAuto()) { + if (autoCount) (*autoCount)++; + } + else + break; + } + return sel; +} + +Ypp::List YGtkPkgUndoList::getList() +{ return impl->changes; } + +void YGtkPkgUndoList::addListener (Listener *listener) +{ impl->listeners.push_back (listener); } + +void YGtkPkgUndoList::removeListener (Listener *listener) +{ impl->listeners.remove (listener); } + +#include "YGPackageSelector.h" +#include "ygtkpkglistview.h" + +struct ChangeSizeInfo : public YGtkPkgUndoList::Listener { + GtkWidget *vbox, *disk_label, *download_label, *warn_label; + + GtkWidget *getWidget() + { return vbox; } + + ChangeSizeInfo() + { + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + GtkWidget *line; + line = createSizeWidget (_("Disk space required:"), &disk_label); + gtk_box_pack_start (GTK_BOX (hbox), line, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), gtk_label_new ("/"), FALSE, TRUE, 0); + line = createSizeWidget (_("Download size:"), &download_label); + gtk_box_pack_start (GTK_BOX (hbox), line, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + + warn_label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (warn_label), 0, .5); + gtk_label_set_selectable (GTK_LABEL (warn_label), TRUE); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), warn_label, FALSE, TRUE, 0); + gtk_widget_show (vbox); + + undoChanged (YGPackageSelector::get()->undoList()); + YGPackageSelector::get()->undoList()->addListener (this); + } + + ~ChangeSizeInfo() + { YGPackageSelector::get()->undoList()->removeListener (this); } + + virtual void undoChanged (YGtkPkgUndoList *undo) + { + Size_t disk, download; + Ypp::List list = undo->getList(); + for (int i = 0; i < list.size(); i++) { + Ypp::Selectable sel = list.get (i); + if (sel.toInstall()) { + Ypp::Version candidate = sel.candidate(); + download += candidate.downloadSize(); + if (sel.isInstalled()) + disk += candidate.size() - sel.installed().size(); + else + disk += candidate.size(); + } + else if (sel.toRemove()) + disk -= sel.installed().size(); + } + + gtk_label_set_text (GTK_LABEL (disk_label), disk.asString().c_str()); + gtk_label_set_text (GTK_LABEL (download_label), download.asString().c_str()); + + gtk_widget_hide (warn_label); + ZyppDuSet diskUsage = zypp::getZYpp()->diskUsage(); + for (ZyppDuSet::iterator it = diskUsage.begin(); it != diskUsage.end(); it++) { + const ZyppDu &point = *it; + if (!point.readonly && point.freeAfterCommit() < 0) { + // Translators: please keep the %s order intact. They refer to mount_point, needed_space + char *over = g_strdup_printf (_("Partition %s is %s over-capacity"), + point.dir.c_str(), point.freeAfterCommit().asString().c_str()); + // Translators: please keep the %s order intact. They refer to used_space, total_space + char *fill = g_strdup_printf (_("%s filled out of %s"), + point.usedAfterCommit().asString().c_str(), + point.totalSize().asString().c_str()); + char *markup = g_strdup_printf ( + "<b><span color="red">%s (%s)</span></b>", over, fill); + gtk_label_set_markup (GTK_LABEL (warn_label), markup); + g_free (over); g_free (fill); g_free (markup); + gtk_widget_show (warn_label); + break; + } + } + } + + static GtkWidget *createSizeWidget (const char *label, GtkWidget **widget) + { + *widget = gtk_label_new (""); + YGUtils::setWidgetFont (*widget, PANGO_STYLE_ITALIC, + PANGO_WEIGHT_NORMAL, PANGO_SCALE_MEDIUM); + gtk_misc_set_alignment (GTK_MISC (*widget), 1, .5); + gtk_label_set_selectable (GTK_LABEL (*widget), TRUE); + + GtkWidget *title = gtk_label_new (label); + gtk_misc_set_alignment (GTK_MISC (title), 0, .5); + + GtkWidget *hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), title, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), *widget, TRUE, TRUE, 0); + return hbox; + } +}; + +#define YAST_SYSCONFIG "/etc/sysconfig/yast2" +#include <zypp/base/Sysconfig.h> + +enum { CLOSE_MODE, RESTART_MODE, SUMMARY_MODE }; + +static int read_PKGMGR_ACTION_AT_EXIT() +{ // from yast2-ncurses, NCPackageSelector.cc + std::map <std::string, std::string> sysconfig = + zypp::base::sysconfig::read (YAST_SYSCONFIG); + std::map <std::string, std::string>::const_iterator it = + sysconfig.find("PKGMGR_ACTION_AT_EXIT"); + if (it != sysconfig.end()) { + yuiMilestone() << "Read sysconfig's action at pkg mgr exit value: " << it->second << endl; + std::string mode (it->second); + if (mode == "restart") + return RESTART_MODE; + else if (mode == "summary") + return SUMMARY_MODE; + else //if (mode == "close") + return CLOSE_MODE; + } + yuiMilestone() << "Could not read PKGMGR_ACTION_AT_EXIT variable from sysconfig" << endl; + return false; +} + +static void write_PKGMGR_ACTION_AT_EXIT (int mode) +{ // from yast2-ncurses, NCPackageSelector.cc + // this is really, really stupid. But we have no other iface for writing sysconfig so far + std::string _mode; + switch (mode) { + case CLOSE_MODE: _mode = "close"; break; + case RESTART_MODE: _mode = "restart"; break; + case SUMMARY_MODE: _mode = "summary"; break; + } + int ret = -1; + string cmd = "sed -i 's/^[ \t]*PKGMGR_ACTION_AT_EXIT.*$/PKGMGR_ACTION_AT_EXIT="" + + _mode + ""/' " + YAST_SYSCONFIG; + ret = system(cmd.c_str()); + yuiMilestone() << "Executing system cmd " << cmd << " returned " << ret << endl; +} + +static void close_when_done_toggled_cb (GtkToggleButton *button) +{ + gtk_toggle_button_set_inconsistent (button, FALSE); + int mode = gtk_toggle_button_get_active (button) ? CLOSE_MODE : RESTART_MODE; + write_PKGMGR_ACTION_AT_EXIT (mode); +} + +static GtkWidget *create_close_when_done_check() +{ + GtkWidget *check_box = gtk_check_button_new_with_label (_("Close software manager when done")); + int mode = read_PKGMGR_ACTION_AT_EXIT(); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_box), mode == CLOSE_MODE); + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (check_box), mode == SUMMARY_MODE); + if (access (YAST_SYSCONFIG, W_OK) != 0) { + gtk_widget_set_sensitive (check_box, FALSE); + gtk_widget_set_tooltip_text (check_box, "Cannot write to " YAST_SYSCONFIG); + } + g_signal_connect_after (G_OBJECT (check_box), "toggled", + G_CALLBACK (close_when_done_toggled_cb), NULL); + gtk_widget_show (check_box); + return check_box; +} + +struct YGtkPkgUndoView : public YGtkPkgListView, YGtkPkgUndoList::Listener +{ + YGtkPkgUndoView() + : YGtkPkgListView (true, -1, true, false, true) + { + addImageColumn (NULL, STATUS_ICON_PROP, true); + addTextColumn (_("Name"), ACTION_NAME_PROP, true, -1); + addTextColumn (_("Version"), SINGLE_VERSION_PROP, true, 125); + addButtonColumn (_("Revert?"), UNDO_BUTTON_PROP); + + undoChanged (YGPackageSelector::get()->undoList()); + YGPackageSelector::get()->undoList()->addListener (this); + } + + ~YGtkPkgUndoView() + { YGPackageSelector::get()->undoList()->removeListener (this); } + + virtual void undoChanged (YGtkPkgUndoList *undo) + { setList (undo->getList()); } +}; + +#include "ygtkpkghistorydialog.h" + +bool YGtkPkgUndoList::popupDialog (bool onApply) +{ + GtkMessageType type = onApply ? GTK_MESSAGE_QUESTION : GTK_MESSAGE_OTHER; + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GtkDialogFlags (0), type, GTK_BUTTONS_NONE, _("Summary of changes")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Review the changes to perform.")); + if (onApply) + gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_NO, + GTK_STOCK_APPLY, GTK_RESPONSE_YES, NULL); + else { + GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_UNDO, GTK_ICON_SIZE_DIALOG); + gtk_widget_show (image); + gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image); + gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("See History"), 1, + GTK_STOCK_CLOSE, GTK_RESPONSE_YES, NULL); + + // work-around for GTK bug: when you set a custom icon, it is packed as expanded + GtkWidget *hbox = gtk_widget_get_parent (image); + gtk_box_set_child_packing (GTK_BOX (hbox), image, FALSE, TRUE, 0, GTK_PACK_START); + } + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500); + + YGtkPkgUndoView view; + ChangeSizeInfo change_size; + + GtkWidget *vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), view.getWidget(), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), change_size.getWidget(), FALSE, TRUE, 0); + if (onApply) + gtk_box_pack_start (GTK_BOX (vbox), create_close_when_done_check(), FALSE, TRUE, 0); + gtk_widget_show (vbox); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox); + + int ret = gtk_dialog_run (GTK_DIALOG (dialog)); + if (ret == 1) + YGPackageSelector::get()->showHistoryDialog(); + gtk_widget_destroy (dialog); + return ret == GTK_RESPONSE_YES; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgundolist.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,35 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* A Ypp::List that stores only +*/ + +#ifndef YGTK_PKG_UNDO_LIST_H +#define YGTK_PKG_UNDO_LIST_H + +#include "yzyppwrapper.h" + +struct YGtkPkgUndoList +{ + YGtkPkgUndoList(); + ~YGtkPkgUndoList(); + + Ypp::Selectable *front (int *autoCount); + Ypp::List getList(); + + struct Listener { + virtual void undoChanged (YGtkPkgUndoList *list) = 0; + }; + + void addListener (Listener *listener); + void removeListener (Listener *listener); + + bool popupDialog (bool onApply); + + struct Impl; + Impl *impl; +}; + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,248 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* YGtkPkgVestigialDialog, dialog */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#include "config.h" +#include "YGDialog.h" +#include "ygtkpkgvestigialdialog.h" +#include "ygtkpkglistview.h" +#include "YGPackageSelector.h" +#include "yzyppwrapper.h" +#include <gtk/gtk.h> + +#include <zypp/parser/HistoryLogReader.h> +#define FILENAME "/var/log/zypp/history" + +struct ZyppVestigialParser +{ + std::set std::string m_dependencies; + + ZyppVestigialParser() + { + zypp::parser::HistoryLogReader parser (FILENAME, boost::ref (*this)); + try { + parser.readAll(); + } + catch (const zypp::Exception &ex) { + yuiWarning () << "Error: Could not load log file" << FILENAME << ": " + << ex.asUserHistory() << std::endl; + } + } + + bool operator() (const zypp::HistoryItem::Ptr &item) + { + if (item->action.toEnum() == zypp::HistoryActionID::INSTALL_e) { + zypp::HistoryItemInstall *_item = + static_cast <zypp::HistoryItemInstall *> (item.get()); + + const std::string &name (_item->name); + bool autoreq = _item->reqby.empty(); + std::string repoName, repoUrl; + Ypp::getRepositoryFromAlias (_item->repoalias, repoName, repoUrl); + bool update = repoUrl.find ("update") != std::string::npos; + + if (!update) { // updates are ambiguous + if (autoreq) + m_dependencies.insert (name); + else + m_dependencies.erase (name); + } + } + + return true; + } +}; + +struct YGtkPkgVestigialDialog::Impl : public YGtkPkgListView::Listener +{ + GtkWidget *dialog; + YGtkPkgListView *view; + GtkWidget *progressbar; + + virtual void selectionChanged() + { + Ypp::List selected (view->getSelected()); + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), 1, selected.size()); + } + + void goto_clicked() + { + Ypp::List selected (view->getSelected()); + if (selected.size()) { + std::string name (selected.get(0).name()); + YGPackageSelector::get()->searchFor (Ypp::PoolQuery::NAME, name); + } + gtk_widget_hide (dialog); + } + + void remove_all() + { + view->getList().remove(); + } + + static void response_cb (GtkDialog *dialog, gint response, Impl *pThis) + { + switch (response) { + case 1: pThis->goto_clicked(); break; + case 2: pThis->remove_all(); break; + default: gtk_widget_hide (GTK_WIDGET (pThis->dialog)); break; + } + } + + void set_progress (gdouble fraction) + { + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progressbar), fraction); + while (g_main_context_iteration (NULL, FALSE)) ; + } +}; + +static bool testIsNeededDependency (ZyppSelectable zsel) +{ // pass only installed packages: zsel->status() == S_KeepInstalled + zsel->setStatus (zypp::ui::S_Del); + zypp::Resolver_Ptr zResolver = zypp::getZYpp()->resolver(); + bool noProbs = zResolver->resolvePool(); + zResolver->undo(); + zsel->setStatus (zypp::ui::S_KeepInstalled); + return !noProbs; +} + +static void filterWhenContains (Ypp::Collection &col, std::list <ZyppSelectable> &pkgs) +{ + std::list <ZyppSelectable>::iterator it = pkgs.begin(); + while (it != pkgs.end()) { + Ypp::Selectable p (*it); + if (col.contains (p)) + it = pkgs.erase (it); + else it++; + } +} + +static gboolean fill_list_idle_cb (void *data) +{ + YGtkPkgVestigialDialog::Impl *impl = (YGtkPkgVestigialDialog::Impl *) data; + + // 1. get list of packages installed as dependencies + ZyppVestigialParser parser; + const std::set std::string &deps = parser.m_dependencies; + impl->set_progress (.1); + // 2. map string names to zypp objects and check whether they are still necessary + int size = deps.size(), i = 0; + std::list <ZyppSelectable> unneeded; + for (std::set std::string::const_iterator it = deps.begin(); + it != deps.end(); it++) { + Ypp::PoolQuery query (Ypp::Selectable::PACKAGE); + query.addStringOr (*it); + query.addStringAttribute (Ypp::PoolQuery::NAME); + query.setStringMode (true, Ypp::PoolQuery::EXACT); + if (query.hasNext()) { + Ypp::Selectable sel (query.next()); + ZyppSelectable zsel = sel.zyppSel(); + if (zsel->status() == zypp::ui::S_KeepInstalled) + if (!testIsNeededDependency (zsel)) + unneeded.push_back (zsel); + } + + if ((i % 2) == 0) + impl->set_progress (.1 + ((i / (gdouble) size) * .80)); + i++; + } + // 3. filter those installed by a container + impl->set_progress (.90); + Ypp::LangQuery langQuery; + size = langQuery.guessSize(); i = 0; + while (langQuery.hasNext()) { + Ypp::Selectable sel (langQuery.next()); + if (!sel.isInstalled()) continue; + Ypp::Collection col (sel); + filterWhenContains (col, unneeded); + } + impl->set_progress (.95); + Ypp::PoolQuery patternQuery (Ypp::Selectable::PATTERN); + patternQuery.addCriteria (new Ypp::StatusMatch (Ypp::StatusMatch::IS_INSTALLED)); + size = patternQuery.guessSize(); i = 0; + while (patternQuery.hasNext()) { + Ypp::Selectable sel (patternQuery.next()); + Ypp::Collection col (sel); + filterWhenContains (col, unneeded); + } + + Ypp::List list (unneeded.size()); + for (std::list <ZyppSelectable>::const_iterator it = unneeded.begin(); + it != unneeded.end(); it++) + list.append (*it); + impl->view->setList (list); + + gtk_widget_hide (impl->progressbar); + gdk_window_set_cursor (impl->dialog->window, NULL); + gtk_dialog_set_response_sensitive (GTK_DIALOG (impl->dialog), 2, unneeded.size()); + gtk_window_present (GTK_WINDOW (impl->dialog)); + return FALSE; +} + +YGtkPkgVestigialDialog::YGtkPkgVestigialDialog() +{ + GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE, + _("Show Unneeded Dependencies")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("This is a listing of dependencies no longer used. It is neither " + "accurate, nor comprehensive. Use with care.")); + + impl = new Impl(); + GtkWidget *action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog)); + impl->progressbar = gtk_progress_bar_new(); + gtk_widget_set_size_request (impl->progressbar, 0, 0); + gtk_container_add (GTK_CONTAINER (action_area), impl->progressbar); + + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_JUMP_TO, 1); + gtk_dialog_add_button (GTK_DIALOG (dialog), _("Remove All"), 2); + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), 1, FALSE); + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), 2, FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 650, 600); + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (Impl::response_cb), impl); + g_signal_connect (G_OBJECT (dialog), "delete-event", + G_CALLBACK (gtk_true), NULL); + + impl->view = new YGtkPkgListView (true, Ypp::List::NAME_SORT, false, true); + impl->view->addCheckColumn (INSTALLED_CHECK_PROP); + impl->view->addTextColumn (_("Name"), NAME_SUMMARY_PROP, true, -1); + impl->view->addTextColumn (_("Version"), VERSION_PROP, true, 125); + impl->view->addTextColumn (_("Size"), SIZE_PROP, false, 85); + impl->view->addTextColumn (_("Repository"), REPOSITORY_PROP, false, 180); + impl->view->addTextColumn (_("Supportability"), SUPPORT_PROP, false, 120); + impl->view->setListener (impl); + + GtkWidget *content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_container_add (GTK_CONTAINER (content), impl->view->getWidget()); + + gtk_widget_show_all (dialog); + impl->dialog = dialog; + + GdkCursor *cursor = gdk_cursor_new (GDK_WATCH); + gdk_window_set_cursor (dialog->window, cursor); + gdk_cursor_unref (cursor); + + g_idle_add_full (G_PRIORITY_LOW, fill_list_idle_cb, impl, NULL); +} + +YGtkPkgVestigialDialog::~YGtkPkgVestigialDialog() +{ + delete impl->view; + gtk_widget_destroy (impl->dialog); + delete impl; +} + +void YGtkPkgVestigialDialog::popup() +{ gtk_window_present (GTK_WINDOW (impl->dialog)); } +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtkpkgvestigialdialog.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,28 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* A dialog to help the user track no longer needed dependencies. +*/ + +#ifndef YGTK_PKG_VESTIGIAL_DIALOG_H +#define YGTK_PKG_VESTIGIAL_DIALOG_H + +#include <gtk/gtkwidget.h> + +struct YGtkPkgVestigialDialog +{ + YGtkPkgVestigialDialog(); + ~YGtkPkgVestigialDialog(); + + void popup(); + + struct Impl; + Impl *impl; +}; + +// you do not want to use this class directly: +// use YGPackageSelector::get()->showVestigialDialog() + +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,221 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkTreeModel, C++ wrapper for gtk+ */ +// check the header file for information about this wrapper + +#include <gtk/gtk.h> +#include "ygtktreemodel.h" + +#define YGTK_TYPE_WRAP_MODEL (ygtk_wrap_model_get_type ()) +#define YGTK_WRAP_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_WRAP_MODEL, YGtkWrapModel)) +#define YGTK_WRAP_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_WRAP_MODEL, YGtkWrapModelClass)) +#define YGTK_IS_WRAP_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YGTK_TYPE_WRAP_MODEL)) +#define YGTK_IS_WRAP_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YGTK_TYPE_WRAP_MODEL)) +#define YGTK_WRAP_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_WRAP_MODEL, YGtkWrapModelClass)) + +struct YGtkWrapModel +{ + GObject parent; + YGtkTreeModel *model; + struct Notify; + Notify *notify; +}; + +struct YGtkWrapModelClass +{ + GObjectClass parent_class; +}; + +// bridge as we don't want to mix c++ class polymorphism and gobject +static void ygtk_wrap_model_entry_changed (YGtkWrapModel *model, int row); +static void ygtk_wrap_model_entry_inserted (YGtkWrapModel *model, int row); +static void ygtk_wrap_model_entry_deleted (YGtkWrapModel *model, int row); + +struct YGtkWrapModel::Notify : public YGtkTreeModel::Listener { +YGtkWrapModel *model; + Notify (YGtkWrapModel *model) : model (model) {} + virtual void rowChanged (int row) + { ygtk_wrap_model_entry_changed (model, row); } + virtual void rowInserted (int row) + { ygtk_wrap_model_entry_inserted (model, row); } + virtual void rowDeleted (int row) + { ygtk_wrap_model_entry_deleted (model, row); } +}; + +static void ygtk_wrap_model_tree_model_init (GtkTreeModelIface *iface); + +G_DEFINE_TYPE_WITH_CODE (YGtkWrapModel, ygtk_wrap_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, ygtk_wrap_model_tree_model_init)) + +static void ygtk_wrap_model_init (YGtkWrapModel *zmodel) +{} + +static void ygtk_wrap_model_finalize (GObject *object) +{ + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (object); + delete ymodel->model; + ymodel->model = NULL; + delete ymodel->notify; + ymodel->notify = NULL; + G_OBJECT_CLASS (ygtk_wrap_model_parent_class)->finalize (object); +} + +static GtkTreeModelFlags ygtk_wrap_model_get_flags (GtkTreeModel *model) +{ return (GtkTreeModelFlags) (GTK_TREE_MODEL_ITERS_PERSIST|GTK_TREE_MODEL_LIST_ONLY); } + +static gboolean ygtk_wrap_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, + GtkTreePath *path) +{ // from Path to Iter + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + gint index = gtk_tree_path_get_indices (path)[0]; + iter->user_data = GINT_TO_POINTER (index); + int rowsNb = ymodel->model->rowsNb(); + if (!rowsNb && index == 0 && ymodel->model->showEmptyEntry()) + return TRUE; + return index < rowsNb; +} + +static GtkTreePath *ygtk_wrap_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) +{ // from Iter to Path + int index = GPOINTER_TO_INT (iter->user_data); + GtkTreePath *path = gtk_tree_path_new(); + gtk_tree_path_append_index (path, index); + return path; +} + +static gboolean ygtk_wrap_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter) +{ + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + int index = GPOINTER_TO_INT (iter->user_data) + 1; + iter->user_data = GINT_TO_POINTER (index); + int rowsNb = ymodel->model->rowsNb(); + return index < rowsNb; +} + +static gboolean ygtk_wrap_model_iter_parent (GtkTreeModel *, GtkTreeIter *, GtkTreeIter *) +{ return FALSE; } + +static gboolean ygtk_wrap_model_iter_has_child (GtkTreeModel *, GtkTreeIter *) +{ return FALSE; } + +static gint ygtk_wrap_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) +{ return 0; } + +static gboolean ygtk_wrap_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, + GtkTreeIter *parent, gint index) +{ + if (parent) return FALSE; + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + iter->user_data = GINT_TO_POINTER (index); + int rowsNb = ymodel->model->rowsNb(); + if (!rowsNb && index == 0 && ymodel->model->showEmptyEntry()) + return TRUE; + return index < rowsNb; +} + +static gboolean ygtk_wrap_model_iter_children ( + GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent) +{ return ygtk_wrap_model_iter_nth_child (model, iter, parent, 0); } + +void ygtk_wrap_model_entry_changed (YGtkWrapModel *model, int row) +{ + GtkTreeIter iter; + iter.user_data = GINT_TO_POINTER (row); + GtkTreePath *path = ygtk_wrap_model_get_path (GTK_TREE_MODEL (model), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); +} + +void ygtk_wrap_model_entry_inserted (YGtkWrapModel *ymodel, int row) +{ + GtkTreeModel *model = GTK_TREE_MODEL (ymodel); + GtkTreeIter iter; + iter.user_data = GINT_TO_POINTER (row); + GtkTreePath *path = ygtk_wrap_model_get_path (model, &iter); + + if (row == 0 && ymodel->model->rowsNb() == 1 && ymodel->model->showEmptyEntry()) + gtk_tree_model_row_changed (model, path, &iter); + else + gtk_tree_model_row_inserted (model, path, &iter); + gtk_tree_path_free (path); +} + +void ygtk_wrap_model_entry_deleted (YGtkWrapModel *ymodel, int row) +{ + GtkTreeModel *model = GTK_TREE_MODEL (ymodel); + GtkTreeIter iter; + iter.user_data = GINT_TO_POINTER (row); + GtkTreePath *path = ygtk_wrap_model_get_path (model, &iter); + + if (row == 0 && ymodel->model->rowsNb() == 1 && ymodel->model->showEmptyEntry()) + gtk_tree_model_row_changed (model, path, &iter); + else + gtk_tree_model_row_deleted (model, path); + gtk_tree_path_free (path); +} + +static gint ygtk_wrap_model_get_n_columns (GtkTreeModel *model) +{ + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + return ymodel->model->columnsNb(); +} + +static GType ygtk_wrap_model_get_column_type (GtkTreeModel *model, gint column) +{ + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + return ymodel->model->columnType (column); +} + +static void ygtk_wrap_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, + gint column, GValue *value) +{ + int row = GPOINTER_TO_INT (iter->user_data); + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + g_value_init (value, ymodel->model->columnType (column)); + if (row == 0 && ymodel->model->rowsNb() == 0) + row = -1; + ymodel->model->getValue (row, column, value); +} + +GtkTreeModel *ygtk_tree_model_new (YGtkTreeModel *model) +{ + YGtkWrapModel *ymodel = (YGtkWrapModel *) g_object_new (YGTK_TYPE_WRAP_MODEL, NULL); + ymodel->model = model; + ymodel->notify = new YGtkWrapModel::Notify (ymodel); + model->listener = ymodel->notify; + return GTK_TREE_MODEL (ymodel); +} + +YGtkTreeModel *ygtk_tree_model_get_model (GtkTreeModel *model) +{ + YGtkWrapModel *ymodel = YGTK_WRAP_MODEL (model); + return ymodel->model; +} + +static void ygtk_wrap_model_class_init (YGtkWrapModelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = ygtk_wrap_model_finalize; +} + +static void ygtk_wrap_model_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = ygtk_wrap_model_get_flags; + iface->get_n_columns = ygtk_wrap_model_get_n_columns; + iface->get_column_type = ygtk_wrap_model_get_column_type; + iface->get_iter = ygtk_wrap_model_get_iter; + iface->get_path = ygtk_wrap_model_get_path; + iface->get_value = ygtk_wrap_model_get_value; + iface->iter_next = ygtk_wrap_model_iter_next; + iface->iter_children = ygtk_wrap_model_iter_children; + iface->iter_has_child = ygtk_wrap_model_iter_has_child; + iface->iter_n_children = ygtk_wrap_model_iter_n_children; + iface->iter_nth_child = ygtk_wrap_model_iter_nth_child; + iface->iter_parent = ygtk_wrap_model_iter_parent; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/ygtktreemodel.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,37 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Wraps GtkTreeModel as a C++ model. +*/ + +#ifndef YGTK_TREE_MODEL_H +#define YGTK_TREE_MODEL_H + +#include <gtk/gtktreemodel.h> + +struct YGtkTreeModel +{ + virtual void getValue (int row, int col, GValue *value) = 0; + virtual int rowsNb() = 0; + virtual int columnsNb() const = 0; + virtual GType columnType (int col) const = 0; + // if 'showEmptyEntry' will call getValue(row=-1) for the empty entry + virtual bool showEmptyEntry() const = 0; + + struct Listener { + virtual void rowChanged (int row) = 0; + virtual void rowInserted (int row) = 0; + virtual void rowDeleted (int row) = 0; + }; + Listener *listener; + + virtual ~YGtkTreeModel() {} +}; + +GtkTreeModel *ygtk_tree_model_new (YGtkTreeModel *model); + +YGtkTreeModel *ygtk_tree_model_get_model (GtkTreeModel *model); + +#endif /*YGTK_TREE_MODEL_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,139 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* Tags PackageKit translator */ +// check the header file for information about this translator + +/* + Textdomain "gtk" + */ + +#include "YGi18n.h" +#define YUILogComponent "gtk-pkg" +#include <YUILog.h> +#include "yzypptags.h" +#include <string> + +/** + * translations taken from packagekit + **/ +const char * +zypp_tag_group_enum_to_localised_text (YPkgGroupEnum group) +{ + switch (group) { + case YPKG_GROUP_EDUCATION: return _( "Education" ); + case YPKG_GROUP_GAMES: return _( "Games" ); + case YPKG_GROUP_GRAPHICS: return _( "Graphics" ); + case YPKG_GROUP_OFFICE: return _( "Office" ); + case YPKG_GROUP_PROGRAMMING: return _( "Programming" ); + case YPKG_GROUP_MULTIMEDIA: return _( "Multimedia" ); + case YPKG_GROUP_SYSTEM: return _( "System" ); + // Translators: keep translation short + case YPKG_GROUP_DESKTOP_GNOME: return _( "Desktop (GNOME)" ); + // Translators: keep translation short + case YPKG_GROUP_DESKTOP_KDE: return _( "Desktop (KDE)" ); + // Translators: keep translation short + case YPKG_GROUP_DESKTOP_XFCE: return _( "Desktop (XFCE)" ); + // Translators: keep translation short + case YPKG_GROUP_DESKTOP_OTHER: return _( "Desktop (Others)" ); + case YPKG_GROUP_PUBLISHING: return _( "Publishing" ); + // Translators: keep translation short + case YPKG_GROUP_ADMIN_TOOLS: return _( "Admin Tools" ); + case YPKG_GROUP_LOCALIZATION: return _( "Localization" ); + case YPKG_GROUP_SECURITY: return _( "Security" ); + case YPKG_GROUP_COMMUNICATION: return _( "Communication" ); + case YPKG_GROUP_NETWORK: return _( "Network" ); + case YPKG_GROUP_DOCUMENTATION: return _( "Documentation" ); + case YPKG_GROUP_UTILITIES: return _( "Utilities" ); + + case YPKG_GROUP_UNKNOWN: return _( "Unknown" ); + case YPKG_GROUP_SUGGESTED: return _( "Suggested" ); + case YPKG_GROUP_RECOMMENDED: return _( "Recommended" ); + // Translators: this refers to packages no longer available in any repository. + case YPKG_GROUP_ORPHANED: return _( "Unmaintained" ); + case YPKG_GROUP_RECENT: return _( "Recent" ); + case YPKG_GROUP_MULTIVERSION: return _( "Multiversion" ); + case YPKG_GROUP_TOTAL: break; + } + return ""; +} + +const char * +zypp_tag_enum_to_icon (YPkgGroupEnum group) +{ + // mosts icons are from /usr/share/icons/hicolor/32x32/apps/package* + switch (group) + { + case YPKG_GROUP_EDUCATION: return( "package_edutainment" ); + case YPKG_GROUP_GAMES: return( "package_games" ); + case YPKG_GROUP_GRAPHICS: return( "package_graphics" ); + case YPKG_GROUP_OFFICE: return( "applications-office" ); + case YPKG_GROUP_PROGRAMMING: return( "package_development" ); + case YPKG_GROUP_MULTIMEDIA: return( "package_multimedia" ); + case YPKG_GROUP_SYSTEM: return( "applications-system" ); + case YPKG_GROUP_DESKTOP_GNOME: return( "pattern-gnome" ); + case YPKG_GROUP_DESKTOP_KDE: return( "pattern-kde" ); + case YPKG_GROUP_DESKTOP_XFCE: return( "pattern-xfce" ); + case YPKG_GROUP_DESKTOP_OTHER: return( "user-desktop" ); + case YPKG_GROUP_PUBLISHING: return( "package_office_projectmanagement" ); + case YPKG_GROUP_ADMIN_TOOLS: return( "yast-sysconfig" ); + case YPKG_GROUP_LOCALIZATION: return( "yast-language" ); + case YPKG_GROUP_SECURITY: return( "yast-security" ); + case YPKG_GROUP_COMMUNICATION: return( "yast-modem" ); + case YPKG_GROUP_NETWORK: return( "package_network" ); + case YPKG_GROUP_DOCUMENTATION: return( "package_documentation" ); + case YPKG_GROUP_UTILITIES: return( "package_utilities" ); + + case YPKG_GROUP_SUGGESTED: return( "gtk-about" ); + case YPKG_GROUP_RECOMMENDED: return( "gtk-about" ); + case YPKG_GROUP_ORPHANED: return( "gtk-missing-image" ); + case YPKG_GROUP_RECENT: return( "gtk-new" ); + case YPKG_GROUP_MULTIVERSION: return( "gtk-dnd-multiple" ); + case YPKG_GROUP_UNKNOWN: return( "package_main" ); + + case YPKG_GROUP_TOTAL: break; + } + return ""; +} + +YPkgGroupEnum +zypp_tag_convert (const std::string &groupu) +{ + std::string group (groupu); // lower-case + for (unsigned int i = 0; i < group.length(); i++) + if (group[i] >= 'A' && group[i] <= 'Z') + group[i] = group[i] - 'A' + 'a'; + + // yast2-qt: (modified to speed up) + if (group.compare (0, 22, "productivity/archiving") == 0) return YPKG_GROUP_ADMIN_TOOLS; + if (group.compare (0, 23, "productivity/clustering") == 0) return YPKG_GROUP_ADMIN_TOOLS; + if (group.compare (0, 22, "productivity/databases") == 0) return YPKG_GROUP_ADMIN_TOOLS; + if (group.compare (0, 17, "system/monitoring") == 0) return YPKG_GROUP_ADMIN_TOOLS; + if (group.compare (0, 17, "system/management") == 0) return YPKG_GROUP_ADMIN_TOOLS; + if (group.compare (0, 23, "productivity/publishing") == 0) return YPKG_GROUP_PUBLISHING; + if (group.compare (0, 22, "productivity/telephony") == 0) return YPKG_GROUP_COMMUNICATION; + if (group.compare (0, 19, "amusements/teaching") == 0) return YPKG_GROUP_EDUCATION; + if (group.compare (0, 17, "publishing/office") == 0) return YPKG_GROUP_OFFICE; + if (group.compare (0, 17, "productivity/text") == 0) return YPKG_GROUP_OFFICE; + if (group.compare (0, 20, "productivity/editors") == 0) return YPKG_GROUP_OFFICE; + if (group.compare (0, 21, "productivity/graphics") == 0) return YPKG_GROUP_GRAPHICS; + if (group.compare (0, 10, "amusements") == 0) return YPKG_GROUP_GAMES; + if (group.compare (0, 19, "system/localization") == 0) return YPKG_GROUP_LOCALIZATION; + if (group.compare (0, 11, "development") == 0) return YPKG_GROUP_PROGRAMMING; + if (group.compare (0, 20, "productivity/network") == 0) return YPKG_GROUP_NETWORK; + if (group.compare (0, 21, "productivity/security") == 0) return YPKG_GROUP_SECURITY; + if (group.compare (0, 16, "system/gui/gnome") == 0) return YPKG_GROUP_DESKTOP_GNOME; + if (group.compare (0, 14, "system/gui/kde") == 0) return YPKG_GROUP_DESKTOP_KDE; + if (group.compare (0, 15, "system/gui/xfce") == 0) return YPKG_GROUP_DESKTOP_XFCE; + if (group.compare (0, 10, "system/gui") == 0) return YPKG_GROUP_DESKTOP_OTHER; + if (group.compare (0, 8, "hardware") == 0) return YPKG_GROUP_SYSTEM; + if (group.compare (0, 6, "system") == 0) return YPKG_GROUP_SYSTEM; + if (group.find ("scientific") != string::npos) return YPKG_GROUP_EDUCATION; + if (group.find ("multimedia") != string::npos) return YPKG_GROUP_MULTIMEDIA; + + // our own: + if (group.compare (0, 13, "documentation") == 0) return YPKG_GROUP_DOCUMENTATION; + if (group.compare (0, 12, "productivity") == 0) return YPKG_GROUP_UTILITIES; + return YPKG_GROUP_UNKNOWN; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzypptags.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,44 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Converts RPM to PackageKit-like terminology. + Code from yast-qt-pkg. +*/ + +typedef enum { + /* PackageKit values */ + YPKG_GROUP_OFFICE, + YPKG_GROUP_PUBLISHING, + YPKG_GROUP_GRAPHICS, + YPKG_GROUP_MULTIMEDIA, + YPKG_GROUP_EDUCATION, + YPKG_GROUP_GAMES, + YPKG_GROUP_DESKTOP_GNOME, + YPKG_GROUP_DESKTOP_KDE, + YPKG_GROUP_DESKTOP_XFCE, + YPKG_GROUP_DESKTOP_OTHER, + YPKG_GROUP_COMMUNICATION, + YPKG_GROUP_NETWORK, + YPKG_GROUP_PROGRAMMING, + YPKG_GROUP_DOCUMENTATION, + YPKG_GROUP_ADMIN_TOOLS, + YPKG_GROUP_SECURITY, + YPKG_GROUP_LOCALIZATION, + YPKG_GROUP_SYSTEM, + YPKG_GROUP_UTILITIES, + YPKG_GROUP_UNKNOWN, + YPKG_GROUP_SUGGESTED, + YPKG_GROUP_RECOMMENDED, + YPKG_GROUP_ORPHANED, + YPKG_GROUP_RECENT, + YPKG_GROUP_MULTIVERSION, + YPKG_GROUP_TOTAL, +} YPkgGroupEnum; + + +YPkgGroupEnum zypp_tag_convert (const std::string &rpm_group); + +const char *zypp_tag_group_enum_to_localised_text (YPkgGroupEnum group); +const char *zypp_tag_enum_to_icon (YPkgGroupEnum group); +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,1511 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ +/* Ypp, the zypp wrapper */ +// check the header file for information about this wrapper + +/* + Textdomain "gtk" + */ + +#include "config.h" +#include "YGi18n.h" +#define YUILogComponent "gtk-pkg" +#include <YUILog.h> +#include "yzyppwrapper.h" +#include "YGUtils.h" +#include <algorithm> + +static Ypp::Interface *g_interface = 0; +static bool g_autoSolver = true; + +// Repository + +Ypp::Repository::Repository (zypp::Repository repo) +: m_repo (repo), m_repo_info (repo.info()), m_onlyInfo (false) {} + +Ypp::Repository::Repository (zypp::RepoInfo repo) +: m_repo (NULL), m_repo_info (repo), m_onlyInfo (true) {} + +std::string Ypp::Repository::name() +{ return m_repo_info.name(); } + +std::string Ypp::Repository::url() +{ return m_repo_info.url().asString(); } + +bool Ypp::Repository::enabled() +{ return m_repo_info.enabled(); } + +bool Ypp::Repository::isOutdated() +{ return m_onlyInfo ? false : m_repo.maybeOutdated(); } + +bool Ypp::Repository::isSystem() +{ return m_repo.isSystemRepo(); } + +bool Ypp::Repository::operator == (const Ypp::Repository &other) const +{ return this->m_repo.info().alias() == other.m_repo.info().alias(); } + +void Ypp::getRepositoryFromAlias (const std::string &alias, std::string &name, std::string &url) +{ + static std::map <std::string, zypp::RepoInfo> repos; + if (repos.empty()) { + zypp::RepoManager manager; + std::list zypp::RepoInfo known_repos = manager.knownRepositories(); + for (std::list zypp::RepoInfo::const_iterator it = known_repos.begin(); + it != known_repos.end(); it++) + repos[it->alias()] = *it; + } + + std::map <std::string, zypp::RepoInfo>::iterator it = repos.find (alias); + if (it != repos.end()) { + zypp::RepoInfo *repo = &it->second; + name = repo->name(); + url = repo->url().asString(); + } + else + name = alias; // return alias if repo not currently setup-ed +} + +// Version + +Ypp::Version::Version (ZyppResObject zobj) +: m_zobj (zobj) {} + +int Ypp::Version::type() +{ return Selectable::asType (m_zobj->kind()); } + +std::string Ypp::Version::number() +{ return m_zobj->edition().asString(); } + +std::string Ypp::Version::arch() +{ return m_zobj->arch().asString(); } + +Ypp::Repository Ypp::Version::repository() +{ return Repository (m_zobj->repository()); } + +Size_t Ypp::Version::size() +{ return m_zobj->installSize(); } + +Size_t Ypp::Version::downloadSize() +{ return m_zobj->downloadSize(); } + +bool Ypp::Version::isInstalled() +{ + zypp::ResStatus status = m_zobj->poolItem().status(); + switch (type()) { + case Ypp::Selectable::PATCH: + case Ypp::Selectable::PATTERN: + return status.isSatisfied() && !status.isToBeInstalled(); + default: + return status.isInstalled(); + } +} + +bool Ypp::Version::toModify() +{ + zypp::ResStatus status = m_zobj->poolItem().status(); + return status.transacts(); +} + +bool Ypp::Version::operator < (Ypp::Version &other) +{ return this->m_zobj->edition() < other.m_zobj->edition(); } + +bool Ypp::Version::operator > (Ypp::Version &other) +{ return this->m_zobj->edition() > other.m_zobj->edition(); } + +bool Ypp::Version::operator == (Ypp::Version &other) +{ return this->m_zobj->edition() == other.m_zobj->edition(); } + +// Selectable + +zypp::ResKind Ypp::Selectable::asKind (Type type) +{ + switch (type) { + case PATTERN: return zypp::ResKind::pattern; + case PATCH: return zypp::ResKind::patch; + case PACKAGE: case LANGUAGE: case ALL: break; + } + return zypp::ResKind::package; +} + +Ypp::Selectable::Type Ypp::Selectable::asType (zypp::ResKind kind) +{ + if (kind == zypp::ResKind::patch) + return PATCH; + if (kind == zypp::ResKind::pattern) + return PATTERN; + return PACKAGE; +} + +Ypp::Selectable::Selectable (ZyppSelectable sel) +: m_type (asType (sel->kind())), m_sel (sel) +{} + +Ypp::Selectable::Selectable (zypp::Locale locale) +: m_type (LANGUAGE), m_locale (locale) {} + +Ypp::Selectable::Type Ypp::Selectable::type() +{ return m_type; } + +std::string Ypp::Selectable::name() +{ + switch (m_type) { + case LANGUAGE: return m_locale.name() + " (" + m_locale.code() + ")"; + case PATTERN: return m_sel->theObj()->summary(); + default: break; + } + return m_sel->name(); +} + +std::string Ypp::Selectable::summary() +{ + switch (m_type) { + case PATTERN: + case LANGUAGE: { + Collection col (*this); + int installed, total; + col.stats (&installed, &total); + std::ostringstream stream; + stream << _("Installed:") << " " << installed << " " << _("of") << " " << total; + return stream.str(); + } + default: break; + } + return m_sel->theObj()->summary(); +} + +std::string Ypp::Selectable::description (bool as_html) +{ + if (m_type == LANGUAGE) + return summary(); + + std::string text (m_sel->theObj()->description()), br ("\n"); + if (as_html) br = "<br />"; + switch (m_type) { + case PACKAGE: { + // if it has this header, then it is HTML + const char *header = "<!-- DT:Rich -->", header_len = 16; + if (!text.compare (0, header_len, header, header_len)) + ; + else { + // cut authors block + std::string::size_type i = text.find ("\nAuthors:", 0); + if (i == std::string::npos) + i = text.find ("\nAuthor:", 0); + if (i != std::string::npos) + text.erase (i); + // cut any lines at the end + while (text.length() > 0 && text [text.length()-1] == '\n') + text.erase (text.length()-1); + + text = YGUtils::escapeMarkup (text); + if (as_html) // break every double line + YGUtils::replace (text, "\n\n", 2, "<br>"); + text += br; + } + break; + } + case PATCH: { + ZyppPatch patch = castZyppPatch (m_sel->theObj()); + if (patch->rebootSuggested()) { + text += br + br + "<b>" + _("Reboot required:") + " </b>"; + text += _("the system will have to be restarted in order for " + "this patch to take effect."); + } + if (patch->reloginSuggested()) { + text += br + br + "<b>" + _("Relogin required:") + " </b>"; + text += _("you must logout and login again for " + "this patch to take effect."); + } + if (patch->referencesBegin() != patch->referencesEnd()) { + text += br + br + "<b>Bugzilla:</b><ul>"; + for (zypp::Patch::ReferenceIterator it = patch->referencesBegin(); + it != patch->referencesEnd(); it++) + text += "<li><a href="" + it.href() + "">" + it.title() + "</a></li>"; + text += "</ul>"; + } + break; + } + case PATTERN: { + text += br + br + summary(); + break; + } + default: break; + } + return text; +} + +bool Ypp::Selectable::visible() +{ + switch (m_type) { + case PATTERN: { + ZyppPattern pattern = castZyppPattern (zyppSel()->theObj()); + return pattern->userVisible(); + } + case PATCH: + if (zyppSel()->hasCandidateObj()) + if (!zyppSel()->candidateObj().isRelevant()) + return false; + return true; + default: break; + } + return true; +} + +bool Ypp::Selectable::isInstalled() +{ + switch (m_type) { + case PACKAGE: + return !m_sel->installedEmpty(); + case PATTERN: + case PATCH: + if (hasCandidateVersion()) + return candidate().isInstalled(); + return true; + case LANGUAGE: + return _zyppPool().isRequestedLocale (m_locale); + case ALL: break; + } + return 0; +} + +bool Ypp::Selectable::hasUpgrade() +{ + switch (m_type) { + case LANGUAGE: break; + default: { + const ZyppResObject candidate = m_sel->candidateObj(); + const ZyppResObject installed = m_sel->installedObj(); + if (!!candidate && !!installed) + return zypp::Edition::compare (candidate->edition(), installed->edition()) > 0; + } + } + return false; +} + +bool Ypp::Selectable::isLocked() +{ + switch (m_type) { + case LANGUAGE: break; + default: { + zypp::ui::Status status = m_sel->status(); + return status == zypp::ui::S_Taboo || status == zypp::ui::S_Protected; + } + } + return false; +} + +bool Ypp::Selectable::toInstall() +{ + switch (m_type) { + case LANGUAGE: break; + default: return m_sel->toInstall(); + } + return false; +} + +bool Ypp::Selectable::toRemove() +{ + switch (m_type) { + case LANGUAGE: break; + default: return m_sel->toDelete(); + } + return false; +} + +bool Ypp::Selectable::toModify() +{ + switch (m_type) { + case LANGUAGE: break; + default: return m_sel->toModify(); + } + return false; +} + +bool Ypp::Selectable::toModifyAuto() +{ + switch (m_type) { + case LANGUAGE: break; + default: + return m_sel->toModify() && m_sel->modifiedBy() != zypp::ResStatus::USER; + } + return false; +} + +void Ypp::Selectable::install() +{ + if (isLocked()) + return; + switch (m_type) { + case LANGUAGE: + if (!_zyppPool().isRequestedLocale (m_locale)) + _zyppPool().addRequestedLocale (m_locale); + break; + default: { + if (!m_sel->hasLicenceConfirmed()) { + ZyppResObject obj = m_sel->candidateObj(); + if (obj && g_interface && g_autoSolver) { + const std::string &license = obj->licenseToConfirm(); + if (!license.empty()) + if (!g_interface->showLicense (*this, license)) + return; + const std::string &msg = obj->insnotify(); + if (!msg.empty()) + if (!g_interface->showMessage (*this, msg)) + return; + } + m_sel->setLicenceConfirmed(); + } + + zypp::ui::Status status = m_sel->status(); + switch (status) { + // not applicable + case zypp::ui::S_Protected: + case zypp::ui::S_Taboo: + case zypp::ui::S_Install: + case zypp::ui::S_Update: + break; + // undo + case zypp::ui::S_Del: + if (hasInstalledVersion()) + status = zypp::ui::S_Update; + else + status = zypp::ui::S_Install; + break; + // nothing to do about it + case zypp::ui::S_AutoDel: + break; + // action + case zypp::ui::S_NoInst: + case zypp::ui::S_AutoInstall: + status = zypp::ui::S_Install; + break; + case zypp::ui::S_KeepInstalled: + case zypp::ui::S_AutoUpdate: + status = zypp::ui::S_Update; + break; + } + m_sel->setStatus (status); + break; + } + } + + if (!runSolver()) undo(); +} + +void Ypp::Selectable::remove() +{ + switch (m_type) { + case LANGUAGE: + if (_zyppPool().isRequestedLocale (m_locale)) + _zyppPool().eraseRequestedLocale (m_locale); + break; + default: { + if (m_sel->hasCandidateObj() && g_interface && g_autoSolver) { + ZyppResObject obj = m_sel->candidateObj(); + const std::string &msg = obj->delnotify(); + if (!msg.empty()) + if (!g_interface->showMessage (*this, msg)) + return; + } + + zypp::ui::Status status = m_sel->status(); + switch (status) { + // not applicable + case zypp::ui::S_Protected: + case zypp::ui::S_Taboo: + case zypp::ui::S_NoInst: + case zypp::ui::S_Del: + break; + // nothing to do about it + case zypp::ui::S_AutoInstall: + case zypp::ui::S_AutoUpdate: + break; + // undo + case zypp::ui::S_Install: + case zypp::ui::S_Update: + // action + case zypp::ui::S_KeepInstalled: + case zypp::ui::S_AutoDel: + status = zypp::ui::S_Del; + break; + } + m_sel->setStatus (status); + break; + } + } + + if (!runSolver()) undo(); +} + +void Ypp::Selectable::undo() +{ + // undo not applicable to language type + + zypp::ui::Status status = m_sel->status(); + zypp::ui::Status prev_status = status; + switch (status) { + // not applicable + case zypp::ui::S_Protected: + case zypp::ui::S_Taboo: + case zypp::ui::S_NoInst: + case zypp::ui::S_KeepInstalled: + break; + // undo + case zypp::ui::S_Install: + case zypp::ui::S_AutoInstall: + status = zypp::ui::S_NoInst; + break; + case zypp::ui::S_AutoUpdate: + case zypp::ui::S_AutoDel: + case zypp::ui::S_Update: + case zypp::ui::S_Del: + status = zypp::ui::S_KeepInstalled; + break; + } + m_sel->setStatus (status); + + if (!runSolver()) { + m_sel->setStatus (prev_status); + runSolver(); + } +} + +void Ypp::Selectable::lock (bool lock) +{ + undo(); + zypp::ui::Status status; + if (lock) + status = isInstalled() ? zypp::ui::S_Protected : zypp::ui::S_Taboo; + else + status = isInstalled() ? zypp::ui::S_KeepInstalled : zypp::ui::S_NoInst; + m_sel->setStatus (status); + + if (!runSolver()) undo(); +} + +bool Ypp::Selectable::canRemove() +{ + switch (m_type) { + case PACKAGE: case LANGUAGE: return true; + default: break; + } + return false; +} + +bool Ypp::Selectable::canLock() +{ + switch (m_type) { + case PACKAGE: + //case PATCH: + return true; + default: break; + } + return false; +} + +int Ypp::Selectable::totalVersions() +{ return m_sel->installedSize() + m_sel->availableSize(); } + +Ypp::Version Ypp::Selectable::version (int n) +{ + if (n < (signed) m_sel->installedSize()) { + zypp::ui::Selectable::installed_iterator it = m_sel->installedBegin(); + for (int i = 0; i < n; i++) it++; + return Version (*it); + } + else { + n -= m_sel->installedSize(); + zypp::ui::Selectable::available_iterator it = m_sel->availableBegin(); + for (int i = 0; i < n; i++) it++; + return Version (*it); + } +} + +bool Ypp::Selectable::hasCandidateVersion() +{ return !m_sel->availableEmpty(); } + +Ypp::Version Ypp::Selectable::candidate() +{ return Version (m_sel->candidateObj()); } + +void Ypp::Selectable::setCandidate (Ypp::Version &version) +{ + m_sel->setCandidate (version.zyppObj()); + runSolver(); +} + +bool Ypp::Selectable::hasInstalledVersion() +{ return !m_sel->installedEmpty(); } + +Ypp::Version Ypp::Selectable::installed() +{ + if (m_type == PATCH || m_type == PATTERN) { + if (m_sel->candidateObj() && m_sel->candidateObj().isSatisfied()) + return Ypp::Version (m_sel->candidateObj()); + } + return Ypp::Version (m_sel->installedObj()); +} + +Ypp::Version Ypp::Selectable::anyVersion() +{ return Version (m_sel->theObj()); } + +bool Ypp::Selectable::operator == (const Ypp::Selectable &other) const +{ return this->m_sel == other.m_sel; } + +bool Ypp::Selectable::operator != (const Ypp::Selectable &other) const +{ return this->m_sel != other.m_sel; } + +// Collection + +Ypp::Collection::Collection (Ypp::Selectable &sel) +: m_sel (sel) {} + +static std::map <ZyppPattern, Ypp::List> g_patternsContent; +static std::map <zypp::Locale, Ypp::List> g_languagesContent; + +Ypp::List *Ypp::Collection::getContent() +{ // zypp::Pattern::Contents is expensive to iterate; this makes it cheap + if (m_sel.type() == Ypp::Selectable::PATTERN) { + ZyppPattern pattern = castZyppPattern (m_sel.zyppSel()->theObj()); + std::map <ZyppPattern, Ypp::List>::iterator it; + if ((it = g_patternsContent.find (pattern)) != g_patternsContent.end()) + return &it->second; + + zypp::Pattern::Contents c = pattern->contents(); + Ypp::List list (c.size()); + for (zypp::Pattern::Contents::Selectable_iterator it = c.selectableBegin(); + it != c.selectableEnd(); it++) + list.append (Selectable (*it)); + g_patternsContent.insert (std::make_pair (pattern, list)); + return &g_patternsContent.find (pattern)->second; + } + else { // Language + zypp::Locale locale = m_sel.zyppLocale(); + std::map <zypp::Locale, Ypp::List>::iterator it; + if ((it = g_languagesContent.find (locale)) != g_languagesContent.end()) + return &it->second; + + zypp::sat::LocaleSupport locale_sup (locale); + Ypp::List list (50); + for_( it, locale_sup.selectableBegin(), locale_sup.selectableEnd() ) { + list.append (Selectable (*it)); + } + g_languagesContent.insert (std::make_pair (locale, list)); + return &g_languagesContent.find (locale)->second; + } +} + +bool Ypp::Collection::contains (Ypp::Selectable &sel) +{ + if (m_sel.type() == Ypp::Selectable::PATCH) { + if (m_sel.hasCandidateVersion()) { + ZyppResObject object = m_sel.zyppSel()->candidateObj(); + ZyppPatch patch = castZyppPatch (object); + zypp::Patch::Contents contents (patch->contents()); + ZyppSelectable pkg = sel.zyppSel(); + for (zypp::Patch::Contents::Selectable_iterator it = + contents.selectableBegin(); it != contents.selectableEnd(); it++) { + if (*it == pkg) + return true; + } + } + return false; + } + + //else + Ypp::List *content = getContent(); + return content->find (sel) != -1; +} + +void Ypp::Collection::stats (int *installed, int *total) +{ + Ypp::List *content = getContent(); + Ypp::ListProps props (*content); + *installed = props.isInstalledNb(); + *total = content->size(); +} + +// Package + +Ypp::Package::Package (Ypp::Selectable &sel) +: m_sel (sel) {} + +int Ypp::Package::support() +{ + ZyppPackage pkg = castZyppPackage (m_sel.zyppSel()->theObj()); + switch (pkg->vendorSupport()) { + case zypp::VendorSupportUnknown: return 0; + case zypp::VendorSupportUnsupported: return 1; + case zypp::VendorSupportACC: return 2; + case zypp::VendorSupportLevel1: return 3; + case zypp::VendorSupportLevel2: return 4; + case zypp::VendorSupportLevel3: return 5; + } + return 0; +} + +int Ypp::Package::supportTotal() +{ return 6; } + +static zypp::VendorSupportOption asSupportOpt (int support) +{ + switch (support) { + case 0: return zypp::VendorSupportUnknown; + case 1: return zypp::VendorSupportUnsupported; + case 2: return zypp::VendorSupportACC; + case 3: return zypp::VendorSupportLevel1; + case 4: return zypp::VendorSupportLevel2; + case 5: return zypp::VendorSupportLevel3; + } + return zypp::VendorSupportUnknown; +} + +std::string Ypp::Package::supportSummary (int support) +{ return zypp::asUserString (asSupportOpt (support)); } + +std::string Ypp::Package::supportDescription (int support) +{ return zypp::asUserStringDescription (asSupportOpt (support)); } + +std::string Ypp::Package::url() +{ + ZyppPackage package = castZyppPackage (m_sel.zyppSel()->theObj()); + return package->url(); +} + +YPkgGroupEnum Ypp::Package::group() +{ + static std::map <ZyppPackage, YPkgGroupEnum> pkgGroupMap; + ZyppPackage pkg = castZyppPackage (m_sel.zyppSel()->theObj()); + std::map <ZyppPackage, YPkgGroupEnum>::iterator it = pkgGroupMap.find (pkg); + if (it == pkgGroupMap.end()) { + YPkgGroupEnum group = zypp_tag_convert (pkg->group()); + pkgGroupMap.insert (std::make_pair (pkg, group)); + return group; + } + return it->second; +} + +std::string Ypp::Package::rpm_group() +{ + ZyppPackage pkg = castZyppPackage (m_sel.zyppSel()->theObj()); + return pkg->group(); +} + +static std::map <std::string, ZyppSelectable> g_selPatch; + +static ZyppSelectable getSelPatch (Ypp::Selectable &sel) +{ + std::string name (sel.name()); + std::map <std::string, ZyppSelectable>::iterator it = g_selPatch.find (name); + if (it != g_selPatch.end()) + return it->second; + + Ypp::PoolQuery query (Ypp::Selectable::PATCH); + query.addCriteria (new Ypp::StatusMatch (Ypp::StatusMatch::NOT_INSTALLED)); + query.addCriteria (new Ypp::CollectionContainsMatch (sel)); + if (query.hasNext()) { + Ypp::Selectable patch = query.next(); + g_selPatch[name] = patch.zyppSel(); + } + else + g_selPatch[name] = NULL; + return g_selPatch[name]; +} + +bool Ypp::Package::isCandidatePatch() +{ + if (m_sel.hasCandidateVersion() && m_sel.hasInstalledVersion()) { + Ypp::Version candidate (m_sel.candidate()), installed (m_sel.installed()); + if (candidate > installed) + return getSelPatch (m_sel) != NULL; + } + return false; +} + +Ypp::Selectable Ypp::Package::getCandidatePatch() +{ return Selectable (getSelPatch (m_sel)); } + +// Patch + +Ypp::Patch::Patch (Ypp::Selectable &sel) +: m_sel (sel) {} + +int Ypp::Patch::priority() +{ + ZyppPatch patch = castZyppPatch (m_sel.zyppSel()->theObj()); + switch (patch->categoryEnum()) { + case zypp::Patch::CAT_SECURITY: return 0; + case zypp::Patch::CAT_RECOMMENDED: return 1; + case zypp::Patch::CAT_YAST: return 2; + case zypp::Patch::CAT_DOCUMENT: return 3; + case zypp::Patch::CAT_OPTIONAL: return 4; + case zypp::Patch::CAT_OTHER: return 5; + } + return 0; +} + +int Ypp::Patch::priorityTotal() +{ return 6; } + +const char *Ypp::Patch::prioritySummary (int priority) +{ + switch (priority) { + // Translators: this refers to patch priority + case 0: return _("Security"); + // Translators: this refers to patch priority + case 1: return _("Recommended"); + case 2: return "YaST"; + case 3: return _("Documentation"); + // Translators: this refers to patch priority + case 4: return _("Optional"); + // Translators: this refers to patch priority + case 5: return _("Other"); + } + return 0; +} + +// Disk + +std::vector std::string Ypp::getPartitionList() +{ + ZyppDuSet diskUsage = zypp::getZYpp()->diskUsage(); + std::vector std::string partitions; + partitions.reserve (diskUsage.size()); + for (ZyppDuSet::iterator it = diskUsage.begin(); it != diskUsage.end(); it++) { + const ZyppDu &point = *it; + if (!point.readonly) + partitions.push_back (point.dir); + } + std::sort (partitions.begin(), partitions.end()); + return partitions; +} + +const ZyppDu Ypp::getPartition (const std::string &mount_point) +{ + ZyppDuSet diskUsage = zypp::getZYpp()->diskUsage(); + for (ZyppDuSet::iterator it = diskUsage.begin(); it != diskUsage.end(); it++) { + const ZyppDu &point = *it; + if (mount_point == point.dir) + return point; + } + return *zypp::getZYpp()->diskUsage().begin(); // error +} + +// Busy + +static Ypp::BusyListener *g_busy_listener = 0; +static bool g_busy_running = false; + +void Ypp::setBusyListener (Ypp::BusyListener *listener) +{ g_busy_listener = listener; } + +struct Ypp::Busy::Impl { + int cur, size; + bool showing; + GTimeVal start_t; + + Impl (int size) + : cur (0), size (size), showing (false) + { + g_get_current_time (&start_t); + g_busy_running = true; + g_busy_listener->loading (0); + } + + ~Impl() + { + g_busy_running = false; + g_busy_listener->loading (1); + } + + void inc() + { + cur++; +#if 0 + // shows continuous progress only when it takes more than 1 sec to drive 'N' loops + if (showing) { + if ((cur % 500) == 0) + g_busy_listener->loading (cur / (float) size); + } + else if (cur == 499) { + GTimeVal now_t; + g_get_current_time (&now_t); + if (now_t.tv_usec - start_t.tv_usec >= 35*1000 || + now_t.tv_sec - start_t.tv_sec >= 1) { + showing = true; + } + } +#else + g_busy_listener->loading (cur / (float) size); +#endif + } +}; + +Ypp::Busy::Busy (int size) : impl (NULL) +{ + if (g_busy_listener && !g_busy_running) + impl = new Impl (size); +} + +Ypp::Busy::~Busy() +{ delete impl; } + +void Ypp::Busy::inc() +{ if (impl) impl->inc(); } + +// Interface + +std::list <Ypp::SelListener *> g_sel_listeners; +static bool g_transacting = false; + +void Ypp::addSelListener (Ypp::SelListener *listener) +{ g_sel_listeners.push_back (listener); } + +void Ypp::removeSelListener (Ypp::SelListener *listener) +{ g_sel_listeners.remove (listener); } + +void Ypp::notifySelModified() +{ + for (std::list <Ypp::SelListener *>::iterator it = g_sel_listeners.begin(); + it != g_sel_listeners.end(); it++) + (*it)->selectableModified(); +} + +void Ypp::setInterface (Ypp::Interface *interface) +{ + g_interface = interface; +} + +Ypp::Interface *Ypp::getInterface() { return g_interface; } + +Ypp::Problem::Solution *Ypp::Problem::getSolution (int nb) +{ return (Solution *) g_slist_nth_data ((GSList *) impl, nb); } + +bool Ypp::runSolver (bool force) +{ + if (g_transacting) return true; + if (!g_autoSolver && !force) { + notifySelModified(); + return true; + } + + if (g_busy_listener) + g_busy_listener->loading (0); + + zypp::Resolver_Ptr zResolver = zypp::getZYpp()->resolver(); + bool resolved = false; + while (true) { + if (zResolver->resolvePool()) { + resolved = true; // no need for user intervention + break; + } + zypp::ResolverProblemList zProblems = zResolver->problems(); + if ((resolved = zProblems.empty())) break; + if (!g_interface) break; + + std::list <Problem *> problems; + for (zypp::ResolverProblemList::iterator it = zProblems.begin(); + it != zProblems.end(); it++) { + Problem *problem = new Problem(); + problem->description = (*it)->description(); + problem->details = (*it)->details(); + GSList *solutions = NULL; + zypp::ProblemSolutionList zSolutions = (*it)->solutions(); + for (zypp::ProblemSolutionList::iterator jt = zSolutions.begin(); + jt != zSolutions.end(); jt++) { + Problem::Solution *solution = new Problem::Solution(); + solution->description = (*jt)->description(); + solution->details = (*jt)->details(); + solution->apply = false; + solution->impl = (void *) get_pointer (*jt); + solutions = g_slist_append (solutions, solution); + } + problem->impl = (void *) solutions; + problems.push_back (problem); + } + + resolved = g_interface->resolveProblems (problems); + if (resolved) { + zypp::ProblemSolutionList choices; + for (std::list <Problem *>::iterator it = problems.begin(); + it != problems.end(); it++) { + for (int i = 0; (*it)->getSolution (i); i++) { + Problem::Solution *solution = (*it)->getSolution (i); + if (resolved && solution->apply) + choices.push_back ((zypp::ProblemSolution *) solution->impl); + delete solution; + } + g_slist_free ((GSList *) (*it)->impl); + delete *it; + } + zResolver->applySolutions (choices); + } + else + break; + } + if (!resolved) + zResolver->undo(); + + notifySelModified(); + if (g_busy_listener) + g_busy_listener->loading (1); + return resolved; +} + +void Ypp::setEnableSolver (bool enabled) +{ + g_autoSolver = enabled; + if (enabled) runSolver(); +} + +bool Ypp::isSolverEnabled() +{ return g_autoSolver; } + +bool Ypp::showPendingLicenses (Ypp::Selectable::Type type) +{ + const zypp::ResKind &kind = Selectable::asKind (type); + for (ZyppPool::const_iterator it = zyppPool().byKindBegin(kind); + it != zyppPool().byKindEnd(kind); it++) { + ZyppSelectable zsel = (*it); + switch (zsel->status()) { + case zypp::ui::S_Install: case zypp::ui::S_AutoInstall: + case zypp::ui::S_Update: case zypp::ui::S_AutoUpdate: + if (zsel->candidateObj()) { + std::string license = zsel->candidateObj()->licenseToConfirm(); + if (!license.empty()) + if (!zsel->hasLicenceConfirmed()) { + Selectable sel (zsel); + if (!g_interface->showLicense (sel, license)) + return false; + } + } + default: break; + } + } + return true; +} + +void Ypp::startTransactions() +{ g_transacting = true; } + +bool Ypp::finishTransactions() +{ g_transacting = false; return runSolver(); } + +// Query + +struct Ypp::StrMatch::Impl { + int attrbs; + std::list std::string strings; + + Impl (int attrbs) : attrbs (attrbs) {} +}; + +Ypp::StrMatch::StrMatch (int attrbs) +: impl (new Impl (attrbs)) {} + +Ypp::StrMatch::~StrMatch() +{ delete impl; } + +void Ypp::StrMatch::add (const std::string &str) +{ impl->strings.push_back (str); } + +bool Ypp::StrMatch::match (Ypp::Selectable &sel) +{ + std::string haystack; + haystack.reserve (2048); + if (impl->attrbs & NAME) + haystack += sel.name(); + if (impl->attrbs & SUMMARY) + haystack += sel.summary(); + if (impl->attrbs & DESCRIPTION) + haystack += sel.description (false); + + for (std::list std::string::iterator it = impl->strings.begin(); + it != impl->strings.end(); it++) { + const std::string &needle = *it; + if (!strcasestr (haystack.c_str(), needle.c_str())) + return false; + } + return true; +} + +Ypp::StatusMatch::StatusMatch (Ypp::StatusMatch::Status status) +: m_status (status) {} + +bool Ypp::StatusMatch::match (Selectable &sel) +{ + switch (m_status) { + case IS_INSTALLED: return sel.isInstalled(); + case NOT_INSTALLED: return !sel.isInstalled(); + case HAS_UPGRADE: return sel.hasUpgrade(); + case IS_LOCKED: return sel.isLocked(); + case TO_MODIFY: return sel.toModify(); + } + return false; +} + +Ypp::PKGroupMatch::PKGroupMatch (YPkgGroupEnum group) +: m_group (group) {} + +bool Ypp::PKGroupMatch::match (Selectable &sel) +{ + ZyppPackage pkg = castZyppPackage (sel.zyppSel()->theObj()); + switch (m_group) { + case YPKG_GROUP_RECOMMENDED: return zypp::PoolItem (pkg).status().isRecommended(); + case YPKG_GROUP_SUGGESTED: return zypp::PoolItem (pkg).status().isSuggested(); + case YPKG_GROUP_ORPHANED: return zypp::PoolItem (pkg).status().isOrphaned(); + case YPKG_GROUP_RECENT: + if (sel.hasCandidateVersion()) { + time_t build = static_cast <time_t> (sel.candidate().zyppObj()->buildtime()); + time_t now = time (NULL); + time_t delta = (now - build) / (60*60*24); // in days + return delta <= 7; + } + return false; +#if ZYPP_VERSION >= 6030004 + case YPKG_GROUP_MULTIVERSION: return sel.zyppSel()->multiversionInstall(); +#endif + default: return Package (sel).group() == m_group; + } + return false; +} + +Ypp::RpmGroupMatch::RpmGroupMatch (const std::string &group) +: m_group (group) {} + +bool Ypp::RpmGroupMatch::match (Selectable &sel) +{ + std::string pkg_group (Package (sel).rpm_group()); + int len = MIN (m_group.size(), pkg_group.size()); + return m_group.compare (0, len, pkg_group, 0, len) == 0; +} + +Ypp::FromCollectionMatch::FromCollectionMatch (Collection &col) +: m_collection (col) {} + +bool Ypp::FromCollectionMatch::match (Selectable &sel) +{ return m_collection.contains (sel); } + +Ypp::CollectionContainsMatch::CollectionContainsMatch (Ypp::Selectable &sel) +: m_contains (sel) {} + +bool Ypp::CollectionContainsMatch::match (Ypp::Selectable &sel) +{ + Collection collection (sel); + return collection.contains (m_contains); +} + +struct Ypp::PoolQuery::Impl { + ZyppQuery query; + std::list <Match *> criterias; + bool filelist_attrb; + ZyppQuery::Selectable_iterator it; + bool begin; + + Impl() : filelist_attrb (false), begin (true) {} + + ~Impl() + { + for (std::list <Match *>::iterator it = criterias.begin(); + it != criterias.end(); it++) + delete *it; + } + + bool match (Ypp::Selectable &sel) + { // exclusive + for (std::list <Match *>::iterator it = criterias.begin(); + it != criterias.end(); it++) + if (!(*it)->match (sel)) + return false; + return true; + } + + ZyppQuery::Selectable_iterator untilMatch (ZyppQuery::Selectable_iterator it) + { // iterates until match -- will return argument if it already matches + while (it != query.selectableEnd()) { + Ypp::Selectable sel (*it); + if (sel.visible() && match (sel)) break; + it++; + } + return it; + } +}; + +Ypp::PoolQuery::PoolQuery (Ypp::Selectable::Type type) +: impl (new Impl()) +{ + assert (type != Selectable::LANGUAGE); + if (type != Selectable::ALL) + impl->query.addKind (Selectable::asKind (type)); +} + +Ypp::PoolQuery::~PoolQuery() +{ delete impl; } + +void Ypp::PoolQuery::setStringMode (bool caseSensitive, Ypp::PoolQuery::MatchType match) +{ + impl->query.setCaseSensitive (caseSensitive); + switch (match) { + case CONTAINS: impl->query.setMatchSubstring(); break; + case EXACT: impl->query.setMatchExact(); break; + case GLOB: impl->query.setMatchGlob(); break; + case REGEX: impl->query.setMatchRegex(); break; + } +} + +void Ypp::PoolQuery::addStringAttribute (Ypp::PoolQuery::StringAttribute attrb) +{ + zypp::sat::SolvAttr _attrb; + impl->filelist_attrb = false; + switch (attrb) { + case NAME: _attrb = ZyppAttribute::name; break; + case SUMMARY: _attrb = ZyppAttribute::summary; break; + case DESCRIPTION: _attrb = ZyppAttribute::description; break; + case FILELIST: _attrb = ZyppAttribute::filelist; impl->filelist_attrb = true; break; + case REQUIRES: _attrb = ZyppAttribute ("solvable:requires"); break; + case PROVIDES: _attrb = ZyppAttribute ("solvable:provides"); break; + } + impl->query.addAttribute (_attrb); +} + +void Ypp::PoolQuery::addStringOr (const std::string &str) +{ + if (impl->filelist_attrb && !str.empty()) + impl->query.setFilesMatchFullPath (str[0] == '/'); + impl->query.addString (str); +} + +void Ypp::PoolQuery::addRepository (Ypp::Repository &repository) +{ impl->query.addRepo (repository.zyppRepo().info().alias()); } + +void Ypp::PoolQuery::addCriteria (Ypp::Match *criteria) +{ impl->criterias.push_back (criteria); } + +bool Ypp::PoolQuery::hasNext() +{ + if (impl->begin) { + impl->it = impl->untilMatch (impl->query.selectableBegin()); + impl->begin = false; + } + return impl->it != impl->query.selectableEnd(); +} + +Ypp::Selectable Ypp::PoolQuery::next() +{ + Ypp::Selectable sel (*impl->it); + impl->it = impl->untilMatch (++impl->it); + return sel; +} + +int Ypp::PoolQuery::guessSize() +{ return impl->query.size(); } + +ZyppQuery &Ypp::PoolQuery::zyppQuery() +{ return impl->query; } + +Ypp::Selectable::Type Ypp::PoolQuery::poolType() +{ return Selectable::asType (*impl->query.kinds().begin()); } + +struct Ypp::LangQuery::Impl { + zypp::LocaleSet locales; + zypp::LocaleSet::const_iterator it; + + Impl() { + locales = _zyppPool().getAvailableLocales(); + it = locales.begin(); + } +}; + +Ypp::LangQuery::LangQuery() +: impl (new Impl()) {} + +Ypp::LangQuery::~LangQuery() +{ delete impl; } + +bool Ypp::LangQuery::hasNext() +{ return impl->it != impl->locales.end(); } + +Ypp::Selectable Ypp::LangQuery::next() +{ return Ypp::Selectable (*(impl->it++)); } + +int Ypp::LangQuery::guessSize() +{ return impl->locales.size(); } + +// List (aggregator) + +struct Ypp::List::Impl { + std::vector <Selectable> vector; + int refcount; + Impl() : refcount (1) {} +}; + +Ypp::List::List (Ypp::Query &query) +: impl (new Impl()) +{ + reserve (query.guessSize()); + while (query.hasNext()) + append (query.next()); +} + +Ypp::List::List (int size) +: impl (new Impl()) +{ reserve (size); } + +Ypp::List::List (const List &other) +: impl (other.impl) +{ impl->refcount++; } + +Ypp::List &Ypp::List::operator= (const List &other) +{ + if (--impl->refcount <= 0) delete impl; + impl = other.impl; + impl->refcount++; + return *this; +} + +Ypp::List::~List() +{ if (--impl->refcount <= 0) delete impl; } + +Ypp::List Ypp::List::clone() +{ + Ypp::List other (size()); + other.impl->vector = this->impl->vector; + return other; +} + +Ypp::Selectable &Ypp::List::get (int index) +{ return impl->vector[index]; } + +int Ypp::List::size() +{ return impl->vector.size(); } + +int Ypp::List::count (Match *match) +{ + int count = 0; + for (std::vector <Selectable>::iterator it = impl->vector.begin(); + it != impl->vector.end(); it++) + if (match->match (*it)) + count++; + return count; +} + +int Ypp::List::find (const std::string &name) +{ + for (unsigned int i = 0; i < impl->vector.size(); i++) + if (name == impl->vector[i].name()) + return i; + return -1; +} + +int Ypp::List::find (Selectable &sel) +{ + for (unsigned int i = 0; i < impl->vector.size(); i++) + if (sel == impl->vector[i]) + return i; + return -1; +} + +void Ypp::List::reserve (int size) +{ impl->vector.reserve (size); } + +void Ypp::List::append (Ypp::Selectable sel) +{ impl->vector.push_back (sel); } + +void Ypp::List::install() +{ + startTransactions(); + for (std::vector <Selectable>::iterator it = impl->vector.begin(); + it != impl->vector.end(); it++) + it->install(); + if (!finishTransactions()) + undo(); +} + +void Ypp::List::remove() +{ + startTransactions(); + for (std::vector <Selectable>::iterator it = impl->vector.begin(); + it != impl->vector.end(); it++) + it->remove(); + if (!finishTransactions()) + undo(); +} + +void Ypp::List::lock (bool lock) +{ + startTransactions(); + for (std::vector <Selectable>::iterator it = impl->vector.begin(); + it != impl->vector.end(); it++) + it->lock (lock); + if (!finishTransactions()) + undo(); +} + +void Ypp::List::undo() +{ + startTransactions(); + for (std::vector <Selectable>::iterator it = impl->vector.begin(); + it != impl->vector.end(); it++) + it->undo(); + finishTransactions(); +} + +static bool installed_order (Ypp::Selectable &a, Ypp::Selectable &b) +{ return (a.isInstalled() ? 0 : 1) < (b.isInstalled() ? 0 : 1); } + +static bool utf8_name_order (Ypp::Selectable &a, Ypp::Selectable &b) +{ return g_utf8_collate (a.name().c_str(), b.name().c_str()) < 0; } + +static bool name_order (Ypp::Selectable &a, Ypp::Selectable &b) +{ return strcasecmp (a.name().c_str(), b.name().c_str()) < 0; } + +static bool size_order (Ypp::Selectable &a, Ypp::Selectable &b) +{ return a.anyVersion().size() < b.anyVersion().size(); } + +static std::string get_alias (Ypp::Selectable &a) +{ + if (a.hasCandidateVersion()) + return a.candidate().repository().zyppRepo().alias(); + return "a"; // system +} + +static bool repository_order (Ypp::Selectable &a, Ypp::Selectable &b) +{ return get_alias (a) < get_alias(b); } + +static bool support_order (Ypp::Selectable &a, Ypp::Selectable &b) +{ return Ypp::Package(a).support() < Ypp::Package(b).support(); } + +typedef bool (* order_cb) (Ypp::Selectable &a, Ypp::Selectable &b); + +static order_cb _order; +static bool _ascendent; // proxy between our order callbacks and std::sort's +static bool proxy_order (const Ypp::Selectable &a, const Ypp::Selectable &b) +{ return _order ((Ypp::Selectable &) a, (Ypp::Selectable &) b) == _ascendent; } + +void Ypp::List::sort (Ypp::List::SortAttribute attrb, bool ascendent) +{ + if (impl->vector.empty()) + return; + + bool unique_criteria = false; + switch (attrb) { + case IS_INSTALLED_SORT: _order = installed_order; break; + case NAME_SORT: + // package names are never internationalized, but langs / patterns may be + if (impl->vector[0].type() != Selectable::PACKAGE) + _order = utf8_name_order; + else + _order = name_order; + unique_criteria = true; + break; + case SIZE_SORT: _order = size_order; unique_criteria = true; break; + case REPOSITORY_SORT: _order = repository_order; break; + case SUPPORT_SORT: _order = support_order; break; + } + _ascendent = ascendent; + if (unique_criteria) + std::sort (impl->vector.begin(), impl->vector.end(), proxy_order); + else // many attributes are equal: maintain previous order in such cases + std::stable_sort (impl->vector.begin(), impl->vector.end(), proxy_order); +} + +void Ypp::List::reverse() +{ std::reverse (impl->vector.begin(), impl->vector.end()); } + +bool Ypp::List::operator == (const Ypp::List &other) const +{ return this->impl == other.impl; } +bool Ypp::List::operator != (const Ypp::List &other) const +{ return this->impl != other.impl; } + +// ListProps + +struct Ypp::ListProps::Impl { + int isInstalled, hasUpgrade, toModify, isLocked, size; + int canRemove : 2, canLock : 2; + + Impl (List list) { + isInstalled = hasUpgrade = toModify = isLocked = canRemove = canLock = 0; + size = list.size(); + for (int i = 0; i < size; i++) { + Selectable &sel = list.get (i); + if (sel.isInstalled()) { + isInstalled++; + if (sel.hasUpgrade()) + hasUpgrade++; + } + if (sel.toModify()) + toModify++; + if (sel.isLocked()) + isLocked++; + } + if (size) { + Selectable sel = list.get (0); + canRemove = sel.canRemove(); + canLock = sel.canLock(); + } + } +}; + +Ypp::ListProps::ListProps (Ypp::List &list) +: impl (new Impl (list)) {} + +Ypp::ListProps::~ListProps() +{ delete impl; } + +bool Ypp::ListProps::isInstalled() const +{ return impl->isInstalled == impl->size; } +bool Ypp::ListProps::isNotInstalled() const +{ return impl->isInstalled == 0; } +bool Ypp::ListProps::hasUpgrade() const +{ return impl->hasUpgrade == impl->size; } +bool Ypp::ListProps::toModify() const +{ return impl->toModify == impl->size; } +bool Ypp::ListProps::isLocked() const +{ return impl->isLocked == impl->size; } +bool Ypp::ListProps::isUnlocked() const +{ return impl->isLocked == 0; } +bool Ypp::ListProps::canRemove() const +{ return impl->canRemove; } +bool Ypp::ListProps::canLock() const +{ return impl->canLock; } + +int Ypp::ListProps::isInstalledNb() const +{ return impl->isInstalled; } +int Ypp::ListProps::isNotInstalledNb() const +{ return impl->size - impl->isInstalled; } +int Ypp::ListProps::hasUpgradeNb() const +{ return impl->hasUpgrade; } +int Ypp::ListProps::isLockedNb() const +{ return impl->isLocked; } +int Ypp::ListProps::toModifyNb() const +{ return impl->toModify; } + +// General + +void Ypp::init() +{ + zyppPool().saveState zypp::Package(); + zyppPool().saveState zypp::Pattern(); + zyppPool().saveState <zypp::Patch >(); +} + +bool Ypp::isModified() +{ + return zyppPool().diffState zypp::Package() || + zyppPool().diffState zypp::Pattern() || + zyppPool().diffState zypp::Patch(); +} + +void Ypp::finish() +{ + g_sel_listeners.clear(); + g_interface = 0; + g_transacting = false; + g_autoSolver = true; + g_busy_listener = 0; + g_busy_running = false; + g_patternsContent.clear(); + g_languagesContent.clear(); + g_selPatch.clear(); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/pkg/yzyppwrapper.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,511 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* Simplifies, unifies and extends libzypp's API. + + Several classes are available to wrap around common Zypp objects + in order to simplify or extend them. These methods can be interwined + with direct libzypp use, but you should call Ypp::notifySelModified() + to broadcast any change to a selectable. + + Use Ypp::QueryPool to iterate through the Selectable pool. + It extends zypp::PoolQuery by adding the possibility to add + custom criterias for filtering, to be apply endogenously. + + If you need to iterate through it several times, you can create + a Ypp::List out of it. This random-access list can be manually + manipulated with several available methods such as for sorting. + The list is ref-counted so you can easily and freely hold it at + several widgets. + In order to inspect several common properties of a group of packages, + pass a list to Ypp::ListProps. + + Usage example (unlock all packages): + + Ypp::PoolQuery query (Ypp::Selectable::PACKAGE_TYPE); + query.addCriteria (new Ypp::StatusMatch (Ypp::StatusMatch::IS_LOCKED)); + + Ypp::List list (query); + list.unlock(); + + You are advised to register an Ypp::Interface implementation as some + transactions are bound by user decisions. Call Ypp::init() and + Ypp::finish() when you begin or are done (respectively) using these + methods. + Use Ypp::addSelListener() to be notified of any 'selectable' change. +*/ + +#ifndef ZYPP_WRAPPER_H +#define ZYPP_WRAPPER_H + +#include <zypp/ZYppFactory.h> +#include <zypp/ResObject.h> +#include <zypp/ResKind.h> +#include <zypp/ResPoolProxy.h> +#include <zypp/PoolQuery.h> +#include <zypp/ui/Selectable.h> +#include <zypp/Patch.h> +#include <zypp/Package.h> +#include <zypp/Pattern.h> +#include <zypp/Product.h> +#include <zypp/Repository.h> +#include <zypp/RepoManager.h> +#include <zypp/sat/LocaleSupport.h> +#include <string> +#include <list> +#include "yzypptags.h" + +typedef zypp::ResPool _ZyppPool; +typedef zypp::ResPoolProxy ZyppPool; +inline ZyppPool zyppPool() { return zypp::getZYpp()->poolProxy(); } +inline _ZyppPool _zyppPool() { return zypp::getZYpp()->pool(); } +typedef zypp::ResObject::constPtr ZyppResObject; +typedef zypp::ResObject* ZyppResObjectPtr; +typedef zypp::ui::Selectable::Ptr ZyppSelectable; +typedef zypp::ui::Selectable* ZyppSelectablePtr; +typedef zypp::Package::constPtr ZyppPackage; +typedef zypp::Patch::constPtr ZyppPatch; +typedef zypp::Pattern::constPtr ZyppPattern; +typedef zypp::Repository ZyppRepository; +typedef zypp::PoolQuery ZyppQuery; +typedef zypp::sat::SolvAttr ZyppAttribute; +typedef zypp::ByteCount Size_t; +typedef zypp::DiskUsageCounter::MountPoint ZyppDu; +typedef zypp::DiskUsageCounter::MountPointSet ZyppDuSet; + +inline ZyppPackage castZyppPackage (ZyppResObject obj) +{ return zypp::dynamic_pointer_cast <const zypp::Package> (obj); } +inline ZyppPatch castZyppPatch (ZyppResObject obj) +{ return zypp::dynamic_pointer_cast <const zypp::Patch> (obj); } +inline ZyppPattern castZyppPattern (ZyppResObject obj) +{ return zypp::dynamic_pointer_cast <const zypp::Pattern> (obj); } + +namespace Ypp +{ + struct List; + + // Repositories setup + + struct Repository { + // merges zypp::Repository and zypp::RepoInfo -- the first are + // the actual repository structure (which includes only the + // enabled ones), the others are from the setup file. + + Repository (zypp::Repository repo); + Repository (zypp::RepoInfo repo); + std::string name(); + std::string url(); + bool enabled(); + bool isOutdated(); + bool isSystem(); + + bool operator == (const Repository &other) const; + + ZyppRepository &zyppRepo() { return m_repo; } + + private: + ZyppRepository m_repo; + zypp::RepoInfo m_repo_info; + bool m_onlyInfo; + }; + + void getRepositoryFromAlias (const std::string &alias, + std::string &name, std::string &url); + + // Selectable & complementory structs + + struct Version { + Version (ZyppResObject zobj); + + int type(); // Ypp::Selectable::Type + + std::string number(); + std::string arch(); + Repository repository(); + + Size_t size(); + Size_t downloadSize(); + + bool isInstalled(); + bool toModify(); + + bool operator < (Version &other); + bool operator > (Version &other); + bool operator == (Version &other); + + ZyppResObject zyppObj() { return m_zobj; } + + private: + ZyppResObject m_zobj; + }; + + struct Selectable { + enum Type { + PACKAGE, PATTERN, LANGUAGE, PATCH, ALL + }; + static zypp::ResKind asKind (Type type); + static Type asType (zypp::ResKind kind); + + Selectable (ZyppSelectable sel); + Selectable (zypp::Locale locale); + + Type type(); + std::string name(); + std::string summary(); + std::string description (bool as_html); + + bool visible(); + + bool isInstalled(); + bool hasUpgrade(); + bool isLocked(); + + bool toInstall(); + bool toRemove(); + bool toModify(); + bool toModifyAuto(); + + void install(); // installs candidate + void remove(); + void undo(); + void lock (bool lock); + + bool canRemove(); + bool canLock(); + + int totalVersions(); + Version version (int n); + + bool hasCandidateVersion(); + Version candidate(); + void setCandidate (Version &version); + + bool hasInstalledVersion(); + Version installed(); + Version anyVersion(); + + bool operator == (const Selectable &other) const; + bool operator != (const Selectable &other) const; + + ZyppSelectable zyppSel() { return m_sel; } + zypp::Locale zyppLocale() { return m_locale; } + + private: + Type m_type; + ZyppSelectable m_sel; + zypp::Locale m_locale; + }; + + struct Collection { + Collection (Selectable &sel); + bool contains (Selectable &sel); + void stats (int *installed, int *total); + + Ypp::List *getContent(); // cached + + Selectable &asSelectable() { return m_sel; } + + private: + Selectable m_sel; + }; + + struct Package { + Package (Selectable &sel); + int support(); + static int supportTotal(); + static std::string supportSummary (int support); + static std::string supportDescription (int support); + + std::string url(); + + YPkgGroupEnum group(); + std::string rpm_group(); + + bool isCandidatePatch(); + Selectable getCandidatePatch(); + + private: + Selectable m_sel; + }; + + struct Patch { + Patch (Selectable &sel); + int priority(); + static int priorityTotal(); + static const char *prioritySummary (int priority); + static const char *priorityIcon (int priority); + + private: + Selectable m_sel; + }; + + struct SelListener { + // a selectable modification has ripple effects; we don't bother + // detecting them + virtual void selectableModified() = 0; + }; + + void addSelListener (SelListener *listener); + void removeSelListener (SelListener *listener); + + // you shouldn't need to call notifySelModified() directly, but instead call + // e.g. runSolver() + void notifySelModified(); + + struct Problem { + std::string description, details; + struct Solution { + std::string description, details; + bool apply; + void *impl; + }; + Solution *getSolution (int nb); + void *impl; + }; + + // runSolver() gets called automatically when you install/remove/... a + // a package using the Ypp::Selectable API + bool runSolver (bool force = false); // returns whether succesful + void setEnableSolver (bool enabled); // true by default + bool isSolverEnabled(); + bool showPendingLicenses (Ypp::Selectable::Type type); + + // temporarily suspends run-solver while installing/removing a few packages at a time + // -- used by Ypp::List + void startTransactions(); + bool finishTransactions(); // returns return of runSolver() + + struct Interface { + virtual bool showLicense (Selectable &sel, const std::string &license) = 0; + virtual bool showMessage (Selectable &sel, const std::string &message) = 0; + // resolveProblems = false to cancel the action that had that effect + virtual bool resolveProblems (const std::list <Problem *> &problems) = 0; + }; + + void setInterface (Interface *interface); + Interface *getInterface(); + + // Pool selectables + + struct Match { + virtual bool match (Selectable &sel) = 0; + virtual ~Match() {} + }; + + struct StrMatch : public Match { + // exclusive match -- zypp only supports or'ed strings + // you can combine attributes (inclusive) + enum Attribute { + NAME = 0x1, SUMMARY = 0x2, DESCRIPTION = 0x4 + }; // use regular zypp methods for other attributes + StrMatch (int attrbs); + void add (const std::string &str); + virtual bool match (Selectable &sel); + + virtual ~StrMatch(); + struct Impl; + Impl *impl; + }; + + struct StatusMatch : public Match { + enum Status { + IS_INSTALLED, NOT_INSTALLED, HAS_UPGRADE, IS_LOCKED, TO_MODIFY + }; + StatusMatch (Status status); + virtual bool match (Selectable &sel); + + private: Status m_status; + }; + + struct PKGroupMatch : public Match { + PKGroupMatch (YPkgGroupEnum group); + virtual bool match (Selectable &sel); + + private: YPkgGroupEnum m_group; + }; + + struct RpmGroupMatch : public Match { + RpmGroupMatch (const std::string &group); + virtual bool match (Selectable &sel); + + private: std::string m_group; + }; + + struct FromCollectionMatch : public Match { + FromCollectionMatch (Collection &col); + virtual bool match (Selectable &sel); + + private: Collection m_collection; + }; + + struct CollectionContainsMatch : public Match { + CollectionContainsMatch (Selectable &sel); + virtual bool match (Selectable &collection); + + private: Selectable m_contains; + }; + + struct SupportMatch : public Match { + SupportMatch (int n) : m_n (n) {} + virtual bool match (Selectable &sel) + { return Package (sel).support() == m_n; } + private: int m_n; + }; // we should replace some of these stuff by generic properties + + struct PriorityMatch : public Match { + PriorityMatch (int n) : m_n (n) {} + virtual bool match (Selectable &sel) + { return Patch (sel).priority() == m_n; } + private: int m_n; + }; + + struct Query { + virtual void addCriteria (Match *match) = 0; // to be free'd by Query + virtual bool hasNext() = 0; + virtual Selectable next() = 0; + virtual int guessSize() = 0; // only knows for sure after iterating + }; + + struct PoolQuery : public Query { + PoolQuery (Selectable::Type type); // for languages, use LangQuery + ~PoolQuery(); + + enum MatchType { + CONTAINS, EXACT, GLOB, REGEX + }; + enum StringAttribute { + NAME, SUMMARY, DESCRIPTION, FILELIST, PROVIDES, REQUIRES, + }; + void setStringMode (bool caseSensitive, MatchType type); + void addStringAttribute (StringAttribute attrb); + void addStringOr (const std::string &str); + + void addRepository (Repository &repository); + + virtual void addCriteria (Match *match); // exclusive + + virtual bool hasNext(); // only after setup + virtual Selectable next(); + virtual int guessSize(); + + ZyppQuery &zyppQuery(); + Selectable::Type poolType(); + + struct Impl; + Impl *impl; + private: // prevent copy + PoolQuery (const PoolQuery&); PoolQuery &operator= (const PoolQuery&); + }; + + struct LangQuery : public Query { + LangQuery(); + ~LangQuery(); + + virtual void addCriteria (Match *match) {} /* not yet supported */ + virtual bool hasNext(); + virtual Selectable next(); + virtual int guessSize(); + + struct Impl; + Impl *impl; + + private: // prevent copy + LangQuery (const LangQuery&); LangQuery &operator= (const LangQuery&); + }; + + // Aggregators + + struct List { + List (Query &query); + List (int reserve); + ~List(); + List clone(); + + Selectable &get (int index); + int size(); + + int count (Match *match); + int find (const std::string &name); + int find (Selectable &sel); + + void reserve (int size); + void append (Selectable sel); + + void install(); + void remove(); + void lock (bool lock); + void undo(); + + enum SortAttribute { + IS_INSTALLED_SORT, NAME_SORT, SIZE_SORT, REPOSITORY_SORT, SUPPORT_SORT + }; + void sort (SortAttribute attrb, bool ascendent); + void reverse(); + + bool operator == (const Ypp::List &other) const; + bool operator != (const Ypp::List &other) const; + + struct Impl; + Impl *impl; + List (const List&); // ref-counted + List &operator= (const List&); + }; + + struct ListProps { + ListProps (List &list); + ~ListProps(); + + bool isInstalled() const; + bool isNotInstalled() const; + bool hasUpgrade() const; + bool toModify() const; + bool isLocked() const; + bool isUnlocked() const; + bool canRemove() const; + bool canLock() const; + + int isInstalledNb() const; + int isNotInstalledNb() const; + int hasUpgradeNb() const; + int isLockedNb() const; + int toModifyNb() const; + + struct Impl; + Impl *impl; + private: // prevent copy + ListProps (const ListProps&); ListProps &operator= (const ListProps&); + }; + + // Disk + + std::vector std::string getPartitionList(); + const ZyppDu getPartition (const std::string &mount_point); + + // Misc utilities + + struct Busy { + // pass 0 as size to only show busy cursor + Busy (int size); + ~Busy(); + void inc(); + + struct Impl; + Impl *impl; + private: // prevent copy + Busy (const Busy&); Busy &operator= (const Busy&); + }; + + struct BusyListener { + virtual void loading (float progress) = 0; + }; + + void setBusyListener (BusyListener *listener); + + void init(); // ensures the floor is clean + void finish(); // ensures the floor is clean + bool isModified(); // anything changed? +}; + +#endif /*ZYPP_WRAPPER_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/test.cc URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/test.cc (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/test.cc Mon Aug 1 09:44:45 2011 @@ -0,0 +1,235 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +#define YUILogComponent "gtk" +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <YUI.h> +#include <gtk/gtk.h> +#include "YGUtils.h" + +bool testMapKBAccel() +{ + fprintf (stderr, "Test map KB accels\t"); + struct { + const char *in; + const char *out; + } aTests[] = { + { "Foo&", "Foo_" }, + { "B&aa", "B_aa" }, + { "&Foo", "_Foo" }, + { "_Foo", "__Foo" }, +// FIXME - unfortunately it seems yast has noescaping policy +// for labels (hmm) at least judging from the code at +// ncurses/src/NCstring.cc (getHotKey) +// { "&&Foo", "&Foo" }, + { NULL, NULL } + }; + for (int i = 0; aTests[i].in; i++) { + string mapped = YGUtils::mapKBAccel(aTests[i].in); + if (mapped != aTests[i].out) { + fprintf (stderr, "Mis-mapped accel '%s' vs '%s'\n", + mapped.c_str(), aTests[i].out); + return false; + } + fprintf (stderr, "%d ", i); + } + fprintf (stderr, "\n"); + return true; +} + +#include "ygtkrichtext.h" + +bool testParse(const char *xml) +{ + GError *error = NULL; + GMarkupParser parser = { 0, }; + GMarkupParseContext *ctx = g_markup_parse_context_new (&parser, GMarkupParseFlags (0), NULL, NULL); + + if (!g_markup_parse_context_parse (ctx, xml, -1, &error)) + { + fprintf (stderr, "Invalid XML: '%s'\n '%s'\n", xml, error->message); + return false; + } + g_markup_parse_context_free (ctx); + + return true; +} + +bool testXHtmlConvert() +{ + fprintf (stderr, "Test HTML->XML rewrite \t"); + struct { + const char *in; + const char *out; + } aTests[] = { + // preservation + { "<p>foo</p>", "<body><p>foo</p></body>" }, + // outer tag + { "some text", "<body>some text</body>" }, + // unquoted attributes + { "<foo baa=Baz></foo>", "<body><foo baa="Baz"></foo></body>" }, + // break tags + { "<br>", "<body><br/></body>" }, + { "<hr>", "<body><hr/></body>" }, + // close with different case ... + { "<p>Foo</P>", "<body><p>Foo</p></body>" }, + // unclosed tags + { "<p>", "<body><p></p></body>" }, + { "<b>unclosed", "<body><b>unclosed</b></body>" }, + { "<b><i>bold</i>", "<body><b><i>bold</i></b></body>" }, + { "<i><i>bold</i>", "<body><i><i>bold</i></i></body>" }, + { "<unclosed foo=baa>", + "<body><unclosed foo="baa"></unclosed></body>" }, + // unclosed 'early close' tags + { "<i>Some<p>text<p> here<p></i>", + "<body><i>Some<p>text</p><p> here</p><p></p></i></body>" }, + { "<ul><li>foo<li>baa", + "<body><ul><li>foo</li><li>baa</li></ul></body>" }, + { "no outer<p>unclosed<p>several<b>unclosed bold", + "<body>no outer<p>unclosed</p><p>several<b>unclosed bold</b></p></body>" }, + // comment + { "we need <b>to <!-- really need to? --> do something</b> about it.", + "<body>we need <b>to do something</b> about it.</body>" }, + { "&", "<body>&</body>" }, + { "&", "<body>&amp</body>" }, + { "& foo", "<body>& foo</body>" }, + { "& foo", "<body>&amp foo</body>" }, + { "<pre>https://foo.com/regsvc-1.0/?lang=de-DE&guid=1529f49dc701449fbd854aebf7e4...</pre>\n", + "<body><pre>https://foo.com/regsvc-1.0/?lang=de-DE&guid=1529f49dc701449fbd854aebf7e4...</pre> </body>" }, + { NULL, NULL } + }; + for (int i = 0; aTests[i].in; i++) { + gchar *out = ygutils_convert_to_xhtml (aTests[i].in); + if (strcmp (out, aTests[i].out)) { + fprintf (stderr, "Mis-converted entry %d XML '%s' should be '%s'\n", + i, out, aTests[i].out); + return false; + } + if (!testParse (out)) + return false; + + fprintf (stderr, "%d ", i); + g_free (out); + } + fprintf (stderr, "\n"); + return true; +} + +bool testMarkupEscape() +{ + fprintf (stderr, "Test markup escape\t"); + struct { + const char *in; + const char *out; + bool escape_br; + } aTests[] = { + { "< text />", "< text />" }, + { "um\ndois\ntres", "um\ndois\ntres" }, + { NULL, NULL } + }; + for (int i = 0; aTests[i].in; i++) { + string out (YGUtils::escapeMarkup (aTests[i].in)); + if (out != aTests[i].out) { + fprintf (stderr, "Mis-converted entry %d XML '%s' should be '%s'\n", + i, out.c_str(), aTests[i].out); + return false; + } + fprintf (stderr, "%d ", i); + } + fprintf (stderr, "\n"); + return true; +} + +bool testTruncate() +{ + fprintf (stderr, "Test truncate\t"); + struct { + const char *in; + const char *out; + int length, pos; + } aTests[] = { + { "this-is-a-very-long-and-tedious-string", "this-is-a-very-lo...", 20, 1 }, + { "this-is-a-very-long-and-tedious-string", "...nd-tedious-string", 20, -1 }, + { "this-is-a-very-long-and-tedious-string", "this-is-a...s-string", 20, 0 }, + { "this-is-a-very-long-and-tedious-string2", "this-is-...s-string2", 20, 0 }, + { "abc", "abc", 3, 1 }, + { "abcd", "...", 3, 1 }, + { "abcd", "...", 3, -1 }, + { "abcd", "...", 3, 0 }, + { "abcdef", "a...", 4, 0 }, + { "áéÃóúçö", "áé...", 5, 1 }, + { "áéÃóúçö", "áéÃ...", 6, 1 }, + { "áéÃóúçö", "...çö", 5, -1 }, + { "áéÃóúçö", "...úçö", 6, -1 }, + { "áéÃóúçö", "á...ö", 5, 0 }, + { "áéÃóúçö", "á...çö", 6, 0 }, + { "áéÃóú", "áéÃóú", 5, 0 }, + { NULL, NULL } + }; + for (int i = 0; aTests[i].in; i++) { + string out = YGUtils::truncate (aTests[i].in, aTests[i].length, aTests[i].pos); + if (out != aTests[i].out) { + fprintf (stderr, "Mis-converted entry %d truncate '%s' should be '%s'\n", + i, out.c_str(), aTests[i].out); + return false; + } + fprintf (stderr, "%d ", i); + } + fprintf (stderr, "\n"); + return true; +} + +bool testHeaderize() +{ + fprintf (stderr, "Test headerize\t"); + struct { + const char *in; + const char *out; + gboolean cut; + } aTests[] = { + { "test", "test", FALSE }, + { "<h1>Purpose</h1><p>This tool lets you install, remove, and update applications.</p><p>openSUSE's software management", + "This tool lets you install, remove, and update applications.", TRUE }, + { "\n<p><big><b>Section List</b></big><br>\n<P>From <B>Other</B>,\nyou can manually edit the boot loader configuration files, clear the current\n" + "configuration and propose a new configuration, start from scratch, or reread\nthe configuration saved on your disk. If you have multiple Linux systems installed,", + "From Other, you can manually edit the boot loader configuration files, clear the current configuration and " + "propose a new configuration, start from scratch, or reread the configuration saved on your disk.", TRUE }, + { "<p><big><b>Sound Cards</b><big></p><P>Select an unconfigured card from the list and press <B>Edit</B> to\nconfigure it. If the card was not detected, press <B>Add</B> and\nconfigure the card manually.</P>", + "Select an unconfigured card from the list and press Edit to configure it.", TRUE }, + { "\n<p><b><big>Service Start</big></b><br>\nTo start the service every time your computer is booted, set\n" + "<b>Enable firewall</b>. Otherwise set <b>Disable firewall</b>.</p>\n<p><b><big>Switch On or Off</big></b><br>", + "To start the service every time your computer is booted, set Enable firewall.", TRUE }, + { NULL, NULL } + }; + for (int i = 0; aTests[i].in; i++) { + gboolean cut = FALSE; + char *out = ygutils_headerize_help (aTests[i].in, &cut); + if (strcmp (aTests[i].out, out)) { + fprintf (stderr, "Mis-headerized test %d:\n-- \n%s\n-- should be --\n%s\n-- xhtml --\n%s\n-- \n", + i, out, aTests[i].out, ygutils_convert_to_xhtml (aTests[i].in)); + return false; + } + if (!!aTests[i].cut != !!cut) + fprintf (stderr, "Mis-labelled as cut (%d)\n", !!cut); + fprintf (stderr, "%d ", i); + } + fprintf (stderr, "\n"); + return true; +} + +int main (int argc, char **argv) +{ + bool bSuccess = true; + + bSuccess &= testMapKBAccel(); + bSuccess &= testXHtmlConvert(); + bSuccess &= testMarkupEscape(); + bSuccess &= testTruncate(); + bSuccess &= testHeaderize(); + + return !bSuccess; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,411 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGdkMngLoader image loader */ +// check the header file for information about this loader + +#include "ygdkmngloader.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/mman.h> + +#include <gdk-pixbuf/gdk-pixbuf.h> + +/* A few chunks ids */ +#define MNG_UINT_MHDR 0x4d484452L +#define MNG_UINT_BACK 0x4241434bL +#define MNG_UINT_PLTE 0x504c5445L +#define MNG_UINT_tRNS 0x74524e53L +#define MNG_UINT_IHDR 0x49484452L +#define MNG_UINT_IDAT 0x49444154L +#define MNG_UINT_IEND 0x49454e44L +#define MNG_UINT_MEND 0x4d454e44L +#define MNG_UINT_FRAM 0x4652414dL +#define MNG_UINT_LOOP 0x4c4f4f50L +#define MNG_UINT_ENDL 0x454e444cL +#define MNG_UINT_TERM 0x5445524dL + +//** Utilities to read the MNG file + +typedef struct DataStream { + const guint8 *data; + const long size; + long offset; +} DataStream; +static DataStream data_stream_constructor (const guint8 *raw_data, long size) +{ + DataStream data = { raw_data, size, 0 }; + return data; +} + +static gboolean read_signature (DataStream *data) +{ + if (data->offset+8 > data->size) + return FALSE; + data->offset += 8; + return memcmp (data->data + data->offset-8, "\212MNG\r\n\032\n", 8) == 0; +} + +static gboolean read_uint8 (DataStream *data, guint8 *value) +{ + if (data->offset+1 > data->size) + return FALSE; + *value = data->data [data->offset++]; + return TRUE; +} + +static gboolean read_uint32 (DataStream *data, guint32 *value) +{ + if (data->offset+4 > data->size) + return FALSE; + *value = data->data[data->offset+0] << 24; + *value |= data->data[data->offset+1] << 16; + *value |= data->data[data->offset+2] << 8; + *value |= data->data[data->offset+3]; + data->offset += 4; + return TRUE; +} + +static gboolean read_data (DataStream *data, guint32 size, GdkPixbufLoader *loader, + GError **error) +{ + if (data->offset+size > data->size) + { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + "Unexpected end of file when reading PNG chunk"); + return FALSE; + } + gboolean ret; + ret = gdk_pixbuf_loader_write (loader, data->data + data->offset, size, error); + data->offset += size; + return ret; +} + +static void image_prepared_cb (GdkPixbufLoader *loader, YGdkMngPixbuf *mng_pixbuf) +{ + GdkPixbuf *pix = gdk_pixbuf_loader_get_pixbuf (loader); + mng_pixbuf->frames = g_list_append (mng_pixbuf->frames, pix); +} + +//** YGdkMngPixbuf + +G_DEFINE_TYPE (YGdkMngPixbuf, ygdk_mng_pixbuf, GDK_TYPE_PIXBUF_ANIMATION) + +static void ygdk_mng_pixbuf_init (YGdkMngPixbuf *pixbuf) +{ +} + +gboolean ygdk_mng_pixbuf_is_file_mng (const gchar *filename) +{ + FILE *file = fopen (filename, "rb"); + if (!file) + goto is_file_mng_failed; + + guchar raw_data [8]; + if (fread (raw_data, 1, 8, file) < 8) + goto is_file_mng_failed; + + gboolean ret = ygdk_mng_pixbuf_is_data_mng (raw_data, 8); + fclose (file); + return ret; + +is_file_mng_failed: + if (file) + fclose (file); + return FALSE; +} + +gboolean ygdk_mng_pixbuf_is_data_mng (const guint8 *raw_data, long size) +{ + DataStream data = data_stream_constructor (raw_data, size); + return read_signature (&data); +} + +#define SET_ERROR(msg) { error = TRUE; \ + g_set_error (error_msg, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, msg); } + +GdkPixbufAnimation *ygdk_mng_pixbuf_new_from_file (const gchar *filename, + GError **error_msg) +{ + gboolean error = FALSE; + FILE *file = fopen (filename, "rb"); + if (!file) + { + SET_ERROR ("Could not open specified file") + return NULL; + } + + fseek (file, 0, SEEK_END); + long file_size = ftell (file); + fseek (file, 0, SEEK_SET); + + GdkPixbufAnimation *mng_pixbuf = 0; + guchar *data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE, + fileno (file), 0); + if (data == MAP_FAILED) + SET_ERROR ("Could not map file") + else + { + mng_pixbuf = ygdk_mng_pixbuf_new_from_data (data, file_size, error_msg); + munmap (data, file_size); + } + fclose (file); + return mng_pixbuf; +} + +GdkPixbufAnimation *ygdk_mng_pixbuf_new_from_data (const guint8 *raw_data, long size, + GError **error_msg) +{ + DataStream data = data_stream_constructor (raw_data, size); + + gboolean error = FALSE; + if (!read_signature (&data)) + { + SET_ERROR ("Not a MNG file") + return NULL; + } + + YGdkMngPixbuf *mng_pixbuf = g_object_new (YGDK_TYPE_MNG_PIXBUF, NULL); + mng_pixbuf->iteration_max = 0x7fffffff; + + guint32 chunk_size, chunk_id; + long chunk_offset; + GdkPixbufLoader *loader = NULL; /* currently loading... */ + gboolean first_read = TRUE; + + do { + error = !read_uint32 (&data, &chunk_size); + error = error || !read_uint32 (&data, &chunk_id); + if (error) + { + SET_ERROR ("Unexpected end of file on new chunk") + break; + } + chunk_offset = data.offset + chunk_size + 4/*CRC*/; + + if (first_read && chunk_id != MNG_UINT_MHDR) + { + SET_ERROR ("MHDR chunk must come first") + break; + } + + // not currently reading a PNG stream + if (!loader) + { + switch (chunk_id) + { + case MNG_UINT_MHDR: + if (!first_read) + { + SET_ERROR ("Only one MHDR chunk allowed") + break; + } + + if (chunk_size == 7*4) + { + // Read MHDR chunk data + error = !read_uint32 (&data, &mng_pixbuf->frame_width); + error = error || !read_uint32 (&data, &mng_pixbuf->frame_height); + error = error || !read_uint32 (&data, &mng_pixbuf->ticks_per_second); + if (error) + SET_ERROR ("Unexpected end of file on MHDR chunk") + /* Next atttributes: Nominal_layer_count, Nominal_frame_count, + Nominal_play_time, Simplicity_profile */ + else if (mng_pixbuf->frame_width <= 0 || + mng_pixbuf->frame_height <= 0 || + mng_pixbuf->ticks_per_second < 0) + SET_ERROR ("Invalid MHDR parameter") +//fprintf(stderr, "ticks per second: %d\n", mng_pixbuf->ticks_per_second); + } + else + SET_ERROR ("MHDR chunk must be 28 bytes long") + break; + case MNG_UINT_IHDR: + loader = gdk_pixbuf_loader_new_with_type ("png", NULL); + g_signal_connect (G_OBJECT (loader), "area-prepared", + G_CALLBACK (image_prepared_cb), mng_pixbuf); + gdk_pixbuf_loader_set_size (loader, mng_pixbuf->frame_width, + mng_pixbuf->frame_height); + + { + const guchar sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + if(!gdk_pixbuf_loader_write (loader, sig, 8, error_msg)) + { + error = TRUE; + break; + } + } + break; + case MNG_UINT_TERM: + if (chunk_size > 1) + { + // Read TERM chunk data + guint8 t = 0; + error = !read_uint8 (&data, &t); // term action + if (t == 3 && chunk_size == 2+8) + { + error = error || !read_uint8 (&data, &t); // after term action + error = error || !read_uint32 (&data, &mng_pixbuf->last_frame_delay); + error = error || !read_uint32 (&data, &mng_pixbuf->iteration_max); + if (error) + SET_ERROR ("Unexpected end of file on TERM chunk") + } + else + SET_ERROR ("TERM chunk must be 10 bytes when term action is 3") + } + else + SET_ERROR ("TERM chunk must have at least 1 byte") + break; + case MNG_UINT_BACK: + // TODO: + break; + case MNG_UINT_IDAT: + case MNG_UINT_IEND: + if (loader != NULL) + SET_ERROR ("Corrupted PNG chunk closures") + break; + case MNG_UINT_MEND: + default: + break; + } + } + + if (error) + break; + + // loading a PNG stream + if (loader) + { + data.offset -= 8; + if (!read_data (&data, chunk_size+8+4, loader, error_msg)) + error = TRUE; + else if (chunk_id == MNG_UINT_IEND) + { + if (!gdk_pixbuf_loader_close (loader, error_msg)) + { + error = TRUE; + break; + } + loader = NULL; + } + } + + data.offset = chunk_offset; + first_read = FALSE; + } while (chunk_id != MNG_UINT_MEND && !error); + + if (error) + { + g_object_unref (G_OBJECT (mng_pixbuf)); + return NULL; + } + return GDK_PIXBUF_ANIMATION (mng_pixbuf); +} + +#undef SET_ERROR + +static gboolean ygdk_mng_pixbuf_is_static_image (GdkPixbufAnimation *anim) +{ + YGdkMngPixbuf *mng_anim = YGDK_MNG_PIXBUF (anim); + return g_list_length (mng_anim->frames) == 1; +} + +static GdkPixbuf *ygdk_mng_pixbuf_get_static_image (GdkPixbufAnimation *anim) +{ + YGdkMngPixbuf *mng_anim = YGDK_MNG_PIXBUF (anim); + return g_list_nth_data (mng_anim->frames, 0); +} + +static void ygdk_mng_pixbuf_get_size (GdkPixbufAnimation *anim, int *width, int *height) +{ + YGdkMngPixbuf *mng_anim = YGDK_MNG_PIXBUF (anim); + if (width) *width = mng_anim->frame_width; + if (height) *height = mng_anim->frame_height; +} + +static GdkPixbufAnimationIter *ygdk_mng_pixbuf_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time) +{ + YGdkMngPixbufIter *iter = g_object_new (YGDK_TYPE_MNG_PIXBUF_ITER, NULL); + iter->mng_pixbuf = YGDK_MNG_PIXBUF( anim ); + iter->cur_frame = 0; + return GDK_PIXBUF_ANIMATION_ITER( iter ); +} + +static void ygdk_mng_pixbuf_class_init (YGdkMngPixbufClass *klass) +{ + ygdk_mng_pixbuf_parent_class = g_type_class_peek_parent (klass); + + GdkPixbufAnimationClass *pixbuf_class = GDK_PIXBUF_ANIMATION_CLASS (klass); + pixbuf_class->is_static_image = ygdk_mng_pixbuf_is_static_image; + pixbuf_class->get_static_image = ygdk_mng_pixbuf_get_static_image; + pixbuf_class->get_size = ygdk_mng_pixbuf_get_size; + pixbuf_class->get_iter = ygdk_mng_pixbuf_get_iter; +} + +//** YGdkMngPixbufIter + +G_DEFINE_TYPE (YGdkMngPixbufIter, ygdk_mng_pixbuf_iter, GDK_TYPE_PIXBUF_ANIMATION_ITER) + +static void ygdk_mng_pixbuf_iter_init (YGdkMngPixbufIter *iter) +{ +} + +static GdkPixbuf *ygdk_mng_pixbuf_iter_get_pixbuf (GdkPixbufAnimationIter *iter) +{ + YGdkMngPixbufIter *mng_iter = YGDK_MNG_PIXBUF_ITER (iter); + return g_list_nth_data (mng_iter->mng_pixbuf->frames, mng_iter->cur_frame); +} + +static gboolean ygdk_mng_pixbuf_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter) +{ + return FALSE; +} + +static int ygdk_mng_pixbuf_iter_get_delay_time (GdkPixbufAnimationIter *iter) +{ + YGdkMngPixbufIter *mng_iter = YGDK_MNG_PIXBUF_ITER (iter); + int delay = 1000.0 / mng_iter->mng_pixbuf->ticks_per_second; + if (mng_iter->cur_frame == g_list_length (mng_iter->mng_pixbuf->frames)-1) + delay += mng_iter->mng_pixbuf->last_frame_delay; + return delay; +} + +static gboolean ygdk_mng_pixbuf_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time) +{ + YGdkMngPixbufIter *mng_iter = YGDK_MNG_PIXBUF_ITER (iter); + YGdkMngPixbuf *mng_pixbuf = mng_iter->mng_pixbuf; + if (!mng_pixbuf->frames) + return FALSE; + + gboolean can_advance = TRUE; + int frames_len = g_list_length (mng_pixbuf->frames); + if (mng_iter->cur_frame+1 == frames_len) + { + if (mng_pixbuf->iteration_max == 0x7fffffff || + mng_iter->cur_iteration < mng_pixbuf->iteration_max) + mng_iter->cur_iteration++; + else + can_advance = FALSE; + } + + if (can_advance) + mng_iter->cur_frame = (mng_iter->cur_frame+1) % frames_len; + return can_advance; +} + +static void ygdk_mng_pixbuf_iter_class_init (YGdkMngPixbufIterClass *klass) +{ + ygdk_mng_pixbuf_iter_parent_class = g_type_class_peek_parent (klass); + + GdkPixbufAnimationIterClass *iter_class = GDK_PIXBUF_ANIMATION_ITER_CLASS (klass); + iter_class->get_delay_time = ygdk_mng_pixbuf_iter_get_delay_time; + iter_class->get_pixbuf = ygdk_mng_pixbuf_iter_get_pixbuf; + iter_class->on_currently_loading_frame = ygdk_mng_pixbuf_iter_on_currently_loading_frame; + iter_class->advance = ygdk_mng_pixbuf_iter_advance; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygdkmngloader.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,80 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* GdkPixbuf doesn't support MNG files... MNG extends PNG adding movie playback + capabilities. + This code aims for MNG-VLC (smallest subset of the format), though + it isn't completely compliable. +*/ + +#ifndef YGDK_MNG_PIXBUF_H +#define YGDK_MNG_PIXBUF_H + +#define GDK_PIXBUF_ENABLE_BACKEND +#include <gdk-pixbuf/gdk-pixbuf-animation.h> +G_BEGIN_DECLS + +#define YGDK_TYPE_MNG_PIXBUF (ygdk_mng_pixbuf_get_type ()) +#define YGDK_MNG_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGDK_TYPE_MNG_PIXBUF, YGdkMngPixbuf)) +#define YGDK_MNG_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGDK_TYPE_MNG_PIXBUF, YGdkMngPixbufClass)) +#define YGDK_IS_MNG_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGDK_TYPE_MNG_PIXBUF)) +#define YGDK_IS_MNG_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGDK_TYPE_MNG_PIXBUF)) +#define YGDK_MNG_PIXBUF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGK_TYPE_MNG_PIXBUF, YGdkMngPixbufClass)) + +typedef struct YGdkMngPixbuf +{ + GdkPixbufAnimation parent; + + // private: (use GdkPixbufAnimation API) + GList *frames; // of GdkPixbufs + // MHDR header + guint32 frame_width, frame_height, ticks_per_second; + // TERM header + guint32 last_frame_delay, iteration_max; +} YGdkMngPixbuf; + +typedef struct YGdkMngPixbufClass +{ + GdkPixbufAnimationClass parent_class; +} YGdkMngPixbufClass; + +GdkPixbufAnimation *ygdk_mng_pixbuf_new_from_file (const gchar *filename, GError **error); +GdkPixbufAnimation *ygdk_mng_pixbuf_new_from_data (const guint8 *data, long size, GError **error); +gboolean ygdk_mng_pixbuf_is_file_mng (const gchar *filename); +gboolean ygdk_mng_pixbuf_is_data_mng (const guint8 *data, long size); + +GType ygdk_mng_pixbuf_get_type (void) G_GNUC_CONST; + +#define YGDK_TYPE_MNG_PIXBUF_ITER (ygdk_mng_pixbuf_iter_get_type ()) +#define YGDK_MNG_PIXBUF_ITER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGDK_TYPE_MNG_PIXBUF_ITER, YGdkMngPixbufIter)) +#define YGDK_MNG_PIXBUF_ITER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), YGDK_TYPE_MNG_PIXBUF_ITER, YGdkMngPixbufIterClass)) +#define YGDK_IS_MNG_PIXBUF_ITER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), YGDK_TYPE_MNG_PIXBUF_ITER)) +#define YGDK_MNG_PIXBUF_ITER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), YGDK_TYPE_MNG_PIXBUF_ITER, YGdkMngPixbufIterClass)) + +typedef struct YGdkMngPixbufIter { + GdkPixbufAnimationIter parent; + + // private: + YGdkMngPixbuf *mng_pixbuf; + int cur_frame, cur_iteration; +} YGdkMngPixbufIter; + +typedef struct YGdkMngPixbufIterClass { + GdkPixbufAnimationIterClass parent_class; +} YGdkMngPixbufIterClass; + +GType ygdk_mng_pixbuf_iter_get_type (void) G_GNUC_CONST; + +G_END_DECLS +#endif /*YGDK_MNG_PIXBUF_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,226 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkBarGraph widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygtkratiobox.h" +#include "ygtkbargraph.h" +#include <gtk/gtk.h> + +G_DEFINE_TYPE (YGtkBarGraph, ygtk_bar_graph, GTK_TYPE_FRAME) + +static void ygtk_bar_graph_init (YGtkBarGraph *bar) +{ + GtkWidget *box = ygtk_ratio_hbox_new (0); + gtk_widget_show (box); + gtk_container_add (GTK_CONTAINER (bar), box); + ygtk_bar_graph_set_style (bar, TRUE); +} + +static void ygtk_bar_graph_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + GTK_WIDGET_CLASS (ygtk_bar_graph_parent_class)->size_request (widget, requisition); + requisition->height += 18; // give room for the labels +} + +GtkWidget *ygtk_bar_graph_new (void) +{ + return g_object_new (YGTK_TYPE_BAR_GRAPH, NULL); +} + +void ygtk_bar_graph_create_entries (YGtkBarGraph *bar, guint entries) +{ + YGtkRatioBox *box = YGTK_RATIO_BOX (GTK_BIN (bar)->child); + + // Remove the ones in excess + guint i; + for (i = entries; i < g_list_length (box->children); i++) + gtk_container_remove (GTK_CONTAINER (box), + (GtkWidget*) g_list_nth_data (box->children, i)); + + // Add new ones, if missing + for (i = g_list_length (box->children); i < entries; i++) { + GtkWidget *label = ygtk_colored_label_new(); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + + // we need a GtkEventBox or something, so we may assign a tooltip to it + GtkWidget *lbox = gtk_event_box_new(); + gtk_container_add (GTK_CONTAINER (lbox), label); + gtk_widget_show_all (lbox); + gtk_container_add (GTK_CONTAINER (box), lbox); + } +} + +static GtkWidget *ygtk_bar_graph_get_label (YGtkBarGraph *bar, int index, GtkWidget **b) +{ + YGtkRatioBox *hbox = YGTK_RATIO_BOX (GTK_BIN (bar)->child); + GtkWidget *box = ((YGtkRatioBoxChild *) g_list_nth_data (hbox->children, index))->widget; + if (b) *b = box; + return gtk_bin_get_child (GTK_BIN (box)); +} + +void ygtk_bar_graph_setup_entry (YGtkBarGraph *bar, int index, const gchar *label_entry, int value) +{ + GtkWidget *box, *label; + label = ygtk_bar_graph_get_label (bar, index, &box); + + if (value < 0) + value = 0; + + // Reading label text + if (label_entry) { + GString *label_text = g_string_new (label_entry); + { // Replace %1 by value(i) + guint i; + for (i = 0; i < label_text->len; i++) + if (label_text->str[i] == '%' && label_text->str[i+1] == '1') { + gchar *value_str = g_strdup_printf ("%d", value); + label_text = g_string_erase (label_text, i, 2); + label_text = g_string_insert (label_text, i, value_str); + g_free (value_str); + } + } + gtk_label_set_label (GTK_LABEL (label), label_text->str); + + // tooltip for the labels -- useful if the bar entry gets too small + gtk_widget_set_tooltip_text (box, label_text->str); + g_string_free (label_text, TRUE); + } + + // Set proportion + gtk_widget_set_size_request (box, 0, -1); + YGtkRatioBox *hbox = YGTK_RATIO_BOX (GTK_BIN (bar)->child); + ygtk_ratio_box_set_child_packing (hbox, box, MAX (value, 1)); + + // Set background color + // The Tango palette + const guint palette [][3] = { + { 138, 226, 52 }, // Chameleon 1 + { 252, 175, 62 }, // Orange 1 + { 114, 159, 207 }, // Sky Blue 1 + { 233, 185, 110 }, // Chocolate 1 + { 239, 41, 41 }, // Scarlet Red 1 + { 252, 233, 79 }, // Butter 1 + { 173, 127, 168 }, // Plum 1 + { 115, 210, 22 }, // Chameleon 2 + { 245, 121, 0 }, // Orange 2 + { 52, 101, 164 }, // Sky Blue 2 + { 193, 125, 17 }, // Chocolate 2 + { 204, 0, 0 }, // Scarlet Red 2 + { 237, 212, 0 }, // Butter 2 + { 117, 80, 123 }, // Plum 2 + { 78, 154, 6 }, // Chameleon 3 + { 206, 92, 0 }, // Orange 3 + { 32, 74, 135 }, // Sky Blue 3 + { 143, 89, 2 }, // Chocolate 3 + { 164, 0, 0 }, // Scarlet Red 3 + { 196, 160, 0 }, // Butter 3 + { 92, 53, 102 }, // Plum 3 + { 238, 238, 236 }, // Aluminium 1 + { 211, 215, 207 }, // Aluminium 2 + { 186, 189, 182 }, // Aluminium 3 + { 136, 138, 133 }, // Aluminium 4 + { 85, 87, 83 }, // Aluminium 5 + { 46, 52, 54 }, // Aluminium 6 + }; + + YGtkColoredLabel *color_label = YGTK_COLORED_LABEL (label); + const guint *color = palette [index % G_N_ELEMENTS (palette)]; + GdkColor gcolor = { 0, color[0] << 8, color[1] << 8, color[2] << 8 }; + ygtk_colored_label_set_background (color_label, &gcolor); +} + +void ygtk_bar_graph_set_style (YGtkBarGraph *bar, gboolean flat) +{ + bar->flat = flat; + GtkShadowType shadow = flat ? GTK_SHADOW_OUT : GTK_SHADOW_NONE; + gtk_frame_set_shadow_type (GTK_FRAME (bar), shadow); +} + +void ygtk_bar_graph_customize_bg (YGtkBarGraph *bar, int index, GdkColor *color) +{ + GtkWidget *label = ygtk_bar_graph_get_label (bar, index, NULL); + ygtk_colored_label_set_background (YGTK_COLORED_LABEL (label), color); +} + +void ygtk_bar_graph_customize_fg (YGtkBarGraph *bar, int index, GdkColor *color) +{ + GtkWidget *label = ygtk_bar_graph_get_label (bar, index, NULL); + ygtk_colored_label_set_foreground (YGTK_COLORED_LABEL (label), color); +} + +static void ygtk_bar_graph_class_init (YGtkBarGraphClass *klass) +{ + ygtk_bar_graph_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = ygtk_bar_graph_size_request; +} + +//** YGtkColoredLabel + +#include <stdlib.h> + +G_DEFINE_TYPE (YGtkColoredLabel, ygtk_colored_label, GTK_TYPE_LABEL) + +static void ygtk_colored_label_init (YGtkColoredLabel *label) +{} + +static inline double pixel_clamp (double val) +{ return MAX (0, MIN (1, val)); } + +static gboolean ygtk_colored_label_expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + GtkAllocation *alloc = &widget->allocation; + + GdkColor *bg_color = &widget->style->bg[GTK_STATE_NORMAL]; + double red = (bg_color->red >> 8) / 255.; + double green = (bg_color->green >> 8) / 255.; + double blue = (bg_color->blue >> 8) / 255.; + + cairo_t *cr = gdk_cairo_create (event->window); + cairo_translate (cr, alloc->x, alloc->y); + cairo_scale (cr, alloc->width, alloc->height); + + double x, y, w, h; + x = alloc->x; + y = alloc->y; + w = alloc->width; + h = alloc->height; + cairo_pattern_t *grad = cairo_pattern_create_linear (0, 0, 0, 1); + + cairo_pattern_add_color_stop_rgba (grad, 0, pixel_clamp (red+.3), pixel_clamp (green+.3), pixel_clamp (blue+.3), 1); + cairo_pattern_add_color_stop_rgba (grad, 0.70, red, green, blue, 1); + cairo_pattern_add_color_stop_rgba (grad, 1, pixel_clamp (red-.2), pixel_clamp (green-.2), pixel_clamp (blue-.2), 1); + + cairo_rectangle (cr, 0, 0, 1, 1); + cairo_set_source (cr, grad); + cairo_fill (cr); + + cairo_pattern_destroy (grad); + cairo_destroy (cr); + + GTK_WIDGET_CLASS (ygtk_colored_label_parent_class)->expose_event (widget, event); + return FALSE; +} + +GtkWidget *ygtk_colored_label_new (void) +{ return g_object_new (YGTK_TYPE_COLORED_LABEL, NULL); } + +void ygtk_colored_label_set_background (YGtkColoredLabel *label, GdkColor *color) +{ gtk_widget_modify_bg (GTK_WIDGET (label), GTK_STATE_NORMAL, color); } + +void ygtk_colored_label_set_foreground (YGtkColoredLabel *label, GdkColor *color) +{ gtk_widget_modify_fg (GTK_WIDGET (label), GTK_STATE_NORMAL, color); } + +static void ygtk_colored_label_class_init (YGtkColoredLabelClass *klass) +{ + ygtk_colored_label_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->expose_event = ygtk_colored_label_expose_event; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkbargraph.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,96 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkBarGraph is just a simple graph of bars that may be + changed during run-time (see ygtk_bar_graph_setup_entry()). +*/ + +#ifndef YGTK_BAR_GRAPH_H +#define YGTK_BAR_GRAPH_H + +#include <gtk/gtkframe.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_BAR_GRAPH (ygtk_bar_graph_get_type ()) +#define YGTK_BAR_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_BAR_GRAPH, YGtkBarGraph)) +#define YGTK_BAR_GRAPH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_BAR_GRAPH, YGtkBarGraphClass)) +#define YGTK_IS_BAR_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_BAR_GRAPH)) +#define YGTK_IS_BAR_GRAPH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_BAR_GRAPH)) +#define YGTK_BAR_GRAPH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_BAR_GRAPH, YGtkBarGraphClass)) + +typedef struct _YGtkBarGraph YGtkBarGraph; +typedef struct _YGtkBarGraphClass YGtkBarGraphClass; + +struct _YGtkBarGraph +{ + GtkFrame parent; + // members: + guint flat : 2; +}; + +struct _YGtkBarGraphClass +{ + GtkFrameClass parent_class; +}; + +GtkWidget *ygtk_bar_graph_new (void); +GType ygtk_bar_graph_get_type (void) G_GNUC_CONST; + +void ygtk_bar_graph_create_entries (YGtkBarGraph *bar, guint entries); +void ygtk_bar_graph_setup_entry (YGtkBarGraph *bar, int index, const gchar *label_entry, int value); + +void ygtk_bar_graph_set_style (YGtkBarGraph *bar, gboolean flat); +void ygtk_bar_graph_customize_bg (YGtkBarGraph *bar, int index, GdkColor *color); +void ygtk_bar_graph_customize_fg (YGtkBarGraph *bar, int index, GdkColor *color); + +G_END_DECLS +#endif /*YGTK_BAR_GRAPH_H*/ + +#ifndef YGTK_COLORED_LABEL_H +#define YGTK_COLORED_LABEL_H + +#include <gtk/gtklabel.h> +G_BEGIN_DECLS + +/* YGtkColoredLabel is a GtkLabel where gtk_modify_bg() can be used. It also + allows a frame around it. */ + +#define YGTK_TYPE_COLORED_LABEL (ygtk_colored_label_get_type ()) +#define YGTK_COLORED_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_COLORED_LABEL, YGtkColoredLabel)) +#define YGTK_COLORED_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_COLORED_LABEL, YGtkColoredLabelClass)) +#define YGTK_IS_COLORED_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_COLORED_LABEL)) +#define YGTK_IS_COLORED_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_COLORED_LABEL)) +#define YGTK_COLORED_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_COLORED_LABEL, YGtkColoredLabelClass)) + +typedef struct _YGtkColoredLabel +{ + GtkLabel parent; +} YGtkColoredLabel; + +typedef struct _YGtkColoredLabelClass +{ + GtkLabelClass parent_class; +} YGtkColoredLabelClass; + +GtkWidget *ygtk_colored_label_new (void); +GType ygtk_colored_label_get_type (void) G_GNUC_CONST; + +// A convenience function (you may use gtk_widget_modify_fg() and +// gtk_widget_modify_bg() instead), where colors range is [0, 255] +void ygtk_colored_label_set_foreground (YGtkColoredLabel *label, GdkColor *color); +void ygtk_colored_label_set_background (YGtkColoredLabel *label, GdkColor *color); + +G_END_DECLS +#endif /*YGTK_COLORED_LABEL*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,201 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkFieldEntry widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygtkfieldentry.h" +#include <gtk/gtk.h> +#include <string.h> + +void ygutils_setFilter (GtkEntry *entry, const char *validChars); +static guint filter_entry_signal = 0; + +//** YGtkFieldEntry + +G_DEFINE_TYPE (YGtkFieldEntry, ygtk_field_entry, GTK_TYPE_HBOX) + +static void ygtk_field_entry_init (YGtkFieldEntry *entry) +{ + gtk_box_set_spacing (GTK_BOX (entry), 4); +} + +static GtkEntry *ygtk_field_entry_focus_next_entry (YGtkFieldEntry *fields, + GtkEntry *current_entry, + gint side) +{ + GList *children = gtk_container_get_children (GTK_CONTAINER (fields)); + gint pos = g_list_index (children, current_entry); + + GtkEntry *next_entry = g_list_nth_data (children, pos + (2 * side)); + g_list_free (children); + + if (next_entry) + gtk_widget_grab_focus (GTK_WIDGET (next_entry)); + return next_entry; +} + +// If max characters reached, jump to next field +static void ygtk_field_entry_insert_text (GtkEditable *editable, const gchar *new_text, + gint new_text_length, gint *position, YGtkFieldEntry *fields) +{ + if (*position == gtk_entry_get_max_length (GTK_ENTRY (editable))) { + GtkEntry *next_entry = ygtk_field_entry_focus_next_entry (fields, + GTK_ENTRY (editable), 1); + if (next_entry) { + gint pos = 0; + gtk_editable_insert_text (GTK_EDITABLE (next_entry), new_text, + new_text_length, &pos); + gtk_editable_set_position (GTK_EDITABLE (next_entry), pos); + + // it would not insert the text anyway, but to avoid the beep + g_signal_stop_emission_by_name (editable, "insert_text"); + } + } +} + +static void ygtk_field_entry_move_cursor (GtkEntry *entry, GtkMovementStep move, + gint count, gboolean selection, YGtkFieldEntry *fields) +{ + if (move == GTK_MOVEMENT_VISUAL_POSITIONS) { + if (count > 0) + ygtk_field_entry_focus_next_entry (fields, GTK_ENTRY (entry), 1); + else + ygtk_field_entry_focus_next_entry (fields, GTK_ENTRY (entry), -1); + } +} + + +GtkWidget *ygtk_field_entry_new (void) +{ return g_object_new (YGTK_TYPE_FIELD_ENTRY, NULL); } + +static void ygtk_field_entry_entry_changed (GtkEditable *editable, YGtkFieldEntry *fields) +{ + GList *children = gtk_container_get_children (GTK_CONTAINER (fields)); + gint nb = g_list_index (children, editable) / 2; + g_list_free (children); + + g_signal_emit (fields, filter_entry_signal, 0, nb); +} + +static guint ygtk_field_entry_length (YGtkFieldEntry *fields) +{ + guint length; + GList *children = gtk_container_get_children (GTK_CONTAINER (fields)); + length = g_list_length (children); + g_list_free (children); + return length; +} + +static inline guint child_to_index (YGtkFieldEntry *fields, guint child_i) +{ return child_i / 2; } +static inline guint index_to_child (YGtkFieldEntry *fields, guint index) +{ return index * 2; } + +GtkEntry *ygtk_field_entry_get_field_widget (YGtkFieldEntry *fields, guint index) +{ + GtkEntry *entry; + GList *children = gtk_container_get_children (GTK_CONTAINER (fields)); + entry = g_list_nth_data (children, index_to_child (fields, index)); + g_list_free (children); + g_assert (GTK_IS_ENTRY (entry)); + return entry; +} +guint ygtk_field_entry_add_field (YGtkFieldEntry *fields, gchar separator) +{ + guint new_index = child_to_index (fields, ygtk_field_entry_length (fields)+1); + + GtkWidget *label = 0, *entry; + if (new_index > 0) { + const gchar str[2] = { separator, '\0' }; + label = gtk_label_new (str); + } + entry = gtk_entry_new(); + g_signal_connect (G_OBJECT (entry), "insert-text", + G_CALLBACK (ygtk_field_entry_insert_text), fields); + g_signal_connect (G_OBJECT (entry), "move-cursor", + G_CALLBACK (ygtk_field_entry_move_cursor), fields); + + g_signal_connect (G_OBJECT (entry), "changed", + G_CALLBACK (ygtk_field_entry_entry_changed), fields); + + GtkBox *box = GTK_BOX (fields); + if (label) { + gtk_box_pack_start (box, label, FALSE, TRUE, 0); + gtk_widget_show (label); + } + gtk_box_pack_start (box, entry, TRUE, TRUE, 0); + gtk_widget_show (entry); + return new_index; +} + +void ygtk_field_entry_setup_field (YGtkFieldEntry *fields, guint index, + gint max_length, const gchar *valid_chars) +{ + GtkEntry *entry = ygtk_field_entry_get_field_widget (fields, index); + gboolean disable_len = (max_length <= 0); + gtk_entry_set_max_length (entry, disable_len ? 0 : max_length); + gtk_entry_set_width_chars (entry, disable_len ? -1 : max_length); + gtk_box_set_child_packing (GTK_BOX (fields), GTK_WIDGET (entry), + disable_len, TRUE, 0, GTK_PACK_START); + ygutils_setFilter (entry, valid_chars); +} + +void ygtk_field_entry_set_field_text (YGtkFieldEntry *fields, guint index, const gchar *text) +{ + GtkEntry *entry = ygtk_field_entry_get_field_widget (fields, index); + + g_signal_handlers_block_by_func (entry, + (gpointer) ygtk_field_entry_entry_changed, fields); + g_signal_handlers_block_by_func (entry, + (gpointer) ygtk_field_entry_insert_text, fields); + + gtk_entry_set_text (entry, text); + + g_signal_handlers_unblock_by_func (entry, + (gpointer) ygtk_field_entry_entry_changed, fields); + g_signal_handlers_unblock_by_func (entry, + (gpointer) ygtk_field_entry_insert_text, fields); +} + +const gchar *ygtk_field_entry_get_field_text (YGtkFieldEntry *fields, guint index) +{ + GtkEntry *entry = ygtk_field_entry_get_field_widget (fields, index); + return gtk_entry_get_text (entry); +} + +static gboolean ygtk_field_entry_mnemonic_activate (GtkWidget *widget, gboolean cycling) +{ + YGtkFieldEntry *fields = YGTK_FIELD_ENTRY (widget); + GtkEntry *entry = ygtk_field_entry_get_field_widget (fields, 0); + gtk_widget_grab_focus (GTK_WIDGET (entry)); + return TRUE; +} + +static void ygtk_field_entry_class_init (YGtkFieldEntryClass *klass) +{ + ygtk_field_entry_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->mnemonic_activate = ygtk_field_entry_mnemonic_activate; + + filter_entry_signal = g_signal_new ("field_entry_changed", + G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (YGtkFieldEntryClass, filter_entry_changed), + NULL, NULL, gtk_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); +} + +gboolean ygtk_field_entry_set_focus (YGtkFieldEntry *fields) +{ + GList *children = gtk_container_get_children (GTK_CONTAINER (fields)); + g_return_val_if_fail (children != NULL, FALSE); + GtkWidget *widget = GTK_WIDGET (children->data); + g_list_free (children); + + gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); + gtk_widget_grab_focus (widget); + return gtk_widget_is_focus (widget); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfieldentry.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,61 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkFieldEntry is an extension of GtkEntry with the added + functionality of being able to define fields (useful for when + you need the user to set a IP address or time/date). The number + of fields, their individual range and separation character + is all customizable. +*/ + +#ifndef YGTK_FIELD_ENTRY_H +#define YGTK_FIELD_ENTRY_H + +#include <gtk/gtkhbox.h> +#include <gtk/gtkentry.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_FIELD_ENTRY (ygtk_field_entry_get_type ()) +#define YGTK_FIELD_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_FIELD_ENTRY, YGtkFieldEntry)) +#define YGTK_FIELD_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_FIELD_ENTRY, YGtkFieldEntryClass)) +#define IS_YGTK_FIELD_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_FIELD_ENTRY)) +#define IS_YGTK_FIELD_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_FIELD_ENTRY)) +#define YGTK_FIELD_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_FIELD_ENTRY, YGtkFieldEntryClass)) + +typedef struct _YGtkFieldEntry +{ + GtkHBox parent; +} YGtkFieldEntry; + +typedef struct _YGtkFieldEntryClass +{ + GtkHBoxClass parent_class; + + void (* filter_entry_changed) (YGtkFieldEntry *entry, gint field_nb); +} YGtkFieldEntryClass; + +GtkWidget* ygtk_field_entry_new (void); +GType ygtk_field_entry_get_type (void) G_GNUC_CONST; + +// if this is the first field, separator will be ignored. +guint ygtk_field_entry_add_field (YGtkFieldEntry *entry, gchar separator); +// max_length can be 0 to disable it. valids_chars can be NULL to disable it. +void ygtk_field_entry_setup_field (YGtkFieldEntry *entry, guint index, + gint max_length, const gchar *valid_chars); + +const gchar *ygtk_field_entry_get_field_text (YGtkFieldEntry *entry, guint index); +void ygtk_field_entry_set_field_text (YGtkFieldEntry *entry, guint index, + const gchar *text); + +GtkEntry *ygtk_field_entry_get_field_widget (YGtkFieldEntry *entry, guint index); +gboolean ygtk_field_entry_set_focus (YGtkFieldEntry *entry); + +G_END_DECLS +#endif /*YGTK_FIELD_ENTRY_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,140 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkFixed container */ +// check the header file for information about this container + +#include <config.h> +#include <math.h> +#include <gtk/gtk.h> +#include "ygtkfixed.h" + +G_DEFINE_TYPE (YGtkFixed, ygtk_fixed, GTK_TYPE_CONTAINER) + +static void ygtk_fixed_init (YGtkFixed *fixed) +{ + GTK_WIDGET_SET_FLAGS (fixed, GTK_NO_WINDOW); + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (fixed), FALSE); +} + +void ygtk_fixed_setup (YGtkFixed *fixed, YGtkPreferredSize cb1, YGtkSetSize cb2, gpointer data) +{ + fixed->preferred_size_cb = cb1; + fixed->set_size_cb = cb2; + fixed->data = data; +} + +static YGtkFixedChild *ygtk_fixed_get_child (YGtkFixed *fixed, GtkWidget *widget) +{ + GSList *i; + for (i = fixed->children; i; i = i->next) { + YGtkFixedChild *child = i->data; + if (child->widget == widget) + return child; + } + g_warning ("YGtkFixed: could not find child."); + return NULL; +} + +void ygtk_fixed_set_child_pos (YGtkFixed *fixed, GtkWidget *widget, gint x, gint y) +{ + YGtkFixedChild *child = ygtk_fixed_get_child (fixed, widget); + child->x = x; + child->y = y; +} + +void ygtk_fixed_set_child_size (YGtkFixed *fixed, GtkWidget *widget, gint width, gint height) +{ + YGtkFixedChild *child = ygtk_fixed_get_child (fixed, widget); + child->width = width; + child->height = height; +} + +static void ygtk_fixed_add (GtkContainer *container, GtkWidget *widget) +{ + YGtkFixed *fixed = YGTK_FIXED (container); + YGtkFixedChild *child = g_new0 (YGtkFixedChild, 1); + child->widget = widget; + child->width = child->height = 50; + fixed->children = g_slist_append (fixed->children, child); + gtk_widget_set_parent (widget, GTK_WIDGET (fixed)); +} + +static void ygtk_fixed_remove (GtkContainer *container, GtkWidget *widget) +{ + YGtkFixed *fixed = YGTK_FIXED (container); + GSList *i; + for (i = fixed->children; i; i = i->next) { + YGtkFixedChild *child = i->data; + if (child->widget == widget) { + gboolean was_visible = GTK_WIDGET_VISIBLE (widget); + gtk_widget_unparent (widget); + fixed->children = g_slist_delete_link (fixed->children, i); + g_free (child); + if (was_visible) + gtk_widget_queue_resize (GTK_WIDGET (container)); + break; + } + } +} + +static void ygtk_fixed_forall (GtkContainer *container, gboolean include_internals, + GtkCallback callback, gpointer callback_data) +{ + g_return_if_fail (callback != NULL); + YGtkFixed *fixed = YGTK_FIXED (container); + GSList *i = fixed->children; + while (i) { + YGtkFixedChild *child = i->data; + i = i->next; // current node might get removed... + (* callback) (child->widget, callback_data); + } +} + +static void ygtk_fixed_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + YGtkFixed *fixed = YGTK_FIXED (widget); + fixed->preferred_size_cb (fixed, &requisition->width, &requisition->height, + fixed->data); + GTK_WIDGET_CLASS (ygtk_fixed_parent_class)->size_request (widget, requisition); +} + +static void ygtk_fixed_size_allocate (GtkWidget *widget, GtkAllocation *allocation) +{ + YGtkFixed *fixed = YGTK_FIXED (widget); + fixed->set_size_cb (fixed, allocation->width, allocation->height, fixed->data); + + GSList *i; + for (i = fixed->children; i; i = i->next) { + YGtkFixedChild *child = i->data; + int x = child->x; + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + x = allocation->width - (child->x + child->width); + x += allocation->x; + int y = child->y + allocation->y; + GtkAllocation child_alloc = + { x, y, MAX (child->width, 1), MAX (child->height, 1) }; + gtk_widget_size_allocate (child->widget, &child_alloc); + } + GTK_WIDGET_CLASS (ygtk_fixed_parent_class)->size_allocate (widget, allocation); +} + +static GType ygtk_fixed_child_type (GtkContainer *container) +{ return GTK_TYPE_WIDGET; } + +static void ygtk_fixed_class_init (YGtkFixedClass *klass) +{ + ygtk_fixed_parent_class = g_type_class_peek_parent (klass); + + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + container_class->add = ygtk_fixed_add; + container_class->remove = ygtk_fixed_remove; + container_class->forall = ygtk_fixed_forall; + container_class->child_type = ygtk_fixed_child_type; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = ygtk_fixed_size_request; + widget_class->size_allocate = ygtk_fixed_size_allocate; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkfixed.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,64 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* GtkFixed just doesn't cut it... gtk_fixed_move_child() does a queue_resize + and the all thing is just not quite appropriate... */ + +#ifndef YGTK_FIXED_H +#define YGTK_FIXED_H + +#include <gtk/gtkcontainer.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_FIXED (ygtk_fixed_get_type ()) +#define YGTK_FIXED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_FIXED, YGtkFixed)) +#define YGTK_FIXED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_FIXED, YGtkFixedClass)) +#define YGTK_IS_FIXED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_FIXED)) +#define YGTK_IS_FIXED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_FIXED)) +#define YGTK_FIXED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_FIXED, YGtkFixedClass)) + +typedef struct _YGtkFixed YGtkFixed; +typedef struct _YGtkFixedClass YGtkFixedClass; + +typedef void (*YGtkPreferredSize) (YGtkFixed *, gint *, gint *, gpointer); +typedef void (*YGtkSetSize) (YGtkFixed *, gint, gint, gpointer); + +struct _YGtkFixed +{ + GtkContainer parent; + // private (read-only): + GSList *children; + YGtkPreferredSize preferred_size_cb; + YGtkSetSize set_size_cb; + gpointer data; +}; + +struct _YGtkFixedClass +{ + GtkContainerClass parent_class; +}; + +typedef struct _YGtkFixedChild +{ + GtkWidget *widget; + // members + // post-pone all position and size setting, to avoid unnecessary work + gint x, y, width, height; +} YGtkFixedChild; + +GType ygtk_fixed_get_type (void) G_GNUC_CONST; + +void ygtk_fixed_setup (YGtkFixed *fixed, YGtkPreferredSize cb1, YGtkSetSize cb2, gpointer data); + +void ygtk_fixed_set_child_pos (YGtkFixed *fixed, GtkWidget *widget, gint x, gint y); +void ygtk_fixed_set_child_size (YGtkFixed *fixed, GtkWidget *widget, gint width, gint height); + +G_END_DECLS +#endif /*YGTK_FIXED_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,343 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkHtmlWrap widget */ +// check the header file for information about this widget + +#include <config.h> +#include <gtk/gtkversion.h> +#include <string.h> +#include "ygtkhtmlwrap.h" + +// ygutils +#include <gtk/gtktextview.h> +void ygutils_scrollAdj (GtkAdjustment *vadj, gboolean top); + +GtkWidget *ygtk_html_wrap_new (void) +{ + GtkWidget *widget = g_object_new (ygtk_html_wrap_get_type(), NULL); + ygtk_html_wrap_init (widget); + return widget; +} + +//** WebKit +#ifdef USE_WEBKIT +#include <webkit/webkit.h> + +GType ygtk_html_wrap_get_type() +{ + return WEBKIT_TYPE_WEB_VIEW; +} + +static void copy_activate_cb (GtkMenuItem *item, WebKitWebView *view) +{ webkit_web_view_copy_clipboard (view); } +static void select_all_activate_cb (GtkMenuItem *item, WebKitWebView *view) +{ webkit_web_view_select_all (view); } + +static gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *event, + WebKitWebView *view) +{ + if (event && event->button != 3) + return FALSE; + // GtkMenu API is horrible for non-persistant menus. Make it persistant. + static GtkWidget *menu = 0; + if (menu) + gtk_widget_destroy (menu); + menu = gtk_menu_new(); + // add a couple of items (based on GtkTextView) + GtkWidget *item; + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + if (webkit_web_view_can_copy_clipboard (view)) + g_signal_connect (item, "activate", G_CALLBACK (copy_activate_cb), widget); + else + gtk_widget_set_sensitive (item, FALSE); + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (item, "activate", G_CALLBACK (select_all_activate_cb), widget); + + int button, event_time; + if (event) { + button = event->button; + event_time = event->time; + } + else { + button = 0; + event_time = gtk_get_current_event_time(); + } + gtk_menu_attach_to_widget (GTK_MENU (menu), widget, NULL); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, event_time); + gtk_widget_show_all (menu); + return TRUE; +} + +static gboolean popup_menu_cb (GtkWidget *widget, WebKitWebView *view) +{ + button_press_event_cb (widget, NULL, view); + return TRUE; +} + +static gboolean button_release_event_cb (GtkWidget *widget, GdkEventButton *event) +{ + if (event->button != 3) return FALSE; + return TRUE; +} + +void ygtk_html_wrap_init (GtkWidget *widget) +{ + /* WebKit popup menu has entries such as "reload", "go back" and "search web" + that we rather not show. "popup-menu" isn't working, use gtkwidget stuff... */ + g_signal_connect (G_OBJECT (widget), "popup-menu", + G_CALLBACK (popup_menu_cb), widget); + g_signal_connect (G_OBJECT (widget), "button-press-event", + G_CALLBACK (button_press_event_cb), widget); + // webkit crashes if we don't overload the release event as well + g_signal_connect (G_OBJECT (widget), "button-release-event", + G_CALLBACK (button_release_event_cb), widget); +} + +// CSS roughly based on Yelp style +const char *CSS = "<style type="text/css">" +"h1 { color: #5c5c5c; font-size: xx-large; font-weight: 900; border-bottom: thin solid cdcfd1; }" +"h2 { color: #5c5c5c; font-size: x-large; font-weight: 800; border-bottom: thin solid cdcfd1; }" +"h3 { color: #5c5c5c; font-size: large; font-weight: 700; border-bottom: thin solid cdcfd1; }" +"h4 { color: #5c5c5c; font-size: large; font-weight: 600; }" +"h5 { color: #5c5c5c; font-size: large; }" +"pre { background-color: #f0f0f0; border: thin solid #bfbfbf; padding: 2px; }" +"a { text-decoration: none; }" +"a:hover { text-decoration: underline; }" +"</style>"; + +void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar *text, gboolean plain_mode) +{ + WebKitWebView *view = WEBKIT_WEB_VIEW (widget); + // webkit prepends base-uri to non-uri hrefs + if (plain_mode) + webkit_web_view_load_string (view, text, "text/plain", "UTF-8", "/"); + else { + GString *str = NULL; + int i, last_i = 0; + for (i = 0; text[i]; i++) { + if (!strncmp (text+i, "href=", 5)) { + i += 5; + if (text[i] == '"') + i++; + int j; + for (j = i; text[j] && text[j] != ':'; j++) + if (text[j] == '"' || g_ascii_isspace (text[j])) { + if (!str) + str = g_string_new (""); + str = g_string_append_len (str, text+last_i, i-last_i); + last_i = i; + str = g_string_append (str, "label:/"); + break; + } + } + } + if (str) { + str = g_string_append (str, text+last_i); + text = g_string_free (str, FALSE); + } + + const gchar *extra_css = g_object_get_data (G_OBJECT (widget), "extra-css"); + if (!extra_css) extra_css = ""; + + gchar *html = g_strdup_printf ("%s\n%s\n%s", CSS, extra_css, text); + if (str) + g_free ((gchar *) text); + webkit_web_view_load_string (view, html, "text/html", "UTF-8", "/"); + g_free (html); + } +} + +void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top) +{ + GtkWidget *scroll_win = gtk_widget_get_parent (widget); + ygutils_scrollAdj (gtk_scrolled_window_get_vadjustment ( + GTK_SCROLLED_WINDOW (scroll_win)), top); +} + +gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text) +{ + WebKitWebView *view = WEBKIT_WEB_VIEW (widget); + webkit_web_view_unmark_text_matches (view); + if (*text) { + gboolean found = webkit_web_view_mark_text_matches (view, text, FALSE, -1); + webkit_web_view_set_highlight_text_matches (view, TRUE); + if (found) + ygtk_html_wrap_search_next (widget, text); + return found; + } + // we want to un-select previous search (no such api though) + return TRUE; +} + +gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text) +{ + WebKitWebView *view = WEBKIT_WEB_VIEW (widget); + return webkit_web_view_search_text (view, text, FALSE, TRUE, TRUE); +} + +static WebKitNavigationResponse ygtk_webkit_navigation_requested_cb ( + WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *request, LinkClickedCb callback) +{ + const gchar *uri = webkit_network_request_get_uri (request); + // look for set_text to see why we need to cut the uri in some cases + // (hint: not an uri) + if (!strncmp (uri, "label:/", sizeof ("label:/")-1)) + uri = uri + sizeof ("label:/")-1; + gpointer data = g_object_get_data (G_OBJECT (view), "pointer"); + (*callback) (GTK_WIDGET (view), uri, data); + return WEBKIT_NAVIGATION_RESPONSE_IGNORE; +} + +void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data) +{ + g_object_set_data (G_OBJECT (widget), "pointer", data); + g_signal_connect (G_OBJECT (widget), "navigation-requested", + G_CALLBACK (ygtk_webkit_navigation_requested_cb), callback); +} + +void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename) +{ + int width = gdk_pixbuf_get_width (pixbuf); + gchar *bg_css = g_strdup_printf ( + "<style type="text/css">" + "body { background-image: url('%s'); background-repeat: no-repeat;" + "background-position: %dpx 100%%; background-attachment: fixed; }" + "</style>", filename, -width+40); + g_object_set_data_full (G_OBJECT (widget), "extra-css", bg_css, g_free); +} + +#else + +//** GtkHTML +#ifdef USE_GTKHTML +#include <gtkhtml/gtkhtml.h> +#include <gtkhtml/gtkhtml-stream.h> +#include <gtkhtml/gtkhtml-search.h> + +GType ygtk_html_wrap_get_type (void) +{ + return GTK_TYPE_HTML; +} + +static void gtkhtml_url_requested_cb (GtkHTML *html, const gchar *url, GtkHTMLStream *stream) +{ // to load images (and possibly other external embed files) + FILE *file = fopen (url, "rb"); + if (!file) { + g_warning ("Error: couldn't open file '%s'\n", url); + return; + } + + fseek (file, 0, SEEK_END); + size_t file_size = ftell (file); + rewind (file); + + gboolean error; + gchar *data = g_new (gchar, file_size); + error = fread (data, 1, file_size, file) < file_size; + fclose (file); + + if (!error) + gtk_html_stream_write (stream, data, file_size); + g_free (data); +} + +void ygtk_html_wrap_init (GtkWidget *widget) +{ + gtk_html_set_editable (GTK_HTML (widget), FALSE); + g_signal_connect (G_OBJECT (widget), "url-requested", + G_CALLBACK (gtkhtml_url_requested_cb), NULL); +} + +void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar* text, gboolean plain_mode) +{ + // TODO: implement plain_mode + GtkHTMLStream *stream = gtk_html_begin (GTK_HTML (widget)); + gtk_html_write (GTK_HTML (widget), stream, text, strlen (text)); + gtk_html_end (GTK_HTML (widget), stream, GTK_HTML_STREAM_OK); +} + +void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top) +{ + ygutils_scrollAdj (GTK_LAYOUT (widget)->vadjustment, top); +} + +gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text) +{ +/* if (*text == '\0') + return TRUE;*/ + return gtk_html_engine_search (GTK_HTML (widget), text, FALSE, TRUE, FALSE); +} + +gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text) +{ + return gtk_html_engine_search_next (GTK_HTML (widget)); +} + +void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data) +{ + g_signal_connect (G_OBJECT (widget), "link-clicked", G_CALLBACK (callback), data); +} + +void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename) +{ + // TODO +} + +#else +//** YGtkRichText (internal) +#include "ygtkrichtext.h" + +GType ygtk_html_wrap_get_type() +{ + return YGTK_TYPE_RICH_TEXT; +} + +void ygtk_html_wrap_init (GtkWidget *widget) +{ +} + +void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar* text, gboolean plain_mode) +{ + YGtkRichText *rtext = YGTK_RICH_TEXT (widget); + if (plain_mode) + ygtk_rich_text_set_plain_text (rtext, text); + else + ygtk_rich_text_set_text (rtext, text); +} + +void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top) +{ + ygutils_scrollAdj (GTK_TEXT_VIEW (widget)->vadjustment, top); +} + +gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text) +{ + gboolean ret = ygtk_rich_text_mark_text (YGTK_RICH_TEXT (widget), text); + ygtk_rich_text_forward_mark (YGTK_RICH_TEXT (widget), text); + return ret; +} + +gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text) +{ + return ygtk_rich_text_forward_mark (YGTK_RICH_TEXT (widget), text); +} + +void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data) +{ + g_signal_connect (G_OBJECT (widget), "link-clicked", G_CALLBACK (callback), data); +} + +void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename) +{ + ygtk_rich_text_set_background (YGTK_RICH_TEXT (widget), pixbuf); +} + +#endif +#endif +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkhtmlwrap.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,36 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkHtmlWrap is a wrapper around either GtkHtml and YGtkRichText, as + set at compilation time. GtkHtml is superior, but to avoid dependencies, + we use our customized in case it isn't installed. + If you flag it as plain_text, it will simply wrap GtkTextView. +*/ + +#ifndef YGTK_HTML_WRAP_H +#define YGTK_HTML_WRAP_H + +#include <gtk/gtkwidget.h> +G_BEGIN_DECLS + +GtkWidget *ygtk_html_wrap_new (void); + +GType ygtk_html_wrap_get_type (void); +void ygtk_html_wrap_init (GtkWidget *widget); // if you use g_object_new(), call this + +void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar* text, gboolean plain_mode); +void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top /* or bottom */); + +typedef void (*LinkClickedCb) (GtkWidget *htmlwrap, const gchar *url, gpointer data); +void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data); + +// not supported on plain text +gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text); +gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text); // F3 + +void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename); + +G_END_DECLS +#endif /* YGTK_HTML_WRAP_H */ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,293 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkImage widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygdkmngloader.h" +#include "ygtkimage.h" +#include <gtk/gtkiconfactory.h> + +G_DEFINE_TYPE (YGtkImage, ygtk_image, GTK_TYPE_DRAWING_AREA) + +static void ygtk_image_init (YGtkImage *image) +{ +} + +static void ygtk_image_free_pixbuf (YGtkImage *image) +{ + if (image->animated) { + if (image->animation) { + g_object_unref (G_OBJECT (image->animation->pixbuf)); + if (image->animation->timeout_id) + g_source_remove (image->animation->timeout_id); + g_free (image->animation); + image->animation = NULL; + } + } + else { + if (image->pixbuf) { + g_object_unref (G_OBJECT (image->pixbuf)); + image->pixbuf = NULL; + } + } +} + +static void ygtk_image_destroy (GtkObject *object) +{ + YGtkImage *image = YGTK_IMAGE (object); + if (image->alt_text) + g_free (image->alt_text); + image->alt_text = NULL; + ygtk_image_free_pixbuf (image); + GTK_OBJECT_CLASS (ygtk_image_parent_class)->destroy (object); +} + +static void ygtk_image_set_pixbuf (YGtkImage *image, GdkPixbuf *pixbuf, const char *error_msg) +{ + ygtk_image_free_pixbuf (image); + gtk_widget_queue_resize (GTK_WIDGET (image)); + + if (pixbuf) { + image->animated = FALSE; + image->pixbuf = pixbuf; + image->loaded = TRUE; + } +/* else + g_warning ("Couldn't load image - %s", error_msg);*/ +} + +static gboolean ygtk_image_advance_frame_cb (gpointer data) +{ + YGtkImage *image = (YGtkImage *) data; + struct _YGtkImageAnimation *animation = image->animation; + + if (!animation->frame) // no frame yet loaded + animation->frame = gdk_pixbuf_animation_get_iter (animation->pixbuf, NULL); + else + if (gdk_pixbuf_animation_iter_advance (animation->frame, NULL)) + gtk_widget_queue_draw (GTK_WIDGET (image)); + + // shedule next frame + int delay = gdk_pixbuf_animation_iter_get_delay_time (animation->frame); + if (delay != -1) + animation->timeout_id = g_timeout_add (delay, ygtk_image_advance_frame_cb, data); + return FALSE; +} + +static void ygtk_image_set_animation (YGtkImage *image, GdkPixbufAnimation *pixbuf, + const char *error_msg) +{ + ygtk_image_free_pixbuf (image); + gtk_widget_queue_resize (GTK_WIDGET (image)); + + if (pixbuf) { + image->animated = TRUE; + image->animation = g_new0 (struct _YGtkImageAnimation, 1); + image->animation->pixbuf = pixbuf; + image->loaded = TRUE; + ygtk_image_advance_frame_cb (image); + } + else if (error_msg) + g_warning ("Couldn't load image - %s", error_msg); +} + +void ygtk_image_set_from_file (YGtkImage *image, const char *filename, gboolean anim) +{ + GError *error = 0; + if (anim) { + GdkPixbufAnimation *pixbuf; + if (ygdk_mng_pixbuf_is_file_mng (filename)) + pixbuf = ygdk_mng_pixbuf_new_from_file (filename, &error); + else + pixbuf = gdk_pixbuf_animation_new_from_file (filename, &error); + ygtk_image_set_animation (image, pixbuf, error ? error->message : "(undefined)"); + } + else { + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, &error); + ygtk_image_set_pixbuf (image, pixbuf, error ? error->message : "(undefined)"); + } +} + +void ygtk_image_set_from_pixbuf (YGtkImage *image, GdkPixbuf *pixbuf) +{ + ygtk_image_set_pixbuf (image, pixbuf, NULL); +} + +static void ygtk_image_loaded_cb (GdkPixbufLoader *loader, YGtkImage *image) +{ + if (image->animated) { + if (image->animation) { + // a new frame loaded -- just redraw the widget + if (gdk_pixbuf_animation_iter_on_currently_loading_frame + (image->animation->frame)) + gtk_widget_queue_draw (GTK_WIDGET (image)); + } + else { + GdkPixbufAnimation *pixbuf = gdk_pixbuf_loader_get_animation (loader); + g_object_ref (G_OBJECT (pixbuf)); + ygtk_image_set_animation (image, pixbuf, "on block data reading callback"); + } + } + else { + GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + g_object_ref (G_OBJECT (pixbuf)); + ygtk_image_set_pixbuf (image, pixbuf, "on block data reading callback"); + } +} + +void ygtk_image_set_from_data (YGtkImage *image, const guint8 *data, long data_size, gboolean anim) +{ + GError *error = 0; + if (anim && ygdk_mng_pixbuf_is_data_mng (data, data_size)) { + GdkPixbufAnimation *pixbuf; + pixbuf = ygdk_mng_pixbuf_new_from_data (data, data_size, &error); + ygtk_image_set_animation (image, pixbuf, error ? error->message : "(undefined)"); + } + else { + image->animated = anim; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + g_signal_connect (G_OBJECT (loader), "area-prepared", + G_CALLBACK (ygtk_image_loaded_cb), image); + if (!gdk_pixbuf_loader_write (loader, data, data_size, &error)) + g_warning ("Could not load image from data blocks: %s", error->message); + gdk_pixbuf_loader_close (loader, &error); + } +} + +void ygtk_image_set_props (YGtkImage *image, YGtkImageAlign align, const gchar *alt_text) +{ + image->align = align; + if (image->alt_text) + g_free (image->alt_text); + if (alt_text) + image->alt_text = g_strdup (alt_text); + gtk_widget_queue_draw (GTK_WIDGET (image)); +} + +static void ygtk_image_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + YGtkImage *image = YGTK_IMAGE (widget); + int width = 0, height = 0; + if (image->loaded) { + if (image->animated) { + width = gdk_pixbuf_animation_get_width (image->animation->pixbuf); + height = gdk_pixbuf_animation_get_height (image->animation->pixbuf); + } + else { + width = gdk_pixbuf_get_width (image->pixbuf); + height = gdk_pixbuf_get_height (image->pixbuf); + } + } + else if (image->alt_text) { + PangoLayout *layout; + layout = gtk_widget_create_pango_layout (widget, image->alt_text); + pango_layout_get_pixel_size (layout, &width, &height); + } + requisition->width = width; + requisition->height = height; +} + +static GdkPixbuf *ygtk_image_render_state (GtkWidget *widget, GdkPixbuf *pixbuf) +{ + // as in GtkImage + GtkIconSource *source = gtk_icon_source_new(); + GdkPixbuf *rendered; + gtk_icon_source_set_pixbuf (source, pixbuf); + gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_icon_source_set_size_wildcarded (source, FALSE); + rendered = gtk_style_render_icon (widget->style, source, + gtk_widget_get_direction (widget), GTK_WIDGET_STATE (widget), + /* arbitrary */ (GtkIconSize)-1, widget, "gtk-image"); + gtk_icon_source_free (source); + return rendered; +} + +static gboolean ygtk_image_expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + YGtkImage *image = YGTK_IMAGE (widget); + int width, height; + width = widget->allocation.width; + height = widget->allocation.height; + + cairo_t *cr = gdk_cairo_create (widget->window); + + if (!image->loaded) { + if (image->alt_text) { + // show alt text if no image was loaded + PangoLayout *layout; + layout = gtk_widget_create_pango_layout (widget, image->alt_text); + + int x, y; + x = (width - widget->requisition.width) / 2; + y = (height - widget->requisition.height) / 2; + + cairo_move_to (cr, x, y); + pango_cairo_show_layout (cr, layout); + + g_object_unref (layout); + } + cairo_destroy (cr); + return FALSE; + } + + GdkPixbuf *pixbuf; + if (image->animated) + pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (image->animation->frame); + else + pixbuf = image->pixbuf; + + gboolean needs_transform = GTK_WIDGET_STATE (widget) != GTK_STATE_NORMAL; + if (needs_transform) + pixbuf = ygtk_image_render_state (widget, pixbuf); + int x = 0, y = 0; + if (image->align == CENTER_IMAGE_ALIGN) { + x = (width - widget->requisition.width) / 2; + y = (height - widget->requisition.height) / 2; + } + + gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y); + + switch (image->align) { + case CENTER_IMAGE_ALIGN: + break; + case SCALE_IMAGE_ALIGN: + { + double scale_x = (double) gdk_pixbuf_get_width (pixbuf) / width; + double scale_y = (double) gdk_pixbuf_get_height (pixbuf) / height; + cairo_matrix_t matrix; + cairo_matrix_init_scale (&matrix, scale_x, scale_y); + cairo_pattern_set_matrix (cairo_get_source (cr), &matrix); + break; + } + case TILE_IMAGE_ALIGN: + cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT); + break; + } + + cairo_rectangle (cr, x, y, width, height); + cairo_fill (cr); + + cairo_destroy (cr); + if (needs_transform) + g_object_unref (G_OBJECT (pixbuf)); + return FALSE; +} + +GtkWidget* ygtk_image_new (void) +{ + return g_object_new (YGTK_TYPE_IMAGE, NULL); +} + +static void ygtk_image_class_init (YGtkImageClass *klass) +{ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->expose_event = ygtk_image_expose_event; + widget_class->size_request = ygtk_image_size_request; + + GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); + gtkobject_class->destroy = ygtk_image_destroy; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkimage.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,76 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* GtkImage doesn't provide all the functionality asked by libyui, + such as scaling and tiling. Thus, YGtkImage is a more powerful + GtkImage. +*/ + +#ifndef YGTK_IMAGE_H +#define YGTK_IMAGE_H + +#include <gtk/gtkdrawingarea.h> +#include <gdk/gdkpixbuf.h> + +G_BEGIN_DECLS + +#define YGTK_TYPE_IMAGE (ygtk_image_get_type ()) +#define YGTK_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_IMAGE, YGtkImage)) +#define YGTK_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_IMAGE, YGtkImageClass)) +#define YGTK_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_IMAGE)) +#define YGTK_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_IMAGE)) +#define YGTK_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_IMAGE, YGtkImageClass)) + +typedef enum { + CENTER_IMAGE_ALIGN, SCALE_IMAGE_ALIGN, TILE_IMAGE_ALIGN +} YGtkImageAlign; + +struct _YGtkImageAnimation { + GdkPixbufAnimation *pixbuf; + GdkPixbufAnimationIter *frame; + guint timeout_id; +}; + +typedef struct _YGtkImage +{ + GtkDrawingArea parent; + + // properties: + YGtkImageAlign align; + + gboolean animated; + union { + GdkPixbuf *pixbuf; + struct _YGtkImageAnimation *animation; + }; + + gboolean loaded; + gchar *alt_text; + +} YGtkImage; + +typedef struct _YGtkImageClass +{ + GtkDrawingAreaClass parent_class; +} YGtkImageClass; + +GtkWidget* ygtk_image_new (void); +GType ygtk_image_get_type (void) G_GNUC_CONST; + +void ygtk_image_set_from_file (YGtkImage *image, const char *filename, gboolean anim); +void ygtk_image_set_from_data (YGtkImage *image, const guint8 *data, long size, gboolean anim); +void ygtk_image_set_from_pixbuf (YGtkImage *image, GdkPixbuf *pixbuf); +void ygtk_image_set_props (YGtkImage *image, YGtkImageAlign align, const gchar *alt_text); + +// as we don't have a window, + +G_END_DECLS + +#endif /* YGTK_IMAGE_H */ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,235 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkLinkLabel container */ +// check the header file for information about this container + +#include <config.h> +#include <math.h> +#include <gtk/gtk.h> +#include "ygtklinklabel.h" + +static guint link_clicked_signal; + +G_DEFINE_TYPE (YGtkLinkLabel, ygtk_link_label, GTK_TYPE_WIDGET) + +static void ygtk_link_label_init (YGtkLinkLabel *label) +{ + GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW); +} + +static void ygtk_link_label_realize (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (ygtk_link_label_parent_class)->realize (widget); + GdkWindowAttr attributes; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.event_mask = gtk_widget_get_events (widget) | + GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; + gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR; + attributes.cursor = gdk_cursor_new_for_display ( + gtk_widget_get_display (widget), GDK_HAND2); + + YGtkLinkLabel *label = YGTK_LINK_LABEL (widget); + label->link_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (label->link_window, widget); + GdkColor white = { 0, 0xffff, 0xffff, 0xffff }; + gdk_rgb_find_color (gtk_widget_get_colormap (widget), &white); + gdk_window_set_background (label->link_window, &white); + gdk_cursor_unref (attributes.cursor); +} + +static void ygtk_link_label_unrealize (GtkWidget *widget) +{ + YGtkLinkLabel *label = YGTK_LINK_LABEL (widget); + if (label->link_window) { + gdk_window_set_user_data (label->link_window, NULL); + gdk_window_destroy (label->link_window); + label->link_window = NULL; + } + GTK_WIDGET_CLASS (ygtk_link_label_parent_class)->unrealize (widget); +} + +static void ygtk_link_label_map (GtkWidget *widget) +{ + // "more" hides on unmap and for some reason showing it clears the + // text... + gtk_widget_queue_resize (widget); + GTK_WIDGET_CLASS (ygtk_link_label_parent_class)->map (widget); +} + +static void ygtk_link_label_clear_layout (YGtkLinkLabel *label) +{ + if (label->layout) { + g_object_unref (label->layout); + label->layout = NULL; + } + if (label->link_layout) { + g_object_unref (label->link_layout); + label->link_layout = NULL; + } +} + +static void ygtk_link_label_ensure_layout (YGtkLinkLabel *label) +{ + GtkWidget *widget = GTK_WIDGET (label); + if (!label->layout) { + label->layout = gtk_widget_create_pango_layout (widget, label->text); + pango_layout_set_single_paragraph_mode (label->layout, TRUE); + pango_layout_set_ellipsize (label->layout, PANGO_ELLIPSIZE_END); + } + if (!label->link_layout) { + label->link_layout = gtk_widget_create_pango_layout (widget, label->link); + PangoAttrList *attrbs = pango_attr_list_new(); + pango_attr_list_insert (attrbs, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE)); + pango_attr_list_insert (attrbs, pango_attr_foreground_new (0, 0, 0xffff)); + pango_layout_set_attributes (label->link_layout, attrbs); + pango_attr_list_unref (attrbs); + } +} + +static void ygtk_link_label_finalize (GObject *object) +{ + YGtkLinkLabel *label = YGTK_LINK_LABEL (object); + g_free (label->text); + g_free (label->link); + ygtk_link_label_clear_layout (label); + G_OBJECT_CLASS (ygtk_link_label_parent_class)->finalize (object); +} + +static void ygtk_link_label_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + YGtkLinkLabel *label = YGTK_LINK_LABEL (widget); + ygtk_link_label_ensure_layout (label); + requisition->width = requisition->height = 0; +// if (label->text && *label->text) + { + PangoContext *context; + PangoFontMetrics *metrics; + gint ascent, descent; + context = pango_layout_get_context (label->layout); + metrics = pango_context_get_metrics (context, widget->style->font_desc, + pango_context_get_language (context)); + ascent = pango_font_metrics_get_ascent (metrics); + descent = pango_font_metrics_get_descent (metrics); + pango_font_metrics_unref (metrics); + requisition->height = PANGO_PIXELS (ascent + descent); + } +} + +#define SPACING 4 + +static void ygtk_link_label_size_allocate (GtkWidget *widget, GtkAllocation *allocation) +{ + GTK_WIDGET_CLASS (ygtk_link_label_parent_class)->size_allocate (widget, allocation); + YGtkLinkLabel *label = YGTK_LINK_LABEL (widget); + + gint width = allocation->width * PANGO_SCALE; + PangoRectangle logical; + pango_layout_set_width (label->layout, -1); + pango_layout_get_extents (label->layout, NULL, &logical); + if (label->link_window) { + if (*label->text && (logical.width > width || label->link_always_visible)) { + PangoRectangle link_logical; + pango_layout_get_extents (label->link_layout, NULL, &link_logical); + gint link_width = link_logical.width / PANGO_SCALE; + gint x; + width = width - link_logical.width - SPACING*PANGO_SCALE; + if (logical.width < width && label->link_always_visible) + x = allocation->x + logical.width/PANGO_SCALE + SPACING; + else + x = allocation->x + (allocation->width - link_width); + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + x = (2*allocation->x + allocation->width) - (x + link_width); + gdk_window_move_resize (label->link_window, x, + allocation->y, link_width, allocation->height); + if (logical.width > width) + pango_layout_set_width (label->layout, width); + gdk_window_show (label->link_window); + } + else + gdk_window_hide (label->link_window); + } +} + +static gboolean ygtk_link_label_expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + YGtkLinkLabel *label = YGTK_LINK_LABEL (widget); + ygtk_link_label_ensure_layout (label); + + gint x = 0, y = 0; + PangoLayout *layout = 0; + if (event->window == widget->window) { + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { + PangoRectangle extent; + pango_layout_get_extents (label->layout, NULL, &extent); + x = widget->allocation.width - extent.width/PANGO_SCALE; + } + x += widget->allocation.x; + y += widget->allocation.y; + layout = label->layout; + } + else if (event->window == label->link_window) + layout = label->link_layout; + + if (layout) + gtk_paint_layout (widget->style, event->window, GTK_WIDGET_STATE (widget), + FALSE, &event->area, widget, "label", x, y, layout); + return FALSE; +} + +static gboolean ygtk_link_label_button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + g_signal_emit (widget, link_clicked_signal, 0, NULL); + return TRUE; +} + +void ygtk_link_label_set_text (YGtkLinkLabel *label, const gchar *text, const gchar *link, + gboolean link_always_visible) +{ + g_free (label->text); + label->text = g_strdup (text); + if (link) { + g_free (label->link); + label->link = g_strdup (link); + } + label->link_always_visible = link_always_visible; + ygtk_link_label_clear_layout (label); + gtk_widget_queue_resize (GTK_WIDGET (label)); +} + +GtkWidget *ygtk_link_label_new (const gchar *text, const gchar *link) +{ + YGtkLinkLabel *label = g_object_new (YGTK_TYPE_LINK_LABEL, NULL); + ygtk_link_label_set_text (label, text, link, TRUE); + return (GtkWidget *) label; +} + +static void ygtk_link_label_class_init (YGtkLinkLabelClass *klass) +{ + ygtk_link_label_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + widget_class->realize = ygtk_link_label_realize; + widget_class->unrealize = ygtk_link_label_unrealize; + widget_class->map = ygtk_link_label_map; + widget_class->size_request = ygtk_link_label_size_request; + widget_class->size_allocate = ygtk_link_label_size_allocate; + widget_class->expose_event = ygtk_link_label_expose_event; + widget_class->button_press_event = ygtk_link_label_button_press_event; + + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = ygtk_link_label_finalize; + + link_clicked_signal = g_signal_new ("link-clicked", + G_TYPE_FROM_CLASS (G_OBJECT_CLASS (klass)), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (YGtkLinkLabelClass, link_clicked), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtklinklabel.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,58 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* A label with embed links. Because we only need a link at the end, that's + only what we support. + + In the future, we may want to use GtkLabel's new link support instead of + this custom widget. +*/ + +#ifndef YGTK_LINK_LABEL_H +#define YGTK_LINK_LABEL_H + +#include <gtk/gtkwidget.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_LINK_LABEL (ygtk_link_label_get_type ()) +#define YGTK_LINK_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_LINK_LABEL, YGtkLinkLabel)) +#define YGTK_LINK_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_LINK_LABEL, YGtkLinkLabelClass)) +#define YGTK_IS_LINK_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_LINK_LABEL)) +#define YGTK_IS_LINK_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_LINK_LABEL)) +#define YGTK_LINK_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_LINK_LABEL, YGtkLinkLabelClass)) + +typedef struct _YGtkLinkLabel YGtkLinkLabel; +typedef struct _YGtkLinkLabelClass YGtkLinkLabelClass; + +struct _YGtkLinkLabel +{ + GtkWidget parent; + // private (read-only): + gchar *text, *link; + gboolean link_always_visible; + PangoLayout *layout, *link_layout; + GdkWindow *link_window; +}; + +struct _YGtkLinkLabelClass +{ + GtkWidgetClass parent_class; + // signals: + void (*link_clicked) (YGtkLinkLabel *label); +}; + +GtkWidget *ygtk_link_label_new (const gchar *text, const gchar *link); +GType ygtk_link_label_get_type (void) G_GNUC_CONST; + +void ygtk_link_label_set_text (YGtkLinkLabel *label, const gchar *text, const gchar *link, + gboolean always_show_link); + +G_END_DECLS +#endif /*YGTK_LINK_LABEL_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,290 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkMenuButton widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygtkmenubutton.h" +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +//** YGtkPopupWindow + +G_DEFINE_TYPE (YGtkPopupWindow, ygtk_popup_window, GTK_TYPE_WINDOW) + +static void ygtk_popup_window_init (YGtkPopupWindow *popup) +{ + GtkWindow *window = GTK_WINDOW (popup); + gtk_window_set_resizable (window, FALSE); + + GtkWidget *frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + gtk_widget_show (frame); + gtk_container_add (GTK_CONTAINER (window), frame); +} + +static void ygtk_popup_window_hide (GtkWidget *widget) +{ + gtk_grab_remove (widget); + GTK_WIDGET_CLASS (ygtk_popup_window_parent_class)->hide (widget); +} + +static gboolean ygtk_popup_window_key_press_event (GtkWidget *widget, GdkEventKey *event) +{ + if (event->keyval == GDK_Escape) { + gtk_widget_hide (widget); + return TRUE; + } + return GTK_WIDGET_CLASS (ygtk_popup_window_parent_class)->key_press_event + (widget, event); +} + +static gboolean ygtk_popup_window_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + // NOTE: You can't rely on events x and y since the event may take place + // outside of the window. + // So, we'll check if this widget (or any of its kids) got the event + // If that's not the case, close the menu + + GtkWidget *child = gtk_get_event_widget ((GdkEvent *) event); + if (child != widget) + while (child) { + if (child == widget) + return FALSE; + child = child->parent; + } + gtk_widget_hide (widget); + return TRUE; +} + +GtkWidget *ygtk_popup_window_new (GtkWidget *child) +{ + GtkWidget *widget = g_object_new (YGTK_TYPE_POPUP_WINDOW, + "type", GTK_WINDOW_POPUP, NULL); + GtkWidget *frame = gtk_bin_get_child (GTK_BIN (widget)); + gtk_container_add (GTK_CONTAINER (frame), child); + return widget; +} + +static void ygtk_popup_window_frame_position (GtkWidget *widget, gint *x, gint *y) +{ // don't let it go outside the screen + GtkRequisition req; + gtk_widget_size_request (widget, &req); + + GdkScreen *screen = gtk_widget_get_screen (widget); + gint monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_root_window (widget)); + GdkRectangle monitor; + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + + if (*x < monitor.x) + *x = monitor.x; + else if (*x + req.width > monitor.x + monitor.width) + *x = monitor.x + monitor.width - req.width; + + if (*y < monitor.y) + *y = monitor.y; + else if (*y + req.height > monitor.y + monitor.height) + *y = monitor.y + monitor.height - req.height; +} + +void ygtk_popup_window_popup (GtkWidget *widget, gint x, gint y, guint activate_time) +{ + ygtk_popup_window_frame_position (widget, &x, &y); + + gtk_grab_add (widget); + gtk_window_move (GTK_WINDOW (widget), x, y); + gtk_widget_grab_focus (widget); + gtk_widget_show (widget); + + // grab this with your teeth + if (gdk_pointer_grab (widget->window, TRUE, + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, + NULL, NULL, activate_time) == 0) + if (gdk_keyboard_grab (widget->window, TRUE, activate_time) != 0) + gdk_pointer_ungrab (activate_time); +} + +static void ygtk_popup_window_class_init (YGtkPopupWindowClass *klass) +{ + ygtk_popup_window_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + widget_class->key_press_event = ygtk_popup_window_key_press_event; + widget_class->button_press_event = ygtk_popup_window_button_press_event; + widget_class->hide = ygtk_popup_window_hide; +} + +//** YGtkMenuButton + +G_DEFINE_TYPE (YGtkMenuButton, ygtk_menu_button, GTK_TYPE_TOGGLE_BUTTON) + +static void ygtk_menu_button_init (YGtkMenuButton *button) +{ +} + +static void ygtk_menu_button_free_popup (YGtkMenuButton *button) +{ + if (button->popup) { + gtk_widget_destroy (GTK_WIDGET (button->popup)); + g_object_unref (G_OBJECT (button->popup)); + button->popup = NULL; + } +} + +static void ygtk_menu_button_finalize (GObject *object) +{ + ygtk_menu_button_free_popup (YGTK_MENU_BUTTON (object)); + G_OBJECT_CLASS (ygtk_menu_button_parent_class)->finalize (object); +} + +static void ygtk_menu_button_get_popup_pos (YGtkMenuButton *button, gint *x, gint *y) +{ + GtkWidget *widget = GTK_WIDGET (button); + GtkAllocation *button_alloc = &widget->allocation; + + // the popup would look awful if smaller than the button + GtkRequisition req; + gtk_widget_size_request (button->popup, &req); + int popup_width = req.width, popup_height = req.height; + if (button_alloc->width > req.width) { + gtk_widget_set_size_request (button->popup, button_alloc->width, -1); + popup_width = button_alloc->width; + } + + gdk_window_get_origin (widget->window, x, y); + *x += button_alloc->x - popup_width*button->xalign; + *y += (button_alloc->y-popup_height) + (button_alloc->height+popup_height)*button->yalign; + + // GTK doesn't push up menus if they are near the bottom, but we will... + int screen_height; + screen_height = gdk_screen_get_height (gtk_widget_get_screen (widget)); + if (*y > screen_height - popup_height) + *y -= popup_height + button_alloc->height; +} + +static void ygtk_menu_button_get_menu_pos (GtkMenu *menu, gint *x, gint *y, + gboolean *push_in, gpointer data) +{ + ygtk_menu_button_get_popup_pos (YGTK_MENU_BUTTON (data), x, y); + *push_in = TRUE; +} + +static void ygtk_menu_button_show_popup (YGtkMenuButton *button) +{ + GtkWidget *popup = button->popup; + if (!popup) + return; + + guint activate_time = gtk_get_current_event_time(); + if (GTK_IS_MENU (popup)) + gtk_menu_popup (GTK_MENU (popup), NULL, NULL, ygtk_menu_button_get_menu_pos, + button, 0, activate_time); + else { // GTK_IS_WINDOW + gint x, y; + ygtk_menu_button_get_popup_pos (button, &x, &y); + ygtk_popup_window_popup (popup, x, y, activate_time); + } +} + +static void ygtk_menu_button_hide_popup (YGtkMenuButton *button) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE); +} + +static void ygtk_menu_button_button_toggle (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) + ygtk_menu_button_show_popup (YGTK_MENU_BUTTON (button)); + else + ygtk_menu_button_hide_popup (YGTK_MENU_BUTTON (button)); +} + +static gint ygtk_menu_button_button_press (GtkWidget *widget, GdkEventButton *event) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); + ygtk_menu_button_show_popup (YGTK_MENU_BUTTON (widget)); + } + else + ygtk_menu_button_hide_popup (YGTK_MENU_BUTTON (widget)); + return TRUE; + } + return FALSE; +} + +GtkWidget *ygtk_menu_button_new (void) +{ + return g_object_new (YGTK_TYPE_MENU_BUTTON, NULL); +} + +GtkWidget *ygtk_menu_button_new_with_label (const gchar *label) +{ + GtkWidget *button = ygtk_menu_button_new(); + ygtk_menu_button_set_label (YGTK_MENU_BUTTON (button), label); + return button; +} + +void ygtk_menu_button_set_label (YGtkMenuButton *button, const gchar *label) +{ + if (!button->label) { + GtkWidget *hbox, *arrow; + hbox = gtk_hbox_new (FALSE, 4); + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN); + button->label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX (hbox), button->label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (button), hbox); + gtk_widget_show_all (hbox); + } + if (label && *label) { + gtk_widget_show (button->label); + gtk_label_set_text_with_mnemonic (GTK_LABEL (button->label), label); + } + else + gtk_widget_hide (button->label); +} + +static void menu_button_hide_popup (GtkWidget *widget, YGtkMenuButton *button) +{ ygtk_menu_button_hide_popup (button); } + +void ygtk_menu_button_set_popup_align (YGtkMenuButton *button, GtkWidget *popup, + gfloat xalign, gfloat yalign) +{ + ygtk_menu_button_free_popup (button); + button->xalign = xalign; + button->yalign = yalign; + + if (!GTK_IS_MENU (popup) && !IS_YGTK_POPUP_WINDOW (popup)) { + // install widget on a YGtkPopupMenu + button->popup = ygtk_popup_window_new (popup); + } + else + button->popup = popup; + + g_object_ref_sink (G_OBJECT (button->popup)); + g_signal_connect (G_OBJECT (button->popup), "hide", + G_CALLBACK (menu_button_hide_popup), button); +} + +void ygtk_menu_button_set_popup (YGtkMenuButton *button, GtkWidget *popup) +{ + ygtk_menu_button_set_popup_align (button, popup, 0.0, 1.0); +} + +static void ygtk_menu_button_class_init (YGtkMenuButtonClass *klass) +{ + ygtk_menu_button_parent_class = g_type_class_peek_parent (klass); + + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = ygtk_menu_button_finalize; + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + widget_class->button_press_event = ygtk_menu_button_button_press; + + GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass); + toggle_button_class->toggled = ygtk_menu_button_button_toggle; +}
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkmenubutton.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,94 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkMenuButton is a button that displays a widget when pressed. + This widget can either be of type GtkMenu or another, like a + GtkCalendar and we'll do the proper "emulation" (the work of + the YGtkPopupWindow widget). +*/ + +#ifndef YGTK_POPUP_WINDOW_H +#define YGTK_POPUP_WINDOW_H + +#include <gtk/gtkwindow.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_POPUP_WINDOW (ygtk_popup_window_get_type ()) +#define YGTK_POPUP_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_POPUP_WINDOW, YGtkPopupWindow)) +#define YGTK_POPUP_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_POPUP_WINDOW, YGtkPopupWindowClass)) +#define IS_YGTK_POPUP_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_POPUP_WINDOW)) +#define IS_YGTK_POPUP_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_POPUP_WINDOW)) +#define YGTK_POPUP_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_POPUP_WINDOW, YGtkPopupWindowClass)) + +typedef struct _YGtkPopupWindow +{ + GtkWindow parent; +} YGtkPopupWindow; + +typedef struct _YGtkPopupWindowClass +{ + GtkWindowClass parent_class; +} YGtkPopupWindowClass; + +// don't forget to use gtk_widget_show() on the child! +GtkWidget* ygtk_popup_window_new (GtkWidget *child); +GType ygtk_popup_window_get_type (void) G_GNUC_CONST; + +void ygtk_popup_window_popup (GtkWidget *widget, gint x, gint y, guint activate_time); + +G_END_DECLS +#endif /*YGTK_POPUP_WINDOW_H*/ + +#ifndef YGTK_MENU_BUTTON_H +#define YGTK_MENU_BUTTON_H + +#include <gtk/gtktogglebutton.h> +#include <gtk/gtkmenu.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_MENU_BUTTON (ygtk_menu_button_get_type ()) +#define YGTK_MENU_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_MENU_BUTTON, YGtkMenuButton)) +#define YGTK_MENU_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_MENU_BUTTON, YGtkMenuButtonClass)) +#define IS_YGTK_MENU_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_MENU_BUTTON)) +#define IS_YGTK_MENU_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_MENU_BUTTON)) +#define YGTK_MENU_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_MENU_BUTTON, YGtkMenuButtonClass)) + +typedef struct _YGtkMenuButton +{ + GtkToggleButton parent; + + // private: + GtkWidget *label, *popup; + gfloat xalign, yalign; +} YGtkMenuButton; + +typedef struct _YGtkMenuButtonClass +{ + GtkToggleButtonClass parent_class; +} YGtkMenuButtonClass; + +GtkWidget* ygtk_menu_button_new (void); +GtkWidget* ygtk_menu_button_new_with_label (const gchar *label); +GType ygtk_menu_button_get_type (void) G_GNUC_CONST; + +void ygtk_menu_button_set_label (YGtkMenuButton *button, const gchar *label); + +/* Popup must be either a GtkMenu or a YGtkPopupWindow. */ +// You may hide your popup "manually" issueing a gtk_widget_hide() on it +void ygtk_menu_button_set_popup (YGtkMenuButton *button, GtkWidget *popup); +void ygtk_menu_button_set_popup_align (YGtkMenuButton *button, GtkWidget *popup, + gfloat xalign, gfloat yalign); + +G_END_DECLS +#endif /*YGTK_MENU_BUTTON_H*/
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,384 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkRatioBox container */ +// check the header file for information about this container + +#include <config.h> +#include <math.h> +#include "ygtkratiobox.h" + +G_DEFINE_ABSTRACT_TYPE (YGtkRatioBox, ygtk_ratio_box, GTK_TYPE_CONTAINER) + +static void ygtk_ratio_box_init (YGtkRatioBox *box) +{ + GTK_WIDGET_SET_FLAGS (box, GTK_NO_WINDOW); + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE); +} + +static GType ygtk_ratio_box_child_type (GtkContainer* container) +{ return GTK_TYPE_WIDGET; } + +void ygtk_ratio_box_pack (YGtkRatioBox *box, GtkWidget *child, gfloat ratio) +{ + YGtkRatioBoxChild* child_info; + child_info = g_new (YGtkRatioBoxChild, 1); + child_info->widget = child; + child_info->ratio = ratio; + + box->children = g_list_append (box->children, child_info); + + gtk_widget_freeze_child_notify (child); + gtk_widget_set_parent (child, GTK_WIDGET (box)); + gtk_widget_thaw_child_notify (child); +} + +static YGtkRatioBoxChild *ygtk_ratio_get_child_info (YGtkRatioBox *box, GtkWidget *child) +{ + YGtkRatioBoxChild *i = NULL; + GList *list; + for (list = box->children; list; list = list->next) { + i = (YGtkRatioBoxChild*) list->data; + if (i->widget == child) + break; + } + if (!list) + return NULL; + return i; +} + +static void ygtk_ratio_box_add (GtkContainer *container, GtkWidget *child) +{ + ygtk_ratio_box_pack (YGTK_RATIO_BOX (container), child, 1.0); +} + +static void ygtk_ratio_box_remove (GtkContainer *container, GtkWidget *widget) +{ + YGtkRatioBox* box = YGTK_RATIO_BOX (container); + + GList* child = box->children; + for (child = box->children; child; child = child->next) { + YGtkRatioBoxChild *box_child = (YGtkRatioBoxChild*) child->data; + if (box_child->widget == widget) { + gboolean was_visible = GTK_WIDGET_VISIBLE (widget); + gtk_widget_unparent (widget); + + box->children = g_list_remove_link (box->children, child); + g_list_free (child); + g_free (box_child); + + if (was_visible) + gtk_widget_queue_resize (GTK_WIDGET (container)); + break; + } + } +} + +static void ygtk_ratio_box_forall (GtkContainer *container, gboolean include_internals, + GtkCallback callback, gpointer callback_data) +{ + g_return_if_fail (callback != NULL); + + YGtkRatioBox* box = YGTK_RATIO_BOX (container); + + GList* children = box->children; + while (children) { + YGtkRatioBoxChild* child = (YGtkRatioBoxChild*) children->data; + children = children->next; + (* callback) (child->widget, callback_data); + } +} + +/* We put size_request and _allocate in the same functions for both + orientations because it's just easier to maintain having the + logic in the same place. */ + +static void ygtk_ratio_box_size_request (GtkWidget *widget, + GtkRequisition *requisition, + GtkOrientation orientation) +{ + requisition->width = requisition->height = 0; + + YGtkRatioBox* box = YGTK_RATIO_BOX (widget); + gint children_nb = 0; + GList *i; + for (i = box->children; i; i = i->next) { + YGtkRatioBoxChild* child = i->data; + if (!GTK_WIDGET_VISIBLE (child->widget)) + continue; + + GtkRequisition child_req; + gtk_widget_size_request (child->widget, &child_req); + if (orientation == GTK_ORIENTATION_HORIZONTAL) + requisition->height = MAX (requisition->height, child_req.height); + else + requisition->width = MAX (requisition->width, child_req.width); + children_nb++; + } + gint spacing = children_nb ? box->spacing*(children_nb-1) : 0; + if (orientation == GTK_ORIENTATION_HORIZONTAL) + requisition->width += spacing; + else + requisition->height += spacing; + + int border = GTK_CONTAINER (box)->border_width; + requisition->width += border*2; + requisition->height += border*2; +} + +static void ygtk_ratio_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation, + GtkOrientation orientation) +{ + YGtkRatioBox* box = YGTK_RATIO_BOX (widget); + + gfloat ratios_sum = 0; + gint children_nb = 0; + + GList* i; + for (i = box->children; i; i = i->next) { + YGtkRatioBoxChild* child = i->data; + if (!GTK_WIDGET_VISIBLE (child->widget)) + continue; + + ratios_sum += child->ratio; + children_nb++; + } + + gint spacing = children_nb ? box->spacing*(children_nb-1) : 0; + + int border = GTK_CONTAINER (box)->border_width; + int x = allocation->x + border, y = allocation->y + border, + width = allocation->width - border*2, height = allocation->height - border*2; + + gint length; + if (orientation == GTK_ORIENTATION_HORIZONTAL) + length = width - spacing; + else + length = height - spacing; + gint child_pos = 0; + + for (i = box->children; i; i = i->next) { + YGtkRatioBoxChild* child = i->data; + if (!GTK_WIDGET_VISIBLE (child->widget)) + continue; + + GtkRequisition child_req; + gtk_widget_get_child_requisition (child->widget, &child_req); + + gint child_length = (child->ratio * length) / ratios_sum; + if (!i->next) // last takes rest (any residual length) + child_length = length - child_pos; + + GtkAllocation child_alloc; + if (orientation == GTK_ORIENTATION_HORIZONTAL) { + child_alloc.x = x + child_pos; + child_alloc.y = y; + child_alloc.width = child_length; + child_alloc.height = height; + } + else { // GTK_ORIENTATION_VERTICAL + child_alloc.x = x; + child_alloc.y = y + child_pos; + child_alloc.width = width; + child_alloc.height = child_length; + } + child_alloc.width = MAX (child_alloc.width, 1); + child_alloc.height = MAX (child_alloc.height, 1); + + gtk_widget_size_allocate (child->widget, &child_alloc); + child_pos += child_length + box->spacing; + } +} + +void ygtk_ratio_box_set_child_packing (YGtkRatioBox *box, GtkWidget *child, gfloat ratio) +{ + YGtkRatioBoxChild *child_info; + child_info = ygtk_ratio_get_child_info (box, child); + if (child_info) { + gtk_widget_freeze_child_notify (child); + child_info->ratio = ratio; + if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box)) + gtk_widget_queue_resize (child); + + gtk_widget_thaw_child_notify (child); + } +} + +void ygtk_ratio_box_set_spacing (YGtkRatioBox *box, guint spacing) +{ + box->spacing = spacing; +} + +static void ygtk_ratio_box_class_init (YGtkRatioBoxClass *klass) +{ + ygtk_ratio_box_parent_class = (GtkContainerClass*) g_type_class_peek_parent (klass); + + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + container_class->add = ygtk_ratio_box_add; + container_class->remove = ygtk_ratio_box_remove; + container_class->forall = ygtk_ratio_box_forall; + container_class->child_type = ygtk_ratio_box_child_type; +} + +//** RatioHBox + +G_DEFINE_TYPE (YGtkRatioHBox, ygtk_ratio_hbox, YGTK_TYPE_RATIO_BOX) + +static void ygtk_ratio_hbox_init (YGtkRatioHBox *box) +{ } + +static void ygtk_ratio_hbox_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ ygtk_ratio_box_size_request (widget, requisition, GTK_ORIENTATION_HORIZONTAL); } + +static void ygtk_ratio_hbox_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ ygtk_ratio_box_size_allocate (widget, allocation, GTK_ORIENTATION_HORIZONTAL); } + +GtkWidget* ygtk_ratio_hbox_new (gint spacing) +{ + YGtkRatioBox* box = (YGtkRatioBox*) g_object_new (YGTK_TYPE_RATIO_HBOX, NULL); + box->spacing = spacing; + return GTK_WIDGET (box); +} + +static void ygtk_ratio_hbox_class_init (YGtkRatioHBoxClass *klass) +{ + ygtk_ratio_hbox_parent_class = (YGtkRatioBoxClass*) g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = ygtk_ratio_hbox_size_request; + widget_class->size_allocate = ygtk_ratio_hbox_size_allocate; +} + +//** RatioVBox + +G_DEFINE_TYPE (YGtkRatioVBox, ygtk_ratio_vbox, YGTK_TYPE_RATIO_BOX) + +static void ygtk_ratio_vbox_init (YGtkRatioVBox *box) +{ } + +static void ygtk_ratio_vbox_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ ygtk_ratio_box_size_request (widget, requisition, GTK_ORIENTATION_VERTICAL); } + +static void ygtk_ratio_vbox_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ ygtk_ratio_box_size_allocate (widget, allocation, GTK_ORIENTATION_VERTICAL); } + +GtkWidget* ygtk_ratio_vbox_new (gint spacing) +{ + YGtkRatioBox* box = (YGtkRatioBox*) g_object_new (YGTK_TYPE_RATIO_VBOX, NULL); + box->spacing = spacing; + return GTK_WIDGET (box); +} + +static void ygtk_ratio_vbox_class_init (YGtkRatioVBoxClass *klass) +{ + ygtk_ratio_vbox_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = ygtk_ratio_vbox_size_request; + widget_class->size_allocate = ygtk_ratio_vbox_size_allocate; +} + +//** YGtkAdjSize + +G_DEFINE_TYPE (YGtkAdjSize, ygtk_adj_size, GTK_TYPE_BIN) + +static void ygtk_adj_size_init (YGtkAdjSize *adj_size) +{ + GTK_WIDGET_SET_FLAGS (adj_size, GTK_NO_WINDOW); + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (adj_size), FALSE); +} + +static void ygtk_adj_size_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkWidget *child = GTK_BIN (widget)->child; + requisition->width = requisition->height = 0; + if (child && GTK_WIDGET_VISIBLE (child)) { + gtk_widget_size_request (child, requisition); + guint border = GTK_CONTAINER (widget)->border_width; + requisition->width += border * 2; + requisition->height += border * 2; + + YGtkAdjSize *adj_size = YGTK_ADJ_SIZE (widget); + if (adj_size->min_size_cb) { + guint min_width, min_height; + adj_size->min_size_cb (&min_width, &min_height, adj_size->min_size_data); + requisition->width = MAX (requisition->width, min_width); + requisition->height = MAX (requisition->height, min_height); + } + requisition->width = MAX (requisition->width, adj_size->min_width); + requisition->height = MAX (requisition->height, adj_size->min_height); + + if (adj_size->max_width) + requisition->width = MIN (requisition->width, adj_size->max_width); + if (adj_size->max_height) + requisition->height = MIN (requisition->height, adj_size->max_height); + + if (adj_size->only_expand) { + adj_size->min_width = requisition->width; + adj_size->min_height = requisition->height; + } + } +} + +static void ygtk_adj_size_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkWidget *child = GTK_BIN (widget)->child; + if (child && GTK_WIDGET_VISIBLE (child)) { + GtkAllocation child_alloc = *allocation; + guint border = GTK_CONTAINER (widget)->border_width; + child_alloc.x += border; + child_alloc.y += border; + child_alloc.width -= border * 2; + child_alloc.height -= border * 2; + child_alloc.width = MAX (child_alloc.width, 1); + child_alloc.height = MAX (child_alloc.height, 1); + gtk_widget_size_allocate (child, &child_alloc); + } + GTK_WIDGET_CLASS (ygtk_adj_size_parent_class)->size_allocate (widget, allocation); +} + +GtkWidget* ygtk_adj_size_new (void) +{ + return GTK_WIDGET (g_object_new (YGTK_TYPE_ADJ_SIZE, NULL)); +} + +void ygtk_adj_size_set_min (YGtkAdjSize *adj_size, guint min_width, guint min_height) +{ + adj_size->min_width = min_width; + adj_size->min_height = min_height; +} + +void ygtk_adj_size_set_max (YGtkAdjSize *adj_size, guint max_width, guint max_height) +{ + adj_size->max_width = max_width; + adj_size->max_height = max_height; +} + +void ygtk_adj_size_set_min_cb (YGtkAdjSize *adj_size, LimitSizeCb min_size_cb, gpointer data) +{ + adj_size->min_size_cb = min_size_cb; + adj_size->min_size_data = data; +} + +void ygtk_adj_size_set_only_expand (YGtkAdjSize *adj_size, gboolean only_expand) +{ + adj_size->only_expand = only_expand; +} + +static void ygtk_adj_size_class_init (YGtkAdjSizeClass *klass) +{ + ygtk_adj_size_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = ygtk_adj_size_size_request; + widget_class->size_allocate = ygtk_adj_size_size_allocate; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkratiobox.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,160 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkRatioBox uses weights instead of the single state expand boolean. +*/ + +#ifndef YGTK_RATIO_BOX_H +#define YGTK_RATIO_BOX_H + +#include <gtk/gtkcontainer.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_RATIO_BOX (ygtk_ratio_box_get_type ()) +#define YGTK_RATIO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_RATIO_BOX, YGtkRatioBox)) +#define YGTK_RATIO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_RATIO_BOX, YGtkRatioBoxClass)) +#define YGTK_IS_RATIO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_RATIO_BOX)) +#define YGTK_IS_RATIO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_RATIO_BOX)) +#define YGTK_RATIO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_RATIO_BOX, YGtkRatioBoxClass)) + +typedef struct _YGtkRatioBox +{ + GtkContainer parent; + + // private (read-only): + GList *children; + guint spacing; +} YGtkRatioBox; + +typedef struct _YGtkRatioBoxClass +{ + GtkContainerClass parent_class; +} YGtkRatioBoxClass; + +typedef struct _YGtkRatioBoxChild +{ + GtkWidget *widget; + // members + gfloat ratio; +} YGtkRatioBoxChild; + +GType ygtk_ratio_box_get_type (void) G_GNUC_CONST; + +void ygtk_ratio_box_set_spacing (YGtkRatioBox *box, guint spacing); + +void ygtk_ratio_box_pack (YGtkRatioBox *box, GtkWidget *child, gfloat ratio); +void ygtk_ratio_box_set_child_packing (YGtkRatioBox *box, GtkWidget *child, gfloat ratio); + +/* RatioHBox */ + +#define YGTK_TYPE_RATIO_HBOX (ygtk_ratio_hbox_get_type ()) +#define YGTK_RATIO_HBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_RATIO_HBOX, YGtkRatioHBox)) +#define YGTK_RATIO_HBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_RATIO_HBOX, YGtkRatioHBoxClass)) +#define YGTK_IS_RATIO_HBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_RATIO_HBOX)) +#define YGTK_IS_RATIO_HBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_RATIO_HBOX)) +#define YGTK_RATIO_HBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_RATIO_HBOX, YGtkRatioHBoxClass)) + +typedef struct _YGtkRatioHBox +{ + YGtkRatioBox parent; +} YGtkRatioHBox; + +typedef struct _YGtkRatioHBoxClass +{ + YGtkRatioBoxClass parent_class; +} YGtkRatioHBoxClass; + +GtkWidget* ygtk_ratio_hbox_new (gint spacing); +GType ygtk_ratio_hbox_get_type (void) G_GNUC_CONST; + +/* RatioVBox */ + +#define YGTK_TYPE_RATIO_VBOX (ygtk_ratio_vbox_get_type ()) +#define YGTK_RATIO_VBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_RATIO_VBOX, YGtkRatioVBox)) +#define YGTK_RATIO_VBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_RATIO_VBOX, YGtkRatioVBoxClass)) +#define YGTK_IS_RATIO_VBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_RATIO_VBOX)) +#define YGTK_IS_RATIO_VBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_RATIO_VBOX)) +#define YGTK_RATIO_VBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_RATIO_VBOX, YGtkRatioVBoxClass)) + +typedef struct _YGtkRatioVBox +{ + YGtkRatioBox parent; +} YGtkRatioVBox; + +typedef struct _YGtkRatioVBoxClass +{ + YGtkRatioBoxClass parent_class; +} YGtkRatioVBoxClass; + +GtkWidget* ygtk_ratio_vbox_new (gint spacing); +GType ygtk_ratio_vbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS +#endif /* YGTK_RATIO_BOX_H */ + +/* YGtkAdjSize container allows for better fine grained size constrains. */ +#ifndef YGTK_ADJ_SIZE_H +#define YGTK_ADJ_SIZE_H + +#include <gtk/gtkbin.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_ADJ_SIZE (ygtk_adj_size_get_type ()) +#define YGTK_ADJ_SIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_ADJ_SIZE, YGtkAdjSize)) +#define YGTK_ADJ_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_ADJ_SIZE, YGtkAdjSizeClass)) +#define YGTK_IS_ADJ_SIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_ADJ_SIZE)) +#define YGTK_IS_ADJ_SIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_ADJ_SIZE)) +#define YGTK_ADJ_SIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_ADJ_SIZE, YGtkAdjSizeClass)) + +typedef void (*LimitSizeCb) (guint *width, guint *height, gpointer data); + +typedef struct _YGtkAdjSize +{ + GtkBin parent; + // members + guint min_width, min_height, max_width, max_height; + LimitSizeCb min_size_cb; + gpointer min_size_data; + gint only_expand : 2; +} YGtkAdjSize; + +typedef struct _YGtkAdjSizeClass +{ + GtkBinClass parent_class; +} YGtkAdjSizeClass; + +GType ygtk_adj_size_get_type (void) G_GNUC_CONST; +GtkWidget* ygtk_adj_size_new (void); + +void ygtk_adj_size_set_min (YGtkAdjSize *adj_size, guint min_width, guint min_height); +void ygtk_adj_size_set_max (YGtkAdjSize *adj_size, guint max_width, guint max_height); + +void ygtk_adj_size_set_min_cb (YGtkAdjSize *adj_size, LimitSizeCb min_size_cb, gpointer data); + +/* Only allow the child to grow (ie. to ask for bigger sizes). */ +void ygtk_adj_size_set_only_expand (YGtkAdjSize *adj_size, gboolean only_expand); + +G_END_DECLS +#endif /*YGTK_ADJ_SIZE_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,735 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkRichText widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygtkrichtext.h" +#include <gtk/gtk.h> +#include <string.h> + +#define IDENT_MARGIN 20 +#define PARAGRAPH_SPACING 12 + +// convert liberal html to xhtml, as we use a xhtml parser +extern gchar *ygutils_convert_to_xhtml (const char *instr); + +G_DEFINE_TYPE (YGtkRichText, ygtk_rich_text, YGTK_TYPE_TEXT_VIEW) + +static guint link_clicked_signal; +static GdkColor link_color = { 0, 0, 0, 0xeeee }; + +// utilities +// Looks at all tags covering the position of iter in the text view, +// and returns the link the text points to, in case that text is a link. +static const char *get_link_at_iter (GtkTextView *text_view, GtkTextIter *iter) +{ + char *link = NULL; + GSList *tags = gtk_text_iter_get_tags (iter), *tagp; + for (tagp = tags; tagp != NULL; tagp = tagp->next) { + GtkTextTag *tag = (GtkTextTag*) tagp->data; + link = (char*) g_object_get_data (G_OBJECT (tag), "link"); + if (link) + break; + } + + if (tags) + g_slist_free (tags); + return link; +} +static const char *get_link (GtkTextView *text_view, gint win_x, gint win_y) +{ + gint buffer_x, buffer_y; + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), + GTK_TEXT_WINDOW_WIDGET, win_x, win_y, &buffer_x, &buffer_y); + GtkTextIter iter; + gtk_text_view_get_iter_at_location (text_view, &iter, buffer_x, buffer_y); + return get_link_at_iter (text_view, &iter); +} + +// callbacks +// Links can also be activated by clicking. +static gboolean event_after (GtkWidget *text_view, GdkEvent *ev) +{ + if (ev->type != GDK_BUTTON_RELEASE) + return FALSE; + GtkTextIter start, end; + GtkTextBuffer *buffer; + GdkEventButton *event = (GdkEventButton *) ev; + if (event->button != 1) + return FALSE; + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + + // We shouldn't follow a link if the user is selecting something. + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end)) + return FALSE; + + const char *link = get_link (GTK_TEXT_VIEW (text_view), event->x, event->y); + if (link) // report link + g_signal_emit (YGTK_RICH_TEXT (text_view), link_clicked_signal, 0, link); + return FALSE; +} + +#include <stdlib.h> +static int mystrcmp(void *a, void *b) +{ return g_ascii_strcasecmp (*(char **)a, *(char **)b); } + +static gboolean isBlockTag (const char *tag) +{ + static const char *Tags[] = + { "blockquote", "h1", "h2", "h3", "h4", "h5", "li", "p", "pre" }; + void *ret; + ret = bsearch (&tag, Tags, sizeof (Tags)/sizeof(char*), sizeof(char *), (void*)mystrcmp); + return ret != 0; +} +static gboolean isIdentTag (const char *tag) +{ + static const char *Tags[] = + { "blockquote", "ol", "ul" }; + void *ret; + ret = bsearch (&tag, Tags, sizeof (Tags)/sizeof(char*), sizeof(char *), (void*)mystrcmp); + return ret != 0; +} + +void ygtk_rich_text_init (YGtkRichText *rtext) +{ + GtkWidget *widget = GTK_WIDGET (rtext); + GtkTextView *tview = GTK_TEXT_VIEW (rtext); + gtk_text_view_set_editable (tview, FALSE); + gtk_text_view_set_wrap_mode (tview, GTK_WRAP_WORD_CHAR); + gtk_text_view_set_pixels_below_lines (tview, 4); + gtk_text_view_set_left_margin (tview, 4); + + // Init link support + GdkDisplay *display = gtk_widget_get_display (widget); + rtext->hand_cursor = gdk_cursor_new_for_display (display, GDK_HAND2); + gdk_cursor_ref (rtext->hand_cursor); + + gtk_widget_style_get (widget, "link_color", &link_color, NULL); + g_signal_connect (tview, "event-after", + G_CALLBACK (event_after), NULL); + + // Create a few tags like 'h3', 'b', 'i'. others need to be created as we parse + GtkTextBuffer *buffer = gtk_text_view_get_buffer (tview); + + gboolean reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + const char *left_margin = reverse ? "right-margin" : "left-margin"; + const char *right_margin = reverse ? "left-margin" : "right-margin"; + + gtk_text_buffer_create_tag (buffer, "body", NULL); + gtk_text_buffer_create_tag (buffer, "h1", "weight", PANGO_WEIGHT_HEAVY, + "scale", PANGO_SCALE_XX_LARGE, "pixels-below-lines", 16, + "foreground", "#5c5c5c", NULL); + gtk_text_buffer_create_tag (buffer, "h2", "weight", PANGO_WEIGHT_ULTRABOLD, + "scale", PANGO_SCALE_X_LARGE, "pixels-below-lines", 15, + "foreground", "#5c5c5c", NULL); + gtk_text_buffer_create_tag (buffer, "h3", "weight", PANGO_WEIGHT_BOLD, + "scale", PANGO_SCALE_LARGE, "pixels-below-lines", 14, + "foreground", "#5c5c5c", NULL); + gtk_text_buffer_create_tag (buffer, "h4", "weight", PANGO_WEIGHT_SEMIBOLD, + "scale", PANGO_SCALE_LARGE, "pixels-below-lines", 13, + "foreground", "#5c5c5c", NULL); + gtk_text_buffer_create_tag (buffer, "h5", + "scale", PANGO_SCALE_LARGE, "foreground", "#5c5c5c", NULL); + gtk_text_buffer_create_tag (buffer, "p", "pixels-below-lines", 12, NULL); + gtk_text_buffer_create_tag (buffer, "big", "scale", PANGO_SCALE_LARGE, NULL); + gtk_text_buffer_create_tag (buffer, "small", "scale", PANGO_SCALE_SMALL, NULL); + gtk_text_buffer_create_tag (buffer, "tt", "family", "monospace", NULL); + gtk_text_buffer_create_tag (buffer, "pre", "family", "monospace", + "paragraph-background", "#f0f0f0", left_margin, 16, right_margin, 20, NULL); + gtk_text_buffer_create_tag (buffer, "b", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag (buffer, "i", "style", PANGO_STYLE_ITALIC, NULL); + gtk_text_buffer_create_tag (buffer, "u", "underline", PANGO_UNDERLINE_SINGLE, NULL); + gtk_text_buffer_create_tag (buffer, "center", "justification", GTK_JUSTIFY_CENTER, NULL); + gtk_text_buffer_create_tag (buffer, "right", "justification", GTK_JUSTIFY_RIGHT, NULL); + // helpers + gtk_text_buffer_create_tag (buffer, "keyword", "background", "yellow", + "foreground", "#000000", NULL); +} + +static void ygtk_rich_text_destroy (GtkObject *object) +{ + // destroy can be called multiple times, and we must ref only once + YGtkRichText *rtext = YGTK_RICH_TEXT (object); + gdk_cursor_unref (rtext->hand_cursor); + ygtk_rich_text_set_background (rtext, NULL); + GTK_OBJECT_CLASS (ygtk_rich_text_parent_class)->destroy (object); +} + +// Change the cursor to the "hands" cursor typically used by web browsers, +// if there is a link in the given position. +static void set_cursor_if_appropriate (GtkTextView *view, gint wx, gint wy) +{ + if (wx == -1) { + GtkWidget *widget = GTK_WIDGET (view); + gdk_window_get_pointer (widget->window, &wx, &wy, NULL); + if (wx < 0 || wy < 0 || wx >= widget->allocation.width || + wy >= widget->allocation.height) + return; + } + + static gboolean hovering_over_link = FALSE; + gboolean hovering = get_link (view, wx, wy) != NULL; + + if (hovering != hovering_over_link) { + hovering_over_link = hovering; + YGtkRichText *rtext = YGTK_RICH_TEXT (view); + GdkWindow *window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_TEXT); + GdkCursor *cursor = hovering ? rtext->hand_cursor : NULL; + gdk_window_set_cursor (window, cursor); + } +} + +// Update the cursor image if the pointer moved. +static gboolean ygtk_rich_text_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + set_cursor_if_appropriate (GTK_TEXT_VIEW (widget), event->x, event->y); + return TRUE; +} + +static gboolean ygtk_rich_text_expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + GtkTextView *text = GTK_TEXT_VIEW (widget); + YGtkRichText *rtext = YGTK_RICH_TEXT (widget); + if (rtext->background_pixbuf) { + GdkWindow *window = gtk_text_view_get_window (text, GTK_TEXT_WINDOW_TEXT); + if (event->window == window) { + int x, y; + int width = gdk_pixbuf_get_width (rtext->background_pixbuf); + int height = gdk_pixbuf_get_height (rtext->background_pixbuf); + gtk_text_view_buffer_to_window_coords (text, GTK_TEXT_WINDOW_TEXT, + widget->allocation.width-((2*width)/5), -height/3, &x, &y); + gdk_draw_pixbuf (GDK_DRAWABLE (window), *widget->style->fg_gc, rtext->background_pixbuf, + 0, 0, x, y, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); + } + } + + gboolean ret; + ret = GTK_WIDGET_CLASS (ygtk_rich_text_parent_class)->expose_event (widget, event); + set_cursor_if_appropriate (text, -1, -1); + return ret; +} + +/* Rich Text parsing methods. */ + +typedef struct _HTMLList +{ + gboolean ordered; + char enumeration; +} HTMLList; + +static void HTMLList_init (HTMLList *list, gboolean ordered) +{ + list->ordered = ordered; + list->enumeration = 1; +} + +typedef struct GRTPTag { + GtkTextMark *mark; + GtkTextTag *tag; +} GRTPTag; +typedef struct GRTParseState { + GtkTextBuffer *buffer; + GtkTextTagTable *tags; + GList *htags; // of GRTPTag + + // Attributes for tags that affect their children + gboolean pre_mode; + gboolean default_color; + int left_margin; + GList *html_list; // of HTMLList + gboolean closed_block_tag; +} GRTParseState; + +static void GRTParseState_init (GRTParseState *state, GtkTextBuffer *buffer) +{ + state->buffer = buffer; + state->pre_mode = FALSE; + state->default_color = TRUE; + state->left_margin = 0; + state->tags = gtk_text_buffer_get_tag_table (buffer); + state->html_list = NULL; + state->htags = NULL; + state->closed_block_tag = FALSE; +} + +static void free_list (GList *list) +{ + GList *i; + for (i = g_list_first (list); i; i = i->next) + g_free (i->data); + g_list_free (list); +} + +static void GRTParseState_free (GRTParseState *state) +{ + // NOTE: some elements might not have been freed because of bad html + free_list (state->html_list); + free_list (state->htags); +} + +static void insert_li_enumeration (GRTParseState *state, GtkTextIter *iter, gboolean start) +{ + gboolean _start = gtk_widget_get_default_direction() != GTK_TEXT_DIR_RTL; + if (_start != start) return; + + HTMLList *front_list; + if (state->html_list && (front_list = g_list_first (state->html_list)->data) && + (front_list->ordered)) { + const gchar *form = start ? "%d. " : " .%d"; + gchar *str = g_strdup_printf (form, front_list->enumeration++); + gtk_text_buffer_insert (state->buffer, iter, str, -1); + g_free (str); + } + else { // \u25cf for bigger bullets + const char *str = start ? "\u2022 " : " \u2022"; + gtk_text_buffer_insert (state->buffer, iter, str, -1); + } +} + +// Tags to support: <p> and not </p>: +// either 'elide' \ns (turn off with <pre> I guess +static void +rt_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ // Called for open tags <foo bar="baz"> + GRTParseState *state = (GRTParseState*) user_data; + GRTPTag *tag = g_malloc (sizeof (GRTPTag)); + GtkTextIter iter; + gtk_text_buffer_get_end_iter (state->buffer, &iter); + tag->mark = gtk_text_buffer_create_mark (state->buffer, NULL, &iter, TRUE); + + if (!g_ascii_strcasecmp (element_name, "pre")) + state->pre_mode = TRUE; + + // Check if this is a block tag + if (isBlockTag (element_name)) { + // make sure this opens a new paragraph + if (state->html_list && gtk_text_iter_get_line_offset (&iter) < 6) + ; // on a list, there is the "1. " in the buffer so we have to do this + else if (!gtk_text_iter_starts_line (&iter)) { + gtk_text_buffer_insert (state->buffer, &iter, "\n", -1); + gtk_text_buffer_get_end_iter (state->buffer, &iter); + } + } + state->closed_block_tag = FALSE; + + char *lower = g_ascii_strdown (element_name, -1); + tag->tag = gtk_text_tag_table_lookup (state->tags, lower); + + // Special tags that must be inserted manually + if (!tag->tag) { + if (!g_ascii_strcasecmp (element_name, "font")) { + int i; + for (i = 0; attribute_names[i]; i++) { + const char *attrb = attribute_names[i]; + const char *value = attribute_values[i]; + if (!g_ascii_strcasecmp (attrb, "color")) { + tag->tag = gtk_text_buffer_create_tag (state->buffer, NULL, + "foreground", value, NULL); + state->default_color = FALSE; + } + // not from html -- we use this internally + else if (!g_ascii_strcasecmp (attrb, "bgcolor")) { + tag->tag = gtk_text_buffer_create_tag (state->buffer, NULL, + "background", value, NULL); + } + else + g_warning ("Unknown font attribute: '%s'", attrb); + } + } + else if (!g_ascii_strcasecmp (element_name, "a")) { + if (attribute_names[0] && + !g_ascii_strcasecmp (attribute_names[0], "href")) { + tag->tag = gtk_text_buffer_create_tag (state->buffer, NULL, + "underline", PANGO_UNDERLINE_SINGLE, NULL); + if (state->default_color) + g_object_set (tag->tag, "foreground-gdk", &link_color, NULL); + g_object_set_data (G_OBJECT (tag->tag), "link", g_strdup (attribute_values[0])); + } + else + g_warning ("Unknown a attribute: '%s'", attribute_names[0]); + } + else if (!g_ascii_strcasecmp (element_name, "li")) + insert_li_enumeration (state, &iter, TRUE); + // Tags that affect the margin + else if (!g_ascii_strcasecmp (element_name, "ul") || + !g_ascii_strcasecmp (element_name, "ol")) { + HTMLList *list = g_malloc (sizeof (HTMLList)); + HTMLList_init (list, !g_ascii_strcasecmp (element_name, "ol")); + state->html_list = g_list_append (state->html_list, list); + } + else if (!g_ascii_strcasecmp (element_name, "img")) { + if (attribute_names[0] && + !g_ascii_strcasecmp (attribute_names[0], "src")) { + const char *filename = attribute_values[0]; + if (filename) { + GdkPixbuf *pixbuf; + if (filename[0] == '/') + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + else + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(), + filename, 64, 0, NULL); + if (pixbuf) { + gtk_text_buffer_insert_pixbuf (state->buffer, &iter, pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + } + } + } + else + g_warning ("Unknown img attribute: '%s'", attribute_names[0]); + } + // tags like <br/>, GMarkup will pass them through the end + // tag callback too, so we'll deal with them there + else if (!g_ascii_strcasecmp (element_name, "br")) ; + else if (!g_ascii_strcasecmp (element_name, "hr")) ; + + else + { + if (isBlockTag (element_name)) + ; + else + g_warning ("Unknown tag '%s'", element_name); + } + } + else if (attribute_names[0]) { // tags that may have extra attributes + if (!g_ascii_strcasecmp (element_name, "p")) { + // not from html (basic html only supports background color in + // tables), but we use this internally + if (!g_ascii_strcasecmp (attribute_names[0], "bgcolor")) + tag->tag = gtk_text_buffer_create_tag (state->buffer, NULL, + "paragraph-background", attribute_values[0], NULL); + else + g_warning ("Unknown p attribute: '%s'", attribute_names[0]); + } + } + + if (!tag->tag && isIdentTag (element_name)) { + state->left_margin += IDENT_MARGIN; + + gboolean reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + const char *margin = reverse ? "right-margin" : "left-margin"; + tag->tag = gtk_text_buffer_create_tag (state->buffer, NULL, + margin, state->left_margin, NULL); + } + + g_free (lower); + state->htags = g_list_append (state->htags, tag); +} + +#include "hr.xpm" + +static void +rt_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ // Called for close tags </foo> + GRTParseState *state = (GRTParseState*) user_data; + + if (g_list_length (state->htags) == 0) { + g_warning ("Urgh - empty tag queue closing '%s'", element_name); + return; + } + + g_return_if_fail (state->htags != NULL); + GRTPTag *tag = g_list_last (state->htags)->data; + state->htags = g_list_remove (state->htags, tag); + + GtkTextIter start, end; + gtk_text_buffer_get_iter_at_mark (state->buffer, &start, tag->mark); + gtk_text_buffer_get_end_iter (state->buffer, &end); + + gint appendLines = 0; + + if (isIdentTag (element_name)) + state->left_margin -= IDENT_MARGIN; + + if (!g_ascii_strcasecmp (element_name, "ul") || + !g_ascii_strcasecmp (element_name, "ol")) { + HTMLList *last_list = g_list_last (state->html_list)->data; + state->html_list = g_list_remove (state->html_list, last_list); + g_free (last_list); + } + else if (!g_ascii_strcasecmp (element_name, "font")) + state->default_color = TRUE; + + else if (!g_ascii_strcasecmp (element_name, "pre")) + state->pre_mode = FALSE; + + else if (!g_ascii_strcasecmp (element_name, "hr")) { + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data (hr_xpm); + gtk_text_buffer_insert_pixbuf (state->buffer, &end, pixbuf); + g_object_unref (pixbuf); + gtk_text_buffer_get_iter_at_mark (state->buffer, &start, tag->mark); + gtk_text_buffer_get_end_iter (state->buffer, &end); + gtk_text_buffer_apply_tag_by_name (state->buffer, "center", &start, &end); + appendLines = 1; + } + else if (!g_ascii_strcasecmp (element_name, "li")) + insert_li_enumeration (state, &end, FALSE); + + if (isBlockTag (element_name) || !g_ascii_strcasecmp (element_name, "br")) { + appendLines = 1; + if (isBlockTag (element_name) && gtk_text_iter_starts_line (&end)) + appendLines = 0; + state->closed_block_tag = TRUE; + } + else + state->closed_block_tag = FALSE; + + if (appendLines) { + gtk_text_buffer_insert (state->buffer, &end, + appendLines == 1 ? "\n" : "\n\n", -1); + gtk_text_buffer_get_iter_at_mark (state->buffer, &start, tag->mark); + gtk_text_buffer_get_end_iter (state->buffer, &end); + } + + if (tag->tag) + gtk_text_buffer_apply_tag (state->buffer, tag->tag, &start, &end); + + gtk_text_buffer_delete_mark (state->buffer, tag->mark); + g_free (tag); +} + +static void +rt_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ // Called for character data, NB. text NOT nul-terminated + GRTParseState *state = (GRTParseState*) user_data; + GtkTextIter start, end; + gtk_text_buffer_get_end_iter (state->buffer, &start); + if (state->pre_mode) + gtk_text_buffer_insert_with_tags (state->buffer, &start, + text, text_len, NULL, NULL); + else { + gboolean rtl = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; + + int i = 0; + if (state->closed_block_tag) { + for (; i < text_len; i++) + if (!g_ascii_isspace (text[i])) + break; + } + + // hack: for right-to-left languages, change "Device:" to ":Device" (bug 581800) + if (rtl && text[text_len-1] == ':') { + gtk_text_buffer_insert (state->buffer, &start, ":", 1); + text_len--; + } + + gtk_text_buffer_insert (state->buffer, &start, text+i, text_len-i); + } + gtk_text_buffer_get_end_iter (state->buffer, &end); +} + +static void +rt_passthrough (GMarkupParseContext *context, + const gchar *passthrough_text, + gsize text_len, + gpointer user_data, + GError **error) +{ + // ignore comments etc. +} + +static void +rt_error (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ +} + +static GMarkupParser rt_parser = { + rt_start_element, + rt_end_element, + rt_text, + rt_passthrough, + rt_error +}; + +GtkWidget *ygtk_rich_text_new (void) +{ return g_object_new (YGTK_TYPE_RICH_TEXT, NULL); } + +static void ygtk_rich_text_set_rtl (YGtkRichText *rtext) +{ + GtkTextView *view = GTK_TEXT_VIEW (rtext); + GtkTextBuffer *buffer = gtk_text_view_get_buffer (view); + GtkTextIter iter; + gtk_text_buffer_get_start_iter (buffer, &iter); + do { + GtkTextIter end = iter; + if (!gtk_text_iter_forward_line (&end)) + gtk_text_buffer_get_end_iter (buffer, &end); + + gchar *text = gtk_text_iter_get_text (&iter, &end); + PangoDirection dir = pango_find_base_dir (text, -1); + if (dir == PANGO_DIRECTION_LTR) + gtk_text_buffer_apply_tag_by_name (buffer, "right", &iter, &end); + + iter = end; + } while (!gtk_text_iter_is_end (&iter)); +} + +void ygtk_rich_text_set_plain_text (YGtkRichText* rtext, const gchar* text) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (rtext)); + gtk_text_buffer_set_text (buffer, text, -1); +} + +void ygtk_rich_text_set_text (YGtkRichText* rtext, const gchar* text) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (rtext)); + gtk_text_buffer_set_text (buffer, "", 0); // remove any existing text + + GRTParseState state; + GRTParseState_init (&state, buffer); + + GMarkupParseContext *ctx; + ctx = g_markup_parse_context_new (&rt_parser, (GMarkupParseFlags)0, &state, NULL); + + char *xml = ygutils_convert_to_xhtml (text); + GError *error = NULL; + if (!g_markup_parse_context_parse (ctx, xml, -1, &error)) { + g_warning ("Markup parse error '%s'", error ? error->message : "Unknown"); + } + g_free (xml); + + g_markup_parse_context_free (ctx); + GRTParseState_free (&state); + + // remove last empty line, if any + GtkTextIter end_it, before_end_it; + gtk_text_buffer_get_end_iter (buffer, &end_it); + before_end_it = end_it; + if (gtk_text_iter_backward_char (&before_end_it) && + gtk_text_iter_get_char (&before_end_it) == '\n') + gtk_text_buffer_delete (buffer, &before_end_it, &end_it); + + // GtkTextView does LTR and RTL depending on the paragraph; we want + // to change that behavior so it's RTL to the all thing for Arabic + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + ygtk_rich_text_set_rtl (rtext); +} + +/* gtk_text_iter_forward_search() is case-sensitive so we roll our own. + The idea is to keep use get_text and strstr there, but to be more + efficient we check per line. */ +static gboolean ygtk_rich_text_forward_search (const GtkTextIter *begin, + const GtkTextIter *end, const gchar *_key, GtkTextIter *match_start, + GtkTextIter *match_end) +{ + if (*_key == 0) + return FALSE; + + /* gtk_text_iter_get_char() returns a gunichar (ucs4 coding), so we + convert the given string (which is utf-8, like anyhting in gtk+) */ + gunichar *key = g_utf8_to_ucs4 (_key, -1, NULL, NULL, NULL); + if (!key) // conversion error -- should not happen + return FALSE; + + // convert key to lower case, to avoid work later + gunichar *k; + for (k = key; *k; k++) + *k = g_unichar_tolower (*k); + + GtkTextIter iter = *begin, iiter; + while (!gtk_text_iter_is_end (&iter) && gtk_text_iter_compare (&iter, end) <= 0) { + iiter = iter; + for (k = key; *k == g_unichar_tolower (gtk_text_iter_get_char (&iiter)) && (*k); + k++, gtk_text_iter_forward_char (&iiter)) + ; + if (!*k) { + *match_start = iter; + *match_end = iiter; + return TRUE; + } + gtk_text_iter_forward_char (&iter); + } + return FALSE; +} + +gboolean ygtk_rich_text_mark_text (YGtkRichText *rtext, const gchar *text) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (rtext)); + GtkTextIter iter, end, match_start, match_end; + + gtk_text_buffer_get_bounds (buffer, &iter, &end); + gtk_text_buffer_remove_tag_by_name (buffer, "keyword", &iter, &end); + + gtk_text_buffer_select_range (buffer, &iter, &iter); // unselect text + if (!text || *text == '\0') + return TRUE; + + gboolean found = FALSE; + while (ygtk_rich_text_forward_search (&iter, &end, text, + &match_start, &match_end)) { + found = TRUE; + gtk_text_buffer_apply_tag_by_name (buffer, "keyword", &match_start, &match_end); + iter = match_end; + gtk_text_iter_forward_char (&iter); + } + return found; +} + +gboolean ygtk_rich_text_forward_mark (YGtkRichText *rtext, const gchar *text) +{ + GtkTextIter start_iter, end_iter; + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (rtext)); + gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, + gtk_text_buffer_get_selection_bound (buffer)); + gtk_text_buffer_get_end_iter (buffer, &end_iter); + + gboolean found; + found = ygtk_rich_text_forward_search (&start_iter, &end_iter, text, + &start_iter, &end_iter); + if (!found) { + gtk_text_buffer_get_start_iter (buffer, &start_iter); + found = ygtk_rich_text_forward_search (&start_iter, &end_iter, text, + &start_iter, &end_iter); + } + + if (found) { + gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (rtext), &start_iter, 0.10, + FALSE, 0, 0); + gtk_text_buffer_select_range (buffer, &start_iter, &end_iter); + return TRUE; + } + return FALSE; +} + +void ygtk_rich_text_set_background (YGtkRichText *rtext, GdkPixbuf *pixbuf) +{ + if (rtext->background_pixbuf) + g_object_unref (G_OBJECT (rtext->background_pixbuf)); + rtext->background_pixbuf = pixbuf; + if (pixbuf) + g_object_ref (G_OBJECT (pixbuf)); +} + +static void ygtk_rich_text_class_init (YGtkRichTextClass *klass) +{ + GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass); + gtkwidget_class->motion_notify_event = ygtk_rich_text_motion_notify_event; + gtkwidget_class->expose_event = ygtk_rich_text_expose_event; + + GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); + gtkobject_class->destroy = ygtk_rich_text_destroy; + + link_clicked_signal = g_signal_new ("link-clicked", + G_TYPE_FROM_CLASS (G_OBJECT_CLASS (klass)), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (YGtkRichTextClass, link_clicked), NULL, NULL, + g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtkrichtext.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,60 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkRichText is a very simple widget that displays HTML code. + It was done, since GTK+ doesn't offer one out of box, and to + avoid dependencies. +*/ + +#ifndef YGTK_RICH_TEXT_H +#define YGTK_RICH_TEXT_H + +#include "ygtktextview.h" +G_BEGIN_DECLS + +#define YGTK_TYPE_RICH_TEXT (ygtk_rich_text_get_type ()) +#define YGTK_RICH_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_RICH_TEXT, YGtkRichText)) +#define YGTK_RICH_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_RICH_TEXT, YGtkRichTextClass)) +#define YGTK_IS_RICH_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_RICH_TEXT)) +#define YGTK_IS_RICH_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_RICH_TEXT)) +#define YGTK_RICH_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_RICH_TEXT, YGtkRichTextClass)) + +typedef struct _YGtkRichText +{ + YGtkTextView parent; + // members: + GdkCursor *hand_cursor; + GdkPixbuf *background_pixbuf; +} YGtkRichText; + +typedef struct _YGtkRichTextClass +{ + YGtkTextViewClass parent_class; + + // signals: + void (*link_clicked) (YGtkRichText *rich_text, const gchar *link); +} YGtkRichTextClass; + +GtkWidget *ygtk_rich_text_new (void); +GType ygtk_rich_text_get_type (void) G_GNUC_CONST; + +/* Sets some text to YGtkRichText, may be HTML or plain text, as indicated by + rich_text. */ +void ygtk_rich_text_set_text (YGtkRichText* rtext, const gchar* text); +void ygtk_rich_text_set_plain_text (YGtkRichText* rtext, const gchar* text); + +// To be used together with an entry box to search for text +gboolean ygtk_rich_text_mark_text (YGtkRichText *rtext, const gchar *text); +gboolean ygtk_rich_text_forward_mark (YGtkRichText *rtext, const gchar *text); // F3 + +void ygtk_rich_text_set_background (YGtkRichText *rtext, GdkPixbuf *pixbuf); + +G_END_DECLS +#endif /* YGTK_RICH_TEXT_H */ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,238 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkSteps widget */ +// check the header file for information about this widget + +/* + Textdomain "gtk" + */ + +#include <config.h> +#include "ygtksteps.h" +#include <gtk/gtk.h> +#define YGI18N_C +#include "YGi18n.h" + +#define CURRENT_MARK_ANIMATION_TIME 250 +#define CURRENT_MARK_ANIMATION_OFFSET 3 +#define CURRENT_MARK_FRAMES_NB (CURRENT_MARK_ANIMATION_OFFSET*2) + +G_DEFINE_TYPE (YGtkSteps, ygtk_steps, GTK_TYPE_VBOX) + +static void ygtk_steps_init (YGtkSteps *steps) +{ + gtk_box_set_spacing (GTK_BOX (steps), 8); + gtk_container_set_border_width (GTK_CONTAINER (steps), 4); + + const gchar *check = "\u2714", *current = "\u25b6", *todo = "\u26ab"; + if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL) + current = "\u25c0"; + PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (steps)); + steps->check_mark_layout = pango_layout_new (context); + steps->current_mark_layout = pango_layout_new (context); + steps->todo_mark_layout = pango_layout_new (context); + pango_layout_set_text (steps->check_mark_layout, check, -1); + pango_layout_set_text (steps->current_mark_layout, current, -1); + pango_layout_set_text (steps->todo_mark_layout, todo, -1); + steps->current_mark_timeout_id = steps->current_mark_frame = 0; +} + +static void ygtk_steps_destroy (GtkObject *object) +{ + YGtkSteps *steps = YGTK_STEPS (object); + if (steps->current_mark_timeout_id) { + g_source_remove (steps->current_mark_timeout_id); + steps->current_mark_timeout_id = 0; + } + if (steps->check_mark_layout) + g_object_unref (steps->check_mark_layout); + steps->check_mark_layout = NULL; + if (steps->current_mark_layout) + g_object_unref (steps->current_mark_layout); + if (steps->todo_mark_layout) + g_object_unref (steps->todo_mark_layout); + steps->todo_mark_layout = NULL; + GTK_OBJECT_CLASS (ygtk_steps_parent_class)->destroy (object); +} + +static void ygtk_step_update_layout (YGtkSteps *steps, gint step) +{ + if (step < 0) return; + gboolean bold = steps->current_step == step; + GList *children = gtk_container_get_children (GTK_CONTAINER (steps)); + GtkWidget *label = (GtkWidget *) g_list_nth_data (children, step); + if (g_object_get_data (G_OBJECT (label), "is-header")) + return; + if (bold) { + PangoAttrList *attrbs = pango_attr_list_new(); + pango_attr_list_insert (attrbs, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); + gtk_label_set_attributes (GTK_LABEL (label), attrbs); + pango_attr_list_unref (attrbs); + atk_object_set_description (gtk_widget_get_accessible (label), _("Current step")); + } + else { + gtk_label_set_attributes (GTK_LABEL (label), NULL); + atk_object_set_description (gtk_widget_get_accessible (label), ""); + } + g_list_free (children); +} + +static gboolean ygtk_steps_expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + GTK_WIDGET_CLASS (ygtk_steps_parent_class)->expose_event (widget, event); + + YGtkSteps *steps = YGTK_STEPS (widget); + gboolean reverse = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; + GList *children = gtk_container_get_children (GTK_CONTAINER (widget)), *i; + + cairo_t *cr = gdk_cairo_create (event->window); + cairo_set_source_rgb (cr, 0, 0, 0); + int n = 0; + for (i = children; i; i = i->next, n++) { + GtkWidget *label = i->data; + if (g_object_get_data (G_OBJECT (label), "is-header")) + continue; + PangoLayout *layout; + if (n < steps->current_step) + layout = steps->check_mark_layout; + else if (n == steps->current_step) + layout = steps->current_mark_layout; + else //if (n > steps->current_step) + layout = steps->todo_mark_layout; + int x = label->allocation.x, y = label->allocation.y; + if (reverse) { + PangoRectangle rect; + pango_layout_get_pixel_extents (layout, NULL, &rect); + x += label->allocation.width - rect.width - 4; + } + else + x += 4; + if (n == steps->current_step) { + int offset; + if (steps->current_mark_frame < CURRENT_MARK_FRAMES_NB/2) + offset = steps->current_mark_frame * CURRENT_MARK_ANIMATION_OFFSET; + else + offset = (CURRENT_MARK_FRAMES_NB - steps->current_mark_frame) * + CURRENT_MARK_ANIMATION_OFFSET; + x += offset * (reverse ? 1 : -1); + } + + cairo_move_to (cr, x, y); + pango_cairo_show_layout (cr, layout); + } + cairo_destroy (cr); + g_list_free (children); + return FALSE; +} + +GtkWidget* ygtk_steps_new (void) +{ + return g_object_new (YGTK_TYPE_STEPS, NULL); +} + +gint ygtk_steps_append (YGtkSteps *steps, const gchar *text) +{ + GtkWidget *label = gtk_label_new (text); + GdkColor black = { 0, 0, 0, 0 }; + gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &black); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + int mark_width = 10; + pango_layout_get_pixel_size (steps->check_mark_layout, &mark_width, NULL); + gtk_misc_set_padding (GTK_MISC (label), mark_width+12, 0); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (steps), label, FALSE, TRUE, 0); + return ygtk_steps_total (steps)-1; +} + +void ygtk_steps_append_heading (YGtkSteps *steps, const gchar *heading) +{ + GtkWidget *label = gtk_label_new (heading); + GdkColor black = { 0, 0, 0, 0 }; + gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &black); + g_object_set_data (G_OBJECT (label), "is-header", GINT_TO_POINTER (1)); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + + PangoAttrList *attrbs = pango_attr_list_new(); + pango_attr_list_insert (attrbs, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); + pango_attr_list_insert (attrbs, pango_attr_scale_new (PANGO_SCALE_LARGE)); + gtk_label_set_attributes (GTK_LABEL (label), attrbs); + pango_attr_list_unref (attrbs); + + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (steps), label, FALSE, TRUE, 6); +} + +static gboolean current_mark_animation_cb (void *steps_ptr) +{ + YGtkSteps *steps = steps_ptr; + + // should use gtk_widget_queue_draw_area (widget, x, y, w, h)... + gtk_widget_queue_draw (GTK_WIDGET (steps)); + + if (++steps->current_mark_frame == CURRENT_MARK_FRAMES_NB) { + steps->current_mark_frame = 0; + return FALSE; + } + return TRUE; +} + +void ygtk_steps_set_current (YGtkSteps *steps, gint step) +{ + gint old_step = steps->current_step; + steps->current_step = step; + + // update step icons + if (old_step != step) { + ygtk_step_update_layout (steps, old_step); + ygtk_step_update_layout (steps, step); + } + + if (step != -1) { + steps->current_mark_frame = 0; + steps->current_mark_timeout_id = g_timeout_add + (CURRENT_MARK_ANIMATION_TIME / CURRENT_MARK_FRAMES_NB, + current_mark_animation_cb, steps); + } +} + +gint ygtk_steps_total (YGtkSteps *steps) +{ + GList *children = gtk_container_get_children (GTK_CONTAINER (steps)); + int steps_nb = g_list_length (children); + g_list_free (children); + return steps_nb; +} + +const gchar *ygtk_steps_get_nth_label (YGtkSteps *steps, gint n) +{ + if (n < 0) return NULL; + GtkWidget *step; + GList *children = gtk_container_get_children (GTK_CONTAINER (steps)); + step = g_list_nth_data (children, n); + g_list_free (children); + if (step) + return gtk_label_get_text (GTK_LABEL (step)); + return NULL; +} + +void ygtk_steps_clear (YGtkSteps *steps) +{ + GList *children = gtk_container_get_children (GTK_CONTAINER (steps)), *i; + for (i = children; i; i = i->next) + gtk_container_remove (GTK_CONTAINER (steps), (GtkWidget *) i->data); + g_list_free (children); +} + +static void ygtk_steps_class_init (YGtkStepsClass *klass) +{ + ygtk_steps_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); + widget_class->expose_event = ygtk_steps_expose_event; + + GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); + gtkobject_class->destroy = ygtk_steps_destroy; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtksteps.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,67 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkSteps is a widget that displays a list of steps, useful + to show the progress of some configuration tool as it does + the work of updating files or services or whatever. + + You use the append functions to initializate the steps. If, + for some reason, you want one step to count as more than one + (ie. so that a ygtk_steps_advance() needs to be called more than + once to actually advance for a given step), you may append a + step with the same name of the previous, that they'll be collapsed. + (Internally, we call that the 'strength' of the step.) + + Using GtkLabels, so it should be ATK friendly. +*/ + +#ifndef YGTK_STEPS_H +#define YGTK_STEPS_H +#include <gtk/gtkvbox.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_STEPS (ygtk_steps_get_type ()) +#define YGTK_STEPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_STEPS, YGtkSteps)) +#define YGTK_STEPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_STEPS, YGtkStepsClass)) +#define YGTK_IS_STEPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_STEPS)) +#define YGTK_IS_STEPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_STEPS)) +#define YGTK_STEPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_STEPS, YGtkStepsClass)) + +typedef struct _YGtkSteps +{ + GtkVBox parent; + + // private: + gint current_step; + PangoLayout *check_mark_layout, *current_mark_layout, *todo_mark_layout; + // for current_mark little animation + guint current_mark_timeout_id, current_mark_frame; + +} YGtkSteps; + +typedef struct _YGtkStepsClass +{ + GtkVBoxClass parent_class; +} YGtkStepsClass; + +GtkWidget* ygtk_steps_new (void); +GType ygtk_steps_get_type (void) G_GNUC_CONST; + +gint ygtk_steps_append (YGtkSteps *steps, const gchar *label); +void ygtk_steps_append_heading (YGtkSteps *steps, const gchar *heading); + +void ygtk_steps_set_current (YGtkSteps *steps, gint step); /* -1 = none */ +gint ygtk_steps_total (YGtkSteps *steps); +const gchar *ygtk_steps_get_nth_label (YGtkSteps *steps, gint n); + +void ygtk_steps_clear (YGtkSteps *steps); + +G_END_DECLS + +#endif /* YGTK_STEPS_H */
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,114 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkTextView widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygtktextview.h" +#include <gtk/gtk.h> + +G_DEFINE_TYPE (YGtkTextView, ygtk_text_view, GTK_TYPE_TEXT_VIEW) + +static void ygtk_text_view_init (YGtkTextView *view) +{ +} + +static void ygtk_text_view_realize (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (ygtk_text_view_parent_class)->realize (widget); + + GtkTextView *view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) { + gtk_text_view_set_cursor_visible (view, FALSE); + GdkWindow *window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_TEXT); + gdk_window_set_cursor (window, NULL); + } +} + +// popup utilities +static void copy_activate_cb (GtkMenuItem *item, GtkTextBuffer *buffer) +{ + gtk_text_buffer_copy_clipboard (buffer, gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)); +} +static void select_all_activate_cb (GtkMenuItem *item, GtkTextBuffer *buffer) +{ + GtkTextIter start, end; + gtk_text_buffer_get_bounds (buffer, &start, &end); + gtk_text_buffer_select_range (buffer, &start, &end); +} + +static void ygtk_text_view_populate_popup (GtkTextView *view, GtkMenu *menu) +{ + if (gtk_text_view_get_editable (view)) + return; + GtkTextBuffer *buffer = gtk_text_view_get_buffer (view); + + GList *items = gtk_container_get_children (GTK_CONTAINER (menu)), *i; + for (i = items; i; i = i->next) + gtk_container_remove (GTK_CONTAINER (menu), i->data); + g_list_free (items); + + GtkWidget *item; + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + if (gtk_text_buffer_get_has_selection (buffer)) + g_signal_connect (item, "activate", G_CALLBACK (copy_activate_cb), buffer); + else + gtk_widget_set_sensitive (item, FALSE); + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect (item, "activate", G_CALLBACK (select_all_activate_cb), buffer); + gtk_widget_show_all (GTK_WIDGET (menu)); +} + +static inline gboolean is_space (gunichar ch) +{ return g_unichar_isspace (ch) || ch == 0xfffc; } + +static gboolean ygtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) +{ // on right-click, select word under cursor if there is no selection + if (event->button == 3) { + GtkTextView *view = GTK_TEXT_VIEW (widget); + GtkTextBuffer *buffer = gtk_text_view_get_buffer (view); + if (!gtk_text_buffer_get_has_selection (buffer)) { + gint buffer_x, buffer_y; + gtk_text_view_window_to_buffer_coords (view, + GTK_TEXT_WINDOW_WIDGET, (gint) event->x, (gint) event->y, &buffer_x, &buffer_y); + GtkTextIter iter; + gtk_text_view_get_iter_at_location (view, &iter, buffer_x, buffer_y); + + if (!is_space (gtk_text_iter_get_char (&iter))) { + GtkTextIter start, end = iter, temp = iter; + do { + start = temp; + if (!gtk_text_iter_backward_char (&temp)) + break; + } while (!is_space (gtk_text_iter_get_char (&temp))); + do { + if (!gtk_text_iter_forward_char (&end)) + break; + } while (!is_space (gtk_text_iter_get_char (&end))); + + gtk_text_buffer_select_range (buffer, &start, &end); + } + } + } + return GTK_WIDGET_CLASS (ygtk_text_view_parent_class)->button_press_event (widget, event); +} + +GtkWidget *ygtk_text_view_new (gboolean editable) +{ return g_object_new (YGTK_TYPE_TEXT_VIEW, "editable", editable, NULL); } + +static void ygtk_text_view_class_init (YGtkTextViewClass *klass) +{ + GtkTextViewClass *gtktextview_class = GTK_TEXT_VIEW_CLASS (klass); + gtktextview_class->populate_popup = ygtk_text_view_populate_popup; + + GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass); + gtkwidget_class->realize = ygtk_text_view_realize; + gtkwidget_class->button_press_event = ygtk_text_view_button_press_event; +} +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.h URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.h (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktextview.h Mon Aug 1 09:44:45 2011 @@ -0,0 +1,42 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkTextView polishes GtkTextView a little bit, especially on + read-only mode. +*/ + +#ifndef YGTK_TEXT_VIEW_H +#define YGTK_TEXT_VIEW_H + +#include <gtk/gtktextview.h> +G_BEGIN_DECLS + +#define YGTK_TYPE_TEXT_VIEW (ygtk_text_view_get_type ()) +#define YGTK_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + YGTK_TYPE_TEXT_VIEW, YGtkTextView)) +#define YGTK_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + YGTK_TYPE_TEXT_VIEW, YGtkTextViewClass)) +#define YGTK_IS_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + YGTK_TYPE_TEXT_VIEW)) +#define YGTK_IS_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + YGTK_TYPE_TEXT_VIEW)) +#define YGTK_TEXT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + YGTK_TYPE_TEXT_VIEW, YGtkTextViewClass)) + +typedef struct _YGtkTextView +{ + GtkTextView parent; +} YGtkTextView; + +typedef struct _YGtkTextViewClass +{ + GtkTextViewClass parent_class; +} YGtkTextViewClass; + +GtkWidget* ygtk_text_view_new (gboolean editable); +GType ygtk_text_view_get_type (void) G_GNUC_CONST; + +G_END_DECLS +#endif /*YGTK_SCROLLED_WINDOW_H*/ +
Added: branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktimezonepicker.c URL: http://svn.opensuse.org/viewcvs/yast/branches/SuSE-Code-11-SP2-Branch/gtk/sr... ============================================================================== --- branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktimezonepicker.c (added) +++ branches/SuSE-Code-11-SP2-Branch/gtk/src/ygtktimezonepicker.c Mon Aug 1 09:44:45 2011 @@ -0,0 +1,571 @@ +/******************************************************************** + * YaST2-GTK - http://en.opensuse.org/YaST2-GTK * + ********************************************************************/ + +/* YGtkTimeZonePicker widget */ +// check the header file for information about this widget + +#include <config.h> +#include "ygtktimezonepicker.h" +#include <gtk/gtk.h> +#include <string.h> +#include <math.h> + +static guint zone_clicked_signal; + +// General utilities + +static char *substring (const char *str, int start, int end) +{ + if (end == -1) + return g_strdup (str+start); + return g_strndup (str+start, end-start); +} + +static gdouble convert_pos (const char *pos, int digits) +{ + if (strlen (pos) < 4 || digits > 9) + return 0.0; + + gchar *whole = substring (pos, 0, digits+1); + gchar *fraction = substring (pos, digits+1, -1); + + gdouble t1 = g_strtod (whole, NULL); + gdouble t2 = g_strtod (fraction, NULL); + + int fraction_len = strlen (fraction); + g_free (whole); + g_free (fraction); + + if (t1 >= 0.0) + return t1 + t2/pow (10.0, fraction_len); + else + return t1 - t2/pow (10.0, fraction_len); +} + +static void ygtk_time_zone_picker_set_cursor_stock (YGtkTimeZonePicker *picker, + const gchar *stock) +{ + GdkPixbuf *pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(), + stock, 24, 0, NULL); + if (pixbuf) { + GtkWidget *widget = GTK_WIDGET (picker); + GdkDisplay *display = gtk_widget_get_display (widget); + GdkCursor *cursor = gdk_cursor_new_from_pixbuf (display, pixbuf, 6, 6); + gdk_window_set_cursor (picker->map_window, cursor); + g_object_unref (G_OBJECT (pixbuf)); + } +} + +static void ygtk_time_zone_picker_set_cursor_type (YGtkTimeZonePicker *picker, + GdkCursorType type) +{ + GtkWidget *widget = GTK_WIDGET (picker); + GdkDisplay *display = gtk_widget_get_display (widget); + GdkCursor *cursor = gdk_cursor_new_for_display (display, type); + gdk_window_set_cursor (picker->map_window, cursor); +} + +// Specific utilities + +static void window_to_map (YGtkTimeZonePicker *picker, gint win_x, gint win_y, + gint *map_x, gint *map_y) +{ + int win_width, win_height; + gdk_window_get_size (picker->map_window, &win_width, &win_height); + + *map_x = ((win_x - win_width/2) / picker->scale) + picker->map_x; + *map_y = ((win_y - win_height/2) / picker->scale) + picker->map_y; +} + +static void map_to_window (YGtkTimeZonePicker *picker, gint map_x, gint map_y, + gint *win_x, gint *win_y) +{ + int win_width, win_height; + gdk_window_get_size (picker->map_window, &win_width, &win_height); + + *win_x = ((map_x - picker->map_x) * picker->scale) + win_width/2; + *win_y = ((map_y - picker->map_y) * picker->scale) + win_height/2; +} + +static void coordinates_to_map (YGtkTimeZonePicker *picker, gdouble latitude, gdouble longitude, gint *map_x, gint *map_y) +{ + *map_x = picker->map_width/2 + (picker->map_width/2 * longitude/180); + *map_y = picker->map_height/2 - (picker->map_height/2 * latitude/90); +} + +static YGtkTimeZoneLocation *find_location_closer_to (YGtkTimeZonePicker *picker, + gint win_x, gint win_y) +{ + gint x, y; + window_to_map (picker, win_x, win_y, &x, &y); + + double min_dist = 4000; + YGtkTimeZoneLocation *best = 0; + GList *i; + for (i = picker->locations; i; i = i->next) { + YGtkTimeZoneLocation *loc = i->data; + gdouble dist = pow (loc->x - x, 2) + pow (loc->y - y, 2); + if (dist < min_dist) { + min_dist = dist; + best = loc; + } + } + return best; +} + +// Internal methods + +static void ygtk_time_zone_picker_sync_cursor (YGtkTimeZonePicker *picker) +{ + if (picker->last_mouse_x) + ygtk_time_zone_picker_set_cursor_type (picker, GDK_FLEUR); + else if (picker->closeup) + ygtk_time_zone_picker_set_cursor_type (picker, GDK_CROSS); + else + ygtk_time_zone_picker_set_cursor_stock (picker, GTK_STOCK_ZOOM_IN); +} + +static void ygtk_time_zone_picker_scroll_to (YGtkTimeZonePicker *picker, + gint x, gint y, gboolean animate) +{ + picker->map_x = x; + picker->map_y = y; + gtk_widget_queue_resize (GTK_WIDGET (picker)); +} + +static void ygtk_time_zone_picker_move (YGtkTimeZonePicker *picker, + gint diff_x, gint diff_y) +{ + ygtk_time_zone_picker_scroll_to (picker, picker->map_x + diff_x, + picker->map_y + diff_y, FALSE); +} + +static void ygtk_time_zone_picker_closeup (YGtkTimeZonePicker *picker, gboolean closeup, + gint map_x, gint map_y, gboolean animate) +{ + if (closeup) + ygtk_time_zone_picker_scroll_to (picker, map_x, map_y, animate); + picker->closeup = closeup; + gtk_widget_queue_resize (GTK_WIDGET (picker)); + ygtk_time_zone_picker_sync_cursor (picker); +} + +// Public methods + +static gint compare_locations (gconstpointer pa, gconstpointer pb) +{ + const YGtkTimeZoneLocation *a = pa; + const YGtkTimeZoneLocation *b = pb; + return a->latitude - b->latitude; +} + +void ygtk_time_zone_picker_set_map (YGtkTimeZonePicker *picker, const char *filename, + TimeZoneToName converter_cb, gpointer converter_data) +{ + GError *error = 0; + picker->map_pixbuf = gdk_pixbuf_new_from_file (filename, &error); + if (picker->map_pixbuf) { + picker->map_width = gdk_pixbuf_get_width (picker->map_pixbuf); + picker->map_height = gdk_pixbuf_get_height (picker->map_pixbuf); + } + else { + g_warning ("Couldn't load map: %s\n%s\n", filename, error ? error->message : "(unknown)"); + picker->map_width = 300; picker->map_height = 50; + } + + char buf [4096]; + FILE *tzfile = fopen ("/usr/share/zoneinfo/zone.tab", "r"); + while (fgets (buf, sizeof (buf), tzfile)) { + if (*buf == '#') continue; + + gchar *trim = g_strstrip (buf); + gchar **arr = g_strsplit (trim, "\t", -1); + + int arr_length; + for (arr_length = 0; arr [arr_length]; arr_length++) ; + + YGtkTimeZoneLocation *loc = g_new0 (YGtkTimeZoneLocation, 1); + loc->country = g_strdup (arr[0]); + loc->zone = g_strdup (arr[2]); + if (arr_length > 3) + loc->comment = g_strdup (arr[3]); + const gchar *tooltip = converter_cb (loc->zone, converter_data); + if (tooltip) + loc->tooltip = g_strdup (tooltip); + + int split_i = 1; + while (split_i < strlen (arr[1]) && arr[1][split_i] != '-' && arr[1][split_i] != '+' ) + split_i++; + char *latitude = substring (arr[1], 0, split_i); + char *longitude = substring (arr[1], split_i, -1); + + loc->latitude = convert_pos (latitude, 2); + loc->longitude = convert_pos (longitude, 3); + g_free (latitude); + g_free (longitude); + + coordinates_to_map (picker, loc->latitude, loc->longitude, &loc->x, &loc->y); + + picker->locations = g_list_append (picker->locations, loc); + g_strfreev (arr); + } + fclose (tzfile); + picker->locations = g_list_sort (picker->locations, compare_locations); +} + +const gchar *ygtk_time_zone_picker_get_current_zone (YGtkTimeZonePicker *picker) +{ + if (picker->selected_loc) + return picker->selected_loc->zone; + return NULL; +} + +void ygtk_time_zone_picker_set_current_zone (YGtkTimeZonePicker *picker, + const gchar *zone, gboolean zoom) +{ + if (picker->selected_loc && !strcmp (picker->selected_loc->zone, zone)) + return; + GList *i; + for (i = picker->locations; i; i = i->next) { + YGtkTimeZoneLocation *loc = i->data; + if (!strcmp (loc->zone, zone)) { + picker->selected_loc = loc; + ygtk_time_zone_picker_closeup (picker, zoom, loc->x, loc->y, TRUE); + break; + } + } + gtk_widget_queue_draw (GTK_WIDGET (picker)); +} + +// GTK stuff + +G_DEFINE_TYPE (YGtkTimeZonePicker, ygtk_time_zone_picker, GTK_TYPE_WIDGET) + +static void ygtk_time_zone_picker_init (YGtkTimeZonePicker *picker) +{ + GTK_WIDGET_SET_FLAGS (picker, GTK_NO_WINDOW); +} + +static void ygtk_time_zone_picker_destroy (GtkObject *object) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (object); + if (picker->map_pixbuf) { + g_object_unref (G_OBJECT (picker->map_pixbuf)); + picker->map_pixbuf = NULL; + } + if (picker->locations) { + GList *i; + for (i = picker->locations; i; i = i->next) { + YGtkTimeZoneLocation *loc = i->data; + g_free (loc->country); + g_free (loc->zone); + g_free (loc->comment); + g_free (loc->tooltip); + g_free (loc); + } + g_list_free (picker->locations); + picker->locations = NULL; + } + GTK_OBJECT_CLASS (ygtk_time_zone_picker_parent_class)->destroy (object); +} + +static void ygtk_time_zone_picker_realize (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (ygtk_time_zone_picker_parent_class)->realize (widget); + + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + + GdkWindowAttr attributes; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= + (GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); + gint attributes_mask; + attributes_mask = GDK_WA_X | GDK_WA_Y; + picker->map_window = gdk_window_new (widget->window, + &attributes, attributes_mask); + gdk_window_set_user_data (picker->map_window, widget); + gtk_style_set_background (widget->style, picker->map_window, GTK_STATE_NORMAL); + + ygtk_time_zone_picker_closeup (picker, FALSE, 0, 0, FALSE); +} + +static void ygtk_time_zone_picker_unrealize (GtkWidget *widget) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (picker->map_window) { + gdk_window_set_user_data (picker->map_window, NULL); + gdk_window_destroy (picker->map_window); + picker->map_window = NULL; + } + GTK_WIDGET_CLASS (ygtk_time_zone_picker_parent_class)->unrealize (widget); +} + +static void ygtk_time_zone_picker_map (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (ygtk_time_zone_picker_parent_class)->map (widget); + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (picker->map_window) + gdk_window_show (picker->map_window); +} + +static void ygtk_time_zone_picker_unmap (GtkWidget *widget) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (picker->map_window) + gdk_window_hide (picker->map_window); + GTK_WIDGET_CLASS (ygtk_time_zone_picker_parent_class)->unmap (widget); +} + +static gboolean ygtk_time_zone_picker_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (event->window == picker->map_window) { + if (picker->scale == 1) { + YGtkTimeZoneLocation *loc; + loc = find_location_closer_to (picker, event->x, event->y); + if (picker->hover_loc != loc) { + picker->hover_loc = loc; + gtk_widget_queue_draw (widget); + } + } + if (picker->last_mouse_x) { + ygtk_time_zone_picker_move (picker, picker->last_mouse_x - event->x, + picker->last_mouse_y - event->y); + picker->last_mouse_x = event->x; + picker->last_mouse_y = event->y; + ygtk_time_zone_picker_sync_cursor (picker); + } + } + return FALSE; +} + +static gboolean ygtk_time_zone_picker_leave_notify_event (GtkWidget *widget, + GdkEventCrossing *event) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (picker->hover_loc) { + picker->hover_loc = NULL; + gtk_widget_queue_draw (widget); + } + return FALSE; +} + +static gboolean ygtk_time_zone_picker_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (event->window == picker->map_window) { + if (event->button == 1) { + if (picker->scale == 1) { + YGtkTimeZoneLocation *loc; + loc = find_location_closer_to (picker, event->x, event->y); + if (loc && loc != picker->selected_loc) { + picker->selected_loc = loc; + g_signal_emit (picker, zone_clicked_signal, 0, picker->selected_loc->zone); + } + picker->last_mouse_x = event->x; + picker->last_mouse_y = event->y; + } + else { + int map_x, map_y; + window_to_map (picker, event->x, event->y, &map_x, &map_y); + ygtk_time_zone_picker_closeup (picker, TRUE, map_x, map_y, TRUE); + } + } + else if (event->button == 3) + ygtk_time_zone_picker_closeup (picker, FALSE, 0, 0, TRUE); + else + return FALSE; + gtk_widget_queue_draw (widget); + } + return FALSE; +} + +static gboolean ygtk_time_zone_picker_button_release_event (GtkWidget *widget, + GdkEventButton *event) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + picker->last_mouse_x = 0; + ygtk_time_zone_picker_sync_cursor (picker); + return FALSE; +} + +static void ygtk_time_zone_picker_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + requisition->width = 600; + requisition->height = 300; + GTK_WIDGET_CLASS (ygtk_time_zone_picker_parent_class)->size_request (widget, requisition); +} + +static void ygtk_time_zone_picker_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + if (!GTK_WIDGET_REALIZED (widget)) + return; + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + int win_width = allocation->width, win_height = allocation->height; + + if (picker->closeup) + picker->scale = 1; + else { + picker->scale = MIN ((double) win_width / picker->map_width, + (double) win_height / picker->map_height); + picker->hover_loc = NULL; + } + + int map_win_width = picker->map_width * picker->scale; + int map_win_height = picker->map_height * picker->scale; + + int x = 0, y = 0, w, h; + x = MAX (0, (win_width - map_win_width) / 2) + allocation->x; + y = MAX (0, (win_height - map_win_height) / 2) + allocation->y; + w = MIN (win_width, map_win_width); + h = MIN (win_height, map_win_height); + + // make sure it clumps to the new window size... + picker->map_x = MIN (MAX (picker->map_x, (w/2)/picker->scale), + picker->map_width - (w/2)/picker->scale); + picker->map_y = MIN (MAX (picker->map_y, (h/2)/picker->scale), + picker->map_height - (h/2)/picker->scale); + + gdk_window_move_resize (picker->map_window, x, y, w, h); + GTK_WIDGET_CLASS (ygtk_time_zone_picker_parent_class)->size_allocate + (widget, allocation); +} + +static gboolean ygtk_time_zone_picker_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + YGtkTimeZonePicker *picker = YGTK_TIME_ZONE_PICKER (widget); + if (event->window != picker->map_window) + return FALSE; + + cairo_t *cr = gdk_cairo_create (event->window); + int width, height; + gdk_window_get_size (event->window, &width, &height); + + if (!picker->map_pixbuf) { + // show alt text if no image was loaded + PangoLayout *layout; + layout = gtk_widget_create_pango_layout (widget, + "Timezone map could not be found.\nVerify the integrity of the yast2-theme-* package."); + cairo_move_to (cr, 10, 10); + pango_cairo_show_layout (cr, layout); + g_object_unref (layout); + goto cleanup; + } + + gdk_cairo_set_source_pixbuf (cr, picker->map_pixbuf, 0, 0); + cairo_matrix_t matrix; + cairo_matrix_init_translate (&matrix, picker->map_x - (width/2)/picker->scale, + picker->map_y - (height/2)/picker->scale); + cairo_matrix_scale (&matrix, 1/picker->scale, 1/picker->scale); + cairo_pattern_set_matrix (cairo_get_source (cr), &matrix); + + cairo_rectangle (cr, 0, 0, width, height); + cairo_fill (cr); + + GList *i; + for (i = picker->locations; i; i = i->next) { + YGtkTimeZoneLocation *loc = i->data; + int x, y; + map_to_window (picker, loc->x, loc->y, &x, &y); + int radius = (picker->scale == 1) ? 3 : 0; + + if (loc == picker->selected_loc) { + cairo_set_source_rgb (cr, 232/255.0, 66/255.0, 66/255.0); + radius = 3; + } + else if (loc == picker->hover_loc) + cairo_set_source_rgb (cr, 255/255.0, 255/255.0, 96/255.0); + else + cairo_set_source_rgb (cr, 192/255.0, 112/255.0, 160/255.0); + + if (radius) { + cairo_arc (cr, x-1, y-1, radius, 0, M_PI*2); + if (radius > 1) { + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 90/255.0, 90/255.0, 90/255.0); + cairo_set_line_width (cr, 1.0); + cairo_stroke (cr); + } + else + cairo_fill (cr); + } + } + + YGtkTimeZoneLocation *label_loc = picker->hover_loc; + if (!label_loc) + label_loc = picker->selected_loc; + if (label_loc) { + const char *text = label_loc->tooltip; + if (!text) { + text = label_loc->country; + if (!text) + text = label_loc->zone; + } + + PangoLayout *layout; + layout = gtk_widget_create_pango_layout (widget, text); + + int x, y; + map_to_window (picker, label_loc->x, label_loc->y, &x, &y); + x += 11; y += 4; + int fw; + pango_layout_get_pixel_size (layout, &fw, NULL); + x = MAX (MIN (x, width - fw - 5), x-11-fw); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_move_to (cr, x, y); + pango_cairo_show_layout (cr, layout); + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_move_to (cr, x-1, y-1); + pango_cairo_show_layout (cr, layout); + g_object_unref (G_OBJECT (layout)); + cairo_new_path (cr); + } + +cleanup: + cairo_destroy (cr); + gtk_paint_shadow (widget->style, event->window, GTK_STATE_NORMAL, + GTK_SHADOW_IN, &event->area, widget, "frame", 0, 0, width, height); + return TRUE; +} + +static void ygtk_time_zone_picker_class_init (YGtkTimeZonePickerClass *klass) +{ + ygtk_time_zone_picker_parent_class = g_type_class_peek_parent (klass); + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + widget_class->realize = ygtk_time_zone_picker_realize; + widget_class->unrealize = ygtk_time_zone_picker_unrealize; + widget_class->map = ygtk_time_zone_picker_map; + widget_class->unmap = ygtk_time_zone_picker_unmap; + widget_class->expose_event = ygtk_time_zone_picker_expose_event; + widget_class->size_request = ygtk_time_zone_picker_size_request; + widget_class->size_allocate = ygtk_time_zone_picker_size_allocate; + widget_class->button_press_event = ygtk_time_zone_picker_button_press_event; + widget_class->button_release_event = ygtk_time_zone_picker_button_release_event; + widget_class->motion_notify_event = ygtk_time_zone_picker_motion_notify_event; + widget_class->leave_notify_event = ygtk_time_zone_picker_leave_notify_event; + + GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); + gtkobject_class->destroy = ygtk_time_zone_picker_destroy; + + zone_clicked_signal = g_signal_new ("zone_clicked", + G_TYPE_FROM_CLASS (G_OB