From 0b0e6bc987da4fd88a7854ebb12bde705e92c428 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 21:51:44 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- capplets/Makefile.am | 31 + capplets/about-me/AUTHORS | 2 + capplets/about-me/Makefile.am | 59 + capplets/about-me/e-image-chooser.c | 455 ++++ capplets/about-me/e-image-chooser.h | 66 + capplets/about-me/eel-alert-dialog.c | 466 ++++ capplets/about-me/eel-alert-dialog.h | 61 + capplets/about-me/eel-gtk-macros.h | 178 ++ capplets/about-me/fingerprint-strings.h | 111 + capplets/about-me/fprintd-marshal.list | 1 + capplets/about-me/icons/Makefile.am | 30 + capplets/about-me/icons/left-index-finger.png | Bin 0 -> 1515 bytes capplets/about-me/icons/left-index-finger.svg | 177 ++ capplets/about-me/icons/left-little-finger.png | Bin 0 -> 1500 bytes capplets/about-me/icons/left-little-finger.svg | 180 ++ capplets/about-me/icons/left-middle-finger.png | Bin 0 -> 1483 bytes capplets/about-me/icons/left-middle-finger.svg | 180 ++ capplets/about-me/icons/left-ring-finger.png | Bin 0 -> 1512 bytes capplets/about-me/icons/left-ring-finger.svg | 180 ++ capplets/about-me/icons/left-thumb.png | Bin 0 -> 1512 bytes capplets/about-me/icons/left-thumb.svg | 180 ++ capplets/about-me/icons/print_error.png | Bin 0 -> 4160 bytes capplets/about-me/icons/print_error.svg | 525 ++++ capplets/about-me/icons/print_ok.png | Bin 0 -> 3677 bytes capplets/about-me/icons/print_ok.svg | 310 +++ capplets/about-me/icons/right-index-finger.png | Bin 0 -> 1506 bytes capplets/about-me/icons/right-index-finger.svg | 179 ++ capplets/about-me/icons/right-little-finger.png | Bin 0 -> 1479 bytes capplets/about-me/icons/right-little-finger.svg | 182 ++ capplets/about-me/icons/right-middle-finger.png | Bin 0 -> 1468 bytes capplets/about-me/icons/right-middle-finger.svg | 182 ++ capplets/about-me/icons/right-ring-finger.png | Bin 0 -> 1506 bytes capplets/about-me/icons/right-ring-finger.svg | 182 ++ capplets/about-me/icons/right-thumb.png | Bin 0 -> 1486 bytes capplets/about-me/icons/right-thumb.svg | 182 ++ capplets/about-me/mate-about-me-dialog.ui | 1622 ++++++++++++ capplets/about-me/mate-about-me-fingerprint.c | 624 +++++ capplets/about-me/mate-about-me-fingerprint.h | 27 + capplets/about-me/mate-about-me-fingerprint.ui | 276 +++ capplets/about-me/mate-about-me-password.c | 1136 +++++++++ capplets/about-me/mate-about-me-password.h | 9 + capplets/about-me/mate-about-me-password.ui | 319 +++ capplets/about-me/mate-about-me.c | 1005 ++++++++ capplets/about-me/mate-about-me.desktop.in.in | 14 + capplets/accessibility/Makefile.am | 3 + capplets/accessibility/at-properties/Makefile.am | 31 + capplets/accessibility/at-properties/Makefile.in | 753 ++++++ .../at-properties/at-enable-dialog.ui | 351 +++ .../at-properties/at-properties.desktop.in.in | 14 + .../accessibility/at-properties/at-startup.png | Bin 0 -> 2879 bytes .../accessibility/at-properties/at-support.png | Bin 0 -> 2991 bytes capplets/accessibility/at-properties/main.c | 293 +++ capplets/appearance/Makefile.am | 58 + capplets/appearance/appearance-desktop.c | 1400 +++++++++++ capplets/appearance/appearance-desktop.h | 22 + capplets/appearance/appearance-font.c | 961 ++++++++ capplets/appearance/appearance-font.h | 22 + capplets/appearance/appearance-main.c | 218 ++ capplets/appearance/appearance-style.c | 1073 ++++++++ capplets/appearance/appearance-style.h | 22 + capplets/appearance/appearance-themes.c | 1179 +++++++++ capplets/appearance/appearance-themes.h | 22 + capplets/appearance/appearance.h | 90 + capplets/appearance/data/Makefile.am | 62 + capplets/appearance/data/Makefile.in | 660 +++++ capplets/appearance/data/appearance.ui | 2603 ++++++++++++++++++++ capplets/appearance/data/cursor-large-white.pcf | Bin 0 -> 18636 bytes capplets/appearance/data/cursor-large.pcf | Bin 0 -> 17432 bytes capplets/appearance/data/cursor-white.pcf | Bin 0 -> 13848 bytes .../appearance/data/gtk-theme-thumbnailing.png | Bin 0 -> 1764 bytes .../appearance/data/icon-theme-thumbnailing.png | Bin 0 -> 1167 bytes .../data/mate-appearance-properties.desktop.in.in | 14 + .../data/mate-theme-installer.desktop.in.in | 16 + capplets/appearance/data/mate-theme-package.xml.in | 9 + .../appearance/data/mouse-cursor-normal-large.png | Bin 0 -> 251 bytes capplets/appearance/data/mouse-cursor-normal.png | Bin 0 -> 241 bytes .../appearance/data/mouse-cursor-white-large.png | Bin 0 -> 268 bytes capplets/appearance/data/mouse-cursor-white.png | Bin 0 -> 221 bytes capplets/appearance/data/subpixel-bgr.png | Bin 0 -> 125 bytes capplets/appearance/data/subpixel-rgb.png | Bin 0 -> 125 bytes capplets/appearance/data/subpixel-vbgr.png | Bin 0 -> 138 bytes capplets/appearance/data/subpixel-vrgb.png | Bin 0 -> 138 bytes capplets/appearance/data/theme-thumbnailing.png | Bin 0 -> 4482 bytes .../appearance/data/window-theme-thumbnailing.png | Bin 0 -> 2183 bytes capplets/appearance/mate-wp-info.c | 87 + capplets/appearance/mate-wp-info.h | 45 + capplets/appearance/mate-wp-item.c | 307 +++ capplets/appearance/mate-wp-item.h | 91 + capplets/appearance/mate-wp-xml.c | 451 ++++ capplets/appearance/mate-wp-xml.h | 28 + capplets/appearance/theme-installer.c | 801 ++++++ capplets/appearance/theme-installer.h | 28 + capplets/appearance/theme-save.c | 381 +++ capplets/appearance/theme-save.h | 22 + capplets/appearance/theme-util.c | 263 ++ capplets/appearance/theme-util.h | 63 + capplets/common/Makefile.am | 63 + capplets/common/activate-settings-daemon.c | 60 + capplets/common/activate-settings-daemon.h | 9 + capplets/common/capplet-stock-icons.c | 101 + capplets/common/capplet-stock-icons.h | 66 + capplets/common/capplet-util.c | 205 ++ capplets/common/capplet-util.h | 46 + capplets/common/file-transfer-dialog.c | 608 +++++ capplets/common/file-transfer-dialog.h | 73 + capplets/common/gtkrc-utils.c | 256 ++ capplets/common/gtkrc-utils.h | 25 + capplets/common/mate-theme-apply.c | 136 + capplets/common/mate-theme-apply.h | 33 + capplets/common/mate-theme-info.c | 1988 +++++++++++++++ capplets/common/mate-theme-info.h | 190 ++ capplets/common/mate-theme-test.c | 134 + capplets/common/mateconf-property-editor-marshal.c | 41 + capplets/common/mateconf-property-editor-marshal.h | 19 + capplets/common/mateconf-property-editor.c | 1801 ++++++++++++++ capplets/common/mateconf-property-editor.h | 162 ++ capplets/common/theme-thumbnail.c | 1144 +++++++++ capplets/common/theme-thumbnail.h | 37 + capplets/common/wm-common.c | 184 ++ capplets/common/wm-common.h | 17 + capplets/default-applications/Makefile.am | 75 + .../default-applications.desktop.in.in | 14 + .../preferences-desktop-default-applications.png | Bin 0 -> 748 bytes .../preferences-desktop-default-applications.png | Bin 0 -> 1310 bytes .../preferences-desktop-default-applications.png | Bin 0 -> 1340 bytes .../preferences-desktop-default-applications.png | Bin 0 -> 1727 bytes .../preferences-desktop-default-applications.png | Bin 0 -> 2825 bytes .../default-applications/mate-at-commandline.in.in | 101 + .../mate-at-session.desktop.in.in | 15 + capplets/default-applications/mate-da-capplet.c | 970 ++++++++ capplets/default-applications/mate-da-capplet.h | 139 ++ capplets/default-applications/mate-da-item.c | 148 ++ capplets/default-applications/mate-da-item.h | 81 + capplets/default-applications/mate-da-xml.c | 327 +++ capplets/default-applications/mate-da-xml.h | 27 + .../mate-default-applications-properties.ui | 1518 ++++++++++++ .../mate-default-applications.pc.in | 10 + .../mate-default-applications.xml.in | 483 ++++ capplets/display/Makefile.am | 78 + capplets/display/TODO | 837 +++++++ capplets/display/display-capplet.ui | 437 ++++ capplets/display/display-properties.desktop.in.in | 14 + capplets/display/foo-marshal.c | 279 +++ capplets/display/foo-marshal.h | 67 + .../16x16/mate-preferences-desktop-display.png | Bin 0 -> 613 bytes .../22x22/mate-preferences-desktop-display.png | Bin 0 -> 866 bytes .../24x24/mate-preferences-desktop-display.png | Bin 0 -> 909 bytes .../32x32/mate-preferences-desktop-display.png | Bin 0 -> 1602 bytes .../scalable/mate-preferences-desktop-display.svg | 470 ++++ .../mate-display-properties-install-systemwide.c | 272 ++ capplets/display/org.mate.randr.policy.in | 29 + capplets/display/scrollarea.c | 1944 +++++++++++++++ capplets/display/scrollarea.h | 124 + capplets/display/xrandr-capplet.c | 2571 +++++++++++++++++++ capplets/keybindings/00-multimedia-key.xml.in | 35 + capplets/keybindings/01-desktop-key.xml.in | 29 + capplets/keybindings/Makefile.am | 46 + capplets/keybindings/eggaccelerators.c | 632 +++++ capplets/keybindings/eggaccelerators.h | 99 + capplets/keybindings/eggcellrendererkeys.c | 695 ++++++ capplets/keybindings/eggcellrendererkeys.h | 93 + capplets/keybindings/mate-keybinding-properties.c | 1948 +++++++++++++++ capplets/keybindings/mate-keybinding-properties.ui | 301 +++ capplets/keybindings/mate-keybinding.desktop.in.in | 14 + capplets/keybindings/mate-keybindings.pc.in | 10 + capplets/keyboard/Makefile.am | 42 + capplets/keyboard/keyboard.desktop.in.in | 14 + .../mate-keyboard-properties-a11y-notifications.ui | 526 ++++ capplets/keyboard/mate-keyboard-properties-a11y.c | 325 +++ capplets/keyboard/mate-keyboard-properties-a11y.h | 32 + .../keyboard/mate-keyboard-properties-dialog.ui | 1857 ++++++++++++++ .../mate-keyboard-properties-layout-chooser.ui | 315 +++ .../mate-keyboard-properties-model-chooser.ui | 142 ++ .../mate-keyboard-properties-options-dialog.ui | 94 + capplets/keyboard/mate-keyboard-properties-xkb.c | 254 ++ capplets/keyboard/mate-keyboard-properties-xkb.h | 104 + capplets/keyboard/mate-keyboard-properties-xkblt.c | 475 ++++ .../keyboard/mate-keyboard-properties-xkbltadd.c | 561 +++++ capplets/keyboard/mate-keyboard-properties-xkbmc.c | 347 +++ capplets/keyboard/mate-keyboard-properties-xkbot.c | 516 ++++ capplets/keyboard/mate-keyboard-properties-xkbpv.c | 136 + capplets/keyboard/mate-keyboard-properties.c | 265 ++ capplets/mouse/Makefile.am | 35 + capplets/mouse/double-click-maybe.png | Bin 0 -> 4643 bytes capplets/mouse/double-click-off.png | Bin 0 -> 4751 bytes capplets/mouse/double-click-on.png | Bin 0 -> 6360 bytes capplets/mouse/mate-mouse-accessibility.c | 232 ++ capplets/mouse/mate-mouse-accessibility.h | 33 + capplets/mouse/mate-mouse-properties.c | 647 +++++ capplets/mouse/mate-mouse-properties.ui | 1707 +++++++++++++ capplets/mouse/mate-settings-mouse.desktop.in.in | 14 + capplets/network/Makefile.am | 50 + .../icons/16x16/mate-network-properties.png | Bin 0 -> 824 bytes .../icons/22x22/mate-network-properties.png | Bin 0 -> 1081 bytes .../icons/24x24/mate-network-properties.png | Bin 0 -> 1081 bytes .../icons/32x32/mate-network-properties.png | Bin 0 -> 1855 bytes .../icons/48x48/mate-network-properties.png | Bin 0 -> 3191 bytes .../icons/scalable/mate-network-properties.svg | 628 +++++ capplets/network/mate-network-properties.c | 1397 +++++++++++ .../network/mate-network-properties.desktop.in.in | 14 + capplets/network/mate-network-properties.ui | 1056 ++++++++ capplets/windows/Makefile.am | 32 + capplets/windows/mate-window-properties.c | 636 +++++ capplets/windows/mate-window-properties.ui | 387 +++ capplets/windows/window-properties.desktop.in.in | 14 + 205 files changed, 56791 insertions(+) create mode 100644 capplets/Makefile.am create mode 100644 capplets/about-me/AUTHORS create mode 100644 capplets/about-me/Makefile.am create mode 100644 capplets/about-me/e-image-chooser.c create mode 100644 capplets/about-me/e-image-chooser.h create mode 100644 capplets/about-me/eel-alert-dialog.c create mode 100644 capplets/about-me/eel-alert-dialog.h create mode 100644 capplets/about-me/eel-gtk-macros.h create mode 100644 capplets/about-me/fingerprint-strings.h create mode 100644 capplets/about-me/fprintd-marshal.list create mode 100644 capplets/about-me/icons/Makefile.am create mode 100644 capplets/about-me/icons/left-index-finger.png create mode 100644 capplets/about-me/icons/left-index-finger.svg create mode 100644 capplets/about-me/icons/left-little-finger.png create mode 100644 capplets/about-me/icons/left-little-finger.svg create mode 100644 capplets/about-me/icons/left-middle-finger.png create mode 100644 capplets/about-me/icons/left-middle-finger.svg create mode 100644 capplets/about-me/icons/left-ring-finger.png create mode 100644 capplets/about-me/icons/left-ring-finger.svg create mode 100644 capplets/about-me/icons/left-thumb.png create mode 100644 capplets/about-me/icons/left-thumb.svg create mode 100644 capplets/about-me/icons/print_error.png create mode 100644 capplets/about-me/icons/print_error.svg create mode 100644 capplets/about-me/icons/print_ok.png create mode 100644 capplets/about-me/icons/print_ok.svg create mode 100644 capplets/about-me/icons/right-index-finger.png create mode 100644 capplets/about-me/icons/right-index-finger.svg create mode 100644 capplets/about-me/icons/right-little-finger.png create mode 100644 capplets/about-me/icons/right-little-finger.svg create mode 100644 capplets/about-me/icons/right-middle-finger.png create mode 100644 capplets/about-me/icons/right-middle-finger.svg create mode 100644 capplets/about-me/icons/right-ring-finger.png create mode 100644 capplets/about-me/icons/right-ring-finger.svg create mode 100644 capplets/about-me/icons/right-thumb.png create mode 100644 capplets/about-me/icons/right-thumb.svg create mode 100644 capplets/about-me/mate-about-me-dialog.ui create mode 100644 capplets/about-me/mate-about-me-fingerprint.c create mode 100644 capplets/about-me/mate-about-me-fingerprint.h create mode 100644 capplets/about-me/mate-about-me-fingerprint.ui create mode 100644 capplets/about-me/mate-about-me-password.c create mode 100644 capplets/about-me/mate-about-me-password.h create mode 100644 capplets/about-me/mate-about-me-password.ui create mode 100644 capplets/about-me/mate-about-me.c create mode 100644 capplets/about-me/mate-about-me.desktop.in.in create mode 100644 capplets/accessibility/Makefile.am create mode 100644 capplets/accessibility/at-properties/Makefile.am create mode 100644 capplets/accessibility/at-properties/Makefile.in create mode 100644 capplets/accessibility/at-properties/at-enable-dialog.ui create mode 100644 capplets/accessibility/at-properties/at-properties.desktop.in.in create mode 100644 capplets/accessibility/at-properties/at-startup.png create mode 100644 capplets/accessibility/at-properties/at-support.png create mode 100644 capplets/accessibility/at-properties/main.c create mode 100644 capplets/appearance/Makefile.am create mode 100644 capplets/appearance/appearance-desktop.c create mode 100644 capplets/appearance/appearance-desktop.h create mode 100644 capplets/appearance/appearance-font.c create mode 100644 capplets/appearance/appearance-font.h create mode 100644 capplets/appearance/appearance-main.c create mode 100644 capplets/appearance/appearance-style.c create mode 100644 capplets/appearance/appearance-style.h create mode 100644 capplets/appearance/appearance-themes.c create mode 100644 capplets/appearance/appearance-themes.h create mode 100644 capplets/appearance/appearance.h create mode 100644 capplets/appearance/data/Makefile.am create mode 100644 capplets/appearance/data/Makefile.in create mode 100644 capplets/appearance/data/appearance.ui create mode 100644 capplets/appearance/data/cursor-large-white.pcf create mode 100644 capplets/appearance/data/cursor-large.pcf create mode 100644 capplets/appearance/data/cursor-white.pcf create mode 100644 capplets/appearance/data/gtk-theme-thumbnailing.png create mode 100644 capplets/appearance/data/icon-theme-thumbnailing.png create mode 100644 capplets/appearance/data/mate-appearance-properties.desktop.in.in create mode 100644 capplets/appearance/data/mate-theme-installer.desktop.in.in create mode 100644 capplets/appearance/data/mate-theme-package.xml.in create mode 100644 capplets/appearance/data/mouse-cursor-normal-large.png create mode 100644 capplets/appearance/data/mouse-cursor-normal.png create mode 100644 capplets/appearance/data/mouse-cursor-white-large.png create mode 100644 capplets/appearance/data/mouse-cursor-white.png create mode 100644 capplets/appearance/data/subpixel-bgr.png create mode 100644 capplets/appearance/data/subpixel-rgb.png create mode 100644 capplets/appearance/data/subpixel-vbgr.png create mode 100644 capplets/appearance/data/subpixel-vrgb.png create mode 100644 capplets/appearance/data/theme-thumbnailing.png create mode 100644 capplets/appearance/data/window-theme-thumbnailing.png create mode 100644 capplets/appearance/mate-wp-info.c create mode 100644 capplets/appearance/mate-wp-info.h create mode 100644 capplets/appearance/mate-wp-item.c create mode 100644 capplets/appearance/mate-wp-item.h create mode 100644 capplets/appearance/mate-wp-xml.c create mode 100644 capplets/appearance/mate-wp-xml.h create mode 100644 capplets/appearance/theme-installer.c create mode 100644 capplets/appearance/theme-installer.h create mode 100644 capplets/appearance/theme-save.c create mode 100644 capplets/appearance/theme-save.h create mode 100644 capplets/appearance/theme-util.c create mode 100644 capplets/appearance/theme-util.h create mode 100644 capplets/common/Makefile.am create mode 100644 capplets/common/activate-settings-daemon.c create mode 100644 capplets/common/activate-settings-daemon.h create mode 100644 capplets/common/capplet-stock-icons.c create mode 100644 capplets/common/capplet-stock-icons.h create mode 100644 capplets/common/capplet-util.c create mode 100644 capplets/common/capplet-util.h create mode 100644 capplets/common/file-transfer-dialog.c create mode 100644 capplets/common/file-transfer-dialog.h create mode 100644 capplets/common/gtkrc-utils.c create mode 100644 capplets/common/gtkrc-utils.h create mode 100644 capplets/common/mate-theme-apply.c create mode 100644 capplets/common/mate-theme-apply.h create mode 100644 capplets/common/mate-theme-info.c create mode 100644 capplets/common/mate-theme-info.h create mode 100644 capplets/common/mate-theme-test.c create mode 100644 capplets/common/mateconf-property-editor-marshal.c create mode 100644 capplets/common/mateconf-property-editor-marshal.h create mode 100644 capplets/common/mateconf-property-editor.c create mode 100644 capplets/common/mateconf-property-editor.h create mode 100644 capplets/common/theme-thumbnail.c create mode 100644 capplets/common/theme-thumbnail.h create mode 100644 capplets/common/wm-common.c create mode 100644 capplets/common/wm-common.h create mode 100644 capplets/default-applications/Makefile.am create mode 100644 capplets/default-applications/default-applications.desktop.in.in create mode 100644 capplets/default-applications/icons/16x16/preferences-desktop-default-applications.png create mode 100644 capplets/default-applications/icons/22x22/preferences-desktop-default-applications.png create mode 100644 capplets/default-applications/icons/24x24/preferences-desktop-default-applications.png create mode 100644 capplets/default-applications/icons/32x32/preferences-desktop-default-applications.png create mode 100644 capplets/default-applications/icons/48x48/preferences-desktop-default-applications.png create mode 100644 capplets/default-applications/mate-at-commandline.in.in create mode 100644 capplets/default-applications/mate-at-session.desktop.in.in create mode 100644 capplets/default-applications/mate-da-capplet.c create mode 100644 capplets/default-applications/mate-da-capplet.h create mode 100644 capplets/default-applications/mate-da-item.c create mode 100644 capplets/default-applications/mate-da-item.h create mode 100644 capplets/default-applications/mate-da-xml.c create mode 100644 capplets/default-applications/mate-da-xml.h create mode 100644 capplets/default-applications/mate-default-applications-properties.ui create mode 100644 capplets/default-applications/mate-default-applications.pc.in create mode 100644 capplets/default-applications/mate-default-applications.xml.in create mode 100644 capplets/display/Makefile.am create mode 100644 capplets/display/TODO create mode 100644 capplets/display/display-capplet.ui create mode 100644 capplets/display/display-properties.desktop.in.in create mode 100644 capplets/display/foo-marshal.c create mode 100644 capplets/display/foo-marshal.h create mode 100644 capplets/display/icons/16x16/mate-preferences-desktop-display.png create mode 100644 capplets/display/icons/22x22/mate-preferences-desktop-display.png create mode 100644 capplets/display/icons/24x24/mate-preferences-desktop-display.png create mode 100644 capplets/display/icons/32x32/mate-preferences-desktop-display.png create mode 100644 capplets/display/icons/scalable/mate-preferences-desktop-display.svg create mode 100644 capplets/display/mate-display-properties-install-systemwide.c create mode 100644 capplets/display/org.mate.randr.policy.in create mode 100644 capplets/display/scrollarea.c create mode 100644 capplets/display/scrollarea.h create mode 100644 capplets/display/xrandr-capplet.c create mode 100644 capplets/keybindings/00-multimedia-key.xml.in create mode 100644 capplets/keybindings/01-desktop-key.xml.in create mode 100644 capplets/keybindings/Makefile.am create mode 100644 capplets/keybindings/eggaccelerators.c create mode 100644 capplets/keybindings/eggaccelerators.h create mode 100644 capplets/keybindings/eggcellrendererkeys.c create mode 100644 capplets/keybindings/eggcellrendererkeys.h create mode 100644 capplets/keybindings/mate-keybinding-properties.c create mode 100644 capplets/keybindings/mate-keybinding-properties.ui create mode 100644 capplets/keybindings/mate-keybinding.desktop.in.in create mode 100644 capplets/keybindings/mate-keybindings.pc.in create mode 100644 capplets/keyboard/Makefile.am create mode 100644 capplets/keyboard/keyboard.desktop.in.in create mode 100644 capplets/keyboard/mate-keyboard-properties-a11y-notifications.ui create mode 100644 capplets/keyboard/mate-keyboard-properties-a11y.c create mode 100644 capplets/keyboard/mate-keyboard-properties-a11y.h create mode 100644 capplets/keyboard/mate-keyboard-properties-dialog.ui create mode 100644 capplets/keyboard/mate-keyboard-properties-layout-chooser.ui create mode 100644 capplets/keyboard/mate-keyboard-properties-model-chooser.ui create mode 100644 capplets/keyboard/mate-keyboard-properties-options-dialog.ui create mode 100644 capplets/keyboard/mate-keyboard-properties-xkb.c create mode 100644 capplets/keyboard/mate-keyboard-properties-xkb.h create mode 100644 capplets/keyboard/mate-keyboard-properties-xkblt.c create mode 100644 capplets/keyboard/mate-keyboard-properties-xkbltadd.c create mode 100644 capplets/keyboard/mate-keyboard-properties-xkbmc.c create mode 100644 capplets/keyboard/mate-keyboard-properties-xkbot.c create mode 100644 capplets/keyboard/mate-keyboard-properties-xkbpv.c create mode 100644 capplets/keyboard/mate-keyboard-properties.c create mode 100644 capplets/mouse/Makefile.am create mode 100644 capplets/mouse/double-click-maybe.png create mode 100644 capplets/mouse/double-click-off.png create mode 100644 capplets/mouse/double-click-on.png create mode 100644 capplets/mouse/mate-mouse-accessibility.c create mode 100644 capplets/mouse/mate-mouse-accessibility.h create mode 100644 capplets/mouse/mate-mouse-properties.c create mode 100644 capplets/mouse/mate-mouse-properties.ui create mode 100644 capplets/mouse/mate-settings-mouse.desktop.in.in create mode 100644 capplets/network/Makefile.am create mode 100644 capplets/network/icons/16x16/mate-network-properties.png create mode 100644 capplets/network/icons/22x22/mate-network-properties.png create mode 100644 capplets/network/icons/24x24/mate-network-properties.png create mode 100644 capplets/network/icons/32x32/mate-network-properties.png create mode 100644 capplets/network/icons/48x48/mate-network-properties.png create mode 100644 capplets/network/icons/scalable/mate-network-properties.svg create mode 100644 capplets/network/mate-network-properties.c create mode 100644 capplets/network/mate-network-properties.desktop.in.in create mode 100644 capplets/network/mate-network-properties.ui create mode 100644 capplets/windows/Makefile.am create mode 100644 capplets/windows/mate-window-properties.c create mode 100644 capplets/windows/mate-window-properties.ui create mode 100644 capplets/windows/window-properties.desktop.in.in (limited to 'capplets') diff --git a/capplets/Makefile.am b/capplets/Makefile.am new file mode 100644 index 00000000..004d00fd --- /dev/null +++ b/capplets/Makefile.am @@ -0,0 +1,31 @@ +SUBDIRS = \ + common \ + accessibility \ + appearance \ + default-applications \ + display \ + keybindings \ + keyboard \ + mouse \ + network \ + windows + +DIST_SUBDIRS = \ + common \ + accessibility \ + appearance \ + default-applications \ + keybindings \ + keyboard \ + mouse \ + network \ + windows \ + display \ + about-me + + +if BUILD_ABOUTME +SUBDIRS += about-me +endif + +-include $(top_srcdir)/git.mk diff --git a/capplets/about-me/AUTHORS b/capplets/about-me/AUTHORS new file mode 100644 index 00000000..c02ecb55 --- /dev/null +++ b/capplets/about-me/AUTHORS @@ -0,0 +1,2 @@ +Diego Gonzalez Gonzalez +Chris Toshok \ No newline at end of file diff --git a/capplets/about-me/Makefile.am b/capplets/about-me/Makefile.am new file mode 100644 index 00000000..6cd5e139 --- /dev/null +++ b/capplets/about-me/Makefile.am @@ -0,0 +1,59 @@ +SUBDIRS = icons + +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = about-me + +ui_files = mate-about-me-dialog.ui mate-about-me-password.ui mate-about-me-fingerprint.ui +Desktop_in_files = mate-about-me.desktop.in + +mate_about_me_SOURCES = \ + eel-alert-dialog.c \ + eel-alert-dialog.h \ + eel-gtk-macros.h \ + mate-about-me-password.c \ + mate-about-me-password.h \ + e-image-chooser.c \ + e-image-chooser.h \ + mate-about-me-fingerprint.c \ + mate-about-me-fingerprint.h \ + $(MARSHALFILES) \ + fingerprint-strings.h \ + mate-about-me.c + +MARSHALFILES = marshal.c marshal.h +BUILT_SOURCES = $(MARSHALFILES) + +marshal.h: fprintd-marshal.list + @GLIB_GENMARSHAL@ --prefix=fprintd_marshal $< --header > $@ +marshal.c: fprintd-marshal.list + @GLIB_GENMARSHAL@ --prefix=fprintd_marshal $< --body --header > $@ + +if BUILD_ABOUTME +bin_PROGRAMS = mate-about-me + +mate_about_me_LDADD = $(MATECC_CAPPLETS_LIBS) $(LIBEBOOK_LIBS) +mate_about_me_LDFLAGS = -export-dynamic + +@INTLTOOL_DESKTOP_RULE@ + +desktopdir = $(datadir)/applications +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +uidir = $(pkgdatadir)/ui +ui_DATA = $(ui_files) + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + $(LIBEBOOK_CFLAGS) \ + -DDATADIR="\"$(datadir)\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_UI_DIR="\"$(uidir)\"" \ + -DMATECC_PIXMAP_DIR="\"$(pkgdatadir)/pixmaps\"" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" + +endif # BUILD_ABOUTME + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(desktop_DATA) $(MARSHALFILES) +EXTRA_DIST = $(ui_files) fprintd-marshal.list + +-include $(top_srcdir)/git.mk diff --git a/capplets/about-me/e-image-chooser.c b/capplets/about-me/e-image-chooser.c new file mode 100644 index 00000000..31a189f1 --- /dev/null +++ b/capplets/about-me/e-image-chooser.c @@ -0,0 +1,455 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-image-chooser.c + * Copyright (C) 2004 Novell, Inc. + * Author: Chris Toshok + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include + +#include +#include +#include +#include + +#include "e-image-chooser.h" + +struct _EImageChooserPrivate { + + GtkWidget *image; + GtkWidget *browse_button; + + char *image_buf; + int image_buf_size; + int image_width; + int image_height; + + gboolean editable; +}; + +enum { + CHANGED, + LAST_SIGNAL +}; + + +static gint image_chooser_signals [LAST_SIGNAL] = { 0 }; + +static void e_image_chooser_init (EImageChooser *chooser); +static void e_image_chooser_class_init (EImageChooserClass *klass); +static void e_image_chooser_dispose (GObject *object); + +static gboolean image_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser); +static gboolean image_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser); +static void image_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, EImageChooser *chooser); + +static GtkObjectClass *parent_class = NULL; +#define PARENT_TYPE GTK_TYPE_VBOX + +enum DndTargetType { + DND_TARGET_TYPE_URI_LIST +}; +#define URI_LIST_TYPE "text/uri-list" + +static GtkTargetEntry image_drag_types[] = { + { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST }, +}; +static const int num_image_drag_types = sizeof (image_drag_types) / sizeof (image_drag_types[0]); + +GtkWidget * +e_image_chooser_new (void) +{ + return g_object_new (E_TYPE_IMAGE_CHOOSER, NULL); +} + +GType +e_image_chooser_get_type (void) +{ + static GType eic_type = 0; + + if (!eic_type) { + static const GTypeInfo eic_info = { + sizeof (EImageChooserClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) e_image_chooser_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EImageChooser), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_image_chooser_init, + }; + + eic_type = g_type_register_static (PARENT_TYPE, "EImageChooser", &eic_info, 0); + } + + return eic_type; +} + + +static void +e_image_chooser_class_init (EImageChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (PARENT_TYPE); + + image_chooser_signals [CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EImageChooserClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + GTK_TYPE_NONE, 0); + + object_class->dispose = e_image_chooser_dispose; +} + +static void +e_image_chooser_init (EImageChooser *chooser) +{ + EImageChooserPrivate *priv; + + priv = chooser->priv = g_new0 (EImageChooserPrivate, 1); + + priv->image = gtk_image_new (); + + gtk_box_set_homogeneous (GTK_BOX (chooser), FALSE); + gtk_box_pack_start (GTK_BOX (chooser), priv->image, TRUE, TRUE, 0); + + gtk_drag_dest_set (priv->image, 0, image_drag_types, num_image_drag_types, GDK_ACTION_COPY); + g_signal_connect (priv->image, + "drag_motion", G_CALLBACK (image_drag_motion_cb), chooser); + g_signal_connect (priv->image, + "drag_drop", G_CALLBACK (image_drag_drop_cb), chooser); + g_signal_connect (priv->image, + "drag_data_received", G_CALLBACK (image_drag_data_received_cb), chooser); + + gtk_widget_show_all (priv->image); + + /* we default to being editable */ + priv->editable = TRUE; +} + +static void +e_image_chooser_dispose (GObject *object) +{ + EImageChooser *eic = E_IMAGE_CHOOSER (object); + + if (eic->priv) { + EImageChooserPrivate *priv = eic->priv; + + if (priv->image_buf) { + g_free (priv->image_buf); + priv->image_buf = NULL; + } + + g_free (eic->priv); + eic->priv = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + (* G_OBJECT_CLASS (parent_class)->dispose) (object); +} + + +static gboolean +set_image_from_data (EImageChooser *chooser, + char *data, int length) +{ + gboolean rv = FALSE; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + GdkPixbuf *pixbuf; + + gdk_pixbuf_loader_write (loader, data, length, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf) + g_object_ref (pixbuf); + g_object_unref (loader); + + if (pixbuf) { + GdkPixbuf *scaled; + GtkRequisition chooser_size; + + float scale; + int new_height, new_width; + + gtk_widget_size_request (gtk_widget_get_parent (GTK_WIDGET (chooser)), + &chooser_size); + chooser_size.width -= 5; + chooser_size.height -= 5; + + new_height = gdk_pixbuf_get_height (pixbuf); + new_width = gdk_pixbuf_get_width (pixbuf); + + if (chooser->priv->image_height == 0 + && chooser->priv->image_width == 0) { + scale = 1.0; + } + else if (chooser->priv->image_height < new_height + || chooser->priv->image_width < new_width) { + /* we need to scale down */ + if (new_height > new_width) + scale = (float)chooser_size.height / new_height; + else + scale = (float)chooser_size.width / new_width; + } + else { + /* we need to scale up */ + if (new_height > new_width) + scale = (float)new_height / chooser_size.height; + else + scale = (float)new_width / chooser_size.width; + } + + if (scale == 1.0) { + gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image), pixbuf); + + chooser->priv->image_width = new_width; + chooser->priv->image_height = new_height; + } + else { + new_width *= scale; + new_height *= scale; + new_width = MIN (new_width, chooser_size.width); + new_height = MIN (new_height, chooser_size.height); + + scaled = gdk_pixbuf_scale_simple (pixbuf, + new_width, new_height, + GDK_INTERP_BILINEAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image), scaled); + g_object_unref (scaled); + } + + g_object_unref (pixbuf); + + g_free (chooser->priv->image_buf); + chooser->priv->image_buf = data; + chooser->priv->image_buf_size = length; + + g_signal_emit (chooser, + image_chooser_signals [CHANGED], 0); + + rv = TRUE; + } + + return rv; +} + +static gboolean +image_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser) +{ + GList *p; + + if (!chooser->priv->editable) + return FALSE; + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gdk_drag_status (context, GDK_ACTION_COPY, time); + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static gboolean +image_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser) +{ + GList *p; + + if (!chooser->priv->editable) + return FALSE; + + if (context->targets == NULL) { + return FALSE; + } + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gtk_drag_get_data (widget, context, + GDK_POINTER_TO_ATOM (p->data), + time); + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static void +image_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, EImageChooser *chooser) +{ + char *target_type; + gboolean handled = FALSE; + + target_type = gdk_atom_name (gtk_selection_data_get_target (selection_data)); + + if (!strcmp (target_type, URI_LIST_TYPE)) { + const char *data = gtk_selection_data_get_data (selection_data); + char *uri; + GFile *file; + GInputStream *istream; + char *nl = strstr (data, "\r\n"); + + if (nl) + uri = g_strndup (data, nl - (char *) data); + else + uri = g_strdup (data); + + file = g_file_new_for_uri (uri); + istream = G_INPUT_STREAM (g_file_read (file, NULL, NULL)); + + if (istream != NULL) { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (info != NULL) { + gsize size; + gboolean success; + gchar *buf; + + size = g_file_info_get_size (info); + g_object_unref (info); + + buf = g_malloc (size); + + success = g_input_stream_read_all (istream, + buf, + size, + &size, + NULL, + NULL); + g_input_stream_close (istream, NULL, NULL); + + if (success && + set_image_from_data (chooser, buf, size)) + handled = TRUE; + else + g_free (buf); + } + + g_object_unref (istream); + } + + g_object_unref (file); + g_free (uri); + } + + gtk_drag_finish (context, handled, FALSE, time); +} + +gboolean +e_image_chooser_set_from_file (EImageChooser *chooser, const char *filename) +{ + gchar *data; + gsize data_length; + + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (filename, FALSE); + + if (!g_file_get_contents (filename, &data, &data_length, NULL)) { + return FALSE; + } + + if (!set_image_from_data (chooser, data, data_length)) + g_free (data); + + return TRUE; +} + +void +e_image_chooser_set_editable (EImageChooser *chooser, gboolean editable) +{ + g_return_if_fail (E_IS_IMAGE_CHOOSER (chooser)); + + chooser->priv->editable = editable; + + gtk_widget_set_sensitive (chooser->priv->browse_button, editable); +} + +gboolean +e_image_chooser_get_image_data (EImageChooser *chooser, char **data, gsize *data_length) +{ + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data_length != NULL, FALSE); + + *data_length = chooser->priv->image_buf_size; + *data = g_malloc (*data_length); + memcpy (*data, chooser->priv->image_buf, *data_length); + + return TRUE; +} + +gboolean +e_image_chooser_set_image_data (EImageChooser *chooser, char *data, gsize data_length) +{ + char *buf; + + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + /* yuck, a copy... */ + buf = g_malloc (data_length); + memcpy (buf, data, data_length); + + if (!set_image_from_data (chooser, buf, data_length)) { + g_free (buf); + return FALSE; + } + + return TRUE; +} diff --git a/capplets/about-me/e-image-chooser.h b/capplets/about-me/e-image-chooser.h new file mode 100644 index 00000000..b2b2fbf2 --- /dev/null +++ b/capplets/about-me/e-image-chooser.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-image-chooser.c + * Copyright (C) 2004 Novell, Inc. + * Author: Chris Toshok + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _E_IMAGE_CHOOSER_H_ +#define _E_IMAGE_CHOOSER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define E_TYPE_IMAGE_CHOOSER (e_image_chooser_get_type ()) +#define E_IMAGE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_IMAGE_CHOOSER, EImageChooser)) +#define E_IMAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_IMAGE_CHOOSER, EImageChooserClass)) +#define E_IS_IMAGE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_IMAGE_CHOOSER)) +#define E_IS_IMAGE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_IMAGE_CHOOSER)) + +typedef struct _EImageChooser EImageChooser; +typedef struct _EImageChooserClass EImageChooserClass; +typedef struct _EImageChooserPrivate EImageChooserPrivate; + +struct _EImageChooser +{ + GtkVBox parent; + + EImageChooserPrivate *priv; +}; + +struct _EImageChooserClass +{ + GtkVBoxClass parent_class; + + /* signals */ + void (*changed) (EImageChooser *chooser); + + +}; + +GtkWidget *e_image_chooser_new (void); +GType e_image_chooser_get_type (void); + +gboolean e_image_chooser_set_from_file (EImageChooser *chooser, const char *filename); +gboolean e_image_chooser_set_image_data (EImageChooser *chooser, char *data, gsize data_length); +void e_image_chooser_set_editable (EImageChooser *chooser, gboolean editable); + +gboolean e_image_chooser_get_image_data (EImageChooser *chooser, char **data, gsize *data_length); + +#endif /* _E_IMAGE_CHOOSER_H_ */ diff --git a/capplets/about-me/eel-alert-dialog.c b/capplets/about-me/eel-alert-dialog.c new file mode 100644 index 00000000..6370d6db --- /dev/null +++ b/capplets/about-me/eel-alert-dialog.c @@ -0,0 +1,466 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* eel-alert-dialog.c: An HIG compliant alert dialog. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include "eel-alert-dialog.h" +#include "eel-gtk-macros.h" + +#include +#include + +#include + +enum { + PROP_0, + PROP_ALERT_TYPE, + PROP_BUTTONS +}; + +struct _EelAlertDialogDetails { + GtkWidget *image; + GtkWidget *primary_label; + GtkWidget *secondary_label; + GtkWidget *details_expander; + GtkWidget *details_label; + GtkMessageType type; +}; + + +static gpointer parent_class; + +static void eel_alert_dialog_finalize (GObject *object); +static void eel_alert_dialog_class_init (EelAlertDialogClass *klass); +static void eel_alert_dialog_init (EelAlertDialog *dialog); +static void eel_alert_dialog_style_set (GtkWidget *widget, + GtkStyle *prev_style); +static void eel_alert_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void eel_alert_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void eel_alert_dialog_add_buttons (EelAlertDialog *alert_dialog, + GtkButtonsType buttons); + +GType +eel_alert_dialog_get_type (void) +{ + static GType dialog_type = 0; + + if (!dialog_type) { + + static const GTypeInfo dialog_info = + { + sizeof (EelAlertDialogClass), + NULL, + NULL, + (GClassInitFunc) eel_alert_dialog_class_init, + NULL, + NULL, + sizeof (EelAlertDialog), + 0, + (GInstanceInitFunc) eel_alert_dialog_init, + }; + + dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "EelAlertDialog", + &dialog_info, 0); + } + return dialog_type; +} + +static void +eel_alert_dialog_class_init (EelAlertDialogClass *class) +{ + GtkWidgetClass *widget_class; + GObjectClass *gobject_class; + + widget_class = GTK_WIDGET_CLASS (class); + gobject_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + G_OBJECT_CLASS (class)->finalize = eel_alert_dialog_finalize; + + widget_class->style_set = eel_alert_dialog_style_set; + + gobject_class->set_property = eel_alert_dialog_set_property; + gobject_class->get_property = eel_alert_dialog_get_property; + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("alert_border", + _("Image/label border"), + _("Width of border around the label and image in the alert dialog"), + 0, + G_MAXINT, + 5, + G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, + PROP_ALERT_TYPE, + g_param_spec_enum ("alert_type", + _("Alert Type"), + _("The type of alert"), + GTK_TYPE_MESSAGE_TYPE, + GTK_MESSAGE_INFO, + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, + PROP_BUTTONS, + g_param_spec_enum ("buttons", + _("Alert Buttons"), + _("The buttons shown in the alert dialog"), + GTK_TYPE_BUTTONS_TYPE, + GTK_BUTTONS_NONE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +eel_alert_dialog_finalize (GObject *object) +{ + EelAlertDialog *dialog; + + dialog = EEL_ALERT_DIALOG (object); + + g_free (dialog->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + + +static void +eel_alert_dialog_init (EelAlertDialog *dialog) +{ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *expander; + + dialog->details = g_new0 (EelAlertDialogDetails, 1); + + dialog->details->primary_label = gtk_label_new (NULL); + dialog->details->secondary_label = gtk_label_new (NULL); + dialog->details->details_label = gtk_label_new (NULL); + dialog->details->image = gtk_image_new_from_stock (NULL, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (dialog->details->image), 0.5, 0.0); + + gtk_label_set_line_wrap (GTK_LABEL (dialog->details->primary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (dialog->details->primary_label), TRUE); + gtk_label_set_use_markup (GTK_LABEL (dialog->details->primary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (dialog->details->primary_label), 0.0, 0.5); + + gtk_label_set_line_wrap (GTK_LABEL (dialog->details->secondary_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (dialog->details->secondary_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (dialog->details->secondary_label), 0.0, 0.5); + + gtk_label_set_line_wrap (GTK_LABEL (dialog->details->details_label), TRUE); + gtk_label_set_selectable (GTK_LABEL (dialog->details->details_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (dialog->details->details_label), 0.0, 0.5); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + + gtk_box_pack_start (GTK_BOX (hbox), dialog->details->image, + FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + + gtk_box_pack_start (GTK_BOX (hbox), vbox, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), dialog->details->primary_label, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), dialog->details->secondary_label, + FALSE, FALSE, 0); + + expander = gtk_expander_new_with_mnemonic (_("Show more _details")); + dialog->details->details_expander = expander; + gtk_expander_set_spacing (GTK_EXPANDER (expander), 6); + gtk_container_add (GTK_CONTAINER (expander), dialog->details->details_label); + + gtk_box_pack_start (GTK_BOX (vbox), expander, + FALSE, FALSE, 0); + + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, + FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + gtk_widget_hide (expander); + +} + +static void +setup_type (EelAlertDialog *dialog, + GtkMessageType type) +{ + const gchar *stock_id = NULL; + GtkStockItem item; + + switch (type) { + case GTK_MESSAGE_INFO: + stock_id = GTK_STOCK_DIALOG_INFO; + break; + case GTK_MESSAGE_QUESTION: + stock_id = GTK_STOCK_DIALOG_QUESTION; + break; + case GTK_MESSAGE_WARNING: + stock_id = GTK_STOCK_DIALOG_WARNING; + break; + case GTK_MESSAGE_ERROR: + stock_id = GTK_STOCK_DIALOG_ERROR; + break; + default: + g_warning ("Unknown GtkMessageType %d", type); + break; + } + + if (stock_id == NULL) { + stock_id = GTK_STOCK_DIALOG_INFO; + } + + if (gtk_stock_lookup (stock_id, &item)) { + gtk_image_set_from_stock (GTK_IMAGE (dialog->details->image), stock_id, + GTK_ICON_SIZE_DIALOG); + } else { + g_warning ("Stock dialog ID doesn't exist?"); + } +} + +static void +eel_alert_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EelAlertDialog *dialog; + + dialog = EEL_ALERT_DIALOG (object); + + switch (prop_id) { + case PROP_ALERT_TYPE: + dialog->details->type = g_value_get_enum (value); + setup_type (dialog, dialog->details->type); + break; + case PROP_BUTTONS: + eel_alert_dialog_add_buttons (dialog, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +eel_alert_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EelAlertDialog *dialog; + + dialog = EEL_ALERT_DIALOG (object); + + switch (prop_id) { + case PROP_ALERT_TYPE: + g_value_set_enum (value, dialog->details->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +void +eel_alert_dialog_set_primary_label (EelAlertDialog *dialog, + const gchar *message) +{ + gchar *markup_str; + char *escaped_message; + + if (message != NULL) { + escaped_message = g_markup_escape_text (message, -1); + markup_str = g_strconcat ("", escaped_message, "", NULL); + gtk_label_set_markup (GTK_LABEL (EEL_ALERT_DIALOG (dialog)->details->primary_label), + markup_str); + g_free (markup_str); + g_free (escaped_message); + } +} + +void +eel_alert_dialog_set_secondary_label (EelAlertDialog *dialog, + const gchar *message) +{ + if (message != NULL) { + gtk_label_set_text (GTK_LABEL (EEL_ALERT_DIALOG (dialog)->details->secondary_label), + message); + } else { + gtk_widget_hide (EEL_ALERT_DIALOG (dialog)->details->secondary_label); + } +} + +void +eel_alert_dialog_set_details_label (EelAlertDialog *dialog, + const gchar *message) +{ + if (message != NULL) { + gtk_widget_show (dialog->details->details_expander); + gtk_label_set_text (GTK_LABEL (dialog->details->details_label), message); + } else { + gtk_widget_hide (dialog->details->details_expander); + } +} + + +GtkWidget* +eel_alert_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + GtkMessageType type, + GtkButtonsType buttons, + const gchar *primary_message, + const gchar *secondary_message, + const gchar *title) +{ + GtkWidget *widget; + GtkDialog *dialog; + + g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL); + + widget = g_object_new (EEL_TYPE_ALERT_DIALOG, + "alert_type", type, + "buttons", buttons, + NULL); + atk_object_set_role (gtk_widget_get_accessible (widget), ATK_ROLE_ALERT); + + dialog = GTK_DIALOG (widget); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_dialog_set_has_separator (dialog, FALSE); + + gtk_window_set_title (GTK_WINDOW (dialog), + (title != NULL) ? title : ""); + + eel_alert_dialog_set_primary_label (EEL_ALERT_DIALOG (dialog), + primary_message); + + eel_alert_dialog_set_secondary_label (EEL_ALERT_DIALOG (dialog), + secondary_message); + + if (parent != NULL) { + gtk_window_set_transient_for (GTK_WINDOW (widget), + GTK_WINDOW (parent)); + } + + if (flags & GTK_DIALOG_MODAL) { + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + } + + if (flags & GTK_DIALOG_DESTROY_WITH_PARENT) { + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + } + return widget; +} + +static void +eel_alert_dialog_add_buttons (EelAlertDialog* alert_dialog, + GtkButtonsType buttons) +{ + GtkDialog* dialog; + + dialog = GTK_DIALOG (alert_dialog); + + switch (buttons) { + case GTK_BUTTONS_NONE: + break; + case GTK_BUTTONS_OK: + gtk_dialog_add_button (dialog, + GTK_STOCK_OK, + GTK_RESPONSE_OK); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_OK); + break; + case GTK_BUTTONS_CLOSE: + gtk_dialog_add_button (dialog, + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_CLOSE); + break; + case GTK_BUTTONS_CANCEL: + gtk_dialog_add_button (dialog, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_CANCEL); + break; + case GTK_BUTTONS_YES_NO: + gtk_dialog_add_button (dialog, + GTK_STOCK_NO, + GTK_RESPONSE_NO); + gtk_dialog_add_button (dialog, + GTK_STOCK_YES, + GTK_RESPONSE_YES); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_YES); + break; + case GTK_BUTTONS_OK_CANCEL: + gtk_dialog_add_button (dialog, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (dialog, + GTK_STOCK_OK, + GTK_RESPONSE_OK); + gtk_dialog_set_default_response (dialog, + GTK_RESPONSE_OK); + break; + default: + g_warning ("Unknown GtkButtonsType"); + break; + } + g_object_notify (G_OBJECT (alert_dialog), "buttons"); +} + +static void +eel_alert_dialog_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GtkWidget *parent; + gint border_width; + + border_width = 0; + + parent = gtk_widget_get_parent (EEL_ALERT_DIALOG (widget)->details->image); + + if (parent != NULL) { + gtk_widget_style_get (widget, "alert_border", + &border_width, NULL); + + gtk_container_set_border_width (GTK_CONTAINER (parent), + border_width); + } + + if (GTK_WIDGET_CLASS (parent_class)->style_set) { + (GTK_WIDGET_CLASS (parent_class)->style_set) (widget, prev_style); + } +} diff --git a/capplets/about-me/eel-alert-dialog.h b/capplets/about-me/eel-alert-dialog.h new file mode 100644 index 00000000..60c6b9c0 --- /dev/null +++ b/capplets/about-me/eel-alert-dialog.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* eel-alert-dialog.h: An HIG compliant alert dialog. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef EEL_ALERT_DIALOG_H +#define EEL_ALERT_DIALOG_H + +#include + +#define EEL_TYPE_ALERT_DIALOG (eel_alert_dialog_get_type ()) +#define EEL_ALERT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_ALERT_DIALOG, EelAlertDialog)) + +typedef struct _EelAlertDialog EelAlertDialog; +typedef struct _EelAlertDialogClass EelAlertDialogClass; +typedef struct _EelAlertDialogDetails EelAlertDialogDetails; + +struct _EelAlertDialog +{ + GtkDialog parent_instance; + EelAlertDialogDetails *details; +}; + +struct _EelAlertDialogClass +{ + GtkDialogClass parent_class; +}; + +GType eel_alert_dialog_get_type (void); + +GtkWidget* eel_alert_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + GtkMessageType type, + GtkButtonsType buttons, + const gchar *primary_message, + const gchar *secondary_message, + const gchar *title); +void eel_alert_dialog_set_primary_label (EelAlertDialog *dialog, + const gchar *message); +void eel_alert_dialog_set_secondary_label (EelAlertDialog *dialog, + const gchar *message); +void eel_alert_dialog_set_details_label (EelAlertDialog *dialog, + const gchar *message); + +#endif /* EEL_ALERT_DIALOG_H */ diff --git a/capplets/about-me/eel-gtk-macros.h b/capplets/about-me/eel-gtk-macros.h new file mode 100644 index 00000000..b3a9d671 --- /dev/null +++ b/capplets/about-me/eel-gtk-macros.h @@ -0,0 +1,178 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + eel-gtk-macros.h: Macros to reduce boilerplate when using GTK. + + Copyright (C) 1999, 2000, 2001 Eazel, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Darin Adler + Ramiro Estrugo +*/ + +#ifndef EEL_GTK_MACROS_H +#define EEL_GTK_MACROS_H + +#ifndef EEL_DISABLE_DEPRECATED + +/* Define a parent_class global and a get_type function for a GTK class. + Since this is boilerplate, it's better not to repeat it over and over again. + Called like this: + + EEL_CLASS_BOILERPLATE (EelBookmark, eel_bookmark, GTK_TYPE_OBJECT) + + The parent_class_type parameter is guaranteed to be evaluated only once + so it can be an expression, even an expression that contains a function call. +*/ + +#define EEL_CLASS_BOILERPLATE(class_name, prefix, parent_class_type) \ + EEL_BOILERPLATE (class_name, class_name, prefix, parent_class_type, \ + EEL_REGISTER_TYPE) +#define EEL_REGISTER_TYPE(class_name, corba_name) \ + g_type_register_static (parent_type, #class_name, &info, 0) + +#define EEL_BOILERPLATE(class_name, corba_name, prefix, parent_class_type, \ + register_type) \ + \ +static gpointer parent_class; \ + \ +GType \ +prefix##_get_type (void) \ +{ \ + GType parent_type; \ + static GType type; \ + \ + if (type == 0) { \ + static GTypeInfo info = { \ + sizeof (class_name##Class), \ + NULL, NULL, \ + (GClassInitFunc) prefix##_class_init, \ + NULL, NULL, \ + sizeof (class_name), 0, \ + (GInstanceInitFunc) prefix##_init, \ + NULL \ + }; \ + \ + parent_type = (parent_class_type); \ + type = register_type (class_name, corba_name); \ + parent_class = g_type_class_ref (parent_type); \ + } \ + \ + return type; \ +} + +/* Call a parent class version of a virtual function (or default + * signal handler since that's the same thing). Nice because it + * documents what it's doing and there is less chance for a + * typo. Depends on the parent class pointer having the conventional + * name "parent_class" as the boilerplate macro above does it. + */ +#define EEL_CALL_PARENT(parent_class_cast_macro, signal, parameters) \ + \ +G_STMT_START { \ + if (parent_class_cast_macro (parent_class)->signal != NULL) { \ + (* parent_class_cast_macro (parent_class)->signal) parameters;\ + } \ +} G_STMT_END + +/* Same thing, for functions with a return value. */ +#define EEL_CALL_PARENT_WITH_RETURN_VALUE(parent_class_cast_macro, signal, \ + parameters) \ + \ +(parent_class_cast_macro (parent_class)->signal == NULL) \ + ? 0 \ + : ((* parent_class_cast_macro (parent_class)->signal) parameters) + +#endif /* EEL_DISABLE_DEPRECATED */ + +/* Call a virtual function. Useful when the virtual function is not a + * signal, otherwise you want to gtk_signal emit. Nice because it + * documents what it's doing and there is less chance for a typo. + */ +#define EEL_CALL_METHOD(class_cast_macro, object, signal, parameters) \ + \ +G_STMT_START { \ + if (class_cast_macro (G_OBJECT_GET_CLASS (object))->signal != NULL) { \ + (* class_cast_macro (G_OBJECT_GET_CLASS (object))->signal) \ + parameters; \ + } \ +} G_STMT_END + +/* Same thing, for functions with a return value. */ +#define EEL_CALL_METHOD_WITH_RETURN_VALUE(class_cast_macro, object, signal, \ + parameters) \ + \ +(class_cast_macro (G_OBJECT_GET_CLASS (object))->signal == NULL) \ + ? 0 \ + : ((* class_cast_macro (G_OBJECT_GET_CLASS (object))->signal) \ + parameters) \ + +#ifndef G_DISABLE_ASSERT + +/* Define a signal that is not implemented by this class but must be + * implemented by subclasses. This macro should be used inside the + * class initialization function. The companion macro EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL + * must be used earlier in the file. Called like this: + * + * EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, + * fm_directory_view, + * clear); + */ +#define EEL_ASSIGN_MUST_OVERRIDE_SIGNAL(class_pointer, prefix, signal) \ + \ +* (void (**)(void)) & (class_pointer)->signal = prefix##_unimplemented_##signal + +/* Provide a debug-only implementation of a signal that must be implemented + * by subclasses. The debug-only implementation fires a warning if it is called. + * This macro should be placed as if it were a function, earlier in the file + * than the class initialization function. Called like this: + * + * EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear); + */ +#define EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL(prefix, signal) \ + \ +static void \ +prefix##_unimplemented_##signal (void) \ +{ \ + g_warning ("failed to override signal " #prefix "->" #signal); \ +} + +#else /* G_DISABLE_ASSERT */ + +#define EEL_DEFINE_MUST_OVERRIDE_SIGNAL(class_cast_macro, class_pointer, prefix, signal) +#define EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL(prefix, signal) +#define EEL_ASSIGN_MUST_OVERRIDE_SIGNAL(class_pointer, prefix, signal) + +#endif /* G_DISABLE_ASSERT */ + +/* Access a method. */ +#define EEL_ACCESS_METHOD(class_cast_macro, object, method) \ +(class_cast_macro (G_OBJECT_GET_CLASS (object))->method) + +/* Invoke a method for a given object. */ +#define EEL_INVOKE_METHOD(class_cast_macro, object, method, parameters) \ +((* EEL_ACCESS_METHOD (class_cast_macro, object, method)) parameters) + +/* Assert the non-nullness of a method for a given object. */ +#define EEL_ASSERT_METHOD(class_cast_macro, object, method) \ +g_assert (EEL_ACCESS_METHOD (class_cast_macro, object, method) != NULL) + +/* Invoke a method if it ain't null. */ +#define EEL_INVOKE_METHOD_IF(class_cast_macro, object, method, parameters) \ +(EEL_ACCESS_METHOD (class_cast_macro, object, method) ? 0 : \ + EEL_INVOKE_METHOD (class_cast_macro, object, method, parameters)) + +#endif /* EEL_GTK_MACROS_H */ diff --git a/capplets/about-me/fingerprint-strings.h b/capplets/about-me/fingerprint-strings.h new file mode 100644 index 00000000..d1b919e9 --- /dev/null +++ b/capplets/about-me/fingerprint-strings.h @@ -0,0 +1,111 @@ +/* + * Helper functions to translate statuses and actions to strings + * Copyright (C) 2008 Bastien Nocera + * + * Experimental code. This will be moved out of fprintd into it's own + * package once the system has matured. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +struct { + const char *dbus_name; + const char *place_str; + const char *swipe_str; +} fingers[11] = { + { "left-thumb", N_("Place your left thumb on %s"), N_("Swipe your left thumb on %s") }, + { "left-index-finger", N_("Place your left index finger on %s"), N_("Swipe your left index finger on %s") }, + { "left-middle-finger", N_("Place your left middle finger on %s"), N_("Swipe your left middle finger on %s") }, + { "left-ring-finger", N_("Place your left ring finger on %s"), N_("Swipe your left ring finger on %s") }, + { "left-little-finger", N_("Place your left little finger on %s"), N_("Swipe your left little finger on %s") }, + { "right-thumb", N_("Place your right thumb on %s"), N_("Swipe your right thumb on %s") }, + { "right-index-finger", N_("Place your right index finger on %s"), N_("Swipe your right index finger on %s") }, + { "right-middle-finger", N_("Place your right middle finger on %s"), N_("Swipe your right middle finger on %s") }, + { "right-ring-finger", N_("Place your right ring finger on %s"), N_("Swipe your right ring finger on %s") }, + { "right-little-finger", N_("Place your right little finger on %s"), N_("Swipe your right little finger on %s") }, + { NULL, NULL, NULL } +}; + +static const char *finger_str_to_msg(const char *finger_name, gboolean is_swipe) +{ + int i; + + if (finger_name == NULL) + return NULL; + + for (i = 0; fingers[i].dbus_name != NULL; i++) { + if (g_str_equal (fingers[i].dbus_name, finger_name)) { + if (is_swipe == FALSE) + return fingers[i].place_str; + else + return fingers[i].swipe_str; + } + } + + return NULL; +} + +/* Cases not handled: + * verify-no-match + * verify-match + * verify-unknown-error + */ +static const char *verify_result_str_to_msg(const char *result, gboolean is_swipe) +{ + if (result == NULL) + return NULL; + + if (strcmp (result, "verify-retry-scan") == 0) { + if (is_swipe == FALSE) + return N_("Place your finger on the reader again"); + else + return N_("Swipe your finger again"); + } + if (strcmp (result, "verify-swipe-too-short") == 0) + return N_("Swipe was too short, try again"); + if (strcmp (result, "verify-finger-not-centered") == 0) + return N_("Your finger was not centered, try swiping your finger again"); + if (strcmp (result, "verify-remove-and-retry") == 0) + return N_("Remove your finger, and try swiping your finger again"); + + return NULL; +} + +/* Cases not handled: + * enroll-completed + * enroll-failed + * enroll-unknown-error + */ +static const char *enroll_result_str_to_msg(const char *result, gboolean is_swipe) +{ + if (result == NULL) + return NULL; + + if (strcmp (result, "enroll-retry-scan") == 0 || strcmp (result, "enroll-stage-passed") == 0) { + if (is_swipe == FALSE) + return N_("Place your finger on the reader again"); + else + return N_("Swipe your finger again"); + } + if (strcmp (result, "enroll-swipe-too-short") == 0) + return N_("Swipe was too short, try again"); + if (strcmp (result, "enroll-finger-not-centered") == 0) + return N_("Your finger was not centered, try swiping your finger again"); + if (strcmp (result, "enroll-remove-and-retry") == 0) + return N_("Remove your finger, and try swiping your finger again"); + + return NULL; +} + diff --git a/capplets/about-me/fprintd-marshal.list b/capplets/about-me/fprintd-marshal.list new file mode 100644 index 00000000..c4effb63 --- /dev/null +++ b/capplets/about-me/fprintd-marshal.list @@ -0,0 +1 @@ +VOID:STRING,BOOLEAN diff --git a/capplets/about-me/icons/Makefile.am b/capplets/about-me/icons/Makefile.am new file mode 100644 index 00000000..a35a9e64 --- /dev/null +++ b/capplets/about-me/icons/Makefile.am @@ -0,0 +1,30 @@ +pixmapsdir = $(pkgdatadir)/pixmaps +pixmaps_DATA = \ + left-index-finger.svg \ + left-little-finger.svg \ + left-middle-finger.svg \ + left-ring-finger.svg \ + left-thumb.svg \ + print_error.svg \ + print_ok.svg \ + right-index-finger.svg \ + right-little-finger.svg \ + right-middle-finger.svg \ + right-ring-finger.svg \ + right-thumb.svg \ + left-index-finger.png \ + left-middle-finger.png \ + left-little-finger.png \ + left-ring-finger.png \ + left-thumb.png \ + print_error.png \ + print_ok.png \ + right-index-finger.png \ + right-middle-finger.png \ + right-little-finger.png \ + right-ring-finger.png \ + right-thumb.png + +EXTRA_DIST = $(pixmaps_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/about-me/icons/left-index-finger.png b/capplets/about-me/icons/left-index-finger.png new file mode 100644 index 00000000..1a9cb2c7 Binary files /dev/null and b/capplets/about-me/icons/left-index-finger.png differ diff --git a/capplets/about-me/icons/left-index-finger.svg b/capplets/about-me/icons/left-index-finger.svg new file mode 100644 index 00000000..3c36aeaa --- /dev/null +++ b/capplets/about-me/icons/left-index-finger.svg @@ -0,0 +1,177 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-little-finger.png b/capplets/about-me/icons/left-little-finger.png new file mode 100644 index 00000000..978942ee Binary files /dev/null and b/capplets/about-me/icons/left-little-finger.png differ diff --git a/capplets/about-me/icons/left-little-finger.svg b/capplets/about-me/icons/left-little-finger.svg new file mode 100644 index 00000000..0835854f --- /dev/null +++ b/capplets/about-me/icons/left-little-finger.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-middle-finger.png b/capplets/about-me/icons/left-middle-finger.png new file mode 100644 index 00000000..406925e9 Binary files /dev/null and b/capplets/about-me/icons/left-middle-finger.png differ diff --git a/capplets/about-me/icons/left-middle-finger.svg b/capplets/about-me/icons/left-middle-finger.svg new file mode 100644 index 00000000..1082da2e --- /dev/null +++ b/capplets/about-me/icons/left-middle-finger.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-ring-finger.png b/capplets/about-me/icons/left-ring-finger.png new file mode 100644 index 00000000..169ff687 Binary files /dev/null and b/capplets/about-me/icons/left-ring-finger.png differ diff --git a/capplets/about-me/icons/left-ring-finger.svg b/capplets/about-me/icons/left-ring-finger.svg new file mode 100644 index 00000000..50ace807 --- /dev/null +++ b/capplets/about-me/icons/left-ring-finger.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/left-thumb.png b/capplets/about-me/icons/left-thumb.png new file mode 100644 index 00000000..eaf875d0 Binary files /dev/null and b/capplets/about-me/icons/left-thumb.png differ diff --git a/capplets/about-me/icons/left-thumb.svg b/capplets/about-me/icons/left-thumb.svg new file mode 100644 index 00000000..fd0f5827 --- /dev/null +++ b/capplets/about-me/icons/left-thumb.svg @@ -0,0 +1,180 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/print_error.png b/capplets/about-me/icons/print_error.png new file mode 100644 index 00000000..d67150b1 Binary files /dev/null and b/capplets/about-me/icons/print_error.png differ diff --git a/capplets/about-me/icons/print_error.svg b/capplets/about-me/icons/print_error.svg new file mode 100644 index 00000000..4ad6beed --- /dev/null +++ b/capplets/about-me/icons/print_error.svg @@ -0,0 +1,525 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/print_ok.png b/capplets/about-me/icons/print_ok.png new file mode 100644 index 00000000..4dd615e7 Binary files /dev/null and b/capplets/about-me/icons/print_ok.png differ diff --git a/capplets/about-me/icons/print_ok.svg b/capplets/about-me/icons/print_ok.svg new file mode 100644 index 00000000..ba821ef7 --- /dev/null +++ b/capplets/about-me/icons/print_ok.svg @@ -0,0 +1,310 @@ + + +image/svg+xml + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-index-finger.png b/capplets/about-me/icons/right-index-finger.png new file mode 100644 index 00000000..4aaeaac4 Binary files /dev/null and b/capplets/about-me/icons/right-index-finger.png differ diff --git a/capplets/about-me/icons/right-index-finger.svg b/capplets/about-me/icons/right-index-finger.svg new file mode 100644 index 00000000..5a621a2e --- /dev/null +++ b/capplets/about-me/icons/right-index-finger.svg @@ -0,0 +1,179 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-little-finger.png b/capplets/about-me/icons/right-little-finger.png new file mode 100644 index 00000000..17946afc Binary files /dev/null and b/capplets/about-me/icons/right-little-finger.png differ diff --git a/capplets/about-me/icons/right-little-finger.svg b/capplets/about-me/icons/right-little-finger.svg new file mode 100644 index 00000000..9fcec2af --- /dev/null +++ b/capplets/about-me/icons/right-little-finger.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-middle-finger.png b/capplets/about-me/icons/right-middle-finger.png new file mode 100644 index 00000000..71bd41ba Binary files /dev/null and b/capplets/about-me/icons/right-middle-finger.png differ diff --git a/capplets/about-me/icons/right-middle-finger.svg b/capplets/about-me/icons/right-middle-finger.svg new file mode 100644 index 00000000..b33a654a --- /dev/null +++ b/capplets/about-me/icons/right-middle-finger.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-ring-finger.png b/capplets/about-me/icons/right-ring-finger.png new file mode 100644 index 00000000..aa73ae6e Binary files /dev/null and b/capplets/about-me/icons/right-ring-finger.png differ diff --git a/capplets/about-me/icons/right-ring-finger.svg b/capplets/about-me/icons/right-ring-finger.svg new file mode 100644 index 00000000..9e264fe6 --- /dev/null +++ b/capplets/about-me/icons/right-ring-finger.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/icons/right-thumb.png b/capplets/about-me/icons/right-thumb.png new file mode 100644 index 00000000..1c967d61 Binary files /dev/null and b/capplets/about-me/icons/right-thumb.png differ diff --git a/capplets/about-me/icons/right-thumb.svg b/capplets/about-me/icons/right-thumb.svg new file mode 100644 index 00000000..0aa0f2e4 --- /dev/null +++ b/capplets/about-me/icons/right-thumb.svg @@ -0,0 +1,182 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capplets/about-me/mate-about-me-dialog.ui b/capplets/about-me/mate-about-me-dialog.ui new file mode 100644 index 00000000..fdf79b6e --- /dev/null +++ b/capplets/about-me/mate-about-me-dialog.ui @@ -0,0 +1,1622 @@ + + + + + + 5 + About Me + dialog + False + + + True + vertical + 2 + + + True + 5 + vertical + 12 + + + True + + + True + 12 + + + 80 + 80 + True + True + False + Select your photo + + + + + + False + False + 0 + + + + + True + 0 + Full Name + + + False + False + 1 + + + + + 0 + + + + + True + vertical + 6 + + + True + 12 + + + True + 0 + User name: + + + False + False + 0 + + + + + True + 0 + + + 1 + + + + + False + False + 0 + + + + + Change Passwo_rd... + True + True + False + True + + + False + False + 1 + + + + + Enable _Fingerprint Login... + True + False + True + + + False + False + 2 + + + + + Disable _Fingerprint Login... + True + False + True + + + False + False + 3 + + + + + False + 1 + + + + + False + False + 0 + + + + + True + + + True + True + + + True + 12 + vertical + 18 + + + True + vertical + 6 + + + True + 0 + Email + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 2 + 2 + 12 + 6 + + + True + 0 + _Work: + True + email-work-e + + + GTK_FILL + + + + + + True + 0 + _Home: + True + email-home-e + + + 1 + 2 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Telephone + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 2 + 4 + 12 + 6 + + + True + 0 + Wor_k: + True + phone-work-e + + + GTK_FILL + + + + + + True + 0 + Hom_e: + True + phone-home-e + + + 1 + 2 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + + + + + + True + 0 + _Mobile: + True + phone-mobile-e + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + Work _fax: + True + phone-work-fax-e + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + True + vertical + 6 + + + True + 0 + Instant Messaging + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 3 + 4 + 12 + 6 + + + True + 0 + _XMPP: + True + im-jabber-e + + + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + _Yahoo: + True + im-yahoo-e + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + M_SN: + True + im-msn-e + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + IC_Q: + True + im-icq-e + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + A_IM/iChat: + True + im-aim-e + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _GroupWise: + True + im-groupwise-e + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + + + 3 + 4 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 2 + 3 + + + + + + 1 + + + + + 1 + + + + + False + False + 2 + + + + + + + True + Contact + + + False + + + + + True + 12 + vertical + 18 + + + True + vertical + + + True + vertical + + + True + 0 + Home + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 4 + 4 + 12 + 6 + + + True + 0 + _Address: + True + addr-street-1 + + + GTK_FILL + + + + + + True + True + automatic + never + in + + + True + True + False + + + + + 1 + 2 + 3 + GTK_FILL + + + + + True + 0 + C_ity: + True + addr-locality-1 + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + _ZIP/Postal code: + True + addr-code-1 + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _State/Province: + True + addr-region-1 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + Co_untry: + True + addr-country-1 + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + True + 0 + P._O. box: + True + addr-po-1 + + + 3 + 4 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + True + + + 3 + 4 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 3 + 4 + + + + + + True + True + + + 3 + 4 + 2 + 3 + + + + + + + + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Work + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 4 + 4 + 12 + 6 + + + True + 0 + A_ddress: + True + addr-street-2 + + + GTK_FILL + + + + + + True + True + automatic + never + in + + + True + True + False + + + + + 1 + 2 + 3 + GTK_FILL + + + + + True + 0 + ZIP/_Postal code: + True + addr-code-2 + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + State/Pro_vince: + True + addr-region-2 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + Cou_ntry: + True + addr-country-2 + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + True + 0 + P.O. _box: + True + addr-po-2 + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + Ci_ty: + True + addr-locality-2 + + + 2 + 3 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + True + + + 3 + 4 + + + + + + True + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + + + 3 + 4 + 2 + 3 + + + + + + True + True + + + 3 + 4 + 3 + 4 + + + + + + + + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 1 + + + + + True + Address + + + 1 + False + + + + + True + 12 + vertical + 18 + + + True + vertical + 6 + + + True + 0 + Web + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 3 + 2 + 12 + 6 + + + True + 0 + _Home page: + True + web-homepage-e + + + GTK_FILL + + + + + + True + 0 + Web _log: + True + web-weblog-e + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Cale_ndar: + True + web-calendar-e + + + 2 + 3 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Job + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 6 + 2 + 12 + 6 + + + True + 0 + _Profession: + True + job-profession-e + + + GTK_FILL + + + + + + True + 0 + C_ompany: + True + job-company-e + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Manager: + True + job-manager-e + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + _Title: + True + job-title-e + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Department: + True + job-dept-e + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + A_ssistant: + True + job-assistant-e + + + 5 + 6 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + True + + + 1 + 2 + 4 + 5 + + + + + + True + True + + + 1 + 2 + 5 + 6 + + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 2 + + + + + True + Personal Info + + + 2 + False + + + + + 0 + + + + + 1 + + + + + 1 + + + + + True + end + + + gtk-close + True + True + True + False + True + + + False + False + 0 + + + + + False + end + 0 + + + + + + closebutton1 + + + diff --git a/capplets/about-me/mate-about-me-fingerprint.c b/capplets/about-me/mate-about-me-fingerprint.c new file mode 100644 index 00000000..0d6e40ae --- /dev/null +++ b/capplets/about-me/mate-about-me-fingerprint.c @@ -0,0 +1,624 @@ +/* mate-about-me-fingerprint.h + * Copyright (C) 2008 Bastien Nocera + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include + +#include "fingerprint-strings.h" +#include "capplet-util.h" + +/* This must match the number of images on the 2nd page in the UI file */ +#define MAX_ENROLL_STAGES 5 + +/* Translate fprintd strings */ +#define TR(s) dgettext("fprintd", s) + +static DBusGProxy *manager = NULL; +static DBusGConnection *connection = NULL; +static gboolean is_disable = FALSE; + +enum { + STATE_NONE, + STATE_CLAIMED, + STATE_ENROLLING +}; + +typedef struct { + GtkWidget *enable; + GtkWidget *disable; + + GtkWidget *ass; + GtkBuilder *dialog; + + DBusGProxy *device; + gboolean is_swipe; + int num_enroll_stages; + int num_stages_done; + char *name; + const char *finger; + gint state; +} EnrollData; + +static void create_manager (void) +{ + GError *error = NULL; + + connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + g_warning ("Failed to connect to session bus: %s", error->message); + return; + } + + manager = dbus_g_proxy_new_for_name (connection, + "net.reactivated.Fprint", + "/net/reactivated/Fprint/Manager", + "net.reactivated.Fprint.Manager"); +} + +static DBusGProxy * +get_first_device (void) +{ + DBusGProxy *device; + char *device_str; + + if (!dbus_g_proxy_call (manager, "GetDefaultDevice", NULL, G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &device_str, G_TYPE_INVALID)) { + return NULL; + } + + device = dbus_g_proxy_new_for_name(connection, + "net.reactivated.Fprint", + device_str, + "net.reactivated.Fprint.Device"); + + g_free (device_str); + + return device; +} + +static const char * +get_reason_for_error (const char *dbus_error) +{ + if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.PermissionDenied")) + return N_("You are not allowed to access the device. Contact your system administrator."); + if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.AlreadyInUse")) + return N_("The device is already in use."); + if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.Internal")) + return N_("An internal error occured"); + + return NULL; +} + +static GtkWidget * +get_error_dialog (const char *title, + const char *dbus_error, + GtkWindow *parent) +{ + GtkWidget *error_dialog; + const char *reason; + + if (dbus_error == NULL) + g_warning ("get_error_dialog called with reason == NULL"); + + error_dialog = + gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", title); + reason = get_reason_for_error (dbus_error); + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (error_dialog), "%s", reason ? _(reason) : _(dbus_error)); + + gtk_window_set_title (GTK_WINDOW (error_dialog), ""); /* as per HIG */ + gtk_container_set_border_width (GTK_CONTAINER (error_dialog), 5); + gtk_dialog_set_default_response (GTK_DIALOG (error_dialog), + GTK_RESPONSE_OK); + gtk_window_set_modal (GTK_WINDOW (error_dialog), TRUE); + gtk_window_set_position (GTK_WINDOW (error_dialog), GTK_WIN_POS_CENTER_ON_PARENT); + + return error_dialog; +} + +void +set_fingerprint_label (GtkWidget *enable, GtkWidget *disable) +{ + char **fingers; + DBusGProxy *device; + GError *error = NULL; + + gtk_widget_set_no_show_all (enable, TRUE); + gtk_widget_set_no_show_all (disable, TRUE); + + if (manager == NULL) { + create_manager (); + if (manager == NULL) { + gtk_widget_hide (enable); + gtk_widget_hide (disable); + return; + } + } + + device = get_first_device (); + if (device == NULL) { + gtk_widget_hide (enable); + gtk_widget_hide (disable); + return; + } + + if (!dbus_g_proxy_call (device, "ListEnrolledFingers", &error, G_TYPE_STRING, "", G_TYPE_INVALID, + G_TYPE_STRV, &fingers, G_TYPE_INVALID)) { + if (dbus_g_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints") == FALSE) { + gtk_widget_hide (enable); + gtk_widget_hide (disable); + g_object_unref (device); + return; + } + fingers = NULL; + } + + if (fingers == NULL || g_strv_length (fingers) == 0) { + gtk_widget_hide (disable); + gtk_widget_show (enable); + is_disable = FALSE; + } else { + gtk_widget_hide (enable); + gtk_widget_show (disable); + is_disable = TRUE; + } + + g_strfreev (fingers); + g_object_unref (device); +} + +static void +delete_fingerprints (void) +{ + DBusGProxy *device; + + if (manager == NULL) { + create_manager (); + if (manager == NULL) + return; + } + + device = get_first_device (); + if (device == NULL) + return; + + dbus_g_proxy_call (device, "DeleteEnrolledFingers", NULL, G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID); + + g_object_unref (device); +} + +static void +delete_fingerprints_question (GtkBuilder *dialog, GtkWidget *enable, GtkWidget *disable) +{ + GtkWidget *question; + GtkWidget *button; + + question = gtk_message_dialog_new (GTK_WINDOW (WID ("about-me-dialog")), + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Delete registered fingerprints?")); + gtk_dialog_add_button (GTK_DIALOG (question), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + button = gtk_button_new_with_mnemonic (_("_Delete Fingerprints")); + gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON)); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + gtk_dialog_add_action_widget (GTK_DIALOG (question), button, GTK_RESPONSE_OK); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (question), + _("Do you want to delete your registered fingerprints so fingerprint login is disabled?")); + gtk_container_set_border_width (GTK_CONTAINER (question), 5); + gtk_dialog_set_default_response (GTK_DIALOG (question), GTK_RESPONSE_OK); + gtk_window_set_position (GTK_WINDOW (question), GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal (GTK_WINDOW (question), TRUE); + + if (gtk_dialog_run (GTK_DIALOG (question)) == GTK_RESPONSE_OK) { + delete_fingerprints (); + set_fingerprint_label (enable, disable); + } + + gtk_widget_destroy (question); +} + +static void +enroll_data_destroy (EnrollData *data) +{ + switch (data->state) { + case STATE_ENROLLING: + dbus_g_proxy_call(data->device, "EnrollStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + /* fall-through */ + case STATE_CLAIMED: + dbus_g_proxy_call(data->device, "Release", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + /* fall-through */ + case STATE_NONE: + g_free (data->name); + g_object_unref (data->device); + g_object_unref (data->dialog); + gtk_widget_destroy (data->ass); + + g_free (data); + } +} + +static const char * +selected_finger (GtkBuilder *dialog) +{ + int index; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton1")))) { + gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE); + return "right-index-finger"; + } + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton2")))) { + gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE); + return "left-index-finger"; + } + gtk_widget_set_sensitive (WID ("finger_combobox"), TRUE); + index = gtk_combo_box_get_active (GTK_COMBO_BOX (WID ("finger_combobox"))); + switch (index) { + case 0: + return "left-thumb"; + case 1: + return "left-middle-finger"; + case 2: + return "left-ring-finger"; + case 3: + return "left-little-finger"; + case 4: + return "right-thumb"; + case 5: + return "right-middle-finger"; + case 6: + return "right-ring-finger"; + case 7: + return "right-little-finger"; + default: + g_assert_not_reached (); + } + + return NULL; +} + +static void +finger_radio_button_toggled (GtkToggleButton *button, EnrollData *data) +{ + GtkBuilder *dialog = data->dialog; + char *msg; + + data->finger = selected_finger (data->dialog); + + msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); + g_free (msg); +} + +static void +finger_combobox_changed (GtkComboBox *combobox, EnrollData *data) +{ + GtkBuilder *dialog = data->dialog; + char *msg; + + data->finger = selected_finger (data->dialog); + + msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); + g_free (msg); +} + +static void +assistant_cancelled (GtkAssistant *ass, EnrollData *data) +{ + GtkWidget *enable, *disable; + + enable = data->enable; + disable = data->disable; + + enroll_data_destroy (data); + set_fingerprint_label (enable, disable); +} + +static void +enroll_result (GObject *object, const char *result, gboolean done, EnrollData *data) +{ + GtkBuilder *dialog = data->dialog; + char *msg; + + if (g_str_equal (result, "enroll-completed") || g_str_equal (result, "enroll-stage-passed")) { + char *name, *path; + + data->num_stages_done++; + name = g_strdup_printf ("image%d", data->num_stages_done); + path = g_build_filename (MATECC_PIXMAP_DIR, "print_ok.png", NULL); + gtk_image_set_from_file (GTK_IMAGE (WID (name)), path); + g_free (name); + g_free (path); + } + if (g_str_equal (result, "enroll-completed")) { + gtk_label_set_text (GTK_LABEL (WID ("status-label")), _("Done!")); + gtk_assistant_set_page_complete (GTK_ASSISTANT (data->ass), WID ("page2"), TRUE); + } + + if (done != FALSE) { + dbus_g_proxy_call(data->device, "EnrollStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_CLAIMED; + if (g_str_equal (result, "enroll-completed") == FALSE) { + /* The enrollment failed, restart it */ + dbus_g_proxy_call(data->device, "EnrollStart", NULL, G_TYPE_STRING, data->finger, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_ENROLLING; + result = "enroll-retry-scan"; + } else { + return; + } + } + + msg = g_strdup_printf (TR(enroll_result_str_to_msg (result, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID ("status-label")), msg); + g_free (msg); +} + +static void +assistant_prepare (GtkAssistant *ass, GtkWidget *page, EnrollData *data) +{ + const char *name; + + name = g_object_get_data (G_OBJECT (page), "name"); + if (name == NULL) + return; + + if (g_str_equal (name, "enroll")) { + DBusGProxy *p; + GError *error = NULL; + GtkBuilder *dialog = data->dialog; + char *path; + guint i; + GValue value = { 0, }; + + if (!dbus_g_proxy_call (data->device, "Claim", &error, G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID)) { + GtkWidget *d; + char *msg; + + /* translators: + * The variable is the name of the device, for example: + * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ + msg = g_strdup_printf (_("Could not access '%s' device"), data->name); + d = get_error_dialog (msg, dbus_g_error_get_name (error), GTK_WINDOW (data->ass)); + g_error_free (error); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + g_free (msg); + + enroll_data_destroy (data); + + return; + } + data->state = STATE_CLAIMED; + + p = dbus_g_proxy_new_from_proxy (data->device, "org.freedesktop.DBus.Properties", NULL); + if (!dbus_g_proxy_call (p, "Get", NULL, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_STRING, "num-enroll-stages", G_TYPE_INVALID, + G_TYPE_VALUE, &value, G_TYPE_INVALID) || g_value_get_int (&value) < 1) { + GtkWidget *d; + char *msg; + + /* translators: + * The variable is the name of the device, for example: + * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ + msg = g_strdup_printf (_("Could not access '%s' device"), data->name); + d = get_error_dialog (msg, "net.reactivated.Fprint.Error.Internal", GTK_WINDOW (data->ass)); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + g_free (msg); + + enroll_data_destroy (data); + + g_object_unref (p); + return; + } + g_object_unref (p); + + data->num_enroll_stages = g_value_get_int (&value); + + /* Hide the extra "bulbs" if not needed */ + for (i = MAX_ENROLL_STAGES; i > data->num_enroll_stages; i--) { + char *name; + + name = g_strdup_printf ("image%d", i); + gtk_widget_hide (WID (name)); + g_free (name); + } + /* And set the right image */ + { + char *filename; + + filename = g_strdup_printf ("%s.png", data->finger); + path = g_build_filename (MATECC_PIXMAP_DIR, filename, NULL); + g_free (filename); + } + for (i = 1; i <= data->num_enroll_stages; i++) { + char *name; + name = g_strdup_printf ("image%d", i); + gtk_image_set_from_file (GTK_IMAGE (WID (name)), path); + g_free (name); + } + g_free (path); + + dbus_g_proxy_add_signal(data->device, "EnrollStatus", G_TYPE_STRING, G_TYPE_BOOLEAN, NULL); + dbus_g_proxy_connect_signal(data->device, "EnrollStatus", G_CALLBACK(enroll_result), data, NULL); + + if (!dbus_g_proxy_call(data->device, "EnrollStart", &error, G_TYPE_STRING, data->finger, G_TYPE_INVALID, G_TYPE_INVALID)) { + GtkWidget *d; + char *msg; + + /* translators: + * The variable is the name of the device, for example: + * "Could you not access "Digital Persona U.are.U 4000/4000B" device */ + msg = g_strdup_printf (_("Could not start finger capture on '%s' device"), data->name); + d = get_error_dialog (msg, dbus_g_error_get_name (error), GTK_WINDOW (data->ass)); + g_error_free (error); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + g_free (msg); + + enroll_data_destroy (data); + + return; + } + data->state = STATE_ENROLLING;; + } else { + if (data->state == STATE_ENROLLING) { + dbus_g_proxy_call(data->device, "EnrollStop", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_CLAIMED; + } + if (data->state == STATE_CLAIMED) { + dbus_g_proxy_call(data->device, "Release", NULL, G_TYPE_INVALID, G_TYPE_INVALID); + data->state = STATE_NONE; + } + } +} + +static void +enroll_fingerprints (GtkWindow *parent, GtkWidget *enable, GtkWidget *disable) +{ + DBusGProxy *device, *p; + GHashTable *props; + GtkBuilder *dialog; + EnrollData *data; + GtkWidget *ass; + char *msg; + + device = NULL; + + if (manager == NULL) { + create_manager (); + if (manager != NULL) + device = get_first_device (); + } else { + device = get_first_device (); + } + + if (manager == NULL || device == NULL) { + GtkWidget *d; + + d = get_error_dialog (_("Could not access any fingerprint readers"), + _("Please contact your system administrator for help."), + parent); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + return; + } + + data = g_new0 (EnrollData, 1); + data->device = device; + data->enable = enable; + data->disable = disable; + + /* Get some details about the device */ + p = dbus_g_proxy_new_from_proxy (device, "org.freedesktop.DBus.Properties", NULL); + if (dbus_g_proxy_call (p, "GetAll", NULL, G_TYPE_STRING, "net.reactivated.Fprint.Device", G_TYPE_INVALID, + dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &props, G_TYPE_INVALID)) { + const char *scan_type; + data->name = g_value_dup_string (g_hash_table_lookup (props, "name")); + scan_type = g_value_dup_string (g_hash_table_lookup (props, "scan-type")); + if (g_str_equal (scan_type, "swipe")) + data->is_swipe = TRUE; + g_hash_table_destroy (props); + } + g_object_unref (p); + + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-about-me-fingerprint.ui", NULL); + data->dialog = dialog; + + ass = WID ("assistant"); + gtk_window_set_title (GTK_WINDOW (ass), _("Enable Fingerprint Login")); + gtk_window_set_transient_for (GTK_WINDOW (ass), parent); + gtk_window_set_position (GTK_WINDOW (ass), GTK_WIN_POS_CENTER_ON_PARENT); + g_signal_connect (G_OBJECT (ass), "cancel", + G_CALLBACK (assistant_cancelled), data); + g_signal_connect (G_OBJECT (ass), "close", + G_CALLBACK (assistant_cancelled), data); + g_signal_connect (G_OBJECT (ass), "prepare", + G_CALLBACK (assistant_prepare), data); + + /* Page 1 */ + gtk_combo_box_set_active (GTK_COMBO_BOX (WID ("finger_combobox")), 0); + + g_signal_connect (G_OBJECT (WID ("radiobutton1")), "toggled", + G_CALLBACK (finger_radio_button_toggled), data); + g_signal_connect (G_OBJECT (WID ("radiobutton2")), "toggled", + G_CALLBACK (finger_radio_button_toggled), data); + g_signal_connect (G_OBJECT (WID ("radiobutton3")), "toggled", + G_CALLBACK (finger_radio_button_toggled), data); + g_signal_connect (G_OBJECT (WID ("finger_combobox")), "changed", + G_CALLBACK (finger_combobox_changed), data); + + data->finger = selected_finger (dialog); + + g_object_set_data (G_OBJECT (WID("page1")), "name", "intro"); + + /* translators: + * The variable is the name of the device, for example: + * "To enable fingerprint login, you need to save one of your fingerprints, using the + * 'Digital Persona U.are.U 4000/4000B' device." */ + msg = g_strdup_printf (_("To enable fingerprint login, you need to save one of your fingerprints, using the '%s' device."), + data->name); + gtk_label_set_text (GTK_LABEL (WID("intro-label")), msg); + g_free (msg); + + gtk_assistant_set_page_complete (GTK_ASSISTANT (ass), WID("page1"), TRUE); + + /* Page 2 */ + if (data->is_swipe != FALSE) + gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page2"), _("Swipe finger on reader")); + else + gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page2"), _("Place finger on reader")); + + g_object_set_data (G_OBJECT (WID("page2")), "name", "enroll"); + + msg = g_strdup_printf (TR(finger_str_to_msg (data->finger, data->is_swipe)), data->name); + gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg); + g_free (msg); + + /* Page 3 */ + g_object_set_data (G_OBJECT (WID("page3")), "name", "summary"); + + data->ass = ass; + gtk_widget_show_all (ass); +} + +void +fingerprint_button_clicked (GtkBuilder *dialog, + GtkWidget *enable, + GtkWidget *disable) +{ + bindtextdomain ("fprintd", MATELOCALEDIR); + bind_textdomain_codeset ("fprintd", "UTF-8"); + + if (is_disable != FALSE) { + delete_fingerprints_question (dialog, enable, disable); + } else { + enroll_fingerprints (GTK_WINDOW (WID ("about-me-dialog")), enable, disable); + } +} + diff --git a/capplets/about-me/mate-about-me-fingerprint.h b/capplets/about-me/mate-about-me-fingerprint.h new file mode 100644 index 00000000..c60ca358 --- /dev/null +++ b/capplets/about-me/mate-about-me-fingerprint.h @@ -0,0 +1,27 @@ +/* mate-about-me-fingerprint.h + * Copyright (C) 2008 Bastien Nocera + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +void set_fingerprint_label (GtkWidget *enable, + GtkWidget *disable); +void fingerprint_button_clicked (GtkBuilder *dialog, + GtkWidget *enable, + GtkWidget *disable); + diff --git a/capplets/about-me/mate-about-me-fingerprint.ui b/capplets/about-me/mate-about-me-fingerprint.ui new file mode 100644 index 00000000..7f154336 --- /dev/null +++ b/capplets/about-me/mate-about-me-fingerprint.ui @@ -0,0 +1,276 @@ + + + + + + + + + + + + Left thumb + + + Left middle finger + + + Left ring finger + + + Left little finger + + + Right thumb + + + Right middle finger + + + Right ring finger + + + Right little finger + + + + + 12 + Enable Fingerprint Login + + + + + + True + 12 + vertical + 12 + + + True + 6 + + + True + gtk-dialog-info + 6 + + + False + 0 + + + + + True + To enable fingerprint login, you need to save one of your fingerprints, using the Acme Foobar 5000. + True + + + False + False + 1 + + + + + 0 + + + + + True + vertical + + + Right index finger + True + True + False + True + True + True + + + False + False + 0 + + + + + Left index finger + True + True + False + True + True + radiobutton1 + + + False + False + 1 + + + + + True + + + Other finger: + True + True + False + True + True + radiobutton1 + + + False + False + 0 + + + + + True + False + model1 + + + + 0 + + + + + False + 1 + + + + + False + 2 + + + + + 1 + + + + + Select finger + + + + + True + vertical + + + True + In order to save your fingerprints, you need to swipe your thumb on the "Acme foobar" device. + True + + + False + False + 0 + + + + + True + + + + + + True + gtk-no + 6 + + + 1 + + + + + True + gtk-no + 6 + + + 2 + + + + + True + gtk-no + 6 + + + 3 + + + + + True + gtk-no + 6 + + + 4 + + + + + True + gtk-no + 6 + + + 5 + + + + + + + + 1 + + + + + True + + + False + False + 2 + + + + + Swipe finger on reader + + + + + True + Your fingerprint was successfully saved. You should now be able to log in using your fingerprint reader. + True + + + summary + Done! + + + + diff --git a/capplets/about-me/mate-about-me-password.c b/capplets/about-me/mate-about-me-password.c new file mode 100644 index 00000000..0699c28d --- /dev/null +++ b/capplets/about-me/mate-about-me-password.c @@ -0,0 +1,1136 @@ +/* mate-about-me.c + * Copyright (C) 2002 Diego Gonzalez + * Copyright (C) 2006 Johannes H. Jensen + * + * Written by: Diego Gonzalez + * Modified by: Johannes H. Jensen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Parts of this code come from Mate-System-Tools. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Are all of these needed? */ +#include +#include +#include +#include +#include +#include +#include + +#if __sun +#include +#include +#endif + +#include "capplet-util.h" +#include "eel-alert-dialog.h" + +/* Passwd states */ +typedef enum { + PASSWD_STATE_NONE, /* Passwd is not asking for anything */ + PASSWD_STATE_AUTH, /* Passwd is asking for our current password */ + PASSWD_STATE_NEW, /* Passwd is asking for our new password */ + PASSWD_STATE_RETYPE, /* Passwd is asking for our retyped new password */ + PASSWD_STATE_ERR /* Passwd reported an error but has not yet exited */ +} PasswdState; + +typedef struct { + GtkBuilder *ui; + + /* Commonly used widgets */ + GtkEntry *current_password; + GtkEntry *new_password; + GtkEntry *retyped_password; + GtkImage *dialog_image; + GtkLabel *status_label; + + /* Whether we have authenticated */ + gboolean authenticated; + + /* Communication with the passwd program */ + GPid backend_pid; + + GIOChannel *backend_stdin; + GIOChannel *backend_stdout; + + GQueue *backend_stdin_queue; /* Write queue to backend_stdin */ + + /* GMainLoop IDs */ + guint backend_child_watch_id; /* g_child_watch_add (PID) */ + guint backend_stdout_watch_id; /* g_io_add_watch (stdout) */ + + /* State of the passwd program */ + PasswdState backend_state; + +} PasswordDialog; + +/* Buffer size for backend output */ +#define BUFSIZE 64 + +/* + * Error handling {{ + */ +#define PASSDLG_ERROR (mate_about_me_password_error_quark()) + +GQuark mate_about_me_password_error_quark(void) +{ + static GQuark q = 0; + + if (q == 0) { + q = g_quark_from_static_string("mate_about_me_password_error"); + } + + return q; +} + +/* error codes */ +enum { + PASSDLG_ERROR_NONE, + PASSDLG_ERROR_NEW_PASSWORD_EMPTY, + PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY, + PASSDLG_ERROR_PASSWORDS_NOT_EQUAL, + PASSDLG_ERROR_BACKEND, /* Backend error */ + PASSDLG_ERROR_USER, /* Generic user error */ + PASSDLG_ERROR_FAILED /* Fatal failure, error->message should explain */ +}; + +/* + * }} Error handling + */ + +/* + * Prototypes {{ + */ +static void +stop_passwd (PasswordDialog *pdialog); + +static void +free_passwd_resources (PasswordDialog *pdialog); + +static gboolean +io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog); + +static void +passdlg_set_auth_state (PasswordDialog *pdialog, gboolean state); + +static void +passdlg_set_status (PasswordDialog *pdialog, gchar *msg); + +static void +passdlg_set_busy (PasswordDialog *pdialog, gboolean busy); + +static void +passdlg_clear (PasswordDialog *pdialog); + +static guint +passdlg_refresh_password_state (PasswordDialog *pdialog); + +/* + * }} Prototypes + */ + +/* + * Spawning and closing of backend {{ + */ + +/* Child watcher */ +static void +child_watch_cb (GPid pid, gint status, PasswordDialog *pdialog) +{ + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) >= 255) { + g_warning (_("Child exited unexpectedly")); + } + } + + free_passwd_resources (pdialog); +} + +/* Spawn passwd backend + * Returns: TRUE on success, FALSE otherwise and sets error appropriately */ +static gboolean +spawn_passwd (PasswordDialog *pdialog, GError **error) +{ + gchar *argv[2]; + gchar *envp[1]; + gint my_stdin, my_stdout, my_stderr; + + argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */ + argv[1] = NULL; + + envp[0] = NULL; /* If we pass an empty array as the environment, + * will the childs environment be empty, and the + * locales set to the C default? From the manual: + * "If envp is NULL, the child inherits its + * parent'senvironment." + * If I'm wrong here, we somehow have to set + * the locales here. + */ + + if (!g_spawn_async_with_pipes (NULL, /* Working directory */ + argv, /* Argument vector */ + envp, /* Environment */ + G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */ + NULL, /* Child setup */ + NULL, /* Data to child setup */ + &pdialog->backend_pid, /* PID */ + &my_stdin, /* Stdin */ + &my_stdout, /* Stdout */ + &my_stderr, /* Stderr */ + error)) { /* GError */ + + /* An error occured */ + free_passwd_resources (pdialog); + + return FALSE; + } + + /* 2>&1 */ + if (dup2 (my_stderr, my_stdout) == -1) { + /* Failed! */ + g_set_error (error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + strerror (errno)); + + /* Clean up */ + stop_passwd (pdialog); + + return FALSE; + } + + /* Open IO Channels */ + pdialog->backend_stdin = g_io_channel_unix_new (my_stdin); + pdialog->backend_stdout = g_io_channel_unix_new (my_stdout); + + /* Set raw encoding */ + /* Set nonblocking mode */ + if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) { + + /* Clean up */ + stop_passwd (pdialog); + return FALSE; + } + + /* Turn off buffering */ + g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); + g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); + + /* Add IO Channel watcher */ + pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, + G_IO_IN | G_IO_PRI, + (GIOFunc) io_watch_stdout, pdialog); + + /* Add child watcher */ + pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog); + + /* Success! */ + + return TRUE; +} + +/* Stop passwd backend */ +static void +stop_passwd (PasswordDialog *pdialog) +{ + /* This is the standard way of returning from the dialog with passwd. + * If we return this way we can safely kill passwd as it has completed + * its task. + */ + + if (pdialog->backend_pid != -1) { + kill (pdialog->backend_pid, 9); + } + + /* We must run free_passwd_resources here and not let our child + * watcher do it, since it will access invalid memory after the + * dialog has been closed and cleaned up. + * + * If we had more than a single thread we'd need to remove + * the child watch before trying to kill the child. + */ + free_passwd_resources (pdialog); +} + +/* Clean up passwd resources */ +static void +free_passwd_resources (PasswordDialog *pdialog) +{ + GError *error = NULL; + + /* Remove the child watcher */ + if (pdialog->backend_child_watch_id != 0) { + + g_source_remove (pdialog->backend_child_watch_id); + + pdialog->backend_child_watch_id = 0; + } + + + /* Close IO channels (internal file descriptors are automatically closed) */ + if (pdialog->backend_stdin != NULL) { + + if (g_io_channel_shutdown (pdialog->backend_stdin, TRUE, &error) != G_IO_STATUS_NORMAL) { + g_warning (_("Could not shutdown backend_stdin IO channel: %s"), error->message); + g_error_free (error); + error = NULL; + } + + g_io_channel_unref (pdialog->backend_stdin); + + pdialog->backend_stdin = NULL; + } + + if (pdialog->backend_stdout != NULL) { + + if (g_io_channel_shutdown (pdialog->backend_stdout, TRUE, &error) != G_IO_STATUS_NORMAL) { + g_warning (_("Could not shutdown backend_stdout IO channel: %s"), error->message); + g_error_free (error); + error = NULL; + } + + g_io_channel_unref (pdialog->backend_stdout); + + pdialog->backend_stdout = NULL; + } + + /* Remove IO watcher */ + if (pdialog->backend_stdout_watch_id != 0) { + + g_source_remove (pdialog->backend_stdout_watch_id); + + pdialog->backend_stdout_watch_id = 0; + } + + /* Close PID */ + if (pdialog->backend_pid != -1) { + + g_spawn_close_pid (pdialog->backend_pid); + + pdialog->backend_pid = -1; + } + + /* Clear backend state */ + pdialog->backend_state = PASSWD_STATE_NONE; +} + +/* + * }} Spawning and closing of backend + */ + +/* + * Backend communication code {{ + */ + +/* Write the first element of queue through channel */ +static void +io_queue_pop (GQueue *queue, GIOChannel *channel) +{ + gchar *buf; + gsize bytes_written; + GError *error = NULL; + + buf = g_queue_pop_head (queue); + + if (buf != NULL) { + + if (g_io_channel_write_chars (channel, buf, -1, &bytes_written, &error) != G_IO_STATUS_NORMAL) { + g_warning ("Could not write queue element \"%s\" to channel: %s", buf, error->message); + g_error_free (error); + } + + g_free (buf); + } +} + +/* Goes through the argument list, checking if one of them occurs in str + * Returns: TRUE as soon as an element is found to match, FALSE otherwise */ +static gboolean +is_string_complete (gchar *str, ...) +{ + va_list ap; + gchar *arg; + + if (strlen (str) == 0) { + return FALSE; + } + + va_start (ap, str); + + while ((arg = va_arg (ap, char *)) != NULL) { + if (g_strrstr (str, arg) != NULL) { + va_end (ap); + return TRUE; + } + } + + va_end (ap); + + return FALSE; +} + +/* Authentication attempt succeeded. Update the GUI accordingly. */ +static void +authenticated_user (PasswordDialog *pdialog) +{ + pdialog->backend_state = PASSWD_STATE_NEW; + + if (pdialog->authenticated) { + /* This is a re-authentication + * It succeeded, so pop our new password from the queue */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + } + + /* Update UI state */ + passdlg_set_auth_state (pdialog, TRUE); + passdlg_set_status (pdialog, _("Authenticated!")); + + /* Check to see if the passwords are valid + * (They might be non-empty if the user had to re-authenticate, + * and thus we need to enable the change-password-button) */ + passdlg_refresh_password_state (pdialog); +} + +/* + * IO watcher for stdout, called whenever there is data to read from the backend. + * This is where most of the actual IO handling happens. + */ +static gboolean +io_watch_stdout (GIOChannel *source, GIOCondition condition, PasswordDialog *pdialog) +{ + static GString *str = NULL; /* Persistent buffer */ + + gchar buf[BUFSIZE]; /* Temporary buffer */ + gsize bytes_read; + GError *error = NULL; + + gchar *msg = NULL; /* Status error message */ + GtkBuilder *dialog; + + gboolean reinit = FALSE; + + /* Initialize buffer */ + if (str == NULL) { + str = g_string_new (""); + } + + dialog = pdialog->ui; + + if (g_io_channel_read_chars (source, buf, BUFSIZE, &bytes_read, &error) != G_IO_STATUS_NORMAL) { + g_warning ("IO Channel read error: %s", error->message); + g_error_free (error); + + return TRUE; + } + + str = g_string_append_len (str, buf, bytes_read); + + /* In which state is the backend? */ + switch (pdialog->backend_state) { + case PASSWD_STATE_AUTH: + /* Passwd is asking for our current password */ + + if (is_string_complete (str->str, "assword: ", "failure", "wrong", "error", NULL)) { + /* Which response did we get? */ + passdlg_set_busy (pdialog, FALSE); + + if (g_strrstr (str->str, "assword: ") != NULL) { + /* Authentication successful */ + + authenticated_user (pdialog); + + } else { + /* Authentication failed */ + + if (pdialog->authenticated) { + /* This is a re-auth, and it failed. + * The password must have been changed in the meantime! + * Ask the user to re-authenticate + */ + + /* Update status message and auth state */ + passdlg_set_status (pdialog, _("Your password has been changed since you initially authenticated! Please re-authenticate.")); + } else { + passdlg_set_status (pdialog, _("That password was incorrect.")); + } + + /* Focus current password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->current_password)); + } + + reinit = TRUE; + } + break; + case PASSWD_STATE_NEW: + /* Passwd is asking for our new password */ + + if (is_string_complete (str->str, "assword: ", NULL)) { + /* Advance to next state */ + pdialog->backend_state = PASSWD_STATE_RETYPE; + + /* Pop retyped password from queue and into IO channel */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + + reinit = TRUE; + } + break; + case PASSWD_STATE_RETYPE: + /* Passwd is asking for our retyped new password */ + + if (is_string_complete (str->str, "successfully", + "short", + "longer", + "palindrome", + "dictionary", + "simpl", /* catches both simple and simplistic */ + "similar", + "different", + "case", + "wrapped", + "recovered", + "recent" + "unchanged", + "match", + "1 numeric or special", + "failure", + NULL)) { + + /* What response did we get? */ + passdlg_set_busy (pdialog, FALSE); + + if (g_strrstr (str->str, "successfully") != NULL) { + /* Hooray! */ + + passdlg_clear (pdialog); + passdlg_set_status (pdialog, _("Your password has been changed.")); + } else { + /* Ohnoes! */ + + /* Focus new password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->new_password)); + + if (g_strrstr (str->str, "recovered") != NULL) { + /* What does this indicate? + * "Authentication information cannot be recovered?" from libpam? */ + msg = g_strdup_printf (_("System error: %s."), str->str); + } else if (g_strrstr (str->str, "short") != NULL || + g_strrstr (str->str, "longer") != NULL) { + msg = g_strdup (_("The password is too short.")); + } else if (g_strrstr (str->str, "palindrome") != NULL || + g_strrstr (str->str, "simpl") != NULL || + g_strrstr (str->str, "dictionary") != NULL) { + msg = g_strdup (_("The password is too simple.")); + } else if (g_strrstr (str->str, "similar") != NULL || + g_strrstr (str->str, "different") != NULL || + g_strrstr (str->str, "case") != NULL || + g_strrstr (str->str, "wrapped") != NULL) { + msg = g_strdup (_("The old and new passwords are too similar.")); + } else if (g_strrstr (str->str, "1 numeric or special") != NULL) { + msg = g_strdup (_("The new password must contain numeric or special character(s).")); + } else if (g_strrstr (str->str, "unchanged") != NULL || + g_strrstr (str->str, "match") != NULL) { + msg = g_strdup (_("The old and new passwords are the same.")); + } else if (g_strrstr (str->str, "recent") != NULL) { + msg = g_strdup (_("The new password has already been used recently.")); + } else if (g_strrstr (str->str, "failure") != NULL) { + /* Authentication failure */ + msg = g_strdup (_("Your password has been changed since you initially authenticated! Please re-authenticate.")); + + passdlg_set_auth_state (pdialog, FALSE); + } + } + + reinit = TRUE; + + if (msg != NULL) { + /* An error occured! */ + passdlg_set_status (pdialog, msg); + g_free (msg); + + /* At this point, passwd might have exited, in which case + * child_watch_cb should clean up for us and remove this watcher. + * On some error conditions though, passwd just re-prompts us + * for our new password. */ + + pdialog->backend_state = PASSWD_STATE_ERR; + } + + /* child_watch_cb should clean up for us now */ + } + break; + case PASSWD_STATE_NONE: + /* Passwd is not asking for anything yet */ + if (is_string_complete (str->str, "assword: ", NULL)) { + + /* If the user does not have a password set, + * passwd will immediately ask for the new password, + * so skip the AUTH phase */ + if (is_string_complete (str->str, "new", "New", NULL)) { + gchar *pw; + + pdialog->backend_state = PASSWD_STATE_NEW; + + passdlg_set_busy (pdialog, FALSE); + authenticated_user (pdialog); + + /* since passwd didn't ask for our old password + * in this case, simply remove it from the queue */ + pw = g_queue_pop_head (pdialog->backend_stdin_queue); + g_free (pw); + } else { + + pdialog->backend_state = PASSWD_STATE_AUTH; + + /* Pop the IO queue, i.e. send current password */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + } + + reinit = TRUE; + } + break; + default: + /* Passwd has returned an error */ + reinit = TRUE; + break; + } + + if (reinit) { + g_string_free (str, TRUE); + str = NULL; + } + + /* Continue calling us */ + return TRUE; +} + +/* + * }} Backend communication code + */ + +/* Adds the current password to the IO queue */ +static void +authenticate (PasswordDialog *pdialog) +{ + gchar *s; + + s = g_strdup_printf ("%s\n", gtk_entry_get_text (pdialog->current_password)); + + g_queue_push_tail (pdialog->backend_stdin_queue, s); +} + +/* Adds the new password twice to the IO queue */ +static void +update_password (PasswordDialog *pdialog) +{ + gchar *s; + + s = g_strdup_printf ("%s\n", gtk_entry_get_text (pdialog->new_password)); + + g_queue_push_tail (pdialog->backend_stdin_queue, s); + /* We need to allocate new space because io_queue_pop() g_free()s + * every element of the queue after it's done */ + g_queue_push_tail (pdialog->backend_stdin_queue, g_strdup (s)); +} + +/* Sets dialog busy state according to busy + * + * When busy: + * Sets the cursor to busy + * Disables the interface to prevent that the user interferes + * Reverts all this when non-busy + * + * Note that this function takes into account the + * authentication state of the dialog. So setting the + * dialog to busy and then back to normal should leave + * the dialog unchanged. + */ +static void +passdlg_set_busy (PasswordDialog *pdialog, gboolean busy) +{ + GtkBuilder *dialog; + GtkWidget *toplevel; + GdkCursor *cursor = NULL; + GdkDisplay *display; + + dialog = pdialog->ui; + + /* Set cursor */ + toplevel = WID ("change-password"); + display = gtk_widget_get_display (toplevel); + if (busy) { + cursor = gdk_cursor_new_for_display (display, GDK_WATCH); + } + + gdk_window_set_cursor (gtk_widget_get_window (toplevel), cursor); + gdk_display_flush (display); + + if (busy) { + gdk_cursor_unref (cursor); + } + + /* Disable/Enable UI */ + if (pdialog->authenticated) { + /* Authenticated state */ + + /* Enable/disable new password section */ + g_object_set (pdialog->new_password, "sensitive", !busy, NULL); + g_object_set (pdialog->retyped_password, "sensitive", !busy, NULL); + g_object_set (WID ("new-password-label"), "sensitive", !busy, NULL); + g_object_set (WID ("retyped-password-label"), "sensitive", !busy, NULL); + + /* Enable/disable change password button */ + g_object_set (WID ("change-password-button"), "sensitive", !busy, NULL); + + } else { + /* Not-authenticated state */ + + /* Enable/disable auth section state */ + g_object_set (pdialog->current_password, "sensitive", !busy, NULL); + g_object_set (WID ("authenticate-button"), "sensitive", !busy, NULL); + g_object_set (WID ("current-password-label"), "sensitive", !busy, NULL); + } +} + +/* Launch an error dialog */ +static void +passdlg_error_dialog (GtkWindow *parent, const gchar *title, + const gchar *msg, const gchar *details) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + msg); + if (title) + gtk_window_set_title (GTK_WINDOW (dialog), title); + + if (details) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + details); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/* Set authenticated state of dialog according to state + * + * When in authenticated state: + * Disables authentication-part of interface + * Enables new-password-part of interface + * When in not-authenticated state: + * Enables authentication-part of interface + * Disables new-password-part of interface + * Disables the change-password-button + */ +static void +passdlg_set_auth_state (PasswordDialog *pdialog, gboolean state) +{ + GtkBuilder *dialog; + + dialog = pdialog->ui; + + /* Widgets which require a not-authenticated state to be accessible */ + g_object_set (pdialog->current_password, "sensitive", !state, NULL); + g_object_set (WID ("current-password-label"), "sensitive", !state, NULL); + g_object_set (WID ("authenticate-button"), "sensitive", !state, NULL); + + /* Widgets which require authentication to be accessible */ + g_object_set (pdialog->new_password, "sensitive", state, NULL); + g_object_set (pdialog->retyped_password, "sensitive", state, NULL); + g_object_set (WID ("new-password-label"), "sensitive", state, NULL); + g_object_set (WID ("retyped-password-label"), "sensitive", state, NULL); + + if (!state) { + /* Disable change-password-button when in not-authenticated state */ + g_object_set (WID ("change-password-button"), "sensitive", FALSE, NULL); + } + + pdialog->authenticated = state; + + if (state) { + /* Authenticated state */ + + /* Focus new password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->new_password)); + + /* Set open lock image */ + gtk_image_set_from_icon_name (GTK_IMAGE (pdialog->dialog_image), "changes-allow", GTK_ICON_SIZE_DIALOG); + } else { + /* Not authenticated state */ + + /* Focus current password */ + gtk_widget_grab_focus (GTK_WIDGET (pdialog->current_password)); + + /* Set closed lock image */ + gtk_image_set_from_icon_name (GTK_IMAGE (pdialog->dialog_image), "changes-prevent", GTK_ICON_SIZE_DIALOG); + } +} + +/* Set status field message */ +static void +passdlg_set_status (PasswordDialog *pdialog, gchar *msg) +{ + g_object_set (pdialog->status_label, "label", msg, NULL); +} + +/* Clear dialog (except the status message) */ +static void +passdlg_clear (PasswordDialog *pdialog) +{ + /* Set non-authenticated state */ + passdlg_set_auth_state (pdialog, FALSE); + + /* Clear password entries */ + gtk_entry_set_text (pdialog->current_password, ""); + gtk_entry_set_text (pdialog->new_password, ""); + gtk_entry_set_text (pdialog->retyped_password, ""); +} + +/* Start backend and handle errors + * If backend is already running, stop it + * If an error occurs, show error dialog */ +static gboolean +passdlg_spawn_passwd (PasswordDialog *pdialog) +{ + GError *error = NULL; + gchar *details; + + /* If backend is running, stop it */ + stop_passwd (pdialog); + + /* Spawn backend */ + if (!spawn_passwd (pdialog, &error)) { + GtkWidget *parent = GTK_WIDGET (gtk_builder_get_object (pdialog->ui, "change-password")); + + /* translators: Unable to launch : */ + details = g_strdup_printf (_("Unable to launch %s: %s"), + "/usr/bin/passwd", error->message); + + passdlg_error_dialog (GTK_WINDOW (parent), + _("Unable to launch backend"), + _("A system error has occurred"), + details); + + g_free (details); + g_error_free (error); + + return FALSE; + } + + return TRUE; +} + +/* Called when the "Authenticate" button is clicked */ +static void +passdlg_authenticate (GtkButton *button, PasswordDialog *pdialog) +{ + /* Set busy as this can be a long process */ + passdlg_set_busy (pdialog, TRUE); + + /* Update status message */ + passdlg_set_status (pdialog, _("Checking password...")); + + /* Spawn backend */ + if (!passdlg_spawn_passwd (pdialog)) { + passdlg_set_busy (pdialog, FALSE); + return; + } + + authenticate (pdialog); + + /* Our IO watcher should now handle the rest */ +} + +/* Validate passwords + * Returns: + * PASSDLG_ERROR_NONE (0) if passwords are valid + * PASSDLG_ERROR_NEW_PASSWORD_EMPTY + * PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY + * PASSDLG_ERROR_PASSWORDS_NOT_EQUAL + */ +static guint +passdlg_validate_passwords (PasswordDialog *pdialog) +{ + GtkBuilder *dialog; + const gchar *new_password, *retyped_password; + glong nlen, rlen; + + dialog = pdialog->ui; + + new_password = gtk_entry_get_text (pdialog->new_password); + retyped_password = gtk_entry_get_text (pdialog->retyped_password); + + nlen = g_utf8_strlen (new_password, -1); + rlen = g_utf8_strlen (retyped_password, -1); + + if (nlen == 0) { + /* New password empty */ + return PASSDLG_ERROR_NEW_PASSWORD_EMPTY; + } + + if (rlen == 0) { + /* Retyped password empty */ + return PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY; + } + + if (nlen != rlen || strncmp (new_password, retyped_password, nlen) != 0) { + /* Passwords not equal */ + return PASSDLG_ERROR_PASSWORDS_NOT_EQUAL; + } + + /* Success */ + return PASSDLG_ERROR_NONE; +} + +/* Refresh the valid password UI state, i.e. re-validate + * and enable/disable the Change Password button. + * Returns: Return value of passdlg_validate_passwords */ +static guint +passdlg_refresh_password_state (PasswordDialog *pdialog) +{ + GtkBuilder *dialog; + guint ret; + gboolean valid = FALSE; + + dialog = pdialog->ui; + + ret = passdlg_validate_passwords (pdialog); + + if (ret == PASSDLG_ERROR_NONE) { + valid = TRUE; + } + + g_object_set (WID ("change-password-button"), "sensitive", valid, NULL); + + return ret; +} + +/* Called whenever any of the new password fields have changed */ +static void +passdlg_check_password (GtkEntry *entry, PasswordDialog *pdialog) +{ + guint ret; + + ret = passdlg_refresh_password_state (pdialog); + + switch (ret) { + case PASSDLG_ERROR_NONE: + passdlg_set_status (pdialog, _("Click Change password to change your password.")); + break; + case PASSDLG_ERROR_NEW_PASSWORD_EMPTY: + passdlg_set_status (pdialog, _("Please type your password in the New password field.")); + break; + case PASSDLG_ERROR_RETYPED_PASSWORD_EMPTY: + passdlg_set_status (pdialog, _("Please type your password again in the Retype new password field.")); + break; + case PASSDLG_ERROR_PASSWORDS_NOT_EQUAL: + passdlg_set_status (pdialog, _("The two passwords are not equal.")); + break; + default: + g_warning ("Unknown passdlg_check_password error: %d", ret); + break; + } +} + +/* Called when the "Change password" dialog-button is clicked + * Returns: TRUE if we want to keep the dialog running, FALSE otherwise */ +static gboolean +passdlg_process_response (PasswordDialog *pdialog, gint response_id) +{ + + if (response_id == GTK_RESPONSE_OK) { + /* Set busy as this can be a long process */ + passdlg_set_busy (pdialog, TRUE); + + /* Stop passwd if an error occured and it is still running */ + if (pdialog->backend_state == PASSWD_STATE_ERR) { + + /* Stop passwd, free resources */ + stop_passwd (pdialog); + } + + /* Check that the backend is still running, or that an error + * hass occured but it has not yet exited */ + if (pdialog->backend_pid == -1) { + /* If it is not, re-run authentication */ + + /* Spawn backend */ + if (!passdlg_spawn_passwd (pdialog)) { + return TRUE; + } + + /* Add current and new passwords to queue */ + authenticate (pdialog); + update_password (pdialog); + } else { + /* Only add new passwords to queue */ + update_password (pdialog); + + /* Pop new password through the backend */ + io_queue_pop (pdialog->backend_stdin_queue, pdialog->backend_stdin); + } + + /* Our IO watcher should now handle the rest */ + + /* Keep the dialog running */ + return TRUE; + } + + return FALSE; +} + +/* Activates (moves focus or activates) widget w */ +static void +passdlg_activate (GtkEntry *entry, GtkWidget *w) +{ + if (GTK_IS_BUTTON (w)) { + gtk_widget_activate (w); + } else { + gtk_widget_grab_focus (w); + } +} + +/* Initialize password dialog */ +static void +passdlg_init (PasswordDialog *pdialog, GtkWindow *parent) +{ + GtkBuilder *dialog; + GtkWidget *wpassdlg; + GtkAccelGroup *group; + + /* Initialize dialog */ + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-about-me-password.ui", NULL); + pdialog->ui = dialog; + + wpassdlg = WID ("change-password"); + capplet_set_icon (wpassdlg, "user-info"); + + group = gtk_accel_group_new (); + + /* + * Initialize backend + */ + + /* Initialize backend_pid. -1 means the backend is not running */ + pdialog->backend_pid = -1; + + /* Initialize IO Channels */ + pdialog->backend_stdin = NULL; + pdialog->backend_stdout = NULL; + + /* Initialize write queue */ + pdialog->backend_stdin_queue = g_queue_new (); + + /* Initialize watchers */ + pdialog->backend_child_watch_id = 0; + pdialog->backend_stdout_watch_id = 0; + + /* Initialize backend state */ + pdialog->backend_state = PASSWD_STATE_NONE; + + /* + * Initialize UI + */ + + /* Initialize pdialog widgets */ + pdialog->current_password = GTK_ENTRY (WID ("current-password")); + pdialog->new_password = GTK_ENTRY (WID ("new-password")); + pdialog->retyped_password = GTK_ENTRY (WID ("retyped-password")); + pdialog->dialog_image = GTK_IMAGE (WID ("dialog-image")); + pdialog->status_label = GTK_LABEL (WID ("status-label")); + + /* Initialize accelerators */ + gtk_widget_add_accelerator (GTK_WIDGET (pdialog->current_password), + "activate", group, + GDK_Return, 0, + 0); + + gtk_widget_add_accelerator (GTK_WIDGET (pdialog->new_password), + "activate", group, + GDK_Return, 0, + 0); + + /* Activate authenticate-button when enter is pressed in current-password */ + g_signal_connect (G_OBJECT (pdialog->current_password), "activate", + G_CALLBACK (passdlg_activate), WID ("authenticate-button")); + + /* Activate retyped-password when enter is pressed in new-password */ + g_signal_connect (G_OBJECT (pdialog->new_password), "activate", + G_CALLBACK (passdlg_activate), pdialog->retyped_password); + + /* Clear status message */ + passdlg_set_status (pdialog, ""); + + /* Set non-authenticated state */ + passdlg_set_auth_state (pdialog, FALSE); + + /* Connect signal handlers */ + g_signal_connect (G_OBJECT (WID ("authenticate-button")), "clicked", + G_CALLBACK (passdlg_authenticate), pdialog); + + /* Verify new passwords on-the-fly */ + g_signal_connect (G_OBJECT (WID ("new-password")), "changed", + G_CALLBACK (passdlg_check_password), pdialog); + g_signal_connect (G_OBJECT (WID ("retyped-password")), "changed", + G_CALLBACK (passdlg_check_password), pdialog); + + /* Set misc dialog properties */ + gtk_window_set_resizable (GTK_WINDOW (wpassdlg), FALSE); + gtk_window_set_transient_for (GTK_WINDOW (wpassdlg), GTK_WINDOW (parent)); +} + +/* Main */ +void +mate_about_me_password (GtkWindow *parent) +{ + PasswordDialog *pdialog; + GtkBuilder *dialog; + GtkWidget *wpassdlg; + + gint result; + gboolean response; + + /* Initialize dialog */ + pdialog = g_new0 (PasswordDialog, 1); + passdlg_init (pdialog, parent); + + dialog = pdialog->ui; + wpassdlg = WID ("change-password"); + + /* Go! */ + gtk_widget_show_all (wpassdlg); + + do { + result = gtk_dialog_run (GTK_DIALOG (wpassdlg)); + response = passdlg_process_response (pdialog, result); + } while (response); + + /* Clean up */ + stop_passwd (pdialog); + gtk_widget_destroy (wpassdlg); + g_queue_free (pdialog->backend_stdin_queue); + g_object_unref (dialog); + g_free (pdialog); +} diff --git a/capplets/about-me/mate-about-me-password.h b/capplets/about-me/mate-about-me-password.h new file mode 100644 index 00000000..39f67cd8 --- /dev/null +++ b/capplets/about-me/mate-about-me-password.h @@ -0,0 +1,9 @@ +#ifndef __MATE_ABOUT_ME_PASSWORD_H__ +#define __MATE_ABOUT_ME_PASSWORD_H__ + +#include + +void +mate_about_me_password (GtkWindow *parent); + +#endif /* __MATE_ABOUT_ME_PASSWORD_H__ */ diff --git a/capplets/about-me/mate-about-me-password.ui b/capplets/about-me/mate-about-me-password.ui new file mode 100644 index 00000000..b9c433a5 --- /dev/null +++ b/capplets/about-me/mate-about-me-password.ui @@ -0,0 +1,319 @@ + + + + + + 6 + Change password + False + dialog + False + + + True + vertical + 6 + + + True + 6 + 12 + + + True + 0 + gtk-dialog-authentication + 6 + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Change your password + + + + + + + False + False + 0 + + + + + True + 0 + 0 + To change your password, enter your current password in the field below and click <b>Authenticate</b>. +After you have authenticated, enter your new password, retype it for verification and click <b>Change password</b>. + True + True + + + False + False + 1 + + + + + True + 6 + + + True + vertical + 6 + True + + + True + 0 + Current _password: + True + current-password + + + False + False + 0 + + + + + True + False + 0 + _New password: + True + new-password + + + False + False + 1 + + + + + True + False + 0 + _Retype new password: + True + retyped-password + + + False + False + 2 + + + + + False + False + 0 + + + + + True + vertical + 6 + True + + + True + 6 + + + True + True + False + + + 0 + + + + + True + True + False + + + True + 0 + 0 + + + True + 2 + + + True + gtk-apply + + + False + False + 0 + + + + + True + _Authenticate + True + + + False + False + 1 + + + + + + + + + False + False + 1 + + + + + 0 + + + + + True + False + True + False + + + False + False + 1 + + + + + True + False + True + False + True + + + False + False + 2 + + + + + 1 + + + + + False + False + 2 + + + + + True + Please type your password again in the <b>Retype new password</b> field. + True + center + True + + + 3 + + + + + 1 + + + + + False + False + 1 + + + + + True + end + + + gtk-close + True + True + True + False + True + + + False + False + 0 + + + + + Change pa_ssword + True + False + True + True + True + False + True + + + False + False + 1 + + + + + False + False + end + 0 + + + + + + cancel-button + change-password-button + + + diff --git a/capplets/about-me/mate-about-me.c b/capplets/about-me/mate-about-me.c new file mode 100644 index 00000000..0935ab51 --- /dev/null +++ b/capplets/about-me/mate-about-me.c @@ -0,0 +1,1005 @@ +/* mate-about-me.c + * Copyright (C) 2002 Diego Gonzalez + * + * Written by: Diego Gonzalez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#define MATE_DESKTOP_USE_UNSTABLE_API +#include + +#include "e-image-chooser.h" +#include "mate-about-me-password.h" +#include "mate-about-me-fingerprint.h" +#include "marshal.h" + +#include "capplet-util.h" + +#define MAX_HEIGHT 150 +#define MAX_WIDTH 150 + +#define EMAIL_SLOTS 4 + +typedef struct { + EContact *contact; + EBook *book; + + GtkBuilder *dialog; + GtkWidget *enable_fingerprint_button; + GtkWidget *disable_fingerprint_button; + GtkWidget *image_chooser; + + GdkScreen *screen; + GtkIconTheme *theme; + MateDesktopThumbnailFactory *thumbs; + + EContactAddress *addr1; + EContactAddress *addr2; + gchar *email[EMAIL_SLOTS]; + const gchar *email_types[EMAIL_SLOTS]; + + gboolean have_image; + gboolean image_changed; + gboolean create_self; + + gchar *person; + gchar *login; + gchar *username; + + guint commit_timeout_id; +} MateAboutMe; + +static MateAboutMe *me = NULL; + +struct WidToCid { + gchar *wid; + guint cid; +}; + +enum { + ADDRESS_STREET = 1, + ADDRESS_POBOX, + ADDRESS_LOCALITY, + ADDRESS_CODE, + ADDRESS_REGION, + ADDRESS_COUNTRY +}; + +#define EMAIL_WORK 0 +#define EMAIL_HOME 1 +#define ADDRESS_HOME 21 +#define ADDRESS_WORK 27 + +struct WidToCid ids[] = { + + { "email-work-e", 0 }, /* 00 */ + { "email-home-e", 1 }, /* 01 */ + + { "phone-home-e", E_CONTACT_PHONE_HOME }, /* 02 */ + { "phone-mobile-e", E_CONTACT_PHONE_MOBILE }, /* 03 */ + { "phone-work-e", E_CONTACT_PHONE_BUSINESS }, /* 04 */ + { "phone-work-fax-e", E_CONTACT_PHONE_BUSINESS_FAX }, /* 05 */ + + { "im-jabber-e", E_CONTACT_IM_JABBER_HOME_1 }, /* 06 */ + { "im-msn-e", E_CONTACT_IM_MSN_HOME_1 }, /* 07 */ + { "im-icq-e", E_CONTACT_IM_ICQ_HOME_1 }, /* 08 */ + { "im-yahoo-e", E_CONTACT_IM_YAHOO_HOME_1 }, /* 09 */ + { "im-aim-e", E_CONTACT_IM_AIM_HOME_1 }, /* 10 */ + { "im-groupwise-e", E_CONTACT_IM_GROUPWISE_HOME_1 }, /* 11 */ + + { "web-homepage-e", E_CONTACT_HOMEPAGE_URL }, /* 12 */ + { "web-calendar-e", E_CONTACT_CALENDAR_URI }, /* 13 */ + { "web-weblog-e", E_CONTACT_BLOG_URL }, /* 14 */ + + { "job-profession-e", E_CONTACT_ROLE }, /* 15 */ + { "job-title-e", E_CONTACT_TITLE }, /* 16 */ + { "job-dept-e", E_CONTACT_ORG_UNIT }, /* 17 */ + { "job-assistant-e", E_CONTACT_ASSISTANT }, /* 18 */ + { "job-company-e", E_CONTACT_ORG }, /* 19 */ + { "job-manager-e", E_CONTACT_MANAGER }, /* 20 */ + + { "addr-street-1", ADDRESS_STREET }, /* 21 */ + { "addr-po-1", ADDRESS_POBOX }, /* 22 */ + { "addr-locality-1", ADDRESS_LOCALITY }, /* 23 */ + { "addr-code-1", ADDRESS_CODE }, /* 24 */ + { "addr-region-1", ADDRESS_REGION }, /* 25 */ + { "addr-country-1", ADDRESS_COUNTRY }, /* 26 */ + + { "addr-street-2", ADDRESS_STREET }, /* 27 */ + { "addr-po-2", ADDRESS_POBOX }, /* 28 */ + { "addr-locality-2", ADDRESS_LOCALITY }, /* 29 */ + { "addr-code-2", ADDRESS_CODE }, /* 30 */ + { "addr-region-2", ADDRESS_REGION }, /* 31 */ + { "addr-country-2", ADDRESS_COUNTRY }, /* 32 */ + + { NULL, 0 } +}; + +#define ATTRIBUTE_HOME "HOME" +#define ATTRIBUTE_WORK "WORK" +#define ATTRIBUTE_OTHER "OTHER" + +static void about_me_set_address_field (EContactAddress *, guint, gchar *); + + +/*** Utility functions ***/ +static void +about_me_error (GtkWindow *parent, gchar *str) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, str); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/********************/ +static void +about_me_destroy (void) +{ + e_contact_address_free (me->addr1); + e_contact_address_free (me->addr2); + + if (me->contact) + g_object_unref (me->contact); + if (me->book) + g_object_unref (me->book); + if (me->dialog) + g_object_unref (me->dialog); + + g_free (me->email[0]); + g_free (me->email[1]); + g_free (me->email[2]); + g_free (me->email[3]); + + g_free (me->person); + g_free (me->login); + g_free (me->username); + g_free (me); + me = NULL; +} + +static void +about_me_update_email (MateAboutMe *me) +{ + GList *attrs = NULL; + gint i; + + for (i = 0; i < EMAIL_SLOTS; ++i) { + if (me->email[i] != NULL) { + EVCardAttribute *attr; + const gchar *type = me->email_types[i]; + + attr = e_vcard_attribute_new (NULL, EVC_EMAIL); + + e_vcard_attribute_add_param_with_value (attr, + e_vcard_attribute_param_new (EVC_TYPE), + type ? type : ATTRIBUTE_OTHER); + + e_vcard_attribute_add_value (attr, me->email[i]); + attrs = g_list_append (attrs, attr); + } + } + + e_contact_set_attributes (me->contact, E_CONTACT_EMAIL, attrs); + + g_list_foreach (attrs, (GFunc) e_vcard_attribute_free, NULL); + g_list_free (attrs); +} + +static void +about_me_commit (MateAboutMe *me) +{ + EContactName *name; + + char *strings[4], **stringptr; + char *fileas; + + name = NULL; + + if (me->create_self) { + if (me->username == NULL) + fileas = g_strdup ("Myself"); + else { + name = e_contact_name_from_string (me->username); + + stringptr = strings; + if (name->family && *name->family) + *(stringptr++) = name->family; + if (name->given && *name->given) + *(stringptr++) = name->given; + *stringptr = NULL; + fileas = g_strjoinv (", ", strings); + } + + e_contact_set (me->contact, E_CONTACT_FILE_AS, fileas); + e_contact_set (me->contact, E_CONTACT_NICKNAME, me->login); + e_contact_set (me->contact, E_CONTACT_FULL_NAME, me->username); + + e_contact_name_free (name); + g_free (fileas); + } + + about_me_update_email (me); + + if (me->create_self) { + e_book_add_contact (me->book, me->contact, NULL); + e_book_set_self (me->book, me->contact, NULL); + } else { + if (!e_book_commit_contact (me->book, me->contact, NULL)) + g_warning ("Could not save contact information"); + } + + me->create_self = FALSE; +} + +static gboolean +about_me_commit_from_timeout (MateAboutMe *me) +{ + about_me_commit (me); + + return FALSE; +} + +static gboolean +about_me_focus_out (GtkWidget *widget, GdkEventFocus *event, MateAboutMe *unused) +{ + gchar *str; + const gchar *wid; + gint i; + + if (me == NULL) + return FALSE; + + wid = gtk_buildable_get_name (GTK_BUILDABLE (widget)); + + if (wid == NULL) + return FALSE; + + for (i = 0; ids[i].wid != NULL; i++) + if (strcmp (ids[i].wid, wid) == 0) + break; + + if (GTK_IS_ENTRY (widget)) { + str = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1); + } else if (GTK_IS_TEXT_VIEW (widget)) { + GtkTextBuffer *buffer; + GtkTextIter iter_start; + GtkTextIter iter_end; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); + gtk_text_buffer_get_start_iter (buffer, &iter_start); + iter_end = iter_start; + gtk_text_iter_forward_to_end (&iter_end); + str = gtk_text_iter_get_text (&iter_start, &iter_end); + } else { + return FALSE; + } + + if (i == EMAIL_HOME || i == EMAIL_WORK) { + + g_free (me->email[ids[i].cid]); + if (str[0] == '\0') + me->email[ids[i].cid] = NULL; + else + me->email[ids[i].cid] = g_strdup (str); + me->email_types[ids[i].cid] = (i == EMAIL_HOME) ? ATTRIBUTE_HOME : ATTRIBUTE_WORK; + /* FIXME: i'm getting an empty address field in evolution */ + } else if (i >= ADDRESS_HOME && i < ADDRESS_WORK) { + about_me_set_address_field (me->addr1, ids[i].cid, str); + e_contact_set (me->contact, E_CONTACT_ADDRESS_HOME, me->addr1); + } else if (i >= ADDRESS_WORK) { + about_me_set_address_field (me->addr2, ids[i].cid, str); + e_contact_set (me->contact, E_CONTACT_ADDRESS_WORK, me->addr2); + } else { + e_contact_set (me->contact, ids[i].cid, str); + } + + g_free (str); + + if (me->commit_timeout_id) { + g_source_remove (me->commit_timeout_id); + } + + me->commit_timeout_id = g_timeout_add (600, (GSourceFunc) about_me_commit_from_timeout, me); + + return FALSE; +} + +/* + * Helpers + */ + +static gchar * +about_me_get_address_field (EContactAddress *addr, guint cid) +{ + gchar *str; + + if (addr == NULL) { + return NULL; + } + + switch (cid) { + case ADDRESS_STREET: + str = addr->street; + break; + case ADDRESS_POBOX: + str = addr->po; + break; + case ADDRESS_LOCALITY: + str = addr->locality; + break; + case ADDRESS_CODE: + str = addr->code; + break; + case ADDRESS_REGION: + str = addr->region; + break; + case ADDRESS_COUNTRY: + str = addr->country; + break; + default: + str = NULL; + break; + } + + return str; +} + +static void +about_me_set_address_field (EContactAddress *addr, guint cid, gchar *str) +{ + switch (cid) { + case ADDRESS_STREET: + g_free (addr->street); + addr->street = g_strdup (str); + break; + case ADDRESS_POBOX: + g_free (addr->po); + addr->po = g_strdup (str); + break; + case ADDRESS_LOCALITY: + g_free (addr->locality); + addr->locality = g_strdup (str); + break; + case ADDRESS_CODE: + g_free (addr->code); + addr->code = g_strdup (str); + break; + case ADDRESS_REGION: + g_free (addr->region); + addr->region = g_strdup (str); + break; + case ADDRESS_COUNTRY: + g_free (addr->country); + addr->country = g_strdup (str); + break; + } +} + +static void +about_me_setup_email (MateAboutMe *me) +{ + GList *attrs, *la; + gboolean has_home = FALSE, has_work = FALSE; + guint i; + + attrs = e_contact_get_attributes (me->contact, E_CONTACT_EMAIL); + + for (la = attrs, i = 0; la; la = la->next, ++i) { + EVCardAttribute *a = la->data; + + me->email[i] = e_vcard_attribute_get_value (a); + if (e_vcard_attribute_has_type (a, ATTRIBUTE_HOME)) { + me->email_types[i] = ATTRIBUTE_HOME; + if (!has_home) { + ids[EMAIL_HOME].cid = i; + has_home = TRUE; + } + } else if (e_vcard_attribute_has_type (a, ATTRIBUTE_WORK)) { + me->email_types[i] = ATTRIBUTE_WORK; + if (!has_work) { + ids[EMAIL_WORK].cid = i; + has_work = TRUE; + } + } else { + me->email_types[i] = ATTRIBUTE_OTHER; + } + + e_vcard_attribute_free (a); + } + + g_list_free (attrs); + + if (ids[EMAIL_HOME].cid == ids[EMAIL_WORK].cid) { + if (has_home) + ids[EMAIL_WORK].cid = 1; + else + ids[EMAIL_HOME].cid = 0; + } + + me->email_types[ids[EMAIL_WORK].cid] = ATTRIBUTE_WORK; + me->email_types[ids[EMAIL_HOME].cid] = ATTRIBUTE_HOME; +} + +/** + * about_me_load_string_field: + * + * wid: UI widget name + * cid: id of the field (EDS id) + * aid: position in the array WidToCid + **/ + +static void +about_me_load_string_field (MateAboutMe *me, const gchar *wid, guint cid, guint aid) +{ + GtkWidget *widget; + GtkBuilder *dialog; + const gchar *str; + + dialog = me->dialog; + + widget = WID (wid); + + if (me->create_self == TRUE) { + g_signal_connect (widget, "focus-out-event", G_CALLBACK (about_me_focus_out), me); + return; + } + + if (aid == EMAIL_HOME || aid == EMAIL_WORK) { + str = e_contact_get_const (me->contact, E_CONTACT_EMAIL_1 + cid); + } else if (aid >= ADDRESS_HOME && aid < ADDRESS_WORK) { + str = about_me_get_address_field (me->addr1, cid); + } else if (aid >= ADDRESS_WORK) { + str = about_me_get_address_field (me->addr2, cid); + } else { + str = e_contact_get_const (me->contact, cid); + } + + str = str ? str : ""; + + if (GTK_IS_ENTRY (widget)) { + gtk_entry_set_text (GTK_ENTRY (widget), str); + } else if (GTK_IS_TEXT_VIEW (widget)) { + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); + gtk_text_buffer_set_text (buffer, str, -1); + } + + g_signal_connect (widget, "focus-out-event", G_CALLBACK (about_me_focus_out), me); +} + +static void +about_me_load_photo (MateAboutMe *me, EContact *contact) +{ + GtkBuilder *dialog; + EContactPhoto *photo; + + dialog = me->dialog; + + if (me->person) + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), me->person); + + photo = e_contact_get (contact, E_CONTACT_PHOTO); + + if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { + me->have_image = TRUE; + e_image_chooser_set_image_data (E_IMAGE_CHOOSER (me->image_chooser), + (char *) photo->data.inlined.data, photo->data.inlined.length); + e_contact_photo_free (photo); + } else { + me->have_image = FALSE; + } +} + +static void +about_me_update_photo (MateAboutMe *me) +{ + GtkBuilder *dialog; + EContactPhoto *photo; + gchar *file; + GError *error; + + guchar *data; + gsize length; + + dialog = me->dialog; + + + if (me->image_changed && me->have_image) { + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + GdkPixbuf *pixbuf, *scaled; + int height, width; + gboolean do_scale = FALSE; + float scale; + + e_image_chooser_get_image_data (E_IMAGE_CHOOSER (me->image_chooser), (char **) &data, &length); + + /* Before updating the image in EDS scale it to a reasonable size + so that the user doesn't get an application that does not respond + or that takes 100% CPU */ + gdk_pixbuf_loader_write (loader, data, length, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (pixbuf) + g_object_ref (pixbuf); + + g_object_unref (loader); + + height = gdk_pixbuf_get_height (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + + if (height >= width && height > MAX_HEIGHT) { + scale = (float)MAX_HEIGHT/height; + do_scale = TRUE; + } else if (width > height && width > MAX_WIDTH) { + scale = (float)MAX_WIDTH/width; + do_scale = TRUE; + } + + if (do_scale) { + char *scaled_data = NULL; + gsize scaled_length; + + scaled = gdk_pixbuf_scale_simple (pixbuf, width*scale, height*scale, GDK_INTERP_BILINEAR); + gdk_pixbuf_save_to_buffer (scaled, &scaled_data, &scaled_length, "png", NULL, + "compression", "9", NULL); + + g_free (data); + data = (guchar *) scaled_data; + length = scaled_length; + } + + photo = g_new0 (EContactPhoto, 1); + photo->type = E_CONTACT_PHOTO_TYPE_INLINED; + photo->data.inlined.data = data; + photo->data.inlined.length = length; + e_contact_set (me->contact, E_CONTACT_PHOTO, photo); + + /* Save the image for MDM */ + /* FIXME: I would have to read the default used by the mdmgreeter program */ + error = NULL; + file = g_build_filename (g_get_home_dir (), ".face", NULL); + if (g_file_set_contents (file, + (gchar *) photo->data.inlined.data, + photo->data.inlined.length, + &error) != FALSE) { + g_chmod (file, 0644); + } else { + g_warning ("Could not create %s: %s", file, error->message); + g_error_free (error); + } + + g_free (file); + + e_contact_photo_free (photo); + + } else if (me->image_changed && !me->have_image) { + /* Update the image in the card */ + e_contact_set (me->contact, E_CONTACT_PHOTO, NULL); + + file = g_build_filename (g_get_home_dir (), ".face", NULL); + + g_unlink (file); + + g_free (file); + } + + about_me_commit (me); +} + +static void +about_me_load_info (MateAboutMe *me) +{ + gint i; + + if (me->create_self == FALSE) { + me->addr1 = e_contact_get (me->contact, E_CONTACT_ADDRESS_HOME); + if (me->addr1 == NULL) + me->addr1 = g_new0 (EContactAddress, 1); + me->addr2 = e_contact_get (me->contact, E_CONTACT_ADDRESS_WORK); + if (me->addr2 == NULL) + me->addr2 = g_new0 (EContactAddress, 1); + } else { + me->addr1 = g_new0 (EContactAddress, 1); + me->addr2 = g_new0 (EContactAddress, 1); + } + + for (i = 0; ids[i].wid != NULL; i++) { + about_me_load_string_field (me, ids[i].wid, ids[i].cid, i); + } + + set_fingerprint_label (me->enable_fingerprint_button, + me->disable_fingerprint_button); +} + +static void +about_me_update_preview (GtkFileChooser *chooser, + MateAboutMe *me) +{ + gchar *uri; + + uri = gtk_file_chooser_get_preview_uri (chooser); + + if (uri) { + GtkWidget *image; + GdkPixbuf *pixbuf = NULL; + GFile *file; + GFileInfo *file_info; + + if (!me->thumbs) + me->thumbs = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + + file = g_file_new_for_uri (uri); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (file_info != NULL) { + const gchar *content_type; + + content_type = g_file_info_get_content_type (file_info); + if (content_type) { + gchar *mime_type; + + mime_type = g_content_type_get_mime_type (content_type); + + pixbuf = mate_desktop_thumbnail_factory_generate_thumbnail (me->thumbs, + uri, + mime_type); + g_free (mime_type); + } + g_object_unref (file_info); + } + + image = gtk_file_chooser_get_preview_widget (chooser); + + if (pixbuf != NULL) { + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + g_object_unref (pixbuf); + } else { + gtk_image_set_from_stock (GTK_IMAGE (image), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + } + gtk_file_chooser_set_preview_widget_active (chooser, TRUE); +} + +static void +about_me_image_clicked_cb (GtkWidget *button, MateAboutMe *me) +{ + GtkFileChooser *chooser_dialog; + gint response; + GtkBuilder *dialog; + GtkWidget *image; + const gchar *chooser_dir = DATADIR"/pixmaps/faces"; + const gchar *pics_dir; + GtkFileFilter *filter; + + dialog = me->dialog; + + chooser_dialog = GTK_FILE_CHOOSER ( + gtk_file_chooser_dialog_new (_("Select Image"), GTK_WINDOW (WID ("about-me-dialog")), + GTK_FILE_CHOOSER_ACTION_OPEN, + _("No Image"), GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL)); + gtk_window_set_modal (GTK_WINDOW (chooser_dialog), TRUE); + gtk_dialog_set_default_response (GTK_DIALOG (chooser_dialog), GTK_RESPONSE_ACCEPT); + + gtk_file_chooser_add_shortcut_folder (chooser_dialog, chooser_dir, NULL); + pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); + if (pics_dir != NULL) + gtk_file_chooser_add_shortcut_folder (chooser_dialog, pics_dir, NULL); + + if (!g_file_test (chooser_dir, G_FILE_TEST_IS_DIR)) + chooser_dir = g_get_home_dir (); + + gtk_file_chooser_set_current_folder (chooser_dialog, chooser_dir); + gtk_file_chooser_set_use_preview_label (chooser_dialog, FALSE); + + image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (chooser_dialog, image); + gtk_widget_set_size_request (image, 128, -1); + + gtk_widget_show (image); + + g_signal_connect (chooser_dialog, "update-preview", + G_CALLBACK (about_me_update_preview), me); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Images")); + gtk_file_filter_add_pixbuf_formats (filter); + gtk_file_chooser_add_filter (chooser_dialog, filter); + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_chooser_add_filter (chooser_dialog, filter); + + response = gtk_dialog_run (GTK_DIALOG (chooser_dialog)); + + if (response == GTK_RESPONSE_ACCEPT) { + gchar* filename; + + filename = gtk_file_chooser_get_filename (chooser_dialog); + me->have_image = TRUE; + me->image_changed = TRUE; + + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), filename); + g_free (filename); + about_me_update_photo (me); + } else if (response == GTK_RESPONSE_NO) { + me->have_image = FALSE; + me->image_changed = TRUE; + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), me->person); + about_me_update_photo (me); + } + + gtk_widget_destroy (GTK_WIDGET (chooser_dialog)); +} + +static void +about_me_image_changed_cb (GtkWidget *widget, MateAboutMe *me) +{ + me->have_image = TRUE; + me->image_changed = TRUE; + about_me_update_photo (me); +} + +/* About Me Dialog Callbacks */ + +static void +about_me_icon_theme_changed (GtkWindow *window, + GtkIconTheme *theme) +{ + GtkIconInfo *icon; + + icon = gtk_icon_theme_lookup_icon (me->theme, "stock_person", 80, 0); + if (icon == NULL) { + g_debug ("Icon not found"); + } + g_free (me->person); + me->person = g_strdup (gtk_icon_info_get_filename (icon)); + + gtk_icon_info_free (icon); + + if (me->have_image) + e_image_chooser_set_from_file (E_IMAGE_CHOOSER (me->image_chooser), me->person); +} + +static void +about_me_button_clicked_cb (GtkDialog *dialog, gint response_id, MateAboutMe *me) +{ + if (response_id == GTK_RESPONSE_HELP) + g_print ("Help goes here"); + else { + if (me->commit_timeout_id) { + g_source_remove (me->commit_timeout_id); + about_me_commit (me); + } + + about_me_destroy (); + gtk_main_quit (); + } +} + +static void +about_me_passwd_clicked_cb (GtkWidget *button, MateAboutMe *me) +{ + GtkBuilder *dialog; + + dialog = me->dialog; + mate_about_me_password (GTK_WINDOW (WID ("about-me-dialog"))); +} + +static void +about_me_fingerprint_button_clicked_cb (GtkWidget *button, MateAboutMe *me) +{ + fingerprint_button_clicked (me->dialog, + me->enable_fingerprint_button, + me->disable_fingerprint_button); +} + +static gint +about_me_setup_dialog (void) +{ + GtkWidget *widget; + GtkWidget *main_dialog; + GtkIconInfo *icon; + GtkBuilder *dialog; + GError *error = NULL; + GList *chain; + gchar *str; + + me = g_new0 (MateAboutMe, 1); + + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-about-me-dialog.ui", NULL); + + me->image_chooser = e_image_chooser_new (); + gtk_container_add (GTK_CONTAINER (WID ("button-image")), me->image_chooser); + + if (dialog == NULL) { + about_me_destroy (); + return -1; + } + + me->dialog = dialog; + + /* Connect the close button signal */ + main_dialog = WID ("about-me-dialog"); + g_signal_connect (main_dialog, "response", + G_CALLBACK (about_me_button_clicked_cb), me); + + gtk_window_set_resizable (GTK_WINDOW (main_dialog), FALSE); + capplet_set_icon (main_dialog, "user-info"); + + /* Setup theme details */ + me->screen = gtk_window_get_screen (GTK_WINDOW (main_dialog)); + me->theme = gtk_icon_theme_get_for_screen (me->screen); + + icon = gtk_icon_theme_lookup_icon (me->theme, "stock_person", 80, 0); + if (icon != NULL) { + me->person = g_strdup (gtk_icon_info_get_filename (icon)); + gtk_icon_info_free (icon); + } + + g_signal_connect_object (me->theme, "changed", + G_CALLBACK (about_me_icon_theme_changed), + main_dialog, + G_CONNECT_SWAPPED); + + /* Get the self contact */ + if (!e_book_get_self (&me->contact, &me->book, &error)) { + if (error->code == E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED) { + about_me_error (NULL, _("There was an error while trying to get the addressbook information\n" \ + "Evolution Data Server can't handle the protocol")); + g_clear_error (&error); + about_me_destroy (); + return -1; + } + + g_clear_error (&error); + + me->create_self = TRUE; + me->contact = e_contact_new (); + + if (me->book == NULL) { + me->book = e_book_new_system_addressbook (&error); + if (me->book == NULL || error != NULL) { + g_error ("%s", error->message); + g_clear_error (&error); + } + + if (e_book_open (me->book, FALSE, NULL) == FALSE) { + about_me_error (GTK_WINDOW (main_dialog), + _("Unable to open address book")); + g_clear_error (&error); + } + } + } else { + about_me_setup_email (me); + } + + me->login = g_strdup (g_get_user_name ()); + me->username = g_strdup (g_get_real_name ()); + + /* Contact Tab */ + about_me_load_photo (me, me->contact); + + widget = WID ("fullname"); + str = g_strdup_printf ("%s", me->username); + + gtk_label_set_markup (GTK_LABEL (widget), str); + g_free (str); + + widget = WID ("login"); + gtk_label_set_text (GTK_LABEL (widget), me->login); + + str = g_strdup_printf (_("About %s"), me->username); + gtk_window_set_title (GTK_WINDOW (main_dialog), str); + g_free (str); + + widget = WID ("password"); + g_signal_connect (widget, "clicked", + G_CALLBACK (about_me_passwd_clicked_cb), me); + + widget = WID ("button-image"); + g_signal_connect (widget, "clicked", + G_CALLBACK (about_me_image_clicked_cb), me); + + me->enable_fingerprint_button = WID ("enable_fingerprint_button"); + me->disable_fingerprint_button = WID ("disable_fingerprint_button"); + + g_signal_connect (me->enable_fingerprint_button, "clicked", + G_CALLBACK (about_me_fingerprint_button_clicked_cb), me); + g_signal_connect (me->disable_fingerprint_button, "clicked", + G_CALLBACK (about_me_fingerprint_button_clicked_cb), me); + + g_signal_connect (me->image_chooser, "changed", + G_CALLBACK (about_me_image_changed_cb), me); + + /* Address tab: set up the focus chains */ + chain = g_list_prepend (NULL, WID ("addr-country-1")); + chain = g_list_prepend (chain, WID ("addr-po-1")); + chain = g_list_prepend (chain, WID ("addr-region-1")); + chain = g_list_prepend (chain, WID ("addr-code-1")); + chain = g_list_prepend (chain, WID ("addr-locality-1")); + chain = g_list_prepend (chain, WID ("addr-scrolledwindow-1")); + widget = WID ("addr-table-1"); + gtk_container_set_focus_chain (GTK_CONTAINER (widget), chain); + g_list_free (chain); + + chain = g_list_prepend (NULL, WID ("addr-country-2")); + chain = g_list_prepend (chain, WID ("addr-po-2")); + chain = g_list_prepend (chain, WID ("addr-region-2")); + chain = g_list_prepend (chain, WID ("addr-code-2")); + chain = g_list_prepend (chain, WID ("addr-locality-2")); + chain = g_list_prepend (chain, WID ("addr-scrolledwindow-2")); + widget = WID ("addr-table-2"); + gtk_container_set_focus_chain (GTK_CONTAINER (widget), chain); + g_list_free (chain); + + about_me_load_info (me); + + gtk_widget_show_all (main_dialog); + + return 0; +} + +int +main (int argc, char **argv) +{ + int rc = 0; + + capplet_init (NULL, &argc, &argv); + + if (!g_thread_supported ()) + g_thread_init (NULL); + + dbus_g_object_register_marshaller (fprintd_marshal_VOID__STRING_BOOLEAN, + G_TYPE_NONE, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID); + + rc = about_me_setup_dialog (); + + if (rc != -1) { + gtk_main (); + } + + return rc; +} diff --git a/capplets/about-me/mate-about-me.desktop.in.in b/capplets/about-me/mate-about-me.desktop.in.in new file mode 100644 index 00000000..e8393304 --- /dev/null +++ b/capplets/about-me/mate-about-me.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=About Me +_Comment=Set your personal information +Exec=mate-about-me +Icon=user-info +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;X-MATE-PersonalSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=about-me +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/accessibility/Makefile.am b/capplets/accessibility/Makefile.am new file mode 100644 index 00000000..4c96fba7 --- /dev/null +++ b/capplets/accessibility/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = at-properties + +-include $(top_srcdir)/git.mk diff --git a/capplets/accessibility/at-properties/Makefile.am b/capplets/accessibility/at-properties/Makefile.am new file mode 100644 index 00000000..c24d0181 --- /dev/null +++ b/capplets/accessibility/at-properties/Makefile.am @@ -0,0 +1,31 @@ +bin_PROGRAMS = mate-at-properties + +mate_at_properties_LDADD = $(AT_CAPPLET_LIBS) $(MATECC_CAPPLETS_LIBS) $(top_builddir)/capplets/common/libcommon.la +mate_at_properties_SOURCES = \ + main.c +mate_at_properties_LDFLAGS = -export-dynamic + +@INTLTOOL_DESKTOP_RULE@ + +desktopdir = $(datadir)/applications +Desktop_in_files = at-properties.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +pixmapdir = $(pkgdatadir)/pixmaps +pixmap_DATA = \ + at-startup.png \ + at-support.png + +uidir = $(pkgdatadir)/ui +ui_DATA = at-enable-dialog.ui + +INCLUDES = $(AT_CAPPLET_CFLAGS) \ + $(MATECC_CAPPLETS_CFLAGS) \ + -DUIDIR=\""$(uidir)"\" \ + -DPIXMAPDIR=\""$(pixmapdir)"\" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) +EXTRA_DIST = $(ui_DATA) $(pixmap_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/accessibility/at-properties/Makefile.in b/capplets/accessibility/at-properties/Makefile.in new file mode 100644 index 00000000..f4dbe405 --- /dev/null +++ b/capplets/accessibility/at-properties/Makefile.in @@ -0,0 +1,753 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = mate-at-properties$(EXEEXT) +subdir = capplets/accessibility/at-properties +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/at-properties.desktop.in.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/mate-doc-utils.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = at-properties.desktop.in +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(desktopdir)" \ + "$(DESTDIR)$(pixmapdir)" "$(DESTDIR)$(uidir)" +PROGRAMS = $(bin_PROGRAMS) +am_mate_at_properties_OBJECTS = main.$(OBJEXT) +mate_at_properties_OBJECTS = $(am_mate_at_properties_OBJECTS) +am__DEPENDENCIES_1 = +mate_at_properties_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) \ + $(top_builddir)/capplets/common/libcommon.la +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +mate_at_properties_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mate_at_properties_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(mate_at_properties_SOURCES) +DIST_SOURCES = $(mate_at_properties_SOURCES) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +DATA = $(desktop_DATA) $(pixmap_DATA) $(ui_DATA) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APP_INDICATOR_CFLAGS = @APP_INDICATOR_CFLAGS@ +APP_INDICATOR_LIBS = @APP_INDICATOR_LIBS@ +AR = @AR@ +AT_CAPPLET_CFLAGS = @AT_CAPPLET_CFLAGS@ +AT_CAPPLET_LIBS = @AT_CAPPLET_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAPPLET_CFLAGS = @CAPPLET_CFLAGS@ +CAPPLET_LIBS = @CAPPLET_LIBS@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DEFAULT_APPLICATIONS_CAPPLET_CFLAGS = @DEFAULT_APPLICATIONS_CAPPLET_CFLAGS@ +DEFAULT_APPLICATIONS_CAPPLET_LIBS = @DEFAULT_APPLICATIONS_CAPPLET_LIBS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DISPLAY_CAPPLET_CFLAGS = @DISPLAY_CAPPLET_CFLAGS@ +DISPLAY_CAPPLET_LIBS = @DISPLAY_CAPPLET_LIBS@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOC_USER_FORMATS = @DOC_USER_FORMATS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTERNAL_LIBSLAB_CFLAGS = @EXTERNAL_LIBSLAB_CFLAGS@ +EXTERNAL_LIBSLAB_LIBS = @EXTERNAL_LIBSLAB_LIBS@ +FGREP = @FGREP@ +FONT_CAPPLET_CFLAGS = @FONT_CAPPLET_CFLAGS@ +FONT_CAPPLET_LIBS = @FONT_CAPPLET_LIBS@ +FONT_VIEWER_CFLAGS = @FONT_VIEWER_CFLAGS@ +FONT_VIEWER_LIBS = @FONT_VIEWER_LIBS@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GSD_DBUS_CFLAGS = @GSD_DBUS_CFLAGS@ +GSD_DBUS_LIBS = @GSD_DBUS_LIBS@ +GTK_ENGINE_DIR = @GTK_ENGINE_DIR@ +HELP_DIR = @HELP_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCANBERRA_GTK_CFLAGS = @LIBCANBERRA_GTK_CFLAGS@ +LIBCANBERRA_GTK_LIBS = @LIBCANBERRA_GTK_LIBS@ +LIBEBOOK_CFLAGS = @LIBEBOOK_CFLAGS@ +LIBEBOOK_LIBS = @LIBEBOOK_LIBS@ +LIBMATEKBDUI_CFLAGS = @LIBMATEKBDUI_CFLAGS@ +LIBMATEKBDUI_LIBS = @LIBMATEKBDUI_LIBS@ +LIBMATEKBD_CFLAGS = @LIBMATEKBD_CFLAGS@ +LIBMATEKBD_LIBS = @LIBMATEKBD_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSLAB_CFLAGS = @LIBSLAB_CFLAGS@ +LIBSLAB_LIBS = @LIBSLAB_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MARCO_CFLAGS = @MARCO_CFLAGS@ +MARCO_LIBS = @MARCO_LIBS@ +MATECC_CAPPLETS_CFLAGS = @MATECC_CAPPLETS_CFLAGS@ +MATECC_CAPPLETS_CLEANFILES = @MATECC_CAPPLETS_CLEANFILES@ +MATECC_CAPPLETS_EXTRA_DIST = @MATECC_CAPPLETS_EXTRA_DIST@ +MATECC_CAPPLETS_LIBS = @MATECC_CAPPLETS_LIBS@ +MATECC_CFLAGS = @MATECC_CFLAGS@ +MATECC_LIBS = @MATECC_LIBS@ +MATECC_SHELL_CFLAGS = @MATECC_SHELL_CFLAGS@ +MATECC_SHELL_LIBS = @MATECC_SHELL_LIBS@ +MATECONFTOOL = @MATECONFTOOL@ +MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_DESKTOP_CFLAGS = @MATE_DESKTOP_CFLAGS@ +MATE_DESKTOP_LIBS = @MATE_DESKTOP_LIBS@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SCREENSAVER_LIBS = @SCREENSAVER_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TYPING_BREAK = @TYPING_BREAK@ +TYPING_CFLAGS = @TYPING_CFLAGS@ +TYPING_LIBS = @TYPING_LIBS@ +UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XCURSOR_CFLAGS = @XCURSOR_CFLAGS@ +XCURSOR_LIBS = @XCURSOR_LIBS@ +XF86MISC_LIBS = @XF86MISC_LIBS@ +XGETTEXT = @XGETTEXT@ +XINPUT_CFLAGS = @XINPUT_CFLAGS@ +XINPUT_LIBS = @XINPUT_LIBS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +mate_at_properties_LDADD = $(AT_CAPPLET_LIBS) $(MATECC_CAPPLETS_LIBS) $(top_builddir)/capplets/common/libcommon.la +mate_at_properties_SOURCES = \ + main.c + +mate_at_properties_LDFLAGS = -export-dynamic +desktopdir = $(datadir)/applications +Desktop_in_files = at-properties.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) +pixmapdir = $(pkgdatadir)/pixmaps +pixmap_DATA = \ + at-startup.png \ + at-support.png + +uidir = $(pkgdatadir)/ui +ui_DATA = at-enable-dialog.ui +INCLUDES = $(AT_CAPPLET_CFLAGS) \ + $(MATECC_CAPPLETS_CFLAGS) \ + -DUIDIR=\""$(uidir)"\" \ + -DPIXMAPDIR=\""$(pixmapdir)"\" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) +EXTRA_DIST = $(ui_DATA) $(pixmap_DATA) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu capplets/accessibility/at-properties/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu capplets/accessibility/at-properties/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +at-properties.desktop.in: $(top_builddir)/config.status $(srcdir)/at-properties.desktop.in.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +mate-at-properties$(EXEEXT): $(mate_at_properties_OBJECTS) $(mate_at_properties_DEPENDENCIES) + @rm -f mate-at-properties$(EXEEXT) + $(AM_V_CCLD)$(mate_at_properties_LINK) $(mate_at_properties_OBJECTS) $(mate_at_properties_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-desktopDATA: $(desktop_DATA) + @$(NORMAL_INSTALL) + test -z "$(desktopdir)" || $(MKDIR_P) "$(DESTDIR)$(desktopdir)" + @list='$(desktop_DATA)'; test -n "$(desktopdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(desktopdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(desktopdir)" || exit $$?; \ + done + +uninstall-desktopDATA: + @$(NORMAL_UNINSTALL) + @list='$(desktop_DATA)'; test -n "$(desktopdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(desktopdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(desktopdir)" && rm -f $$files +install-pixmapDATA: $(pixmap_DATA) + @$(NORMAL_INSTALL) + test -z "$(pixmapdir)" || $(MKDIR_P) "$(DESTDIR)$(pixmapdir)" + @list='$(pixmap_DATA)'; test -n "$(pixmapdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pixmapdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pixmapdir)" || exit $$?; \ + done + +uninstall-pixmapDATA: + @$(NORMAL_UNINSTALL) + @list='$(pixmap_DATA)'; test -n "$(pixmapdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pixmapdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pixmapdir)" && rm -f $$files +install-uiDATA: $(ui_DATA) + @$(NORMAL_INSTALL) + test -z "$(uidir)" || $(MKDIR_P) "$(DESTDIR)$(uidir)" + @list='$(ui_DATA)'; test -n "$(uidir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(uidir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(uidir)" || exit $$?; \ + done + +uninstall-uiDATA: + @$(NORMAL_UNINSTALL) + @list='$(ui_DATA)'; test -n "$(uidir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(uidir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(uidir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(pixmapdir)" "$(DESTDIR)$(uidir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-desktopDATA install-pixmapDATA install-uiDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-desktopDATA \ + uninstall-pixmapDATA uninstall-uiDATA + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am \ + install-desktopDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pixmapDATA install-ps install-ps-am install-strip \ + install-uiDATA installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-desktopDATA \ + uninstall-pixmapDATA uninstall-uiDATA + + +@INTLTOOL_DESKTOP_RULE@ + +-include $(top_srcdir)/git.mk + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/capplets/accessibility/at-properties/at-enable-dialog.ui b/capplets/accessibility/at-properties/at-enable-dialog.ui new file mode 100644 index 00000000..721c45c8 --- /dev/null +++ b/capplets/accessibility/at-properties/at-enable-dialog.ui @@ -0,0 +1,351 @@ + + + + + + 5 + Assistive Technologies Preferences + False + dialog + False + + + True + 2 + + + True + 5 + 18 + + + True + + + True + 6 + + + True + 0 + Assistive Technologies + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + _Preferred Applications + True + True + False + pref_button_img + True + + + Jump to Preferred Applications dialog + + + + + False + False + 0 + + + + + _Enable assistive technologies + True + True + False + True + True + + + Changes to enable assistive technologies will not take effect until your next log in. + + + + + False + False + 1 + + + + + 1 + + + + + 1 + + + + + 0 + + + + + 0 + + + + + True + + + True + 6 + + + True + 0 + Preferences + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + _Keyboard Accessibility + True + True + False + keyboard_button_img + True + + + Jump to the Keyboard Accessibility dialog + + + + + False + False + 5 + 0 + + + + + _Mouse Accessibility + True + True + False + mouse_button_img + True + + + Jump to the Mouse Accessibility dialog + + + + + False + False + 5 + 1 + + + + + Accessible Lo_gin + True + True + False + login_button_img + True + + + Jump to the Accessible Login dialog + + + + + False + False + 5 + 2 + + + + + False + False + 0 + + + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + 1 + + + + + False + False + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + Close and _Log Out + True + False + True + True + False + close_logout_button_img + True + + + False + False + 1 + + + + + gtk-close + True + True + True + True + False + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + at_help_button + at_close_logout_button + at_close_button + + + + True + gtk-quit + + + True + gtk-jump-to + + + True + gtk-jump-to + + + True + gtk-jump-to + + + True + gtk-jump-to + + diff --git a/capplets/accessibility/at-properties/at-properties.desktop.in.in b/capplets/accessibility/at-properties/at-properties.desktop.in.in new file mode 100644 index 00000000..f79b4bcf --- /dev/null +++ b/capplets/accessibility/at-properties/at-properties.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Assistive Technologies +_Comment=Choose which accessibility features to enable when you log in +Exec=mate-at-properties +Icon=preferences-desktop-accessibility +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;X-MATE-PersonalSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Assistive Technology Preferences +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/accessibility/at-properties/at-startup.png b/capplets/accessibility/at-properties/at-startup.png new file mode 100644 index 00000000..c09f8d49 Binary files /dev/null and b/capplets/accessibility/at-properties/at-startup.png differ diff --git a/capplets/accessibility/at-properties/at-support.png b/capplets/accessibility/at-properties/at-support.png new file mode 100644 index 00000000..1a350a8e Binary files /dev/null and b/capplets/accessibility/at-properties/at-support.png differ diff --git a/capplets/accessibility/at-properties/main.c b/capplets/accessibility/at-properties/main.c new file mode 100644 index 00000000..766c473b --- /dev/null +++ b/capplets/accessibility/at-properties/main.c @@ -0,0 +1,293 @@ +#include +#include +#include + +#include +#include + +#define GSM_SERVICE_DBUS "org.mate.SessionManager" +#define GSM_PATH_DBUS "/org/mate/SessionManager" +#define GSM_INTERFACE_DBUS "org.mate.SessionManager" + +enum { + GSM_LOGOUT_MODE_NORMAL = 0, + GSM_LOGOUT_MODE_NO_CONFIRMATION, + GSM_LOGOUT_MODE_FORCE +}; + +#include "capplet-util.h" +#include "mateconf-property-editor.h" +#include "activate-settings-daemon.h" + +#define ACCESSIBILITY_KEY "/desktop/mate/interface/accessibility" +#define ACCESSIBILITY_KEY_DIR "/desktop/mate/interface" + +static gboolean initial_state; + +static GtkBuilder * +create_builder (void) +{ + GtkBuilder *builder; + GError *error = NULL; + static const gchar *uifile = UIDIR "/at-enable-dialog.ui"; + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, uifile, &error)) { + GObject *object; + gchar *prog; + + object = gtk_builder_get_object (builder, "at_enable_image"); + gtk_image_set_from_file (GTK_IMAGE (object), + PIXMAPDIR "/at-startup.png"); + + object = gtk_builder_get_object (builder, + "at_applications_image"); + gtk_image_set_from_file (GTK_IMAGE (object), + PIXMAPDIR "/at-support.png"); + + prog = g_find_program_in_path ("mdmsetup"); + if (prog == NULL) { + object = gtk_builder_get_object (builder, + "login_button"); + gtk_widget_hide (GTK_WIDGET (object)); + } + + g_free (prog); + } else { + g_warning ("Could not load UI: %s", error->message); + g_error_free (error); + g_object_unref (builder); + builder = NULL; + } + + return builder; +} + +static void +cb_at_preferences (GtkDialog *dialog, gint response_id) +{ + g_spawn_command_line_async ("mate-default-applications-properties --show-page=a11y", NULL); +} + +static void +cb_keyboard_preferences (GtkDialog *dialog, gint response_id) +{ + g_spawn_command_line_async ("mate-keyboard-properties --a11y", NULL); +} + +static void +cb_mouse_preferences (GtkDialog *dialog, gint response_id) +{ + g_spawn_command_line_async ("mate-mouse-properties --show-page=accessibility", NULL); +} + +static void +cb_login_preferences (GtkDialog *dialog, gint response_id) +{ + g_spawn_command_line_async ("mdmsetup", NULL); +} + +/* get_session_bus(), get_sm_proxy(), and do_logout() are all + * based on code from mate-session-save.c from mate-session. + */ +static DBusGConnection * +get_session_bus (void) +{ + DBusGConnection *bus; + GError *error = NULL; + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + + if (bus == NULL) { + g_warning ("Couldn't connect to session bus: %s", error->message); + g_error_free (error); + } + + return bus; +} + +static DBusGProxy * +get_sm_proxy (void) +{ + DBusGConnection *connection; + DBusGProxy *sm_proxy; + + if (!(connection = get_session_bus ())) + return NULL; + + sm_proxy = dbus_g_proxy_new_for_name (connection, + GSM_SERVICE_DBUS, + GSM_PATH_DBUS, + GSM_INTERFACE_DBUS); + + return sm_proxy; +} + +static gboolean +do_logout (GError **err) +{ + DBusGProxy *sm_proxy; + GError *error; + gboolean res; + + sm_proxy = get_sm_proxy (); + if (sm_proxy == NULL) + return FALSE; + + res = dbus_g_proxy_call (sm_proxy, + "Logout", + &error, + G_TYPE_UINT, 0, /* '0' means 'log out normally' */ + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (sm_proxy) + g_object_unref (sm_proxy); + + return res; +} + +static void +cb_dialog_response (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) + capplet_help (GTK_WINDOW (dialog), + "goscustaccess-11"); + else if (response_id == GTK_RESPONSE_CLOSE || response_id == GTK_RESPONSE_DELETE_EVENT) + gtk_main_quit (); + else { + g_message ("CLOSE AND LOGOUT!"); + + if (!do_logout (NULL)) + gtk_main_quit (); + } +} + +static void +close_logout_update (GtkBuilder *builder) +{ + MateConfClient *client = mateconf_client_get_default (); + gboolean curr_state = mateconf_client_get_bool (client, ACCESSIBILITY_KEY, NULL); + GObject *btn = gtk_builder_get_object (builder, + "at_close_logout_button"); + + gtk_widget_set_sensitive (GTK_WIDGET (btn), initial_state != curr_state); + g_object_unref (client); +} + +static void +at_enable_toggled (GtkToggleButton *toggle_button, + GtkBuilder *builder) +{ + MateConfClient *client = mateconf_client_get_default (); + gboolean is_enabled = gtk_toggle_button_get_active (toggle_button); + + mateconf_client_set_bool (client, ACCESSIBILITY_KEY, + is_enabled, + NULL); + g_object_unref (client); +} + +static void +at_enable_update (MateConfClient *client, + GtkBuilder *builder) +{ + gboolean is_enabled = mateconf_client_get_bool (client, ACCESSIBILITY_KEY, NULL); + GObject *btn = gtk_builder_get_object (builder, "at_enable_toggle"); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), + is_enabled); +} + +static void +at_enable_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + at_enable_update (client, user_data); + close_logout_update (user_data); +} + +static void +setup_dialog (GtkBuilder *builder) +{ + MateConfClient *client; + GtkWidget *widget; + GObject *object; + GObject *peditor; + + client = mateconf_client_get_default (); + + mateconf_client_add_dir (client, ACCESSIBILITY_KEY_DIR, + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + object = gtk_builder_get_object (builder, "at_enable_toggle"); + g_signal_connect (object, "toggled", + G_CALLBACK (at_enable_toggled), + builder); + + peditor = mateconf_peditor_new_boolean (NULL, ACCESSIBILITY_KEY, + GTK_WIDGET (object), + NULL); + + initial_state = mateconf_client_get_bool (client, ACCESSIBILITY_KEY, NULL); + + at_enable_update (client, builder); + + mateconf_client_notify_add (client, ACCESSIBILITY_KEY_DIR, + at_enable_changed, + builder, NULL, NULL); + + object = gtk_builder_get_object (builder, "at_pref_button"); + g_signal_connect (object, "clicked", + G_CALLBACK (cb_at_preferences), NULL); + + object = gtk_builder_get_object (builder, "keyboard_button"); + g_signal_connect (object, "clicked", + G_CALLBACK (cb_keyboard_preferences), NULL); + + object = gtk_builder_get_object (builder, "mouse_button"); + g_signal_connect (object, "clicked", + G_CALLBACK (cb_mouse_preferences), NULL); + + object = gtk_builder_get_object (builder, "login_button"); + g_signal_connect (object, "clicked", + G_CALLBACK (cb_login_preferences), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, + "at_properties_dialog")); + capplet_set_icon (widget, "preferences-desktop-accessibility"); + + g_signal_connect (G_OBJECT (widget), + "response", + G_CALLBACK (cb_dialog_response), NULL); + + gtk_widget_show (widget); + + g_object_unref (client); +} + +int +main (int argc, char *argv[]) +{ + GtkBuilder *builder; + + capplet_init (NULL, &argc, &argv); + + activate_settings_daemon (); + + builder = create_builder (); + + if (builder) { + + setup_dialog (builder); + + gtk_main (); + + g_object_unref (builder); + } + + return 0; +} diff --git a/capplets/appearance/Makefile.am b/capplets/appearance/Makefile.am new file mode 100644 index 00000000..5c5dc2ca --- /dev/null +++ b/capplets/appearance/Makefile.am @@ -0,0 +1,58 @@ +SUBDIRS = data + +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = appearance + +bin_PROGRAMS = mate-appearance-properties + +mate_appearance_properties_SOURCES = \ + appearance.h \ + appearance-desktop.c \ + appearance-desktop.h \ + appearance-font.c \ + appearance-font.h \ + appearance-main.c \ + appearance-themes.c \ + appearance-themes.h \ + appearance-style.c \ + appearance-style.h \ + mate-wp-info.c \ + mate-wp-info.h \ + mate-wp-item.c \ + mate-wp-item.h \ + mate-wp-xml.c \ + mate-wp-xml.h \ + theme-installer.c \ + theme-installer.h \ + theme-save.c \ + theme-save.h \ + theme-util.c \ + theme-util.h + +AM_CFLAGS = -DMATE_DESKTOP_USE_UNSTABLE_API + +mate_appearance_properties_LDADD = \ + $(top_builddir)/libwindow-settings/libmate-window-settings.la \ + $(top_builddir)/capplets/common/libcommon.la \ + $(MATECC_CAPPLETS_LIBS) \ + $(FONT_CAPPLET_LIBS) \ + $(MARCO_LIBS) +mate_appearance_properties_LDFLAGS = -export-dynamic + +gtkbuilderdir = $(pkgdatadir)/ui +pixmapdir = $(pkgdatadir)/pixmaps +wallpaperdir = $(datadir)/mate-background-properties + +INCLUDES = \ + $(MARCO_CFLAGS) \ + $(MATECC_CAPPLETS_CFLAGS) \ + $(FONT_CAPPLET_CFLAGS) \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_GTKBUILDER_DIR="\"$(gtkbuilderdir)\"" \ + -DMATECC_PIXMAP_DIR="\"$(pixmapdir)\"" \ + -DWALLPAPER_DATADIR="\"$(wallpaperdir)\"" + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) + +-include $(top_srcdir)/git.mk diff --git a/capplets/appearance/appearance-desktop.c b/capplets/appearance/appearance-desktop.c new file mode 100644 index 00000000..f0dc803e --- /dev/null +++ b/capplets/appearance/appearance-desktop.c @@ -0,0 +1,1400 @@ +/* + * Copyright (C) 2007,2008 The MATE Foundation + * Written by Rodney Dawes + * Denis Washington + * Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" +#include "mate-wp-info.h" +#include "mate-wp-item.h" +#include "mate-wp-xml.h" +#include +#include +#include +#include +#include +#include + +enum { + TARGET_URI_LIST, + TARGET_BGIMAGE +}; + +static const GtkTargetEntry drop_types[] = { + {"text/uri-list", 0, TARGET_URI_LIST}, + {"property/bgimage", 0, TARGET_BGIMAGE} +}; + +static const GtkTargetEntry drag_types[] = { + {"text/uri-list", GTK_TARGET_OTHER_WIDGET, TARGET_URI_LIST} +}; + + +static void wp_update_preview(GtkFileChooser* chooser, AppearanceData* data); + +static void select_item(AppearanceData* data, MateWPItem* item, gboolean scroll) +{ + GtkTreePath* path; + + g_return_if_fail(data != NULL); + + if (item == NULL) + return; + + path = gtk_tree_row_reference_get_path(item->rowref); + + gtk_icon_view_select_path(data->wp_view, path); + + if (scroll) + { + gtk_icon_view_scroll_to_path(data->wp_view, path, FALSE, 0.5, 0.0); + } + + gtk_tree_path_free(path); +} + +static MateWPItem* get_selected_item(AppearanceData* data, GtkTreeIter* iter) +{ + MateWPItem* item = NULL; + GList* selected; + + selected = gtk_icon_view_get_selected_items (data->wp_view); + + if (selected != NULL) + { + GtkTreeIter sel_iter; + + gtk_tree_model_get_iter(data->wp_model, &sel_iter, selected->data); + + g_list_foreach(selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free(selected); + + if (iter) + *iter = sel_iter; + + gtk_tree_model_get(data->wp_model, &sel_iter, 1, &item, -1); + } + + return item; +} + +static gboolean predicate (gpointer key, gpointer value, gpointer data) +{ + MateBG *bg = data; + MateWPItem *item = value; + + return item->bg == bg; +} + +static void on_item_changed (MateBG *bg, AppearanceData *data) { + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + MateWPItem *item; + + item = g_hash_table_find (data->wp_hash, predicate, bg); + + if (!item) + return; + + model = gtk_tree_row_reference_get_model (item->rowref); + path = gtk_tree_row_reference_get_path (item->rowref); + + if (gtk_tree_model_get_iter (model, &iter, path)) { + GdkPixbuf *pixbuf; + + g_signal_handlers_block_by_func (bg, G_CALLBACK (on_item_changed), data); + + pixbuf = mate_wp_item_get_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height); + if (pixbuf) { + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + g_object_unref (pixbuf); + } + + g_signal_handlers_unblock_by_func (bg, G_CALLBACK (on_item_changed), data); + } +} + +static void +wp_props_load_wallpaper (gchar *key, + MateWPItem *item, + AppearanceData *data) +{ + GtkTreeIter iter; + GtkTreePath *path; + GdkPixbuf *pixbuf; + + if (item->deleted == TRUE) + return; + + gtk_list_store_append (GTK_LIST_STORE (data->wp_model), &iter); + + pixbuf = mate_wp_item_get_thumbnail (item, data->thumb_factory, + data->thumb_width, + data->thumb_height); + mate_wp_item_update_description (item); + + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, + 0, pixbuf, + 1, item, + -1); + + if (pixbuf != NULL) + g_object_unref (pixbuf); + + path = gtk_tree_model_get_path (data->wp_model, &iter); + item->rowref = gtk_tree_row_reference_new (data->wp_model, path); + g_signal_connect (item->bg, "changed", G_CALLBACK (on_item_changed), data); + gtk_tree_path_free (path); +} + +static MateWPItem * +wp_add_image (AppearanceData *data, + const gchar *filename) +{ + MateWPItem *item; + + if (!filename) + return NULL; + + item = g_hash_table_lookup (data->wp_hash, filename); + + if (item != NULL) + { + if (item->deleted) + { + item->deleted = FALSE; + wp_props_load_wallpaper (item->filename, item, data); + } + } + else + { + item = mate_wp_item_new (filename, data->wp_hash, data->thumb_factory); + + if (item != NULL) + { + wp_props_load_wallpaper (item->filename, item, data); + } + } + + return item; +} + +static void +wp_add_images (AppearanceData *data, + GSList *images) +{ + GdkWindow *window; + GtkWidget *w; + GdkCursor *cursor; + MateWPItem *item; + + w = appearance_capplet_get_widget (data, "appearance_window"); + window = gtk_widget_get_window (w); + + item = NULL; + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), + GDK_WATCH); + gdk_window_set_cursor (window, cursor); + gdk_cursor_unref (cursor); + + while (images != NULL) + { + gchar *uri = images->data; + + item = wp_add_image (data, uri); + images = g_slist_remove (images, uri); + g_free (uri); + } + + gdk_window_set_cursor (window, NULL); + + if (item != NULL) + { + select_item (data, item, TRUE); + } +} + +static void +wp_option_menu_set (AppearanceData *data, + int value, + gboolean shade_type) +{ + if (shade_type) + { + gtk_combo_box_set_active (GTK_COMBO_BOX (data->wp_color_menu), + value); + + if (value == MATE_BG_COLOR_SOLID) + gtk_widget_hide (data->wp_scpicker); + else + gtk_widget_show (data->wp_scpicker); + } + else + { + gtk_combo_box_set_active (GTK_COMBO_BOX (data->wp_style_menu), + value); + } +} + +static void +wp_set_sensitivities (AppearanceData *data) +{ + MateWPItem *item; + gchar *filename = NULL; + + item = get_selected_item (data, NULL); + + if (item != NULL) + filename = item->filename; + + if (!mateconf_client_key_is_writable (data->client, WP_OPTIONS_KEY, NULL) + || (filename && !strcmp (filename, "(none)"))) + gtk_widget_set_sensitive (data->wp_style_menu, FALSE); + else + gtk_widget_set_sensitive (data->wp_style_menu, TRUE); + + if (!mateconf_client_key_is_writable (data->client, WP_SHADING_KEY, NULL)) + gtk_widget_set_sensitive (data->wp_color_menu, FALSE); + else + gtk_widget_set_sensitive (data->wp_color_menu, TRUE); + + if (!mateconf_client_key_is_writable (data->client, WP_PCOLOR_KEY, NULL)) + gtk_widget_set_sensitive (data->wp_pcpicker, FALSE); + else + gtk_widget_set_sensitive (data->wp_pcpicker, TRUE); + + if (!mateconf_client_key_is_writable (data->client, WP_SCOLOR_KEY, NULL)) + gtk_widget_set_sensitive (data->wp_scpicker, FALSE); + else + gtk_widget_set_sensitive (data->wp_scpicker, TRUE); + + if (!filename || !strcmp (filename, "(none)")) + gtk_widget_set_sensitive (data->wp_rem_button, FALSE); + else + gtk_widget_set_sensitive (data->wp_rem_button, TRUE); +} + +static void +wp_scale_type_changed (GtkComboBox *combobox, + AppearanceData *data) +{ + MateWPItem *item; + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + item = get_selected_item (data, &iter); + + if (item == NULL) + return; + + item->options = gtk_combo_box_get_active (GTK_COMBO_BOX (data->wp_style_menu)); + + pixbuf = mate_wp_item_get_thumbnail (item, data->thumb_factory, + data->thumb_width, + data->thumb_height); + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + if (pixbuf != NULL) + g_object_unref (pixbuf); + + if (mateconf_client_key_is_writable (data->client, WP_OPTIONS_KEY, NULL)) + mateconf_client_set_string (data->client, WP_OPTIONS_KEY, + wp_item_option_to_string (item->options), NULL); +} + +static void +wp_shade_type_changed (GtkWidget *combobox, + AppearanceData *data) +{ + MateWPItem *item; + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + item = get_selected_item (data, &iter); + + if (item == NULL) + return; + + item->shade_type = gtk_combo_box_get_active (GTK_COMBO_BOX (data->wp_color_menu)); + + pixbuf = mate_wp_item_get_thumbnail (item, data->thumb_factory, + data->thumb_width, + data->thumb_height); + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + if (pixbuf != NULL) + g_object_unref (pixbuf); + + if (mateconf_client_key_is_writable (data->client, WP_SHADING_KEY, NULL)) + mateconf_client_set_string (data->client, WP_SHADING_KEY, + wp_item_shading_to_string (item->shade_type), NULL); +} + +static void +wp_color_changed (AppearanceData *data, + gboolean update) +{ + MateWPItem *item; + + item = get_selected_item (data, NULL); + + if (item == NULL) + return; + + gtk_color_button_get_color (GTK_COLOR_BUTTON (data->wp_pcpicker), item->pcolor); + gtk_color_button_get_color (GTK_COLOR_BUTTON (data->wp_scpicker), item->scolor); + + if (update) + { + gchar *pcolor, *scolor; + + pcolor = gdk_color_to_string (item->pcolor); + scolor = gdk_color_to_string (item->scolor); + mateconf_client_set_string (data->client, WP_PCOLOR_KEY, pcolor, NULL); + mateconf_client_set_string (data->client, WP_SCOLOR_KEY, scolor, NULL); + g_free (pcolor); + g_free (scolor); + } + + wp_shade_type_changed (NULL, data); +} + +static void +wp_scolor_changed (GtkWidget *widget, + AppearanceData *data) +{ + wp_color_changed (data, TRUE); +} + +static void +wp_remove_wallpaper (GtkWidget *widget, + AppearanceData *data) +{ + MateWPItem *item; + GtkTreeIter iter; + GtkTreePath *path; + + item = get_selected_item (data, &iter); + + if (item) + { + item->deleted = TRUE; + + if (gtk_list_store_remove (GTK_LIST_STORE (data->wp_model), &iter)) + path = gtk_tree_model_get_path (data->wp_model, &iter); + else + path = gtk_tree_path_new_first (); + + gtk_icon_view_select_path (data->wp_view, path); + gtk_icon_view_set_cursor (data->wp_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } +} + +static void +wp_uri_changed (const gchar *uri, + AppearanceData *data) +{ + MateWPItem *item, *selected; + + item = g_hash_table_lookup (data->wp_hash, uri); + selected = get_selected_item (data, NULL); + + if (selected != NULL && strcmp (selected->filename, uri) != 0) + { + if (item == NULL) + item = wp_add_image (data, uri); + + if (item) + select_item (data, item, TRUE); + } +} + +static void +wp_file_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + const gchar *uri; + gchar *wpfile; + + uri = mateconf_value_get_string (entry->value); + + if (g_utf8_validate (uri, -1, NULL) && g_file_test (uri, G_FILE_TEST_EXISTS)) + wpfile = g_strdup (uri); + else + wpfile = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL); + + wp_uri_changed (wpfile, data); + + g_free (wpfile); +} + +static void +wp_options_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + MateWPItem *item; + const gchar *option; + + option = mateconf_value_get_string (entry->value); + + /* "none" means we don't use a background image */ + if (option == NULL || !strcmp (option, "none")) + { + /* temporarily disconnect so we don't override settings when + * updating the selection */ + data->wp_update_mateconf = FALSE; + wp_uri_changed ("(none)", data); + data->wp_update_mateconf = TRUE; + return; + } + + item = get_selected_item (data, NULL); + + if (item != NULL) + { + item->options = wp_item_string_to_option (option); + wp_option_menu_set (data, item->options, FALSE); + } +} + +static void +wp_shading_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + MateWPItem *item; + + wp_set_sensitivities (data); + + item = get_selected_item (data, NULL); + + if (item != NULL) + { + item->shade_type = wp_item_string_to_shading (mateconf_value_get_string (entry->value)); + wp_option_menu_set (data, item->shade_type, TRUE); + } +} + +static void +wp_color1_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + GdkColor color; + const gchar *colorhex; + + colorhex = mateconf_value_get_string (entry->value); + + gdk_color_parse (colorhex, &color); + + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_pcpicker), &color); + + wp_color_changed (data, FALSE); +} + +static void +wp_color2_changed (MateConfClient *client, guint id, + MateConfEntry *entry, + AppearanceData *data) +{ + GdkColor color; + const gchar *colorhex; + + wp_set_sensitivities (data); + + colorhex = mateconf_value_get_string (entry->value); + + gdk_color_parse (colorhex, &color); + + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_scpicker), &color); + + wp_color_changed (data, FALSE); +} + +static gboolean +wp_props_wp_set (AppearanceData *data, MateWPItem *item) +{ + MateConfChangeSet *cs; + gchar *pcolor, *scolor; + + cs = mateconf_change_set_new (); + + if (!strcmp (item->filename, "(none)")) + { + mateconf_change_set_set_string (cs, WP_OPTIONS_KEY, "none"); + mateconf_change_set_set_string (cs, WP_FILE_KEY, ""); + } + else + { + gchar *uri; + + if (g_utf8_validate (item->filename, -1, NULL)) + uri = g_strdup (item->filename); + else + uri = g_filename_to_utf8 (item->filename, -1, NULL, NULL, NULL); + + if (uri == NULL) { + g_warning ("Failed to convert filename to UTF-8: %s", item->filename); + } else { + mateconf_change_set_set_string (cs, WP_FILE_KEY, uri); + g_free (uri); + } + + mateconf_change_set_set_string (cs, WP_OPTIONS_KEY, + wp_item_option_to_string (item->options)); + } + + mateconf_change_set_set_string (cs, WP_SHADING_KEY, + wp_item_shading_to_string (item->shade_type)); + + pcolor = gdk_color_to_string (item->pcolor); + scolor = gdk_color_to_string (item->scolor); + mateconf_change_set_set_string (cs, WP_PCOLOR_KEY, pcolor); + mateconf_change_set_set_string (cs, WP_SCOLOR_KEY, scolor); + g_free (pcolor); + g_free (scolor); + + mateconf_client_commit_change_set (data->client, cs, TRUE, NULL); + + mateconf_change_set_unref (cs); + + return FALSE; +} + +static void +wp_props_wp_selected (GtkTreeSelection *selection, + AppearanceData *data) +{ + MateWPItem *item; + + item = get_selected_item (data, NULL); + + if (item != NULL) + { + wp_set_sensitivities (data); + + if (strcmp (item->filename, "(none)") != 0) + wp_option_menu_set (data, item->options, FALSE); + + wp_option_menu_set (data, item->shade_type, TRUE); + + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_pcpicker), + item->pcolor); + gtk_color_button_set_color (GTK_COLOR_BUTTON (data->wp_scpicker), + item->scolor); + + if (data->wp_update_mateconf) + wp_props_wp_set (data, item); + } + else + { + gtk_widget_set_sensitive (data->wp_rem_button, FALSE); + } +} + +void +wp_create_filechooser (AppearanceData *data) +{ + const char *start_dir, *pictures = NULL; + GtkFileFilter *filter; + + data->wp_filesel = GTK_FILE_CHOOSER ( + gtk_file_chooser_dialog_new (_("Add Wallpaper"), + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window")), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, + GTK_RESPONSE_OK, + NULL)); + + gtk_dialog_set_default_response (GTK_DIALOG (data->wp_filesel), GTK_RESPONSE_OK); + gtk_file_chooser_set_select_multiple (data->wp_filesel, TRUE); + gtk_file_chooser_set_use_preview_label (data->wp_filesel, FALSE); + + start_dir = g_get_home_dir (); + + if (g_file_test ("/usr/share/backgrounds", G_FILE_TEST_IS_DIR)) { + gtk_file_chooser_add_shortcut_folder (data->wp_filesel, + "/usr/share/backgrounds", NULL); + start_dir = "/usr/share/backgrounds"; + } + + pictures = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); + if (pictures != NULL && g_file_test (pictures, G_FILE_TEST_IS_DIR)) { + gtk_file_chooser_add_shortcut_folder (data->wp_filesel, pictures, NULL); + start_dir = pictures; + } + + gtk_file_chooser_set_current_folder (data->wp_filesel, start_dir); + + filter = gtk_file_filter_new (); + gtk_file_filter_add_pixbuf_formats (filter); + gtk_file_filter_set_name (filter, _("Images")); + gtk_file_chooser_add_filter (data->wp_filesel, filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (data->wp_filesel, filter); + + data->wp_image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (data->wp_filesel, data->wp_image); + gtk_widget_set_size_request (data->wp_image, 128, -1); + + gtk_widget_show (data->wp_image); + + g_signal_connect (data->wp_filesel, "update-preview", + (GCallback) wp_update_preview, data); +} + +static void +wp_file_open_dialog (GtkWidget *widget, + AppearanceData *data) +{ + GSList *files; + + if (!data->wp_filesel) + wp_create_filechooser (data); + + switch (gtk_dialog_run (GTK_DIALOG (data->wp_filesel))) + { + case GTK_RESPONSE_OK: + files = gtk_file_chooser_get_filenames (data->wp_filesel); + wp_add_images (data, files); + case GTK_RESPONSE_CANCEL: + default: + gtk_widget_hide (GTK_WIDGET (data->wp_filesel)); + break; + } +} + +static void +wp_drag_received (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, + AppearanceData *data) +{ + if (info == TARGET_URI_LIST || info == TARGET_BGIMAGE) + { + GSList *realuris = NULL; + gchar **uris; + + uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); + if (uris != NULL) + { + GtkWidget *w; + GdkWindow *window; + GdkCursor *cursor; + gchar **uri; + + w = appearance_capplet_get_widget (data, "appearance_window"); + window = gtk_widget_get_window (w); + + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), + GDK_WATCH); + gdk_window_set_cursor (window, cursor); + gdk_cursor_unref (cursor); + + for (uri = uris; *uri; ++uri) + { + GFile *f; + + f = g_file_new_for_uri (*uri); + realuris = g_slist_append (realuris, g_file_get_path (f)); + g_object_unref (f); + } + + wp_add_images (data, realuris); + gdk_window_set_cursor (window, NULL); + + g_strfreev (uris); + } + } +} + +static void +wp_drag_get_data (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint type, guint time, + AppearanceData *data) +{ + if (type == TARGET_URI_LIST) { + MateWPItem *item = get_selected_item (data, NULL); + + if (item != NULL) { + char *uris[2]; + + uris[0] = g_filename_to_uri (item->filename, NULL, NULL); + uris[1] = NULL; + + gtk_selection_data_set_uris (selection_data, uris); + + g_free (uris[0]); + } + } +} + +static gboolean +wp_view_tooltip_cb (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_mode, + GtkTooltip *tooltip, + AppearanceData *data) +{ + GtkTreeIter iter; + MateWPItem *item; + + if (gtk_icon_view_get_tooltip_context (data->wp_view, + &x, &y, + keyboard_mode, + NULL, + NULL, + &iter)) + { + gtk_tree_model_get (data->wp_model, &iter, 1, &item, -1); + gtk_tooltip_set_markup (tooltip, item->description); + + return TRUE; + } + + return FALSE; +} + +static gint +wp_list_sort (GtkTreeModel *model, + GtkTreeIter *a, GtkTreeIter *b, + AppearanceData *data) +{ + MateWPItem *itema, *itemb; + gint retval; + + gtk_tree_model_get (model, a, 1, &itema, -1); + gtk_tree_model_get (model, b, 1, &itemb, -1); + + if (!strcmp (itema->filename, "(none)")) + { + retval = -1; + } + else if (!strcmp (itemb->filename, "(none)")) + { + retval = 1; + } + else + { + retval = g_utf8_collate (itema->description, itemb->description); + } + + return retval; +} + +static void +wp_update_preview (GtkFileChooser *chooser, + AppearanceData *data) +{ + gchar *uri; + + uri = gtk_file_chooser_get_preview_uri (chooser); + + if (uri) + { + GdkPixbuf *pixbuf = NULL; + const gchar *mime_type = NULL; + GFile *file; + GFileInfo *file_info; + + file = g_file_new_for_uri (uri); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (file_info != NULL) + { + mime_type = g_file_info_get_content_type (file_info); + g_object_unref (file_info); + } + + if (mime_type) + { + pixbuf = mate_desktop_thumbnail_factory_generate_thumbnail (data->thumb_factory, + uri, + mime_type); + } + + if (pixbuf != NULL) + { + gtk_image_set_from_pixbuf (GTK_IMAGE (data->wp_image), pixbuf); + g_object_unref (pixbuf); + } + else + { + gtk_image_set_from_stock (GTK_IMAGE (data->wp_image), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + } + + gtk_file_chooser_set_preview_widget_active (chooser, TRUE); +} + +static gboolean +reload_item (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + AppearanceData *data) +{ + MateWPItem *item; + GdkPixbuf *pixbuf; + + gtk_tree_model_get (model, iter, 1, &item, -1); + + pixbuf = mate_wp_item_get_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height); + if (pixbuf) { + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), iter, 0, pixbuf, -1); + g_object_unref (pixbuf); + } + + return FALSE; +} + +static gdouble +get_monitor_aspect_ratio_for_widget (GtkWidget *widget) +{ + gdouble aspect; + gint monitor; + GdkRectangle rect; + + monitor = gdk_screen_get_monitor_at_window (gtk_widget_get_screen (widget), gtk_widget_get_window (widget)); + gdk_screen_get_monitor_geometry (gtk_widget_get_screen (widget), monitor, &rect); + aspect = rect.height / (gdouble)rect.width; + + return aspect; +} + +#define LIST_IMAGE_SIZE 108 + +static void +compute_thumbnail_sizes (AppearanceData *data) +{ + gdouble aspect; + + aspect = get_monitor_aspect_ratio_for_widget (GTK_WIDGET (data->wp_view)); + if (aspect > 1) { + /* portrait */ + data->thumb_width = LIST_IMAGE_SIZE / aspect; + data->thumb_height = LIST_IMAGE_SIZE; + } else { + data->thumb_width = LIST_IMAGE_SIZE; + data->thumb_height = LIST_IMAGE_SIZE * aspect; + } +} + +static void +reload_wallpapers (AppearanceData *data) +{ + compute_thumbnail_sizes (data); + gtk_tree_model_foreach (data->wp_model, (GtkTreeModelForeachFunc)reload_item, data); +} + +static gboolean +wp_load_stuffs (void *user_data) +{ + AppearanceData *data; + gchar *imagepath, *uri, *style; + MateWPItem *item; + + data = (AppearanceData *) user_data; + + compute_thumbnail_sizes (data); + + mate_wp_xml_load_list (data); + g_hash_table_foreach (data->wp_hash, (GHFunc) wp_props_load_wallpaper, + data); + + style = mateconf_client_get_string (data->client, + WP_OPTIONS_KEY, + NULL); + if (style == NULL) + style = g_strdup ("none"); + + uri = mateconf_client_get_string (data->client, + WP_FILE_KEY, + NULL); + + if (uri && *uri == '\0') + { + g_free (uri); + uri = NULL; + } + + if (uri == NULL) + uri = g_strdup ("(none)"); + + if (g_utf8_validate (uri, -1, NULL) && g_file_test (uri, G_FILE_TEST_EXISTS)) + imagepath = g_strdup (uri); + else + imagepath = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL); + + g_free (uri); + + item = g_hash_table_lookup (data->wp_hash, imagepath); + + if (item != NULL) + { + /* update with the current mateconf settings */ + mate_wp_item_update (item); + + if (strcmp (style, "none") != 0) + { + if (item->deleted == TRUE) + { + item->deleted = FALSE; + wp_props_load_wallpaper (item->filename, item, data); + } + + select_item (data, item, FALSE); + } + } + else if (strcmp (style, "none") != 0) + { + item = wp_add_image (data, imagepath); + if (item) + select_item (data, item, FALSE); + } + + item = g_hash_table_lookup (data->wp_hash, "(none)"); + if (item == NULL) + { + item = mate_wp_item_new ("(none)", data->wp_hash, data->thumb_factory); + if (item != NULL) + { + wp_props_load_wallpaper (item->filename, item, data); + } + } + else + { + if (item->deleted == TRUE) + { + item->deleted = FALSE; + wp_props_load_wallpaper (item->filename, item, data); + } + + if (!strcmp (style, "none")) + { + select_item (data, item, FALSE); + wp_option_menu_set (data, MATE_BG_PLACEMENT_SCALED, FALSE); + } + } + g_free (imagepath); + g_free (style); + + if (data->wp_uris) { + wp_add_images (data, data->wp_uris); + data->wp_uris = NULL; + } + + return FALSE; +} + +static void +wp_select_after_realize (GtkWidget *widget, + AppearanceData *data) +{ + MateWPItem *item; + + g_idle_add (wp_load_stuffs, data); + + item = get_selected_item (data, NULL); + if (item == NULL) + item = g_hash_table_lookup (data->wp_hash, "(none)"); + + select_item (data, item, TRUE); +} + +static GdkPixbuf *buttons[3]; + +static void +create_button_images (AppearanceData *data) +{ + GtkWidget *widget = (GtkWidget*)data->wp_view; + GtkStyle *style = gtk_widget_get_style (widget); + GtkIconSet *icon_set; + GdkPixbuf *pixbuf, *pb, *pb2; + gint i, w, h; + + icon_set = gtk_style_lookup_icon_set (style, "gtk-media-play"); + pb = gtk_icon_set_render_icon (icon_set, + style, + GTK_TEXT_DIR_RTL, + GTK_STATE_NORMAL, + GTK_ICON_SIZE_MENU, + widget, + NULL); + pb2 = gtk_icon_set_render_icon (icon_set, + style, + GTK_TEXT_DIR_LTR, + GTK_STATE_NORMAL, + GTK_ICON_SIZE_MENU, + widget, + NULL); + w = gdk_pixbuf_get_width (pb); + h = gdk_pixbuf_get_height (pb); + + for (i = 0; i < 3; i++) { + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 2 * w, h); + gdk_pixbuf_fill (pixbuf, 0); + if (i > 0) + gdk_pixbuf_composite (pb, pixbuf, 0, 0, w, h, 0, 0, 1, 1, GDK_INTERP_NEAREST, 255); + if (i < 2) + gdk_pixbuf_composite (pb2, pixbuf, w, 0, w, h, w, 0, 1, 1, GDK_INTERP_NEAREST, 255); + + buttons[i] = pixbuf; + } + + g_object_unref (pb); + g_object_unref (pb2); +} + +static void +next_frame (AppearanceData *data, + GtkCellRenderer *cr, + gint direction) +{ + MateWPItem *item; + GtkTreeIter iter; + GdkPixbuf *pixbuf, *pb; + gint frame; + + pixbuf = NULL; + + frame = data->frame + direction; + item = get_selected_item (data, &iter); + + if (frame >= 0) + pixbuf = mate_wp_item_get_frame_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height, + frame); + if (pixbuf) { + gtk_list_store_set (GTK_LIST_STORE (data->wp_model), &iter, 0, pixbuf, -1); + g_object_unref (pixbuf); + data->frame = frame; + } + + pb = buttons[1]; + if (direction < 0) { + if (frame == 0) + pb = buttons[0]; + } + else { + pixbuf = mate_wp_item_get_frame_thumbnail (item, + data->thumb_factory, + data->thumb_width, + data->thumb_height, + frame + 1); + if (pixbuf) + g_object_unref (pixbuf); + else + pb = buttons[2]; + } + g_object_set (cr, "pixbuf", pb, NULL); +} + +static gboolean +wp_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + AppearanceData *data) +{ + GtkCellRenderer *cell; + GdkEventButton *button_event = (GdkEventButton *) event; + + if (event->type != GDK_BUTTON_PRESS) + return FALSE; + + if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (widget), + button_event->x, button_event->y, + NULL, &cell)) { + if (g_object_get_data (G_OBJECT (cell), "buttons")) { + gint w, h; + GtkCellRenderer *cell2 = NULL; + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); + if (gtk_icon_view_get_item_at_pos (GTK_ICON_VIEW (widget), + button_event->x + w, button_event->y, + NULL, &cell2) && cell == cell2) + next_frame (data, cell, -1); + else + next_frame (data, cell, 1); + return TRUE; + } + } + + return FALSE; +} + +static void +wp_selected_changed_cb (GtkIconView *view, + AppearanceData *data) +{ + GtkCellRenderer *cr; + GList *cells, *l; + + data->frame = -1; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (data->wp_view)); + for (l = cells; l; l = l->next) { + cr = l->data; + if (g_object_get_data (G_OBJECT (cr), "buttons")) + g_object_set (cr, "pixbuf", buttons[0], NULL); + } + g_list_free (cells); +} + +static void +buttons_cell_data_func (GtkCellLayout *layout, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + AppearanceData *data = user_data; + GtkTreePath *path; + MateWPItem *item; + gboolean visible; + + path = gtk_tree_model_get_path (model, iter); + + if (gtk_icon_view_path_is_selected (GTK_ICON_VIEW (layout), path)) { + item = get_selected_item (data, NULL); + visible = mate_bg_changes_with_time (item->bg); + } + else + visible = FALSE; + + g_object_set (G_OBJECT (cell), "visible", visible, NULL); + + gtk_tree_path_free (path); +} + +static void +screen_monitors_changed (GdkScreen *screen, + AppearanceData *data) +{ + reload_wallpapers (data); +} + +void +desktop_init (AppearanceData *data, + const gchar **uris) +{ + GtkWidget *add_button, *w; + GtkCellRenderer *cr; + char *url; + + data->wp_update_mateconf = TRUE; + + data->wp_uris = NULL; + if (uris != NULL) { + while (*uris != NULL) { + data->wp_uris = g_slist_append (data->wp_uris, g_strdup (*uris)); + uris++; + } + } + + w = appearance_capplet_get_widget (data, "more_backgrounds_linkbutton"); + url = mateconf_client_get_string (data->client, MORE_BACKGROUNDS_URL_KEY, NULL); + if (url != NULL && url[0] != '\0') { + gtk_link_button_set_uri (GTK_LINK_BUTTON (w), url); + gtk_widget_show (w); + } else { + gtk_widget_hide (w); + } + g_free (url); + + data->wp_hash = g_hash_table_new (g_str_hash, g_str_equal); + + mateconf_client_add_dir (data->client, WP_PATH_KEY, + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + mateconf_client_notify_add (data->client, + WP_FILE_KEY, + (MateConfClientNotifyFunc) wp_file_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_OPTIONS_KEY, + (MateConfClientNotifyFunc) wp_options_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_SHADING_KEY, + (MateConfClientNotifyFunc) wp_shading_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_PCOLOR_KEY, + (MateConfClientNotifyFunc) wp_color1_changed, + data, NULL, NULL); + mateconf_client_notify_add (data->client, + WP_SCOLOR_KEY, + (MateConfClientNotifyFunc) wp_color2_changed, + data, NULL, NULL); + + data->wp_model = GTK_TREE_MODEL (gtk_list_store_new (2, GDK_TYPE_PIXBUF, + G_TYPE_POINTER)); + + data->wp_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "wp_view")); + gtk_icon_view_set_model (data->wp_view, GTK_TREE_MODEL (data->wp_model)); + + g_signal_connect_after (data->wp_view, "realize", + (GCallback) wp_select_after_realize, data); + + gtk_cell_layout_clear (GTK_CELL_LAYOUT (data->wp_view)); + + cr = gtk_cell_renderer_pixbuf_new (); + g_object_set (cr, "xpad", 5, "ypad", 5, NULL); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->wp_view), cr, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->wp_view), cr, + "pixbuf", 0, + NULL); + + cr = gtk_cell_renderer_pixbuf_new (); + create_button_images (data); + g_object_set (cr, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, + "pixbuf", buttons[0], + NULL); + g_object_set_data (G_OBJECT (cr), "buttons", GINT_TO_POINTER (TRUE)); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->wp_view), cr, FALSE); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (data->wp_view), cr, + buttons_cell_data_func, data, NULL); + g_signal_connect (data->wp_view, "selection-changed", + (GCallback) wp_selected_changed_cb, data); + g_signal_connect (data->wp_view, "button-press-event", + G_CALLBACK (wp_button_press_cb), data); + + data->frame = -1; + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (data->wp_model), 1, + (GtkTreeIterCompareFunc) wp_list_sort, + data, NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->wp_model), + 1, GTK_SORT_ASCENDING); + + gtk_drag_dest_set (GTK_WIDGET (data->wp_view), GTK_DEST_DEFAULT_ALL, drop_types, + G_N_ELEMENTS (drop_types), GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (data->wp_view, "drag_data_received", + (GCallback) wp_drag_received, data); + + gtk_drag_source_set (GTK_WIDGET (data->wp_view), GDK_BUTTON1_MASK, + drag_types, G_N_ELEMENTS (drag_types), GDK_ACTION_COPY); + g_signal_connect (data->wp_view, "drag-data-get", + (GCallback) wp_drag_get_data, data); + + data->wp_style_menu = appearance_capplet_get_widget (data, "wp_style_menu"); + + g_signal_connect (data->wp_style_menu, "changed", + (GCallback) wp_scale_type_changed, data); + + data->wp_color_menu = appearance_capplet_get_widget (data, "wp_color_menu"); + + g_signal_connect (data->wp_color_menu, "changed", + (GCallback) wp_shade_type_changed, data); + + data->wp_scpicker = appearance_capplet_get_widget (data, "wp_scpicker"); + + g_signal_connect (data->wp_scpicker, "color-set", + (GCallback) wp_scolor_changed, data); + + data->wp_pcpicker = appearance_capplet_get_widget (data, "wp_pcpicker"); + + g_signal_connect (data->wp_pcpicker, "color-set", + (GCallback) wp_scolor_changed, data); + + add_button = appearance_capplet_get_widget (data, "wp_add_button"); + gtk_button_set_image (GTK_BUTTON (add_button), + gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_BUTTON)); + + g_signal_connect (add_button, "clicked", + (GCallback) wp_file_open_dialog, data); + + data->wp_rem_button = appearance_capplet_get_widget (data, "wp_rem_button"); + + g_signal_connect (data->wp_rem_button, "clicked", + (GCallback) wp_remove_wallpaper, data); + data->screen_monitors_handler = g_signal_connect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + "monitors-changed", + G_CALLBACK (screen_monitors_changed), + data); + data->screen_size_handler = g_signal_connect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + "size-changed", + G_CALLBACK (screen_monitors_changed), + data); + + g_signal_connect (data->wp_view, "selection-changed", + (GCallback) wp_props_wp_selected, data); + g_signal_connect (data->wp_view, "query-tooltip", + (GCallback) wp_view_tooltip_cb, data); + gtk_widget_set_has_tooltip (GTK_WIDGET (data->wp_view), TRUE); + + wp_set_sensitivities (data); + + /* create the file selector later to save time on startup */ + data->wp_filesel = NULL; + +} + +void +desktop_shutdown (AppearanceData *data) +{ + mate_wp_xml_save_list (data); + + if (data->screen_monitors_handler > 0) { + g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + data->screen_monitors_handler); + data->screen_monitors_handler = 0; + } + if (data->screen_size_handler > 0) { + g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (data->wp_view)), + data->screen_size_handler); + data->screen_size_handler = 0; + } + + g_slist_foreach (data->wp_uris, (GFunc) g_free, NULL); + g_slist_free (data->wp_uris); + if (data->wp_filesel) + { + g_object_ref_sink (data->wp_filesel); + g_object_unref (data->wp_filesel); + } +} diff --git a/capplets/appearance/appearance-desktop.h b/capplets/appearance/appearance-desktop.h new file mode 100644 index 00000000..ae0b25a9 --- /dev/null +++ b/capplets/appearance/appearance-desktop.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Denis Washington + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void desktop_init (AppearanceData *data, const gchar **uris); +void desktop_shutdown (AppearanceData *data); diff --git a/capplets/appearance/appearance-font.c b/capplets/appearance/appearance-font.c new file mode 100644 index 00000000..d9f4d3c2 --- /dev/null +++ b/capplets/appearance/appearance-font.c @@ -0,0 +1,961 @@ +/* + * Copyright (C) 2007 The GNOME Foundation + * Written by Jonathan Blandford + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include + +#ifdef HAVE_XFT2 + #include + #include +#endif /* HAVE_XFT2 */ + +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" + +#define GTK_FONT_KEY "/desktop/mate/interface/font_name" +#define DESKTOP_FONT_KEY "/apps/caja/preferences/desktop_font" + +#define MARCO_DIR "/apps/marco/general" +#define WINDOW_TITLE_FONT_KEY MARCO_DIR "/titlebar_font" +#define WINDOW_TITLE_USES_SYSTEM_KEY MARCO_DIR "/titlebar_uses_system_font" +#define MONOSPACE_FONT_KEY "/desktop/mate/interface/monospace_font_name" +#define DOCUMENT_FONT_KEY "/desktop/mate/interface/document_font_name" + +#ifdef HAVE_XFT2 +#define FONT_RENDER_DIR "/desktop/mate/font_rendering" +#define FONT_ANTIALIASING_KEY FONT_RENDER_DIR "/antialiasing" +#define FONT_HINTING_KEY FONT_RENDER_DIR "/hinting" +#define FONT_RGBA_ORDER_KEY FONT_RENDER_DIR "/rgba_order" +#define FONT_DPI_KEY FONT_RENDER_DIR "/dpi" + +/* X servers sometimes lie about the screen's physical dimensions, so we cannot + * compute an accurate DPI value. When this happens, the user gets fonts that + * are too huge or too tiny. So, we see what the server returns: if it reports + * something outside of the range [DPI_LOW_REASONABLE_VALUE, + * DPI_HIGH_REASONABLE_VALUE], then we assume that it is lying and we use + * DPI_FALLBACK instead. + * + * See get_dpi_from_mateconf_or_server() below, and also + * https://bugzilla.novell.com/show_bug.cgi?id=217790 + */ +#define DPI_FALLBACK 96 +#define DPI_LOW_REASONABLE_VALUE 50 +#define DPI_HIGH_REASONABLE_VALUE 500 +#endif /* HAVE_XFT2 */ + +static gboolean in_change = FALSE; +static gchar* old_font = NULL; + +#define MAX_FONT_POINT_WITHOUT_WARNING 32 +#define MAX_FONT_SIZE_WITHOUT_WARNING MAX_FONT_POINT_WITHOUT_WARNING * 1024 + +#ifdef HAVE_XFT2 + +/* + * Code for displaying previews of font rendering with various Xft options + */ + +static void sample_size_request(GtkWidget* darea, GtkRequisition* requisition) +{ + GdkPixbuf* pixbuf = g_object_get_data(G_OBJECT(darea), "sample-pixbuf"); + + requisition->width = gdk_pixbuf_get_width(pixbuf) + 2; + requisition->height = gdk_pixbuf_get_height(pixbuf) + 2; +} + +static void sample_expose(GtkWidget* darea, GdkEventExpose* expose) +{ + GtkAllocation allocation; + GdkPixbuf* pixbuf = g_object_get_data(G_OBJECT(darea), "sample-pixbuf"); + GdkWindow* window = gtk_widget_get_window(darea); + GtkStyle* style = gtk_widget_get_style(darea); + int width = gdk_pixbuf_get_width(pixbuf); + int height = gdk_pixbuf_get_height(pixbuf); + + gtk_widget_get_allocation (darea, &allocation); + + int x = (allocation.width - width) / 2; + int y = (allocation.height - height) / 2; + + gdk_draw_rectangle(window, style->white_gc, TRUE, 0, 0, allocation.width, allocation.height); + gdk_draw_rectangle(window, style->black_gc, FALSE, 0, 0, allocation.width - 1, allocation.height - 1); + + gdk_draw_pixbuf(window, NULL, pixbuf, 0, 0, x, y, width, height, GDK_RGB_DITHER_NORMAL, 0, 0); +} + +typedef enum { + ANTIALIAS_NONE, + ANTIALIAS_GRAYSCALE, + ANTIALIAS_RGBA +} Antialiasing; + +static MateConfEnumStringPair antialias_enums[] = { + {ANTIALIAS_NONE, "none"}, + {ANTIALIAS_GRAYSCALE, "grayscale"}, + {ANTIALIAS_RGBA, "rgba"}, + {-1, NULL} +}; + +typedef enum { + HINT_NONE, + HINT_SLIGHT, + HINT_MEDIUM, + HINT_FULL +} Hinting; + +static MateConfEnumStringPair hint_enums[] = { + {HINT_NONE, "none"}, + {HINT_SLIGHT, "slight"}, + {HINT_MEDIUM, "medium"}, + {HINT_FULL, "full"}, + {-1, NULL} +}; + +typedef enum { + RGBA_RGB, + RGBA_BGR, + RGBA_VRGB, + RGBA_VBGR +} RgbaOrder; + +static MateConfEnumStringPair rgba_order_enums[] = { + {RGBA_RGB, "rgb" }, + {RGBA_BGR, "bgr" }, + {RGBA_VRGB, "vrgb" }, + {RGBA_VBGR, "vbgr" }, + {-1, NULL } +}; + +static XftFont* open_pattern(FcPattern* pattern, Antialiasing antialiasing, Hinting hinting) +{ + #ifdef FC_HINT_STYLE + static const int hintstyles[] = { + FC_HINT_NONE, FC_HINT_SLIGHT, FC_HINT_MEDIUM, FC_HINT_FULL + }; + #endif /* FC_HINT_STYLE */ + + FcPattern* res_pattern; + FcResult result; + XftFont* font; + + Display* xdisplay = gdk_x11_get_default_xdisplay(); + int screen = gdk_x11_get_default_screen(); + + res_pattern = XftFontMatch(xdisplay, screen, pattern, &result); + + if (res_pattern == NULL) + { + return NULL; + } + + FcPatternDel(res_pattern, FC_HINTING); + FcPatternAddBool(res_pattern, FC_HINTING, hinting != HINT_NONE); + + #ifdef FC_HINT_STYLE + FcPatternDel(res_pattern, FC_HINT_STYLE); + FcPatternAddInteger(res_pattern, FC_HINT_STYLE, hintstyles[hinting]); + #endif /* FC_HINT_STYLE */ + + FcPatternDel(res_pattern, FC_ANTIALIAS); + FcPatternAddBool(res_pattern, FC_ANTIALIAS, antialiasing != ANTIALIAS_NONE); + + FcPatternDel(res_pattern, FC_RGBA); + FcPatternAddInteger(res_pattern, FC_RGBA, antialiasing == ANTIALIAS_RGBA ? FC_RGBA_RGB : FC_RGBA_NONE); + + FcPatternDel(res_pattern, FC_DPI); + FcPatternAddInteger(res_pattern, FC_DPI, 96); + + font = XftFontOpenPattern(xdisplay, res_pattern); + + if (!font) + { + FcPatternDestroy(res_pattern); + } + + return font; +} + +static void setup_font_sample(GtkWidget* darea, Antialiasing antialiasing, Hinting hinting) +{ + const char* string1 = "abcfgop AO "; + const char* string2 = "abcfgop"; + + XftColor black, white; + XRenderColor rendcolor; + + Display* xdisplay = gdk_x11_get_default_xdisplay(); + + GdkColormap* colormap = gdk_rgb_get_colormap(); + Colormap xcolormap = GDK_COLORMAP_XCOLORMAP(colormap); + + GdkVisual* visual = gdk_colormap_get_visual(colormap); + Visual* xvisual = GDK_VISUAL_XVISUAL(visual); + + FcPattern* pattern; + XftFont* font1; + XftFont* font2; + XGlyphInfo extents1 = { 0 }; + XGlyphInfo extents2 = { 0 }; + GdkPixmap* pixmap; + XftDraw* draw; + GdkPixbuf* tmp_pixbuf; + GdkPixbuf* pixbuf; + + int width, height; + int ascent, descent; + + pattern = FcPatternBuild (NULL, + FC_FAMILY, FcTypeString, "Serif", + FC_SLANT, FcTypeInteger, FC_SLANT_ROMAN, + FC_SIZE, FcTypeDouble, 18., + NULL); + font1 = open_pattern (pattern, antialiasing, hinting); + FcPatternDestroy (pattern); + + pattern = FcPatternBuild (NULL, + FC_FAMILY, FcTypeString, "Serif", + FC_SLANT, FcTypeInteger, FC_SLANT_ITALIC, + FC_SIZE, FcTypeDouble, 20., + NULL); + font2 = open_pattern (pattern, antialiasing, hinting); + FcPatternDestroy (pattern); + + ascent = 0; + descent = 0; + + if (font1) + { + XftTextExtentsUtf8 (xdisplay, font1, (unsigned char*) string1, + strlen (string1), &extents1); + ascent = MAX (ascent, font1->ascent); + descent = MAX (descent, font1->descent); + } + + if (font2) + { + XftTextExtentsUtf8 (xdisplay, font2, (unsigned char*) string2, strlen (string2), &extents2); + ascent = MAX (ascent, font2->ascent); + descent = MAX (descent, font2->descent); + } + + width = extents1.xOff + extents2.xOff + 4; + height = ascent + descent + 2; + + pixmap = gdk_pixmap_new (NULL, width, height, visual->depth); + + draw = XftDrawCreate (xdisplay, GDK_DRAWABLE_XID (pixmap), xvisual, xcolormap); + + rendcolor.red = 0; + rendcolor.green = 0; + rendcolor.blue = 0; + rendcolor.alpha = 0xffff; + + XftColorAllocValue(xdisplay, xvisual, xcolormap, &rendcolor, &black); + + rendcolor.red = 0xffff; + rendcolor.green = 0xffff; + rendcolor.blue = 0xffff; + rendcolor.alpha = 0xffff; + + XftColorAllocValue(xdisplay, xvisual, xcolormap, &rendcolor, &white); + XftDrawRect(draw, &white, 0, 0, width, height); + + if (font1) + { + XftDrawStringUtf8(draw, &black, font1, 2, 2 + ascent, (unsigned char*) string1, strlen(string1)); + } + + if (font2) + { + XftDrawStringUtf8(draw, &black, font2, 2 + extents1.xOff, 2 + ascent, (unsigned char*) string2, strlen(string2)); + } + + XftDrawDestroy(draw); + + if (font1) + { + XftFontClose(xdisplay, font1); + } + + if (font2) + { + XftFontClose(xdisplay, font2); + } + + tmp_pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, colormap, 0, 0, 0, 0, width, height); + pixbuf = gdk_pixbuf_scale_simple(tmp_pixbuf, 1 * width, 1 * height, GDK_INTERP_TILES); + + g_object_unref(pixmap); + g_object_unref(tmp_pixbuf); + + g_object_set_data_full(G_OBJECT(darea), "sample-pixbuf", pixbuf, (GDestroyNotify) g_object_unref); + + g_signal_connect(darea, "size_request", G_CALLBACK(sample_size_request), NULL); + g_signal_connect(darea, "expose_event", G_CALLBACK(sample_expose), NULL); +} + +/* + * Code implementing a group of radio buttons with different Xft option combinations. + * If one of the buttons is matched by the MateConf key, we pick it. Otherwise we + * show the group as inconsistent. + */ +static void +font_render_get_mateconf (MateConfClient *client, + Antialiasing *antialiasing, + Hinting *hinting) +{ + gchar *antialias_str = mateconf_client_get_string (client, FONT_ANTIALIASING_KEY, NULL); + gchar *hint_str = mateconf_client_get_string (client, FONT_HINTING_KEY, NULL); + gint val; + + val = ANTIALIAS_GRAYSCALE; + if (antialias_str) { + mateconf_string_to_enum (antialias_enums, antialias_str, &val); + g_free (antialias_str); + } + *antialiasing = val; + + val = HINT_FULL; + if (hint_str) { + mateconf_string_to_enum (hint_enums, hint_str, &val); + g_free (hint_str); + } + *hinting = val; +} + +typedef struct { + Antialiasing antialiasing; + Hinting hinting; + GtkToggleButton *radio; +} FontPair; + +static GSList *font_pairs = NULL; + +static void +font_render_load (MateConfClient *client) +{ + Antialiasing antialiasing; + Hinting hinting; + gboolean inconsistent = TRUE; + GSList *tmp_list; + + font_render_get_mateconf (client, &antialiasing, &hinting); + + in_change = TRUE; + + for (tmp_list = font_pairs; tmp_list; tmp_list = tmp_list->next) { + FontPair *pair = tmp_list->data; + + if (antialiasing == pair->antialiasing && hinting == pair->hinting) { + gtk_toggle_button_set_active (pair->radio, TRUE); + inconsistent = FALSE; + break; + } + } + + for (tmp_list = font_pairs; tmp_list; tmp_list = tmp_list->next) { + FontPair *pair = tmp_list->data; + + gtk_toggle_button_set_inconsistent (pair->radio, inconsistent); + } + + in_change = FALSE; +} + +static void +font_render_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + font_render_load (client); +} + +static void +font_radio_toggled (GtkToggleButton *toggle_button, + FontPair *pair) +{ + if (!in_change) { + MateConfClient *client = mateconf_client_get_default (); + + mateconf_client_set_string (client, FONT_ANTIALIASING_KEY, + mateconf_enum_to_string (antialias_enums, pair->antialiasing), + NULL); + mateconf_client_set_string (client, FONT_HINTING_KEY, + mateconf_enum_to_string (hint_enums, pair->hinting), + NULL); + + /* Restore back to the previous state until we get notification */ + font_render_load (client); + g_object_unref (client); + } +} + +static void +setup_font_pair (GtkWidget *radio, + GtkWidget *darea, + Antialiasing antialiasing, + Hinting hinting) +{ + FontPair *pair = g_new (FontPair, 1); + + pair->antialiasing = antialiasing; + pair->hinting = hinting; + pair->radio = GTK_TOGGLE_BUTTON (radio); + + setup_font_sample (darea, antialiasing, hinting); + font_pairs = g_slist_prepend (font_pairs, pair); + + g_signal_connect (radio, "toggled", + G_CALLBACK (font_radio_toggled), pair); +} +#endif /* HAVE_XFT2 */ + +static void +marco_titlebar_load_sensitivity (AppearanceData *data) +{ + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "window_title_font"), + !mateconf_client_get_bool (data->client, + WINDOW_TITLE_USES_SYSTEM_KEY, + NULL)); +} + +static void +marco_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + marco_titlebar_load_sensitivity (user_data); +} + +/* returns 0 if the font is safe, otherwise returns the size in points. */ +static gint +font_dangerous (const char *font) +{ + PangoFontDescription *pfd; + gboolean retval = 0; + + pfd = pango_font_description_from_string (font); + if (pfd == NULL) + /* an invalid font was passed in. This isn't our problem. */ + return 0; + + if ((pango_font_description_get_set_fields (pfd) & PANGO_FONT_MASK_SIZE) && + (pango_font_description_get_size (pfd) >= MAX_FONT_SIZE_WITHOUT_WARNING)) { + retval = pango_font_description_get_size (pfd)/1024; + } + pango_font_description_free (pfd); + + return retval; +} + +static MateConfValue * +application_font_to_mateconf (MateConfPropertyEditor *peditor, + MateConfValue *value) +{ + MateConfValue *new_value; + const char *new_font; + GtkWidget *font_button; + gint danger_level; + + font_button = GTK_WIDGET (mateconf_property_editor_get_ui_control (peditor)); + g_return_val_if_fail (font_button != NULL, NULL); + + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + new_font = mateconf_value_get_string (value); + if (font_dangerous (old_font)) { + /* If we're already too large, we don't warn again. */ + mateconf_value_set_string (new_value, new_font); + return new_value; + } + + danger_level = font_dangerous (new_font); + if (danger_level) { + GtkWidget *warning_dialog, *apply_button; + const gchar *warning_label; + gchar *warning_label2; + + warning_label = _("Font may be too large"); + + if (danger_level > MAX_FONT_POINT_WITHOUT_WARNING) { + warning_label2 = g_strdup_printf (ngettext ( + "The font selected is %d point large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a size smaller than %d.", + "The font selected is %d points large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a size smaller than %d.", + danger_level), + danger_level, + MAX_FONT_POINT_WITHOUT_WARNING); + } else { + warning_label2 = g_strdup_printf (ngettext ( + "The font selected is %d point large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a smaller sized font.", + "The font selected is %d points large, " + "and may make it difficult to effectively " + "use the computer. It is recommended that " + "you select a smaller sized font.", + danger_level), + danger_level); + } + + warning_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + "%s", + warning_label); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warning_dialog), + "%s", warning_label2); + + gtk_dialog_add_button (GTK_DIALOG (warning_dialog), + _("Use previous font"), GTK_RESPONSE_CLOSE); + + apply_button = gtk_button_new_with_label (_("Use selected font")); + + gtk_button_set_image (GTK_BUTTON (apply_button), gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON)); + gtk_dialog_add_action_widget (GTK_DIALOG (warning_dialog), apply_button, GTK_RESPONSE_APPLY); + gtk_widget_set_can_default (apply_button, TRUE); + gtk_widget_show (apply_button); + + gtk_dialog_set_default_response (GTK_DIALOG (warning_dialog), GTK_RESPONSE_CLOSE); + + g_free (warning_label2); + + if (gtk_dialog_run (GTK_DIALOG (warning_dialog)) == GTK_RESPONSE_APPLY) { + mateconf_value_set_string (new_value, new_font); + } else { + mateconf_value_set_string (new_value, old_font); + gtk_font_button_set_font_name (GTK_FONT_BUTTON (font_button), old_font); + } + + gtk_widget_destroy (warning_dialog); + } else { + mateconf_value_set_string (new_value, new_font); + } + + return new_value; +} + +static void +application_font_changed (GtkWidget *font_button) +{ + const gchar *font; + + font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (font_button)); + g_free (old_font); + old_font = g_strdup (font); +} + +#ifdef HAVE_XFT2 +/* + * EnumGroup - a group of radio buttons tied to a string enumeration + * value. We add this here because the mateconf peditor + * equivalent of this is both painful to use (you have + * to supply functions to convert from enums to indices) + * and conceptually broken (the order of radio buttons + * in a group when using Glade is not predictable. + */ +typedef struct +{ + MateConfClient *client; + GSList *items; + gchar *mateconf_key; + MateConfEnumStringPair *enums; + int default_value; +} EnumGroup; + +typedef struct +{ + EnumGroup *group; + GtkToggleButton *widget; + int value; +} EnumItem; + +static void +enum_group_load (EnumGroup *group) +{ + gchar *str = mateconf_client_get_string (group->client, group->mateconf_key, NULL); + gint val = group->default_value; + GSList *tmp_list; + + if (str) + mateconf_string_to_enum (group->enums, str, &val); + + g_free (str); + + in_change = TRUE; + + for (tmp_list = group->items; tmp_list; tmp_list = tmp_list->next) { + EnumItem *item = tmp_list->data; + + if (val == item->value) + gtk_toggle_button_set_active (item->widget, TRUE); + } + + in_change = FALSE; +} + +static void +enum_group_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + enum_group_load (user_data); +} + +static void +enum_item_toggled (GtkToggleButton *toggle_button, + EnumItem *item) +{ + EnumGroup *group = item->group; + + if (!in_change) { + mateconf_client_set_string (group->client, group->mateconf_key, + mateconf_enum_to_string (group->enums, item->value), + NULL); + } + + /* Restore back to the previous state until we get notification */ + enum_group_load (group); +} + +static EnumGroup * +enum_group_create (const gchar *mateconf_key, + MateConfEnumStringPair *enums, + int default_value, + GtkWidget *first_widget, + ...) +{ + EnumGroup *group; + GtkWidget *widget; + va_list args; + + group = g_new (EnumGroup, 1); + + group->client = mateconf_client_get_default (); + group->mateconf_key = g_strdup (mateconf_key); + group->enums = enums; + group->default_value = default_value; + group->items = NULL; + + va_start (args, first_widget); + + widget = first_widget; + while (widget) { + EnumItem *item; + + item = g_new (EnumItem, 1); + item->group = group; + item->widget = GTK_TOGGLE_BUTTON (widget); + item->value = va_arg (args, int); + + g_signal_connect (item->widget, "toggled", + G_CALLBACK (enum_item_toggled), item); + + group->items = g_slist_prepend (group->items, item); + + widget = va_arg (args, GtkWidget *); + } + + va_end (args); + + enum_group_load (group); + + mateconf_client_notify_add (group->client, mateconf_key, + enum_group_changed, + group, NULL, NULL); + + return group; +} + +static void +enum_group_destroy (EnumGroup *group) +{ + g_object_unref (group->client); + g_free (group->mateconf_key); + + g_slist_foreach (group->items, (GFunc) g_free, NULL); + g_slist_free (group->items); + + g_free (group); +} + +static double +dpi_from_pixels_and_mm (int pixels, int mm) +{ + double dpi; + + if (mm >= 1) + dpi = pixels / (mm / 25.4); + else + dpi = 0; + + return dpi; +} + +static double +get_dpi_from_x_server (void) +{ + GdkScreen *screen; + double dpi; + + screen = gdk_screen_get_default (); + if (screen) { + double width_dpi, height_dpi; + + width_dpi = dpi_from_pixels_and_mm (gdk_screen_get_width (screen), + gdk_screen_get_width_mm (screen)); + height_dpi = dpi_from_pixels_and_mm (gdk_screen_get_height (screen), + gdk_screen_get_height_mm (screen)); + + if (width_dpi < DPI_LOW_REASONABLE_VALUE || width_dpi > DPI_HIGH_REASONABLE_VALUE || + height_dpi < DPI_LOW_REASONABLE_VALUE || height_dpi > DPI_HIGH_REASONABLE_VALUE) + dpi = DPI_FALLBACK; + else + dpi = (width_dpi + height_dpi) / 2.0; + } else { + /* Huh!? No screen? */ + dpi = DPI_FALLBACK; + } + + return dpi; +} + +/* + * The font rendering details dialog + */ +static void +dpi_load (MateConfClient *client, + GtkSpinButton *spinner) +{ + MateConfValue *value; + gdouble dpi; + + value = mateconf_client_get_without_default (client, FONT_DPI_KEY, NULL); + + if (value) { + dpi = mateconf_value_get_float (value); + mateconf_value_free (value); + } else + dpi = get_dpi_from_x_server (); + + if (dpi < DPI_LOW_REASONABLE_VALUE) + dpi = DPI_LOW_REASONABLE_VALUE; + + in_change = TRUE; + gtk_spin_button_set_value (spinner, dpi); + in_change = FALSE; +} + +static void +dpi_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + dpi_load (client, user_data); +} + +static void +dpi_value_changed (GtkSpinButton *spinner, + MateConfClient *client) +{ + /* Like any time when using a spin button with MateConf, there is + * a race condition here. When we change, we send the new + * value to MateConf, then restore to the old value until + * we get a response to emulate the proper model/view behavior. + * + * If the user changes the value faster than responses are + * received from MateConf, this may cause mildly strange effects. + */ + if (!in_change) { + gdouble new_dpi = gtk_spin_button_get_value (spinner); + + mateconf_client_set_float (client, FONT_DPI_KEY, new_dpi, NULL); + + dpi_load (client, spinner); + } +} + +static void +cb_details_response (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) { + capplet_help (GTK_WINDOW (dialog), + "goscustdesk-38"); + } else + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +static void +cb_show_details (GtkWidget *button, + AppearanceData *data) +{ + if (!data->font_details) { + GtkAdjustment *adjustment; + GtkWidget *widget; + EnumGroup *group; + + data->font_details = appearance_capplet_get_widget (data, "render_details"); + + gtk_window_set_transient_for (GTK_WINDOW (data->font_details), + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window"))); + + widget = appearance_capplet_get_widget (data, "dpi_spinner"); + + /* pick a sensible maximum dpi */ + adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget)); + gtk_adjustment_set_lower (adjustment, DPI_LOW_REASONABLE_VALUE); + gtk_adjustment_set_upper (adjustment, DPI_HIGH_REASONABLE_VALUE); + gtk_adjustment_set_step_increment (adjustment, 1); + + dpi_load (data->client, GTK_SPIN_BUTTON (widget)); + g_signal_connect (widget, "value_changed", + G_CALLBACK (dpi_value_changed), data->client); + + mateconf_client_notify_add (data->client, FONT_DPI_KEY, + dpi_changed, widget, NULL, NULL); + + setup_font_sample (appearance_capplet_get_widget (data, "antialias_none_sample"), ANTIALIAS_NONE, HINT_FULL); + setup_font_sample (appearance_capplet_get_widget (data, "antialias_grayscale_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL); + setup_font_sample (appearance_capplet_get_widget (data, "antialias_subpixel_sample"), ANTIALIAS_RGBA, HINT_FULL); + + group = enum_group_create ( + FONT_ANTIALIASING_KEY, antialias_enums, ANTIALIAS_GRAYSCALE, + appearance_capplet_get_widget (data, "antialias_none_radio"), ANTIALIAS_NONE, + appearance_capplet_get_widget (data, "antialias_grayscale_radio"), ANTIALIAS_GRAYSCALE, + appearance_capplet_get_widget (data, "antialias_subpixel_radio"), ANTIALIAS_RGBA, + NULL); + data->font_groups = g_slist_prepend (data->font_groups, group); + + setup_font_sample (appearance_capplet_get_widget (data, "hint_none_sample"), ANTIALIAS_GRAYSCALE, HINT_NONE); + setup_font_sample (appearance_capplet_get_widget (data, "hint_slight_sample"), ANTIALIAS_GRAYSCALE, HINT_SLIGHT); + setup_font_sample (appearance_capplet_get_widget (data, "hint_medium_sample"), ANTIALIAS_GRAYSCALE, HINT_MEDIUM); + setup_font_sample (appearance_capplet_get_widget (data, "hint_full_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL); + + group = enum_group_create (FONT_HINTING_KEY, hint_enums, HINT_FULL, + appearance_capplet_get_widget (data, "hint_none_radio"), HINT_NONE, + appearance_capplet_get_widget (data, "hint_slight_radio"), HINT_SLIGHT, + appearance_capplet_get_widget (data, "hint_medium_radio"), HINT_MEDIUM, + appearance_capplet_get_widget (data, "hint_full_radio"), HINT_FULL, + NULL); + data->font_groups = g_slist_prepend (data->font_groups, group); + + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_rgb_image")), + MATECC_PIXMAP_DIR "/subpixel-rgb.png"); + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_bgr_image")), + MATECC_PIXMAP_DIR "/subpixel-bgr.png"); + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_vrgb_image")), + MATECC_PIXMAP_DIR "/subpixel-vrgb.png"); + gtk_image_set_from_file (GTK_IMAGE (appearance_capplet_get_widget (data, "subpixel_vbgr_image")), + MATECC_PIXMAP_DIR "/subpixel-vbgr.png"); + + group = enum_group_create (FONT_RGBA_ORDER_KEY, rgba_order_enums, RGBA_RGB, + appearance_capplet_get_widget (data, "subpixel_rgb_radio"), RGBA_RGB, + appearance_capplet_get_widget (data, "subpixel_bgr_radio"), RGBA_BGR, + appearance_capplet_get_widget (data, "subpixel_vrgb_radio"), RGBA_VRGB, + appearance_capplet_get_widget (data, "subpixel_vbgr_radio"), RGBA_VBGR, + NULL); + data->font_groups = g_slist_prepend (data->font_groups, group); + + g_signal_connect (G_OBJECT (data->font_details), + "response", + G_CALLBACK (cb_details_response), NULL); + g_signal_connect (G_OBJECT (data->font_details), + "delete_event", + G_CALLBACK (gtk_true), NULL); + } + + gtk_window_present (GTK_WINDOW (data->font_details)); +} +#endif /* HAVE_XFT2 */ + +void font_init(AppearanceData* data) +{ + GObject* peditor; + GtkWidget* widget; + + data->font_details = NULL; + data->font_groups = NULL; + + mateconf_client_add_dir(data->client, "/desktop/mate/interface", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir(data->client, "/apps/caja/preferences", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir(data->client, MARCO_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + #ifdef HAVE_XFT2 + mateconf_client_add_dir(data->client, FONT_RENDER_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + #endif /* HAVE_XFT2 */ + + widget = appearance_capplet_get_widget(data, "application_font"); + peditor = mateconf_peditor_new_font(NULL, GTK_FONT_KEY, widget, "conv-from-widget-cb", application_font_to_mateconf, NULL); + g_signal_connect_swapped(peditor, "value-changed", G_CALLBACK (application_font_changed), widget); + application_font_changed(widget); + + peditor = mateconf_peditor_new_font(NULL, DOCUMENT_FONT_KEY, appearance_capplet_get_widget (data, "document_font"), NULL); + + peditor = mateconf_peditor_new_font(NULL, DESKTOP_FONT_KEY, appearance_capplet_get_widget (data, "desktop_font"), NULL); + + peditor = mateconf_peditor_new_font(NULL, WINDOW_TITLE_FONT_KEY, appearance_capplet_get_widget (data, "window_title_font"), NULL); + + peditor = mateconf_peditor_new_font(NULL, MONOSPACE_FONT_KEY, appearance_capplet_get_widget (data, "monospace_font"), NULL); + + mateconf_client_notify_add (data->client, WINDOW_TITLE_USES_SYSTEM_KEY, marco_changed, data, NULL, NULL); + + marco_titlebar_load_sensitivity(data); + + #ifdef HAVE_XFT2 + setup_font_pair(appearance_capplet_get_widget(data, "monochrome_radio"), appearance_capplet_get_widget (data, "monochrome_sample"), ANTIALIAS_NONE, HINT_FULL); + setup_font_pair(appearance_capplet_get_widget(data, "best_shapes_radio"), appearance_capplet_get_widget (data, "best_shapes_sample"), ANTIALIAS_GRAYSCALE, HINT_MEDIUM); + setup_font_pair(appearance_capplet_get_widget(data, "best_contrast_radio"), appearance_capplet_get_widget (data, "best_contrast_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL); + setup_font_pair(appearance_capplet_get_widget(data, "subpixel_radio"), appearance_capplet_get_widget (data, "subpixel_sample"), ANTIALIAS_RGBA, HINT_FULL); + + font_render_load (data->client); + + mateconf_client_notify_add (data->client, FONT_RENDER_DIR, font_render_changed, data->client, NULL, NULL); + + g_signal_connect (appearance_capplet_get_widget (data, "details_button"), "clicked", G_CALLBACK (cb_show_details), data); + #else /* !HAVE_XFT2 */ + gtk_widget_hide (appearance_capplet_get_widget (data, "font_render_frame")); + #endif /* HAVE_XFT2 */ +} + +void font_shutdown(AppearanceData* data) +{ + g_slist_foreach(data->font_groups, (GFunc) enum_group_destroy, NULL); + g_slist_free(data->font_groups); + g_slist_foreach(font_pairs, (GFunc) g_free, NULL); + g_slist_free(font_pairs); + g_free(old_font); +} diff --git a/capplets/appearance/appearance-font.h b/capplets/appearance/appearance-font.h new file mode 100644 index 00000000..995aa328 --- /dev/null +++ b/capplets/appearance/appearance-font.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The GNOME Foundation + * Written by Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void font_init(AppearanceData* data); +void font_shutdown(AppearanceData* data); diff --git a/capplets/appearance/appearance-main.c b/capplets/appearance/appearance-main.c new file mode 100644 index 00000000..a7995105 --- /dev/null +++ b/capplets/appearance/appearance-main.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include "appearance.h" +#include "appearance-desktop.h" +#include "appearance-font.h" +#include "appearance-themes.h" +#include "appearance-style.h" +#include "theme-installer.h" +#include "theme-thumbnail.h" +#include "activate-settings-daemon.h" +#include "capplet-util.h" + +static AppearanceData * +init_appearance_data (int *argc, char ***argv, GOptionContext *context) +{ + AppearanceData *data = NULL; + gchar *uifile; + GtkBuilder *ui; + GError *err = NULL; + + g_thread_init (NULL); + gdk_threads_init (); + gdk_threads_enter (); + theme_thumbnail_factory_init (*argc, *argv); + capplet_init (context, argc, argv); + activate_settings_daemon (); + + /* set up the data */ + uifile = g_build_filename (MATECC_GTKBUILDER_DIR, "appearance.ui", + NULL); + ui = gtk_builder_new (); + gtk_builder_add_from_file (ui, uifile, &err); + g_free (uifile); + + if (err) + { + g_warning (_("Could not load user interface file: %s"), err->message); + g_error_free (err); + g_object_unref (ui); + } + else + { + data = g_new (AppearanceData, 1); + data->client = mateconf_client_get_default (); + data->ui = ui; + data->thumb_factory = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + } + + return data; +} + +static void +main_window_response (GtkWidget *widget, + gint response_id, + AppearanceData *data) +{ + if (response_id == GTK_RESPONSE_CLOSE || + response_id == GTK_RESPONSE_DELETE_EVENT) + { + gtk_main_quit (); + + themes_shutdown (data); + style_shutdown (data); + desktop_shutdown (data); + font_shutdown (data); + + g_object_unref (data->thumb_factory); + g_object_unref (data->client); + g_object_unref (data->ui); + } + else if (response_id == GTK_RESPONSE_HELP) + { + GtkNotebook *nb; + gint pindex; + + nb = GTK_NOTEBOOK (appearance_capplet_get_widget (data, "main_notebook")); + pindex = gtk_notebook_get_current_page (nb); + + switch (pindex) + { + case 0: /* theme */ + capplet_help (GTK_WINDOW (widget), "goscustdesk-12"); + break; + case 1: /* background */ + capplet_help (GTK_WINDOW (widget), "goscustdesk-7"); + break; + case 2: /* fonts */ + capplet_help (GTK_WINDOW (widget), "goscustdesk-38"); + break; + case 3: /* interface */ + capplet_help (GTK_WINDOW (widget), "goscustuserinter-2"); + break; + default: + capplet_help (GTK_WINDOW (widget), "prefs-look-and-feel"); + break; + } + } +} + +int +main (int argc, char **argv) +{ + AppearanceData *data; + GtkWidget *w; + + gchar *install_filename = NULL; + gchar *start_page = NULL; + gchar **wallpaper_files = NULL; + GOptionContext *option_context; + GOptionEntry option_entries[] = { + { "install-theme", + 'i', + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_FILENAME, + &install_filename, + N_("Specify the filename of a theme to install"), + N_("filename") }, + { "show-page", + 'p', + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_STRING, + &start_page, + /* TRANSLATORS: don't translate the terms in brackets */ + N_("Specify the name of the page to show (theme|background|fonts|interface)"), + N_("page") }, + { G_OPTION_REMAINING, + 0, + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_FILENAME_ARRAY, + &wallpaper_files, + NULL, + N_("[WALLPAPER...]") }, + { NULL } + }; + + option_context = g_option_context_new (NULL); + g_option_context_add_main_entries (option_context, option_entries, GETTEXT_PACKAGE); + + /* init */ + data = init_appearance_data (&argc, &argv, option_context); + if (!data) + return 1; + + /* init tabs */ + themes_init (data); + style_init (data); + desktop_init (data, (const gchar **) wallpaper_files); + g_strfreev (wallpaper_files); + font_init (data); + + /* prepare the main window */ + w = appearance_capplet_get_widget (data, "appearance_window"); + capplet_set_icon (w, "preferences-desktop-theme"); + gtk_widget_show_all (w); + + g_signal_connect_after (w, "response", + (GCallback) main_window_response, data); + + /* default to background page if files were given on the command line */ + if (wallpaper_files && !install_filename && !start_page) + start_page = g_strdup ("background"); + + if (start_page != NULL) { + gchar *page_name; + + page_name = g_strconcat (start_page, "_vbox", NULL); + g_free (start_page); + + w = appearance_capplet_get_widget (data, page_name); + if (w != NULL) { + GtkNotebook *nb; + gint pindex; + + nb = GTK_NOTEBOOK (appearance_capplet_get_widget (data, "main_notebook")); + pindex = gtk_notebook_page_num (nb, w); + if (pindex != -1) + gtk_notebook_set_current_page (nb, pindex); + } + g_free (page_name); + } + + if (install_filename != NULL) { + GFile *inst = g_file_new_for_commandline_arg (install_filename); + g_free (install_filename); + mate_theme_install (inst, GTK_WINDOW (w)); + g_object_unref (inst); + } + + g_option_context_free (option_context); + + /* start the mainloop */ + gtk_main (); + gdk_threads_leave (); + + /* free stuff */ + g_free (data); + + return 0; +} diff --git a/capplets/appearance/appearance-style.c b/capplets/appearance/appearance-style.c new file mode 100644 index 00000000..21612248 --- /dev/null +++ b/capplets/appearance/appearance-style.c @@ -0,0 +1,1073 @@ +/* + * Copyright (C) 2007, 2010 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "appearance.h" + +#include +#include +#include + +#include "theme-util.h" +#include "gtkrc-utils.h" +#include "mateconf-property-editor.h" +#include "theme-thumbnail.h" +#include "capplet-util.h" + +typedef void (* ThumbnailGenFunc) (void *type, + ThemeThumbnailFunc theme, + AppearanceData *data, + GDestroyNotify *destroy); + +typedef struct { + AppearanceData *data; + GdkPixbuf *thumbnail; +} PEditorConvData; + +static void update_message_area (AppearanceData *data); +static void create_thumbnail (const gchar *name, GdkPixbuf *default_thumb, AppearanceData *data); + +static const gchar *symbolic_names[NUM_SYMBOLIC_COLORS] = { + "fg_color", "bg_color", + "text_color", "base_color", + "selected_fg_color", "selected_bg_color", + "tooltip_fg_color", "tooltip_bg_color" +}; + +static gchar * +find_string_in_model (GtkTreeModel *model, const gchar *value, gint column) +{ + GtkTreeIter iter; + gboolean valid; + gchar *path = NULL, *test; + + if (!value) + return NULL; + + for (valid = gtk_tree_model_get_iter_first (model, &iter); valid; + valid = gtk_tree_model_iter_next (model, &iter)) + { + gtk_tree_model_get (model, &iter, column, &test, -1); + + if (test) + { + gint cmp = strcmp (test, value); + g_free (test); + + if (!cmp) + { + path = gtk_tree_model_get_string_from_iter (model, &iter); + break; + } + } + } + + return path; +} + +static MateConfValue * +conv_to_widget_cb (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + GtkTreeModel *store; + GtkTreeView *list; + const gchar *curr_value; + MateConfValue *new_value; + gchar *path; + + /* find value in model */ + curr_value = mateconf_value_get_string (value); + list = GTK_TREE_VIEW (mateconf_property_editor_get_ui_control (peditor)); + store = gtk_tree_view_get_model (list); + + path = find_string_in_model (store, curr_value, COL_NAME); + + /* Add a temporary item if we can't find a match + * TODO: delete this item if it is no longer selected? + */ + if (!path) + { + GtkListStore *list_store; + GtkTreeIter iter, sort_iter; + PEditorConvData *conv; + + list_store = GTK_LIST_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (store))); + + g_object_get (peditor, "data", &conv, NULL); + gtk_list_store_insert_with_values (list_store, &iter, 0, + COL_LABEL, curr_value, + COL_NAME, curr_value, + COL_THUMBNAIL, conv->thumbnail, + -1); + /* convert the tree store iter for use with the sort model */ + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (store), + &sort_iter, &iter); + path = gtk_tree_model_get_string_from_iter (store, &sort_iter); + + create_thumbnail (curr_value, conv->thumbnail, conv->data); + } + + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (new_value, path); + g_free (path); + + return new_value; +} + +static MateConfValue * +conv_from_widget_cb (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *new_value = NULL; + GtkTreeIter iter; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeView *list; + + list = GTK_TREE_VIEW (mateconf_property_editor_get_ui_control (peditor)); + selection = gtk_tree_view_get_selection (list); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *list_value; + + gtk_tree_model_get (model, &iter, COL_NAME, &list_value, -1); + + if (list_value) { + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (new_value, list_value); + g_free (list_value); + } + } + + return new_value; +} + +static gint +cursor_theme_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *a_label = NULL; + gchar *b_label = NULL; + const gchar *default_label; + gint result; + + gtk_tree_model_get (model, a, COL_LABEL, &a_label, -1); + gtk_tree_model_get (model, b, COL_LABEL, &b_label, -1); + + default_label = _("Default Pointer"); + + if (!strcmp (a_label, default_label)) + result = -1; + else if (!strcmp (b_label, default_label)) + result = 1; + else + result = strcmp (a_label, b_label); + + g_free (a_label); + g_free (b_label); + + return result; +} + +static void +style_message_area_response_cb (GtkWidget *w, + gint response_id, + AppearanceData *data) +{ + GtkSettings *settings = gtk_settings_get_default (); + gchar *theme; + gchar *engine_path; + + g_object_get (settings, "gtk-theme-name", &theme, NULL); + engine_path = gtk_theme_info_missing_engine (theme, FALSE); + g_free (theme); + + if (engine_path != NULL) { + theme_install_file (GTK_WINDOW (gtk_widget_get_toplevel (data->style_message_area)), + engine_path); + g_free (engine_path); + } + update_message_area (data); +} + +static void update_message_area(AppearanceData* data) +{ + GtkSettings* settings = gtk_settings_get_default(); + gchar* theme = NULL; + gchar* engine; + + g_object_get(settings, "gtk-theme-name", &theme, NULL); + engine = gtk_theme_info_missing_engine(theme, TRUE); + g_free(theme); + + if (data->style_message_area == NULL) + { + GtkWidget* hbox; + GtkWidget* parent; + GtkWidget* icon; + GtkWidget* content; + + if (engine == NULL) + { + return; + } + + data->style_message_area = gtk_info_bar_new (); + + g_signal_connect (data->style_message_area, "response", (GCallback) style_message_area_response_cb, data); + + data->style_install_button = gtk_info_bar_add_button(GTK_INFO_BAR (data->style_message_area), _("Install"), GTK_RESPONSE_APPLY); + + data->style_message_label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (data->style_message_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (data->style_message_label), 0.0, 0.5); + + hbox = gtk_hbox_new (FALSE, 9); + icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->style_message_label, TRUE, TRUE, 0); + content = gtk_info_bar_get_content_area (GTK_INFO_BAR (data->style_message_area)); + gtk_container_add (GTK_CONTAINER (content), hbox); + gtk_widget_show_all (data->style_message_area); + gtk_widget_set_no_show_all (data->style_message_area, TRUE); + + parent = appearance_capplet_get_widget (data, "gtk_themes_vbox"); + gtk_box_pack_start (GTK_BOX (parent), data->style_message_area, FALSE, FALSE, 0); + } + + if (engine != NULL) + { + gchar* message = g_strdup_printf(_("This theme will not look as intended because the required GTK+ theme engine '%s' is not installed."), engine); + gtk_label_set_text(GTK_LABEL(data->style_message_label), message); + g_free(message); + g_free(engine); + + if (packagekit_available()) + { + gtk_widget_show(data->style_install_button); + } + else + { + gtk_widget_hide(data->style_install_button); + } + + gtk_widget_show(data->style_message_area); + gtk_widget_queue_draw(data->style_message_area); + } + else + { + gtk_widget_hide(data->style_message_area); + } +} + +static void +update_color_buttons_from_string (const gchar *color_scheme, AppearanceData *data) +{ + GdkColor colors[NUM_SYMBOLIC_COLORS]; + GtkWidget *widget; + gint i; + + if (!mate_theme_color_scheme_parse (color_scheme, colors)) + return; + + /* now set all the buttons to the correct settings */ + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + widget = appearance_capplet_get_widget (data, symbolic_names[i]); + gtk_color_button_set_color (GTK_COLOR_BUTTON (widget), &colors[i]); + } +} + +static void +update_color_buttons_from_settings (GtkSettings *settings, + AppearanceData *data) +{ + gchar *scheme, *setting; + + scheme = mateconf_client_get_string (data->client, COLOR_SCHEME_KEY, NULL); + g_object_get (settings, "gtk-color-scheme", &setting, NULL); + + if (scheme == NULL || strcmp (scheme, "") == 0) + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), FALSE); + + g_free (scheme); + update_color_buttons_from_string (setting, data); + g_free (setting); +} + +static void +color_scheme_changed (GObject *settings, + GParamSpec *pspec, + AppearanceData *data) +{ + update_color_buttons_from_settings (GTK_SETTINGS (settings), data); +} + +static void +check_color_schemes_enabled (GtkSettings *settings, + AppearanceData *data) +{ + gchar *theme = NULL; + gchar *filename; + GSList *symbolic_colors = NULL; + gboolean enable_colors = FALSE; + gint i; + + g_object_get (settings, "gtk-theme-name", &theme, NULL); + filename = gtkrc_find_named (theme); + g_free (theme); + + gtkrc_get_details (filename, NULL, &symbolic_colors); + g_free (filename); + + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + gboolean found; + + found = (g_slist_find_custom (symbolic_colors, symbolic_names[i], (GCompareFunc) strcmp) != NULL); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, symbolic_names[i]), found); + + enable_colors |= found; + } + + g_slist_foreach (symbolic_colors, (GFunc) g_free, NULL); + g_slist_free (symbolic_colors); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_table"), enable_colors); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), enable_colors); + + if (enable_colors) + gtk_widget_hide (appearance_capplet_get_widget (data, "color_scheme_message_hbox")); + else + gtk_widget_show (appearance_capplet_get_widget (data, "color_scheme_message_hbox")); +} + +static void +color_button_clicked_cb (GtkWidget *colorbutton, AppearanceData *data) +{ + GtkWidget *widget; + GdkColor color; + GString *scheme = g_string_new (NULL); + gchar *colstr; + gchar *old_scheme = NULL; + gint i; + + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + widget = appearance_capplet_get_widget (data, symbolic_names[i]); + gtk_color_button_get_color (GTK_COLOR_BUTTON (widget), &color); + + colstr = gdk_color_to_string (&color); + g_string_append_printf (scheme, "%s:%s\n", symbolic_names[i], colstr); + g_free (colstr); + } + /* remove the last newline */ + g_string_truncate (scheme, scheme->len - 1); + + /* verify that the scheme really has changed */ + g_object_get (gtk_settings_get_default (), "gtk-color-scheme", &old_scheme, NULL); + + if (!mate_theme_color_scheme_equal (old_scheme, scheme->str)) { + mateconf_client_set_string (data->client, COLOR_SCHEME_KEY, scheme->str, NULL); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), TRUE); + } + g_free (old_scheme); + g_string_free (scheme, TRUE); +} + +static void +color_scheme_defaults_button_clicked_cb (GtkWidget *button, AppearanceData *data) +{ + mateconf_client_unset (data->client, COLOR_SCHEME_KEY, NULL); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), FALSE); +} + +static void +style_response_cb (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) { + capplet_help (GTK_WINDOW (dialog), "goscustdesk-61"); + } else { + gtk_widget_hide (GTK_WIDGET (dialog)); + } +} + +static void +gtk_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeInfo *theme = NULL; + const gchar *name; + GtkSettings *settings = gtk_settings_get_default (); + + if (value && (name = mateconf_value_get_string (value))) { + gchar *current; + + theme = mate_theme_info_find (name); + + /* Manually update GtkSettings to new gtk+ theme. + * This will eventually happen anyway, but we need the + * info for the color scheme updates already. */ + g_object_get (settings, "gtk-theme-name", ¤t, NULL); + + if (strcmp (current, name) != 0) { + g_object_set (settings, "gtk-theme-name", name, NULL); + update_message_area (data); + } + + g_free (current); + + check_color_schemes_enabled (settings, data); + update_color_buttons_from_settings (settings, data); + } + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "gtk_themes_delete"), + theme_is_writable (theme)); +} + +static void +window_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeInfo *theme = NULL; + const gchar *name; + + if (value && (name = mateconf_value_get_string (value))) + theme = mate_theme_info_find (name); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "window_themes_delete"), + theme_is_writable (theme)); +} + +static void +icon_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeIconInfo *theme = NULL; + const gchar *name; + + if (value && (name = mateconf_value_get_string (value))) + theme = mate_theme_icon_info_find (name); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "icon_themes_delete"), + theme_is_writable (theme)); +} + +#ifdef HAVE_XCURSOR +static void +cursor_size_changed_cb (int size, AppearanceData *data) +{ + mateconf_client_set_int (data->client, CURSOR_SIZE_KEY, size, NULL); +} + +static void +cursor_size_scale_value_changed_cb (GtkRange *range, AppearanceData *data) +{ + MateThemeCursorInfo *theme; + gchar *name; + + name = mateconf_client_get_string (data->client, CURSOR_THEME_KEY, NULL); + if (name == NULL) + return; + + theme = mate_theme_cursor_info_find (name); + g_free (name); + + if (theme) { + gint size; + + size = g_array_index (theme->sizes, gint, (int) gtk_range_get_value (range)); + cursor_size_changed_cb (size, data); + } +} +#endif + +static void +update_cursor_size_scale (MateThemeCursorInfo *theme, + AppearanceData *data) +{ +#ifdef HAVE_XCURSOR + GtkWidget *cursor_size_scale; + GtkWidget *cursor_size_label; + GtkWidget *cursor_size_small_label; + GtkWidget *cursor_size_large_label; + gboolean sensitive; + gint size, mateconf_size; + + cursor_size_scale = appearance_capplet_get_widget (data, "cursor_size_scale"); + cursor_size_label = appearance_capplet_get_widget (data, "cursor_size_label"); + cursor_size_small_label = appearance_capplet_get_widget (data, "cursor_size_small_label"); + cursor_size_large_label = appearance_capplet_get_widget (data, "cursor_size_large_label"); + + sensitive = theme && theme->sizes->len > 1; + gtk_widget_set_sensitive (cursor_size_scale, sensitive); + gtk_widget_set_sensitive (cursor_size_label, sensitive); + gtk_widget_set_sensitive (cursor_size_small_label, sensitive); + gtk_widget_set_sensitive (cursor_size_large_label, sensitive); + + mateconf_size = mateconf_client_get_int (data->client, CURSOR_SIZE_KEY, NULL); + + if (sensitive) { + GtkAdjustment *adjustment; + gint i, index; + GtkRange *range = GTK_RANGE (cursor_size_scale); + + adjustment = gtk_range_get_adjustment (range); + g_object_set (adjustment, "upper", (gdouble) theme->sizes->len - 1, NULL); + + + /* fallback if the mateconf value is bigger than all available sizes; + use the largest we have */ + index = theme->sizes->len - 1; + + /* set the slider to the cursor size which matches the mateconf setting best */ + for (i = 0; i < theme->sizes->len; i++) { + size = g_array_index (theme->sizes, gint, i); + + if (size == mateconf_size) { + index = i; + break; + } else if (size > mateconf_size) { + if (i == 0) { + index = 0; + } else { + gint diff, diff_to_last; + + diff = size - mateconf_size; + diff_to_last = mateconf_size - g_array_index (theme->sizes, gint, i - 1); + + index = (diff < diff_to_last) ? i : i - 1; + } + break; + } + } + + gtk_range_set_value (range, (gdouble) index); + + size = g_array_index (theme->sizes, gint, index); + } else { + if (theme && theme->sizes->len > 0) + size = g_array_index (theme->sizes, gint, 0); + else + size = 18; + } + + if (size != mateconf_size) + cursor_size_changed_cb (size, data); +#endif +} + +static void +cursor_theme_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + AppearanceData *data) +{ + MateThemeCursorInfo *theme = NULL; + const gchar *name; + + if (value && (name = mateconf_value_get_string (value))) + theme = mate_theme_cursor_info_find (name); + + update_cursor_size_scale (theme, data); + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "cursor_themes_delete"), + theme_is_writable (theme)); + +} + +static void +generic_theme_delete (const gchar *tv_name, ThemeType type, AppearanceData *data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *name; + + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + if (name != NULL && theme_delete (name, type)) { + /* remove theme from the model, too */ + GtkTreeIter child; + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_model_sort_convert_iter_to_child_iter ( + GTK_TREE_MODEL_SORT (model), &child, &iter); + gtk_list_store_remove (GTK_LIST_STORE ( + gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model))), &child); + + if (gtk_tree_model_get_iter (model, &iter, path) || + theme_model_iter_last (model, &iter)) { + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_selection_select_path (selection, path); + gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0); + } + gtk_tree_path_free (path); + } + g_free (name); + } +} + +static void +gtk_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("gtk_themes_list", THEME_TYPE_GTK, data); +} + +static void +window_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("window_themes_list", THEME_TYPE_WINDOW, data); +} + +static void +icon_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("icon_themes_list", THEME_TYPE_ICON, data); +} + +static void +cursor_theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + generic_theme_delete ("cursor_themes_list", THEME_TYPE_CURSOR, data); +} + +static void +add_to_treeview (const gchar *tv_name, + const gchar *theme_name, + const gchar *theme_label, + GdkPixbuf *theme_thumbnail, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + gtk_list_store_insert_with_values (model, NULL, 0, + COL_LABEL, theme_label, + COL_NAME, theme_name, + COL_THUMBNAIL, theme_thumbnail, + -1); +} + +static void +remove_from_treeview (const gchar *tv_name, + const gchar *theme_name, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + GtkTreeIter iter; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + if (theme_find_in_model (GTK_TREE_MODEL (model), theme_name, &iter)) + gtk_list_store_remove (model, &iter); +} + +static void +update_in_treeview (const gchar *tv_name, + const gchar *theme_name, + const gchar *theme_label, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + GtkTreeIter iter; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + if (theme_find_in_model (GTK_TREE_MODEL (model), theme_name, &iter)) { + gtk_list_store_set (model, &iter, + COL_LABEL, theme_label, + COL_NAME, theme_name, + -1); + } +} + +static void +update_thumbnail_in_treeview (const gchar *tv_name, + const gchar *theme_name, + GdkPixbuf *theme_thumbnail, + AppearanceData *data) +{ + GtkTreeView *treeview; + GtkListStore *model; + GtkTreeIter iter; + + if (theme_thumbnail == NULL) + return; + + treeview = GTK_TREE_VIEW (appearance_capplet_get_widget (data, tv_name)); + model = GTK_LIST_STORE ( + gtk_tree_model_sort_get_model ( + GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (treeview)))); + + if (theme_find_in_model (GTK_TREE_MODEL (model), theme_name, &iter)) { + gtk_list_store_set (model, &iter, + COL_THUMBNAIL, theme_thumbnail, + -1); + } +} + +static void +gtk_theme_thumbnail_cb (GdkPixbuf *pixbuf, + gchar *theme_name, + AppearanceData *data) +{ + update_thumbnail_in_treeview ("gtk_themes_list", theme_name, pixbuf, data); +} + +static void +marco_theme_thumbnail_cb (GdkPixbuf *pixbuf, + gchar *theme_name, + AppearanceData *data) +{ + update_thumbnail_in_treeview ("window_themes_list", theme_name, pixbuf, data); +} + +static void +icon_theme_thumbnail_cb (GdkPixbuf *pixbuf, + gchar *theme_name, + AppearanceData *data) +{ + update_thumbnail_in_treeview ("icon_themes_list", theme_name, pixbuf, data); +} + +static void +create_thumbnail (const gchar *name, GdkPixbuf *default_thumb, AppearanceData *data) +{ + if (default_thumb == data->icon_theme_icon) { + MateThemeIconInfo *info; + info = mate_theme_icon_info_find (name); + if (info != NULL) { + generate_icon_theme_thumbnail_async (info, + (ThemeThumbnailFunc) icon_theme_thumbnail_cb, data, NULL); + } + } else if (default_thumb == data->gtk_theme_icon) { + MateThemeInfo *info; + info = mate_theme_info_find (name); + if (info != NULL && info->has_gtk) { + generate_gtk_theme_thumbnail_async (info, + (ThemeThumbnailFunc) gtk_theme_thumbnail_cb, data, NULL); + } + } else if (default_thumb == data->window_theme_icon) { + MateThemeInfo *info; + info = mate_theme_info_find (name); + if (info != NULL && info->has_marco) { + generate_marco_theme_thumbnail_async (info, + (ThemeThumbnailFunc) marco_theme_thumbnail_cb, data, NULL); + } + } +} + +static void +changed_on_disk_cb (MateThemeCommonInfo *theme, + MateThemeChangeType change_type, + MateThemeElement element_type, + AppearanceData *data) +{ + if (theme->type == MATE_THEME_TYPE_REGULAR) { + MateThemeInfo *info = (MateThemeInfo *) theme; + + if (change_type == MATE_THEME_CHANGE_DELETED) { + if (element_type & MATE_THEME_GTK_2) + remove_from_treeview ("gtk_themes_list", info->name, data); + if (element_type & MATE_THEME_MARCO) + remove_from_treeview ("window_themes_list", info->name, data); + + } else { + if (element_type & MATE_THEME_GTK_2) { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("gtk_themes_list", info->name, info->name, data->gtk_theme_icon, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("gtk_themes_list", info->name, info->name, data); + + generate_gtk_theme_thumbnail_async (info, + (ThemeThumbnailFunc) gtk_theme_thumbnail_cb, data, NULL); + } + + if (element_type & MATE_THEME_MARCO) { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("window_themes_list", info->name, info->name, data->window_theme_icon, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("window_themes_list", info->name, info->name, data); + + generate_marco_theme_thumbnail_async (info, + (ThemeThumbnailFunc) marco_theme_thumbnail_cb, data, NULL); + } + } + + } else if (theme->type == MATE_THEME_TYPE_ICON) { + MateThemeIconInfo *info = (MateThemeIconInfo *) theme; + + if (change_type == MATE_THEME_CHANGE_DELETED) { + remove_from_treeview ("icon_themes_list", info->name, data); + } else { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("icon_themes_list", info->name, info->readable_name, data->icon_theme_icon, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("icon_themes_list", info->name, info->readable_name, data); + + generate_icon_theme_thumbnail_async (info, + (ThemeThumbnailFunc) icon_theme_thumbnail_cb, data, NULL); + } + + } else if (theme->type == MATE_THEME_TYPE_CURSOR) { + MateThemeCursorInfo *info = (MateThemeCursorInfo *) theme; + + if (change_type == MATE_THEME_CHANGE_DELETED) { + remove_from_treeview ("cursor_themes_list", info->name, data); + } else { + if (change_type == MATE_THEME_CHANGE_CREATED) + add_to_treeview ("cursor_themes_list", info->name, info->readable_name, info->thumbnail, data); + else if (change_type == MATE_THEME_CHANGE_CHANGED) + update_in_treeview ("cursor_themes_list", info->name, info->readable_name, data); + } + } +} + +static void +prepare_list (AppearanceData *data, GtkWidget *list, ThemeType type, GCallback callback) +{ + GtkListStore *store; + GList *l, *themes = NULL; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeModel *sort_model; + GdkPixbuf *thumbnail; + const gchar *key; + GObject *peditor; + MateConfValue *value; + ThumbnailGenFunc generator; + ThemeThumbnailFunc thumb_cb; + PEditorConvData *conv_data; + + switch (type) + { + case THEME_TYPE_GTK: + themes = mate_theme_info_find_by_type (MATE_THEME_GTK_2); + thumbnail = data->gtk_theme_icon; + key = GTK_THEME_KEY; + generator = (ThumbnailGenFunc) generate_gtk_theme_thumbnail_async; + thumb_cb = (ThemeThumbnailFunc) gtk_theme_thumbnail_cb; + break; + + case THEME_TYPE_WINDOW: + themes = mate_theme_info_find_by_type (MATE_THEME_MARCO); + thumbnail = data->window_theme_icon; + key = MARCO_THEME_KEY; + generator = (ThumbnailGenFunc) generate_marco_theme_thumbnail_async; + thumb_cb = (ThemeThumbnailFunc) marco_theme_thumbnail_cb; + break; + + case THEME_TYPE_ICON: + themes = mate_theme_icon_info_find_all (); + thumbnail = data->icon_theme_icon; + key = ICON_THEME_KEY; + generator = (ThumbnailGenFunc) generate_icon_theme_thumbnail_async; + thumb_cb = (ThemeThumbnailFunc) icon_theme_thumbnail_cb; + break; + + case THEME_TYPE_CURSOR: + themes = mate_theme_cursor_info_find_all (); + thumbnail = NULL; + key = CURSOR_THEME_KEY; + generator = NULL; + thumb_cb = NULL; + break; + + default: + /* we don't deal with any other type of themes here */ + return; + } + + store = gtk_list_store_new (NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + for (l = themes; l; l = g_list_next (l)) + { + MateThemeCommonInfo *theme = (MateThemeCommonInfo *) l->data; + GtkTreeIter i; + + if (type == THEME_TYPE_CURSOR) { + thumbnail = ((MateThemeCursorInfo *) theme)->thumbnail; + } else { + generator (theme, thumb_cb, data, NULL); + } + + gtk_list_store_insert_with_values (store, &i, 0, + COL_LABEL, theme->readable_name, + COL_NAME, theme->name, + COL_THUMBNAIL, thumbnail, + -1); + + if (type == THEME_TYPE_CURSOR && thumbnail) { + g_object_unref (thumbnail); + thumbnail = NULL; + } + } + g_list_free (themes); + + sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + COL_LABEL, GTK_SORT_ASCENDING); + + if (type == THEME_TYPE_CURSOR) + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), COL_LABEL, + (GtkTreeIterCompareFunc) cursor_theme_sort_func, + NULL, NULL); + + gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (sort_model)); + + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (renderer, "xpad", 3, "ypad", 3, NULL); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COL_THUMBNAIL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); + + renderer = gtk_cell_renderer_text_new (); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute (column, renderer, "text", COL_LABEL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), column); + + conv_data = g_new (PEditorConvData, 1); + conv_data->data = data; + conv_data->thumbnail = thumbnail; + peditor = mateconf_peditor_new_tree_view (NULL, key, list, + "conv-to-widget-cb", conv_to_widget_cb, + "conv-from-widget-cb", conv_from_widget_cb, + "data", conv_data, + "data-free-cb", g_free, + NULL); + g_signal_connect (peditor, "value-changed", callback, data); + + /* init the delete buttons */ + value = mateconf_client_get (data->client, key, NULL); + (*((void (*) (MateConfPropertyEditor *, const gchar *, const MateConfValue *, gpointer)) callback)) + (MATECONF_PROPERTY_EDITOR (peditor), key, value, data); + if (value) + mateconf_value_free (value); +} + +void +style_init (AppearanceData *data) +{ + GtkSettings *settings; + GtkWidget *w; + gchar *label; + gint i; + + data->gtk_theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/gtk-theme-thumbnailing.png", NULL); + data->window_theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/window-theme-thumbnailing.png", NULL); + data->icon_theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/icon-theme-thumbnailing.png", NULL); + data->style_message_area = NULL; + data->style_message_label = NULL; + data->style_install_button = NULL; + + w = appearance_capplet_get_widget (data, "theme_details"); + g_signal_connect (w, "response", (GCallback) style_response_cb, NULL); + g_signal_connect (w, "delete_event", (GCallback) gtk_true, NULL); + + prepare_list (data, appearance_capplet_get_widget (data, "window_themes_list"), THEME_TYPE_WINDOW, (GCallback) window_theme_changed); + prepare_list (data, appearance_capplet_get_widget (data, "gtk_themes_list"), THEME_TYPE_GTK, (GCallback) gtk_theme_changed); + prepare_list (data, appearance_capplet_get_widget (data, "icon_themes_list"), THEME_TYPE_ICON, (GCallback) icon_theme_changed); + prepare_list (data, appearance_capplet_get_widget (data, "cursor_themes_list"), THEME_TYPE_CURSOR, (GCallback) cursor_theme_changed); + + w = appearance_capplet_get_widget (data, "color_scheme_message_hbox"); + gtk_widget_set_no_show_all (w, TRUE); + + w = appearance_capplet_get_widget (data, "color_scheme_defaults_button"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_REVERT_TO_SAVED, + GTK_ICON_SIZE_BUTTON)); + + settings = gtk_settings_get_default (); + g_signal_connect (settings, "notify::gtk-color-scheme", (GCallback) color_scheme_changed, data); + +#ifdef HAVE_XCURSOR + w = appearance_capplet_get_widget (data, "cursor_size_scale"); + g_signal_connect (w, "value-changed", (GCallback) cursor_size_scale_value_changed_cb, data); + + w = appearance_capplet_get_widget (data, "cursor_size_small_label"); + label = g_strdup_printf ("%s", gtk_label_get_text (GTK_LABEL (w))); + gtk_label_set_markup (GTK_LABEL (w), label); + g_free (label); + + w = appearance_capplet_get_widget (data, "cursor_size_large_label"); + label = g_strdup_printf ("%s", gtk_label_get_text (GTK_LABEL (w))); + gtk_label_set_markup (GTK_LABEL (w), label); + g_free (label); +#else + w = appearance_capplet_get_widget (data, "cursor_size_hbox"); + gtk_widget_set_no_show_all (w, TRUE); + gtk_widget_hide (w); + gtk_widget_show (appearance_capplet_get_widget (data, "cursor_message_hbox")); + gtk_box_set_spacing (GTK_BOX (appearance_capplet_get_widget (data, "cursor_vbox")), 12); +#endif + + /* connect signals */ + /* color buttons */ + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) + g_signal_connect (appearance_capplet_get_widget (data, symbolic_names[i]), "color-set", (GCallback) color_button_clicked_cb, data); + + /* revert button */ + g_signal_connect (appearance_capplet_get_widget (data, "color_scheme_defaults_button"), "clicked", (GCallback) color_scheme_defaults_button_clicked_cb, data); + /* delete buttons */ + g_signal_connect (appearance_capplet_get_widget (data, "gtk_themes_delete"), "clicked", (GCallback) gtk_theme_delete_cb, data); + g_signal_connect (appearance_capplet_get_widget (data, "window_themes_delete"), "clicked", (GCallback) window_theme_delete_cb, data); + g_signal_connect (appearance_capplet_get_widget (data, "icon_themes_delete"), "clicked", (GCallback) icon_theme_delete_cb, data); + g_signal_connect (appearance_capplet_get_widget (data, "cursor_themes_delete"), "clicked", (GCallback) cursor_theme_delete_cb, data); + + update_message_area (data); + mate_theme_info_register_theme_change ((ThemeChangedCallback) changed_on_disk_cb, data); +} + +void +style_shutdown (AppearanceData *data) +{ + if (data->gtk_theme_icon) + g_object_unref (data->gtk_theme_icon); + if (data->window_theme_icon) + g_object_unref (data->window_theme_icon); + if (data->icon_theme_icon) + g_object_unref (data->icon_theme_icon); +} diff --git a/capplets/appearance/appearance-style.h b/capplets/appearance/appearance-style.h new file mode 100644 index 00000000..65203104 --- /dev/null +++ b/capplets/appearance/appearance-style.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void style_init (AppearanceData *data); +void style_shutdown (AppearanceData *data); diff --git a/capplets/appearance/appearance-themes.c b/capplets/appearance/appearance-themes.c new file mode 100644 index 00000000..132aa4b4 --- /dev/null +++ b/capplets/appearance/appearance-themes.c @@ -0,0 +1,1179 @@ +/* + * Copyright (C) 2007, 2010 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" +#include "theme-thumbnail.h" +#include "mate-theme-apply.h" +#include "theme-installer.h" +#include "theme-save.h" +#include "theme-util.h" +#include "gtkrc-utils.h" + +#include +#include +#include +#include + +#define CUSTOM_THEME_NAME "__custom__" + +enum { + RESPONSE_APPLY_BG, + RESPONSE_REVERT_FONT, + RESPONSE_APPLY_FONT, + RESPONSE_INSTALL_ENGINE +}; + +enum { + TARGET_URI_LIST, + TARGET_NS_URL +}; + +static const GtkTargetEntry drop_types[] = +{ + {"text/uri-list", 0, TARGET_URI_LIST}, + {"_NETSCAPE_URL", 0, TARGET_NS_URL} +}; + +static void theme_message_area_update(AppearanceData* data); + +static time_t theme_get_mtime(const char* name) +{ + MateThemeMetaInfo* theme; + time_t mtime = -1; + + theme = mate_theme_meta_info_find(name); + if (theme != NULL) + { + GFile* file; + GFileInfo* file_info; + + file = g_file_new_for_path(theme->path); + file_info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL); + g_object_unref(file); + + if (file_info != NULL) + { + mtime = g_file_info_get_attribute_uint64(file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED); + g_object_unref(file_info); + } + } + + return mtime; +} + +static void theme_thumbnail_update(GdkPixbuf* pixbuf, gchar* theme_name, AppearanceData* data, gboolean cache) +{ + GtkTreeIter iter; + GtkTreeModel* model = GTK_TREE_MODEL(data->theme_store); + + /* find item in model and update thumbnail */ + if (!pixbuf) + return; + + if (theme_find_in_model(model, theme_name, &iter)) + { + time_t mtime; + + gtk_list_store_set(data->theme_store, &iter, COL_THUMBNAIL, pixbuf, -1); + + /* cache thumbnail */ + if (cache && (mtime = theme_get_mtime(theme_name)) != -1) + { + gchar* path; + + /* try to share thumbs with caja, use themes:/// */ + path = g_strconcat("themes:///", theme_name, NULL); + + mate_desktop_thumbnail_factory_save_thumbnail(data->thumb_factory, pixbuf, path, mtime); + + g_free(path); + } + } +} + +static GdkPixbuf* theme_get_thumbnail_from_cache(MateThemeMetaInfo* info, AppearanceData* data) +{ + GdkPixbuf* thumb = NULL; + gchar* path, *thumb_filename; + time_t mtime; + + if (info == data->theme_custom) + return NULL; + + mtime = theme_get_mtime(info->name); + + if (mtime == -1) + return NULL; + + /* try to share thumbs with caja, use themes:/// */ + path = g_strconcat ("themes:///", info->name, NULL); + thumb_filename = mate_desktop_thumbnail_factory_lookup(data->thumb_factory, path, mtime); + g_free(path); + + if (thumb_filename != NULL) + { + thumb = gdk_pixbuf_new_from_file(thumb_filename, NULL); + g_free(thumb_filename); + } + + return thumb; +} + +static void +theme_thumbnail_done_cb (GdkPixbuf *pixbuf, gchar *theme_name, AppearanceData *data) +{ + theme_thumbnail_update (pixbuf, theme_name, data, TRUE); +} + +static void theme_thumbnail_generate(MateThemeMetaInfo* info, AppearanceData* data) +{ + GdkPixbuf* thumb = theme_get_thumbnail_from_cache(info, data); + + if (thumb != NULL) + { + theme_thumbnail_update(thumb, info->name, data, FALSE); + g_object_unref(thumb); + } + else + { + generate_meta_theme_thumbnail_async(info, (ThemeThumbnailFunc) theme_thumbnail_done_cb, data, NULL); + } +} + +static void theme_changed_on_disk_cb(MateThemeCommonInfo* theme, MateThemeChangeType change_type, MateThemeElement element_type, AppearanceData* data) +{ + if (theme->type == MATE_THEME_TYPE_METATHEME) + { + MateThemeMetaInfo* meta = (MateThemeMetaInfo*) theme; + + if (change_type == MATE_THEME_CHANGE_CREATED) + { + gtk_list_store_insert_with_values (data->theme_store, NULL, 0, COL_LABEL, meta->readable_name, COL_NAME, meta->name, COL_THUMBNAIL, data->theme_icon, -1); + theme_thumbnail_generate(meta, data); + } + else if (change_type == MATE_THEME_CHANGE_DELETED) + { + GtkTreeIter iter; + + if (theme_find_in_model(GTK_TREE_MODEL(data->theme_store), meta->name, &iter)) + { + gtk_list_store_remove(data->theme_store, &iter); + } + } + else if (change_type == MATE_THEME_CHANGE_CHANGED) + { + theme_thumbnail_generate(meta, data); + } + } +} + +static gchar* get_default_string_from_key(MateConfClient* client, const char* key) +{ + gchar* str = NULL; + + MateConfValue* value = mateconf_client_get_default_from_schema(client, key, NULL); + + if (value) + { + if (value->type == MATECONF_VALUE_STRING) + { + str = mateconf_value_to_string (value); + } + + mateconf_value_free (value); + } + + return str; +} + +/* Find out if the lockdown key has been set. + * Currently returns false on error... */ +static gboolean is_locked_down(MateConfClient* client) +{ + return mateconf_client_get_bool(client, LOCKDOWN_KEY, NULL); +} + +static MateThemeMetaInfo * +theme_load_from_mateconf (MateConfClient *client) +{ + MateThemeMetaInfo *theme; + gchar *scheme; + + theme = mate_theme_meta_info_new (); + + theme->gtk_theme_name = mateconf_client_get_string (client, GTK_THEME_KEY, NULL); + if (theme->gtk_theme_name == NULL) + theme->gtk_theme_name = g_strdup ("Clearlooks"); + + scheme = mateconf_client_get_string (client, COLOR_SCHEME_KEY, NULL); + if (scheme == NULL || !strcmp (scheme, "")) { + g_free (scheme); + scheme = gtkrc_get_color_scheme_for_theme (theme->gtk_theme_name); + } + theme->gtk_color_scheme = scheme; + + theme->marco_theme_name = mateconf_client_get_string (client, MARCO_THEME_KEY, NULL); + if (theme->marco_theme_name == NULL) + theme->marco_theme_name = g_strdup ("Clearlooks"); + + theme->icon_theme_name = mateconf_client_get_string (client, ICON_THEME_KEY, NULL); + if (theme->icon_theme_name == NULL) + theme->icon_theme_name = g_strdup ("mate"); + + theme->notification_theme_name = mateconf_client_get_string (client, NOTIFICATION_THEME_KEY, NULL); + + theme->cursor_theme_name = mateconf_client_get_string (client, CURSOR_THEME_KEY, NULL); +#ifdef HAVE_XCURSOR + theme->cursor_size = mateconf_client_get_int (client, CURSOR_SIZE_KEY, NULL); +#endif + if (theme->cursor_theme_name == NULL) + theme->cursor_theme_name = g_strdup ("default"); + + theme->application_font = mateconf_client_get_string (client, APPLICATION_FONT_KEY, NULL); + + return theme; +} + +static gchar * +theme_get_selected_name (GtkIconView *icon_view, AppearanceData *data) +{ + gchar *name = NULL; + GList *selected = gtk_icon_view_get_selected_items (icon_view); + + if (selected) { + GtkTreePath *path = selected->data; + GtkTreeModel *model = gtk_icon_view_get_model (icon_view); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (model, &iter, path)) + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + } + + return name; +} + +static const MateThemeMetaInfo * +theme_get_selected (GtkIconView *icon_view, AppearanceData *data) +{ + MateThemeMetaInfo *theme = NULL; + gchar *name = theme_get_selected_name (icon_view, data); + + if (name != NULL) { + if (!strcmp (name, data->theme_custom->name)) { + theme = data->theme_custom; + } else { + theme = mate_theme_meta_info_find (name); + } + + g_free (name); + } + + return theme; +} + +static void +theme_select_iter (GtkIconView *icon_view, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (gtk_icon_view_get_model (icon_view), iter); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.5, 0.0); + gtk_tree_path_free (path); +} + +static void +theme_select_name (GtkIconView *icon_view, const gchar *theme) +{ + GtkTreeIter iter; + GtkTreeModel *model = gtk_icon_view_get_model (icon_view); + + if (theme_find_in_model (model, theme, &iter)) + theme_select_iter (icon_view, &iter); +} + +static gboolean +theme_is_equal (const MateThemeMetaInfo *a, const MateThemeMetaInfo *b) +{ + gboolean a_set, b_set; + + if (!(a->gtk_theme_name && b->gtk_theme_name) || + strcmp (a->gtk_theme_name, b->gtk_theme_name)) + return FALSE; + + if (!(a->icon_theme_name && b->icon_theme_name) || + strcmp (a->icon_theme_name, b->icon_theme_name)) + return FALSE; + + if (!(a->marco_theme_name && b->marco_theme_name) || + strcmp (a->marco_theme_name, b->marco_theme_name)) + return FALSE; + + if (!(a->cursor_theme_name && b->cursor_theme_name) || + strcmp (a->cursor_theme_name, b->cursor_theme_name)) + return FALSE; + + if (a->cursor_size != b->cursor_size) + return FALSE; + + a_set = a->gtk_color_scheme && strcmp (a->gtk_color_scheme, ""); + b_set = b->gtk_color_scheme && strcmp (b->gtk_color_scheme, ""); + if ((a_set != b_set) || + (a_set && !mate_theme_color_scheme_equal (a->gtk_color_scheme, b->gtk_color_scheme))) + return FALSE; + + return TRUE; +} + +static void +theme_set_custom_from_theme (const MateThemeMetaInfo *info, AppearanceData *data) +{ + MateThemeMetaInfo *custom = data->theme_custom; + GtkIconView *icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + + if (info == custom) + return; + + /* if info is not NULL, we'll copy those theme settings over */ + if (info != NULL) { + g_free (custom->gtk_theme_name); + g_free (custom->icon_theme_name); + g_free (custom->marco_theme_name); + g_free (custom->gtk_color_scheme); + g_free (custom->cursor_theme_name); + g_free (custom->application_font); + custom->gtk_color_scheme = NULL; + custom->application_font = NULL; + + /* these settings are guaranteed to be non-NULL */ + custom->gtk_theme_name = g_strdup (info->gtk_theme_name); + custom->icon_theme_name = g_strdup (info->icon_theme_name); + custom->marco_theme_name = g_strdup (info->marco_theme_name); + custom->cursor_theme_name = g_strdup (info->cursor_theme_name); + custom->cursor_size = info->cursor_size; + + /* these can be NULL */ + if (info->gtk_color_scheme) + custom->gtk_color_scheme = g_strdup (info->gtk_color_scheme); + else + custom->gtk_color_scheme = get_default_string_from_key (data->client, COLOR_SCHEME_KEY); + + if (info->application_font) + custom->application_font = g_strdup (info->application_font); + else + custom->application_font = get_default_string_from_key (data->client, APPLICATION_FONT_KEY); + } + + /* select the custom theme */ + model = gtk_icon_view_get_model (icon_view); + if (!theme_find_in_model (model, custom->name, &iter)) { + GtkTreeIter child; + + gtk_list_store_insert_with_values (data->theme_store, &child, 0, + COL_LABEL, custom->readable_name, + COL_NAME, custom->name, + COL_THUMBNAIL, data->theme_icon, + -1); + gtk_tree_model_sort_convert_child_iter_to_iter ( + GTK_TREE_MODEL_SORT (model), &iter, &child); + } + + path = gtk_tree_model_get_path (model, &iter); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.5, 0.0); + gtk_tree_path_free (path); + + /* update the theme thumbnail */ + theme_thumbnail_generate (custom, data); +} + +/** GUI Callbacks **/ + +static void custom_font_cb(GtkWidget* button, AppearanceData* data) +{ + g_free(data->revert_application_font); + g_free(data->revert_documents_font); + g_free(data->revert_desktop_font); + g_free(data->revert_windowtitle_font); + g_free(data->revert_monospace_font); + data->revert_application_font = NULL; + data->revert_documents_font = NULL; + data->revert_desktop_font = NULL; + data->revert_windowtitle_font = NULL; + data->revert_monospace_font = NULL; +} + +static void +theme_message_area_response_cb (GtkWidget *w, + gint response_id, + AppearanceData *data) +{ + const MateThemeMetaInfo *theme; + gchar *tmpfont; + gchar *engine_path; + + theme = theme_get_selected (GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")), data); + if (!theme) + return; + + switch (response_id) + { + case RESPONSE_APPLY_BG: + mateconf_client_set_string (data->client, BACKGROUND_KEY, + theme->background_image, NULL); + break; + + case RESPONSE_REVERT_FONT: + if (data->revert_application_font != NULL) { + mateconf_client_set_string (data->client, APPLICATION_FONT_KEY, + data->revert_application_font, NULL); + g_free (data->revert_application_font); + data->revert_application_font = NULL; + } + + if (data->revert_documents_font != NULL) { + mateconf_client_set_string (data->client, DOCUMENTS_FONT_KEY, + data->revert_documents_font, NULL); + g_free (data->revert_documents_font); + data->revert_documents_font = NULL; + } + + if (data->revert_desktop_font != NULL) { + mateconf_client_set_string (data->client, DESKTOP_FONT_KEY, + data->revert_desktop_font, NULL); + g_free (data->revert_desktop_font); + data->revert_desktop_font = NULL; + } + + if (data->revert_windowtitle_font != NULL) { + mateconf_client_set_string (data->client, WINDOWTITLE_FONT_KEY, + data->revert_windowtitle_font, NULL); + g_free (data->revert_windowtitle_font); + data->revert_windowtitle_font = NULL; + } + + if (data->revert_monospace_font != NULL) { + mateconf_client_set_string (data->client, MONOSPACE_FONT_KEY, + data->revert_monospace_font, NULL); + g_free (data->revert_monospace_font); + data->revert_monospace_font = NULL; + } + break; + + case RESPONSE_APPLY_FONT: + if (theme->application_font) { + tmpfont = mateconf_client_get_string (data->client, APPLICATION_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_application_font); + + if (strcmp (theme->application_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_application_font = NULL; + } else + data->revert_application_font = tmpfont; + } + mateconf_client_set_string (data->client, APPLICATION_FONT_KEY, + theme->application_font, NULL); + } + + if (theme->documents_font) { + tmpfont = mateconf_client_get_string (data->client, DOCUMENTS_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_documents_font); + + if (strcmp (theme->documents_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_documents_font = NULL; + } else + data->revert_documents_font = tmpfont; + } + mateconf_client_set_string (data->client, DOCUMENTS_FONT_KEY, + theme->documents_font, NULL); + } + + if (theme->desktop_font) { + tmpfont = mateconf_client_get_string (data->client, DESKTOP_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_desktop_font); + + if (strcmp (theme->desktop_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_desktop_font = NULL; + } else + data->revert_desktop_font = tmpfont; + } + mateconf_client_set_string (data->client, DESKTOP_FONT_KEY, + theme->desktop_font, NULL); + } + + if (theme->windowtitle_font) { + tmpfont = mateconf_client_get_string (data->client, WINDOWTITLE_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_windowtitle_font); + + if (strcmp (theme->windowtitle_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_windowtitle_font = NULL; + } else + data->revert_windowtitle_font = tmpfont; + } + mateconf_client_set_string (data->client, WINDOWTITLE_FONT_KEY, + theme->windowtitle_font, NULL); + } + + if (theme->monospace_font) { + tmpfont = mateconf_client_get_string (data->client, MONOSPACE_FONT_KEY, NULL); + if (tmpfont != NULL) { + g_free (data->revert_monospace_font); + + if (strcmp (theme->monospace_font, tmpfont) == 0) { + g_free (tmpfont); + data->revert_monospace_font = NULL; + } else + data->revert_monospace_font = tmpfont; + } + mateconf_client_set_string (data->client, MONOSPACE_FONT_KEY, + theme->monospace_font, NULL); + } + break; + + case RESPONSE_INSTALL_ENGINE: + + engine_path = gtk_theme_info_missing_engine(theme->gtk_theme_name, FALSE); + + if (engine_path != NULL) + { + theme_install_file(GTK_WINDOW(gtk_widget_get_toplevel(data->install_button)), engine_path); + g_free (engine_path); + } + + theme_message_area_update(data); + break; + } +} + +static void +theme_message_area_update (AppearanceData *data) +{ + const MateThemeMetaInfo *theme; + gboolean show_apply_background = FALSE; + gboolean show_apply_font = FALSE; + gboolean show_revert_font = FALSE; + gboolean show_error; + const gchar *message; + gchar *font; + GError *error = NULL; + + theme = theme_get_selected (GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")), data); + + if (!theme) { + if (data->theme_message_area != NULL) + gtk_widget_hide (data->theme_message_area); + return; + } + + show_error = !mate_theme_meta_info_validate (theme, &error); + + if (!show_error) { + if (theme->background_image != NULL) { + gchar *background; + + background = mateconf_client_get_string (data->client, BACKGROUND_KEY, NULL); + show_apply_background = + (!background || strcmp (theme->background_image, background) != 0); + g_free (background); + } + + if (theme->application_font) { + font = mateconf_client_get_string (data->client, APPLICATION_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->documents_font) { + font = mateconf_client_get_string (data->client, DOCUMENTS_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->desktop_font) { + font = mateconf_client_get_string (data->client, DESKTOP_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->windowtitle_font) { + font = mateconf_client_get_string (data->client, WINDOWTITLE_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + if (!show_apply_font && theme->monospace_font) { + font = mateconf_client_get_string (data->client, MONOSPACE_FONT_KEY, NULL); + show_apply_font = + (!font || strcmp (theme->application_font, font) != 0); + g_free (font); + } + + show_revert_font = (data->revert_application_font != NULL || + data->revert_documents_font != NULL || data->revert_desktop_font != NULL || + data->revert_windowtitle_font != NULL || data->revert_monospace_font != NULL); + } + + if (data->theme_message_area == NULL) { + GtkWidget *hbox; + GtkWidget *parent; + GtkWidget *content; + + if (!show_apply_background && !show_revert_font && !show_apply_font && !show_error) + return; + + data->theme_message_area = gtk_info_bar_new (); + gtk_widget_set_no_show_all (data->theme_message_area, TRUE); + + g_signal_connect (data->theme_message_area, "response", + (GCallback) theme_message_area_response_cb, data); + + data->apply_background_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Apply Background"), + RESPONSE_APPLY_BG); + data->apply_font_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Apply Font"), + RESPONSE_APPLY_FONT); + data->revert_font_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Revert Font"), + RESPONSE_REVERT_FONT); + data->install_button = gtk_info_bar_add_button ( + GTK_INFO_BAR (data->theme_message_area), + _("Install"), + RESPONSE_INSTALL_ENGINE); + + data->theme_message_label = gtk_label_new (NULL); + gtk_widget_show (data->theme_message_label); + gtk_label_set_line_wrap (GTK_LABEL (data->theme_message_label), TRUE); + gtk_misc_set_alignment (GTK_MISC (data->theme_message_label), 0.0, 0.5); + + hbox = gtk_hbox_new (FALSE, 9); + gtk_widget_show (hbox); + data->theme_info_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); + data->theme_error_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (data->theme_info_icon), 0.5, 0); + gtk_misc_set_alignment (GTK_MISC (data->theme_error_icon), 0.5, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->theme_info_icon, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->theme_error_icon, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), data->theme_message_label, TRUE, TRUE, 0); + content = gtk_info_bar_get_content_area (GTK_INFO_BAR (data->theme_message_area)); + gtk_container_add (GTK_CONTAINER (content), hbox); + + parent = appearance_capplet_get_widget (data, "theme_list_vbox"); + gtk_box_pack_start (GTK_BOX (parent), data->theme_message_area, FALSE, FALSE, 0); + } + + if (show_error) + message = error->message; + else if (show_apply_background && show_apply_font && show_revert_font) + message = _("The current theme suggests a background and a font. Also, the last applied font suggestion can be reverted."); + else if (show_apply_background && show_revert_font) + message = _("The current theme suggests a background. Also, the last applied font suggestion can be reverted."); + else if (show_apply_background && show_apply_font) + message = _("The current theme suggests a background and a font."); + else if (show_apply_font && show_revert_font) + message = _("The current theme suggests a font. Also, the last applied font suggestion can be reverted."); + else if (show_apply_background) + message = _("The current theme suggests a background."); + else if (show_revert_font) + message = _("The last applied font suggestion can be reverted."); + else if (show_apply_font) + message = _("The current theme suggests a font."); + else + message = NULL; + + if (show_apply_background) + gtk_widget_show (data->apply_background_button); + else + gtk_widget_hide (data->apply_background_button); + + if (show_apply_font) + gtk_widget_show (data->apply_font_button); + else + gtk_widget_hide (data->apply_font_button); + + if (show_revert_font) + gtk_widget_show (data->revert_font_button); + else + gtk_widget_hide (data->revert_font_button); + + if (show_error + && g_error_matches (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE) + && packagekit_available ()) + gtk_widget_show (data->install_button); + else + gtk_widget_hide (data->install_button); + + if (show_error || show_apply_background || show_apply_font || show_revert_font) { + gtk_widget_show (data->theme_message_area); + gtk_widget_queue_draw (data->theme_message_area); + + if (show_error) { + gtk_widget_show (data->theme_error_icon); + gtk_widget_hide (data->theme_info_icon); + } else { + gtk_widget_show (data->theme_info_icon); + gtk_widget_hide (data->theme_error_icon); + } + } else { + gtk_widget_hide (data->theme_message_area); + } + + gtk_label_set_text (GTK_LABEL (data->theme_message_label), message); + g_clear_error (&error); +} + +static void +theme_selection_changed_cb (GtkWidget *icon_view, AppearanceData *data) +{ + GList *selection; + MateThemeMetaInfo *theme = NULL; + gboolean is_custom = FALSE; + + selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view)); + + if (selection) { + GtkTreeModel *model; + GtkTreeIter iter; + gchar *name; + + model = gtk_icon_view_get_model (GTK_ICON_VIEW (icon_view)); + gtk_tree_model_get_iter (model, &iter, selection->data); + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + is_custom = !strcmp (name, CUSTOM_THEME_NAME); + + if (is_custom) + theme = data->theme_custom; + else + theme = mate_theme_meta_info_find (name); + + if (theme) { + mate_meta_theme_set (theme); + theme_message_area_update (data); + } + + g_free (name); + g_list_foreach (selection, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selection); + } + + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "theme_delete"), + theme_is_writable (theme)); + gtk_widget_set_sensitive (appearance_capplet_get_widget (data, "theme_save"), is_custom); +} + +static void +theme_custom_cb (GtkWidget *button, AppearanceData *data) +{ + GtkWidget *w, *parent; + + w = appearance_capplet_get_widget (data, "theme_details"); + parent = appearance_capplet_get_widget (data, "appearance_window"); + gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (parent)); + gtk_widget_show_all (w); +} + +static void +theme_save_cb (GtkWidget *button, AppearanceData *data) +{ + theme_save_dialog_run (data->theme_custom, data); +} + +static void +theme_install_cb (GtkWidget *button, AppearanceData *data) +{ + mate_theme_installer_run ( + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window")), NULL); +} + +static void +theme_delete_cb (GtkWidget *button, AppearanceData *data) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + GList *selected = gtk_icon_view_get_selected_items (icon_view); + + if (selected) { + GtkTreePath *path = selected->data; + GtkTreeModel *model = gtk_icon_view_get_model (icon_view); + GtkTreeIter iter; + gchar *name = NULL; + + if (gtk_tree_model_get_iter (model, &iter, path)) + gtk_tree_model_get (model, &iter, COL_NAME, &name, -1); + + if (name != NULL && + strcmp (name, data->theme_custom->name) && + theme_delete (name, THEME_TYPE_META)) { + /* remove theme from the model, too */ + GtkTreeIter child; + + if (gtk_tree_model_iter_next (model, &iter) || + theme_model_iter_last (model, &iter)) + theme_select_iter (icon_view, &iter); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_sort_convert_iter_to_child_iter ( + GTK_TREE_MODEL_SORT (model), &child, &iter); + gtk_list_store_remove (data->theme_store, &child); + } + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + g_free (name); + } +} + +static void +theme_details_changed_cb (AppearanceData *data) +{ + MateThemeMetaInfo *mateconf_theme; + const MateThemeMetaInfo *selected; + GtkIconView *icon_view; + gboolean done = FALSE; + + /* load new state from mateconf */ + mateconf_theme = theme_load_from_mateconf (data->client); + + /* check if it's our currently selected theme */ + icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + selected = theme_get_selected (icon_view, data); + + if (!selected || !(done = theme_is_equal (selected, mateconf_theme))) { + /* look for a matching metatheme */ + GList *theme_list, *l; + + theme_list = mate_theme_meta_info_find_all (); + + for (l = theme_list; l; l = l->next) { + MateThemeMetaInfo *info = l->data; + + if (theme_is_equal (mateconf_theme, info)) { + theme_select_name (icon_view, info->name); + done = TRUE; + break; + } + } + g_list_free (theme_list); + } + + if (!done) + /* didn't find a match, set or update custom */ + theme_set_custom_from_theme (mateconf_theme, data); + + mate_theme_meta_info_free (mateconf_theme); +} + +static void +theme_setting_changed_cb (GObject *settings, + GParamSpec *pspec, + AppearanceData *data) +{ + theme_details_changed_cb (data); +} + +static void +theme_mateconf_changed (MateConfClient *client, + guint conn_id, + MateConfEntry *entry, + AppearanceData *data) +{ + theme_details_changed_cb (data); +} + +static gint +theme_list_sort_func (MateThemeMetaInfo *a, + MateThemeMetaInfo *b) +{ + return strcmp (a->readable_name, b->readable_name); +} + +static gint +theme_store_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *a_name, *a_label; + gint rc; + + gtk_tree_model_get (model, a, COL_NAME, &a_name, COL_LABEL, &a_label, -1); + + if (!strcmp (a_name, CUSTOM_THEME_NAME)) { + rc = -1; + } else { + gchar *b_name, *b_label; + + gtk_tree_model_get (model, b, COL_NAME, &b_name, COL_LABEL, &b_label, -1); + + if (!strcmp (b_name, CUSTOM_THEME_NAME)) { + rc = 1; + } else { + gchar *a_case, *b_case; + + a_case = g_utf8_casefold (a_label, -1); + b_case = g_utf8_casefold (b_label, -1); + rc = g_utf8_collate (a_case, b_case); + g_free (a_case); + g_free (b_case); + } + + g_free (b_name); + g_free (b_label); + } + + g_free (a_name); + g_free (a_label); + + return rc; +} + +static void +theme_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, + AppearanceData *data) +{ + gchar **uris; + + if (!(info == TARGET_URI_LIST || info == TARGET_NS_URL)) + return; + + uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); + + if (uris != NULL && uris[0] != NULL) { + GFile *f = g_file_new_for_uri (uris[0]); + + mate_theme_install (f, + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window"))); + g_object_unref (f); + } + + g_strfreev (uris); +} + +static void background_or_font_changed(MateConfEngine* conf, guint cnxn_id, MateConfEntry* entry, AppearanceData* data) +{ + theme_message_area_update(data); +} + +void themes_init(AppearanceData* data) +{ + GtkWidget *w, *del_button; + GList *theme_list, *l; + GtkListStore *theme_store; + GtkTreeModel *sort_model; + MateThemeMetaInfo *meta_theme = NULL; + GtkIconView *icon_view; + GtkCellRenderer *renderer; + GtkSettings *settings; + char *url; + + /* initialise some stuff */ + mate_theme_init (); + mate_wm_manager_init (); + + data->revert_application_font = NULL; + data->revert_documents_font = NULL; + data->revert_desktop_font = NULL; + data->revert_windowtitle_font = NULL; + data->revert_monospace_font = NULL; + data->theme_save_dialog = NULL; + data->theme_message_area = NULL; + data->theme_info_icon = NULL; + data->theme_error_icon = NULL; + data->theme_custom = mate_theme_meta_info_new (); + data->theme_icon = gdk_pixbuf_new_from_file (MATECC_PIXMAP_DIR "/theme-thumbnailing.png", NULL); + data->theme_store = theme_store = + gtk_list_store_new (NUM_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + /* set up theme list */ + theme_list = mate_theme_meta_info_find_all (); + mate_theme_info_register_theme_change ((ThemeChangedCallback) theme_changed_on_disk_cb, data); + + data->theme_custom = theme_load_from_mateconf (data->client); + data->theme_custom->name = g_strdup (CUSTOM_THEME_NAME); + data->theme_custom->readable_name = g_strdup_printf ("%s", _("Custom")); + + for (l = theme_list; l; l = l->next) { + MateThemeMetaInfo *info = l->data; + + gtk_list_store_insert_with_values (theme_store, NULL, 0, + COL_LABEL, info->readable_name, + COL_NAME, info->name, + COL_THUMBNAIL, data->theme_icon, + -1); + + if (!meta_theme && theme_is_equal (data->theme_custom, info)) + meta_theme = info; + } + + if (!meta_theme) { + /* add custom theme */ + meta_theme = data->theme_custom; + + gtk_list_store_insert_with_values (theme_store, NULL, 0, + COL_LABEL, meta_theme->readable_name, + COL_NAME, meta_theme->name, + COL_THUMBNAIL, data->theme_icon, + -1); + + theme_thumbnail_generate (meta_theme, data); + } + + theme_list = g_list_sort (theme_list, (GCompareFunc) theme_list_sort_func); + + g_list_foreach (theme_list, (GFunc) theme_thumbnail_generate, data); + g_list_free (theme_list); + + icon_view = GTK_ICON_VIEW (appearance_capplet_get_widget (data, "theme_list")); + + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (renderer, "xpad", 5, "ypad", 5, + "xalign", 0.5, "yalign", 1.0, NULL); + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer, + "pixbuf", COL_THUMBNAIL, NULL); + + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "alignment", PANGO_ALIGN_CENTER, + "wrap-mode", PANGO_WRAP_WORD_CHAR, + "wrap-width", gtk_icon_view_get_item_width (icon_view), + "width", gtk_icon_view_get_item_width (icon_view), + "xalign", 0.0, "yalign", 0.0, NULL); + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer, + "markup", COL_LABEL, NULL); + + sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (theme_store)); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), COL_LABEL, theme_store_sort_func, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), COL_LABEL, GTK_SORT_ASCENDING); + gtk_icon_view_set_model (icon_view, GTK_TREE_MODEL (sort_model)); + + g_signal_connect (icon_view, "selection-changed", (GCallback) theme_selection_changed_cb, data); + g_signal_connect_after (icon_view, "realize", (GCallback) theme_select_name, meta_theme->name); + + w = appearance_capplet_get_widget (data, "theme_install"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (w, "clicked", (GCallback) theme_install_cb, data); + + w = appearance_capplet_get_widget (data, "theme_save"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (w, "clicked", (GCallback) theme_save_cb, data); + + w = appearance_capplet_get_widget (data, "theme_custom"); + gtk_button_set_image (GTK_BUTTON (w), + gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON)); + g_signal_connect (w, "clicked", (GCallback) theme_custom_cb, data); + + del_button = appearance_capplet_get_widget (data, "theme_delete"); + g_signal_connect (del_button, "clicked", (GCallback) theme_delete_cb, data); + + w = appearance_capplet_get_widget (data, "theme_vbox"); + gtk_drag_dest_set (w, GTK_DEST_DEFAULT_ALL, + drop_types, G_N_ELEMENTS (drop_types), + GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE); + g_signal_connect (w, "drag-data-received", (GCallback) theme_drag_data_received_cb, data); + if (is_locked_down (data->client)) + gtk_widget_set_sensitive (w, FALSE); + + w = appearance_capplet_get_widget (data, "more_themes_linkbutton"); + url = mateconf_client_get_string (data->client, MORE_THEMES_URL_KEY, NULL); + if (url != NULL && url[0] != '\0') { + gtk_link_button_set_uri (GTK_LINK_BUTTON (w), url); + gtk_widget_show (w); + } else { + gtk_widget_hide (w); + } + g_free (url); + + /* listen to mateconf changes, too */ + mateconf_client_add_dir (data->client, "/apps/marco/general", MATECONF_CLIENT_PRELOAD_NONE, NULL); + mateconf_client_add_dir (data->client, "/desktop/mate/interface", MATECONF_CLIENT_PRELOAD_NONE, NULL); + mateconf_client_notify_add (data->client, MARCO_THEME_KEY, (MateConfClientNotifyFunc) theme_mateconf_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, CURSOR_THEME_KEY, (MateConfClientNotifyFunc) theme_mateconf_changed, data, NULL, NULL); +#ifdef HAVE_XCURSOR + mateconf_client_notify_add (data->client, CURSOR_SIZE_KEY, (MateConfClientNotifyFunc) theme_mateconf_changed, data, NULL, NULL); +#endif + mateconf_client_notify_add (data->client, BACKGROUND_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, APPLICATION_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, DOCUMENTS_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, DESKTOP_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, WINDOWTITLE_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + mateconf_client_notify_add (data->client, MONOSPACE_FONT_KEY, (MateConfClientNotifyFunc) background_or_font_changed, data, NULL, NULL); + + settings = gtk_settings_get_default (); + g_signal_connect (settings, "notify::gtk-color-scheme", (GCallback) theme_setting_changed_cb, data); + g_signal_connect (settings, "notify::gtk-theme-name", (GCallback) theme_setting_changed_cb, data); + g_signal_connect (settings, "notify::gtk-icon-theme-name", (GCallback) theme_setting_changed_cb, data); + + /* monitor individual font choice buttons, so + "revert font" option (if any) can be cleared */ + w = appearance_capplet_get_widget (data, "application_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "document_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "desktop_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "window_title_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); + w = appearance_capplet_get_widget (data, "monospace_font"); + g_signal_connect (w, "font_set", (GCallback) custom_font_cb, data); +} + +void +themes_shutdown (AppearanceData *data) +{ + mate_theme_meta_info_free (data->theme_custom); + + if (data->theme_icon) + g_object_unref (data->theme_icon); + if (data->theme_save_dialog) + gtk_widget_destroy (data->theme_save_dialog); + g_free (data->revert_application_font); + g_free (data->revert_documents_font); + g_free (data->revert_desktop_font); + g_free (data->revert_windowtitle_font); + g_free (data->revert_monospace_font); +} diff --git a/capplets/appearance/appearance-themes.h b/capplets/appearance/appearance-themes.h new file mode 100644 index 00000000..6d1c59ff --- /dev/null +++ b/capplets/appearance/appearance-themes.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void themes_init(AppearanceData* data); +void themes_shutdown(AppearanceData* data); diff --git a/capplets/appearance/appearance.h b/capplets/appearance/appearance.h new file mode 100644 index 00000000..3658ea0f --- /dev/null +++ b/capplets/appearance/appearance.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "mate-theme-info.h" + +#define APPEARANCE_KEY_DIR "/apps/control-center/appearance" +#define MORE_THEMES_URL_KEY APPEARANCE_KEY_DIR "/more_themes_url" +#define MORE_BACKGROUNDS_URL_KEY APPEARANCE_KEY_DIR "/more_backgrounds_url" + +typedef struct { + MateConfClient* client; + GtkBuilder* ui; + MateDesktopThumbnailFactory* thumb_factory; + gulong screen_size_handler; + gulong screen_monitors_handler; + + /* desktop */ + GHashTable* wp_hash; + gboolean wp_update_mateconf; + GtkIconView* wp_view; + GtkTreeModel* wp_model; + GtkWidget* wp_scpicker; + GtkWidget* wp_pcpicker; + GtkWidget* wp_style_menu; + GtkWidget* wp_color_menu; + GtkWidget* wp_rem_button; + GtkFileChooser* wp_filesel; + GtkWidget* wp_image; + GSList* wp_uris; + gint frame; + gint thumb_width; + gint thumb_height; + + /* font */ + GtkWidget* font_details; + GSList* font_groups; + + /* themes */ + GtkListStore* theme_store; + MateThemeMetaInfo* theme_custom; + GdkPixbuf* theme_icon; + GtkWidget* theme_save_dialog; + GtkWidget* theme_message_area; + GtkWidget* theme_message_label; + GtkWidget* apply_background_button; + GtkWidget* revert_font_button; + GtkWidget* apply_font_button; + GtkWidget* install_button; + GtkWidget* theme_info_icon; + GtkWidget* theme_error_icon; + gchar* revert_application_font; + gchar* revert_documents_font; + gchar* revert_desktop_font; + gchar* revert_windowtitle_font; + gchar* revert_monospace_font; + + /* style */ + GdkPixbuf* gtk_theme_icon; + GdkPixbuf* window_theme_icon; + GdkPixbuf* icon_theme_icon; + GtkWidget* style_message_area; + GtkWidget* style_message_label; + GtkWidget* style_install_button; +} AppearanceData; + +#define appearance_capplet_get_widget(x, y) (GtkWidget*) gtk_builder_get_object(x->ui, y) diff --git a/capplets/appearance/data/Makefile.am b/capplets/appearance/data/Makefile.am new file mode 100644 index 00000000..987213fb --- /dev/null +++ b/capplets/appearance/data/Makefile.am @@ -0,0 +1,62 @@ + +gtkbuilderdir = $(pkgdatadir)/ui +dist_gtkbuilder_DATA = appearance.ui + +pixmapdir = $(pkgdatadir)/pixmaps +dist_pixmap_DATA = \ + subpixel-bgr.png \ + subpixel-rgb.png \ + subpixel-vbgr.png \ + subpixel-vrgb.png \ + theme-thumbnailing.png \ + gtk-theme-thumbnailing.png \ + window-theme-thumbnailing.png \ + icon-theme-thumbnailing.png \ + mouse-cursor-normal.png \ + mouse-cursor-normal-large.png \ + mouse-cursor-white.png \ + mouse-cursor-white-large.png + +cursorfontdir = $(datadir)/mate/cursor-fonts +dist_cursorfont_DATA = \ + cursor-large.pcf \ + cursor-white.pcf \ + cursor-large-white.pcf + +@INTLTOOL_DESKTOP_RULE@ + +desktopdir = $(datadir)/applications +desktop_in_files = \ + mate-appearance-properties.desktop.in \ + mate-theme-installer.desktop.in +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) + + + + +@INTLTOOL_XML_RULE@ + +xml_in_files = \ + mate-theme-package.xml.in + +mimedir = $(datadir)/mime/packages +mime_DATA = $(xml_in_files:.xml.in=.xml) + + +install-data-hook: +if ENABLE_UPDATE_MIMEDB + $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" +endif + +uninstall-hook: +if ENABLE_UPDATE_MIMEDB + $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" +endif + + +EXTRA_DIST = $(xml_in_files) + + +CLEANFILES = $(desktop_in_files) $(desktop_DATA) $(mime_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/appearance/data/Makefile.in b/capplets/appearance/data/Makefile.in new file mode 100644 index 00000000..84174029 --- /dev/null +++ b/capplets/appearance/data/Makefile.in @@ -0,0 +1,660 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = capplets/appearance/data +DIST_COMMON = $(dist_cursorfont_DATA) $(dist_gtkbuilder_DATA) \ + $(dist_pixmap_DATA) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in \ + $(srcdir)/mate-appearance-properties.desktop.in.in \ + $(srcdir)/mate-theme-installer.desktop.in.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/intltool.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/mate-doc-utils.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = mate-appearance-properties.desktop.in \ + mate-theme-installer.desktop.in +CONFIG_CLEAN_VPATH_FILES = +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(desktopdir)" \ + "$(DESTDIR)$(cursorfontdir)" "$(DESTDIR)$(gtkbuilderdir)" \ + "$(DESTDIR)$(pixmapdir)" "$(DESTDIR)$(mimedir)" +DATA = $(desktop_DATA) $(dist_cursorfont_DATA) $(dist_gtkbuilder_DATA) \ + $(dist_pixmap_DATA) $(mime_DATA) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APP_INDICATOR_CFLAGS = @APP_INDICATOR_CFLAGS@ +APP_INDICATOR_LIBS = @APP_INDICATOR_LIBS@ +AR = @AR@ +AT_CAPPLET_CFLAGS = @AT_CAPPLET_CFLAGS@ +AT_CAPPLET_LIBS = @AT_CAPPLET_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAPPLET_CFLAGS = @CAPPLET_CFLAGS@ +CAPPLET_LIBS = @CAPPLET_LIBS@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DEFAULT_APPLICATIONS_CAPPLET_CFLAGS = @DEFAULT_APPLICATIONS_CAPPLET_CFLAGS@ +DEFAULT_APPLICATIONS_CAPPLET_LIBS = @DEFAULT_APPLICATIONS_CAPPLET_LIBS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DISPLAY_CAPPLET_CFLAGS = @DISPLAY_CAPPLET_CFLAGS@ +DISPLAY_CAPPLET_LIBS = @DISPLAY_CAPPLET_LIBS@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOC_USER_FORMATS = @DOC_USER_FORMATS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTERNAL_LIBSLAB_CFLAGS = @EXTERNAL_LIBSLAB_CFLAGS@ +EXTERNAL_LIBSLAB_LIBS = @EXTERNAL_LIBSLAB_LIBS@ +FGREP = @FGREP@ +FONT_CAPPLET_CFLAGS = @FONT_CAPPLET_CFLAGS@ +FONT_CAPPLET_LIBS = @FONT_CAPPLET_LIBS@ +FONT_VIEWER_CFLAGS = @FONT_VIEWER_CFLAGS@ +FONT_VIEWER_LIBS = @FONT_VIEWER_LIBS@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GSD_DBUS_CFLAGS = @GSD_DBUS_CFLAGS@ +GSD_DBUS_LIBS = @GSD_DBUS_LIBS@ +GTK_ENGINE_DIR = @GTK_ENGINE_DIR@ +HELP_DIR = @HELP_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCANBERRA_GTK_CFLAGS = @LIBCANBERRA_GTK_CFLAGS@ +LIBCANBERRA_GTK_LIBS = @LIBCANBERRA_GTK_LIBS@ +LIBEBOOK_CFLAGS = @LIBEBOOK_CFLAGS@ +LIBEBOOK_LIBS = @LIBEBOOK_LIBS@ +LIBMATEKBDUI_CFLAGS = @LIBMATEKBDUI_CFLAGS@ +LIBMATEKBDUI_LIBS = @LIBMATEKBDUI_LIBS@ +LIBMATEKBD_CFLAGS = @LIBMATEKBD_CFLAGS@ +LIBMATEKBD_LIBS = @LIBMATEKBD_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSLAB_CFLAGS = @LIBSLAB_CFLAGS@ +LIBSLAB_LIBS = @LIBSLAB_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MARCO_CFLAGS = @MARCO_CFLAGS@ +MARCO_LIBS = @MARCO_LIBS@ +MATECC_CAPPLETS_CFLAGS = @MATECC_CAPPLETS_CFLAGS@ +MATECC_CAPPLETS_CLEANFILES = @MATECC_CAPPLETS_CLEANFILES@ +MATECC_CAPPLETS_EXTRA_DIST = @MATECC_CAPPLETS_EXTRA_DIST@ +MATECC_CAPPLETS_LIBS = @MATECC_CAPPLETS_LIBS@ +MATECC_CFLAGS = @MATECC_CFLAGS@ +MATECC_LIBS = @MATECC_LIBS@ +MATECC_SHELL_CFLAGS = @MATECC_SHELL_CFLAGS@ +MATECC_SHELL_LIBS = @MATECC_SHELL_LIBS@ +MATECONFTOOL = @MATECONFTOOL@ +MATECONF_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MATE_DESKTOP_CFLAGS = @MATE_DESKTOP_CFLAGS@ +MATE_DESKTOP_LIBS = @MATE_DESKTOP_LIBS@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SCREENSAVER_LIBS = @SCREENSAVER_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TYPING_BREAK = @TYPING_BREAK@ +TYPING_CFLAGS = @TYPING_CFLAGS@ +TYPING_LIBS = @TYPING_LIBS@ +UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XCURSOR_CFLAGS = @XCURSOR_CFLAGS@ +XCURSOR_LIBS = @XCURSOR_LIBS@ +XF86MISC_LIBS = @XF86MISC_LIBS@ +XGETTEXT = @XGETTEXT@ +XINPUT_CFLAGS = @XINPUT_CFLAGS@ +XINPUT_LIBS = @XINPUT_LIBS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +gtkbuilderdir = $(pkgdatadir)/ui +dist_gtkbuilder_DATA = appearance.ui +pixmapdir = $(pkgdatadir)/pixmaps +dist_pixmap_DATA = \ + subpixel-bgr.png \ + subpixel-rgb.png \ + subpixel-vbgr.png \ + subpixel-vrgb.png \ + theme-thumbnailing.png \ + gtk-theme-thumbnailing.png \ + window-theme-thumbnailing.png \ + icon-theme-thumbnailing.png \ + mouse-cursor-normal.png \ + mouse-cursor-normal-large.png \ + mouse-cursor-white.png \ + mouse-cursor-white-large.png + +cursorfontdir = $(datadir)/mate/cursor-fonts +dist_cursorfont_DATA = \ + cursor-large.pcf \ + cursor-white.pcf \ + cursor-large-white.pcf + +desktopdir = $(datadir)/applications +desktop_in_files = \ + mate-appearance-properties.desktop.in \ + mate-theme-installer.desktop.in + +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) +xml_in_files = \ + mate-theme-package.xml.in + +mimedir = $(datadir)/mime/packages +mime_DATA = $(xml_in_files:.xml.in=.xml) +EXTRA_DIST = $(xml_in_files) +CLEANFILES = $(desktop_in_files) $(desktop_DATA) $(mime_DATA) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu capplets/appearance/data/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu capplets/appearance/data/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +mate-appearance-properties.desktop.in: $(top_builddir)/config.status $(srcdir)/mate-appearance-properties.desktop.in.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +mate-theme-installer.desktop.in: $(top_builddir)/config.status $(srcdir)/mate-theme-installer.desktop.in.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-desktopDATA: $(desktop_DATA) + @$(NORMAL_INSTALL) + test -z "$(desktopdir)" || $(MKDIR_P) "$(DESTDIR)$(desktopdir)" + @list='$(desktop_DATA)'; test -n "$(desktopdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(desktopdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(desktopdir)" || exit $$?; \ + done + +uninstall-desktopDATA: + @$(NORMAL_UNINSTALL) + @list='$(desktop_DATA)'; test -n "$(desktopdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(desktopdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(desktopdir)" && rm -f $$files +install-dist_cursorfontDATA: $(dist_cursorfont_DATA) + @$(NORMAL_INSTALL) + test -z "$(cursorfontdir)" || $(MKDIR_P) "$(DESTDIR)$(cursorfontdir)" + @list='$(dist_cursorfont_DATA)'; test -n "$(cursorfontdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(cursorfontdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(cursorfontdir)" || exit $$?; \ + done + +uninstall-dist_cursorfontDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_cursorfont_DATA)'; test -n "$(cursorfontdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(cursorfontdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(cursorfontdir)" && rm -f $$files +install-dist_gtkbuilderDATA: $(dist_gtkbuilder_DATA) + @$(NORMAL_INSTALL) + test -z "$(gtkbuilderdir)" || $(MKDIR_P) "$(DESTDIR)$(gtkbuilderdir)" + @list='$(dist_gtkbuilder_DATA)'; test -n "$(gtkbuilderdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(gtkbuilderdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(gtkbuilderdir)" || exit $$?; \ + done + +uninstall-dist_gtkbuilderDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_gtkbuilder_DATA)'; test -n "$(gtkbuilderdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(gtkbuilderdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(gtkbuilderdir)" && rm -f $$files +install-dist_pixmapDATA: $(dist_pixmap_DATA) + @$(NORMAL_INSTALL) + test -z "$(pixmapdir)" || $(MKDIR_P) "$(DESTDIR)$(pixmapdir)" + @list='$(dist_pixmap_DATA)'; test -n "$(pixmapdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pixmapdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pixmapdir)" || exit $$?; \ + done + +uninstall-dist_pixmapDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_pixmap_DATA)'; test -n "$(pixmapdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pixmapdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pixmapdir)" && rm -f $$files +install-mimeDATA: $(mime_DATA) + @$(NORMAL_INSTALL) + test -z "$(mimedir)" || $(MKDIR_P) "$(DESTDIR)$(mimedir)" + @list='$(mime_DATA)'; test -n "$(mimedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mimedir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(mimedir)" || exit $$?; \ + done + +uninstall-mimeDATA: + @$(NORMAL_UNINSTALL) + @list='$(mime_DATA)'; test -n "$(mimedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(mimedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(mimedir)" && rm -f $$files +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(DATA) +installdirs: + for dir in "$(DESTDIR)$(desktopdir)" "$(DESTDIR)$(cursorfontdir)" "$(DESTDIR)$(gtkbuilderdir)" "$(DESTDIR)$(pixmapdir)" "$(DESTDIR)$(mimedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-desktopDATA install-dist_cursorfontDATA \ + install-dist_gtkbuilderDATA install-dist_pixmapDATA \ + install-mimeDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-desktopDATA uninstall-dist_cursorfontDATA \ + uninstall-dist_gtkbuilderDATA uninstall-dist_pixmapDATA \ + uninstall-mimeDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +.MAKE: install-am install-data-am install-strip uninstall-am + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-hook \ + install-desktopDATA install-dist_cursorfontDATA \ + install-dist_gtkbuilderDATA install-dist_pixmapDATA \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-mimeDATA install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-desktopDATA uninstall-dist_cursorfontDATA \ + uninstall-dist_gtkbuilderDATA uninstall-dist_pixmapDATA \ + uninstall-hook uninstall-mimeDATA + + +@INTLTOOL_DESKTOP_RULE@ + +@INTLTOOL_XML_RULE@ + +install-data-hook: +@ENABLE_UPDATE_MIMEDB_TRUE@ $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" + +uninstall-hook: +@ENABLE_UPDATE_MIMEDB_TRUE@ $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" + +-include $(top_srcdir)/git.mk + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/capplets/appearance/data/appearance.ui b/capplets/appearance/data/appearance.ui new file mode 100644 index 00000000..d97b2371 --- /dev/null +++ b/capplets/appearance/data/appearance.ui @@ -0,0 +1,2603 @@ + + + + + + 5 + Font Rendering Details + False + dialog + False + + + True + vertical + 2 + + + True + 5 + vertical + 18 + + + True + 0 + 0 + + + True + 12 + + + True + R_esolution: + True + dpi_spinner + + + False + False + 0 + + + + + True + 6 + + + True + True + 1 + + + False + 1 + + + + + True + dots per inch + + + False + False + 2 + + + + + False + False + 1 + + + + + + + False + False + 0 + + + + + True + vertical + + + True + 0 + Smoothing + + + + + + False + False + 0 + + + + + True + 6 + 12 + + + True + 2 + 2 + 12 + 6 + + + True + vertical + 3 + + + _None + True + True + False + True + True + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + GTK_FILL + + + + + True + vertical + 3 + + + Gra_yscale + True + True + False + True + True + antialias_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + 3 + + + Sub_pixel (LCDs) + True + True + False + True + True + antialias_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + + + + + + 1 + + + + + 1 + + + + + True + vertical + + + True + 0 + Hinting + + + + + + False + False + 0 + + + + + True + 6 + 12 + + + True + 2 + 2 + 12 + 6 + + + True + vertical + 3 + + + N_one + True + True + False + True + True + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + + + True + vertical + 3 + + + _Slight + True + True + False + True + True + hint_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + 3 + + + _Medium + True + True + False + True + True + hint_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + vertical + 3 + + + _Full + True + True + False + True + True + hint_none_radio + + + False + False + 0 + + + + + True + + + True + + + + + 1 + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + + + 1 + + + + + 2 + + + + + True + vertical + + + True + 0 + Subpixel Order + + + + + + False + False + 0 + + + + + True + 6 + 12 + + + True + 2 + 2 + 12 + 6 + + + True + + + _RGB + True + True + False + True + True + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + + + True + + + _BGR + True + True + False + True + True + subpixel_rgb_radio + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + + + _VRGB + True + True + False + True + True + subpixel_rgb_radio + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + 1 + 2 + GTK_FILL + + + + + True + + + VB_GR + True + True + False + True + True + subpixel_rgb_radio + + + 0 + + + + + True + gtk-missing-image + + + False + False + 1 + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + + + 1 + + + + + 3 + + + + + 1 + + + + + True + end + + + gtk-close + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + button3 + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Appearance Preferences + dialog + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + 1 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + browse + 138 + 3 + 18 + 18 + 18 + + + + + 0 + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + Save _As... + True + False + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + C_ustomize... + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 2 + + + + + _Install... + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 3 + + + + + False + 1 + + + + + True + + + Get more themes online + True + True + True + True + True + none + http://gnome-look.org/ + + + False + False + 0 + + + + + False + False + end + 2 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Theme + + + False + + + + + True + 12 + vertical + 6 + + + True + vertical + 6 + + + True + 6 + + + True + True + never + automatic + in + + + 1 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + browse + 3 + 3 + + + + + 0 + + + + + 0 + + + + + True + 24 + + + True + 12 + + + True + _Style: + True + wp_style_menu + + + False + False + 0 + + + + + True + wp_style_liststore + + + + 0 + + + + + False + False + 1 + + + + + False + False + 0 + + + + + True + 12 + + + True + C_olors: + True + wp_color_menu + + + False + False + 0 + + + + + True + 6 + + + True + wp_color_liststore + + + + 0 + + + + + False + False + 0 + + + + + True + True + True + #000000000000 + + + Open a dialog to specify the color + + + + + False + False + 1 + + + + + True + True + #000000000000 + + + Open a dialog to specify the color + + + + + False + False + 2 + + + + + 1 + + + + + False + False + 1 + + + + + False + False + 1 + + + + + 0 + + + + + True + + + False + False + 1 + + + + + True + + + Get more backgrounds online + True + True + True + True + none + http://gnome-look.org/ + + + False + False + 0 + + + + + True + 6 + True + end + + + _Add... + True + True + True + True + + + False + False + end + 1 + + + + + gtk-remove + True + True + True + True + + + False + False + end + 0 + + + + + False + False + end + 1 + + + + + False + False + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Background + + + 1 + False + + + + + True + 12 + vertical + 18 + + + True + 5 + 2 + 12 + 6 + + + True + True + False + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Document font: + True + document_font + + + 1 + 2 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + + True + 0 + Des_ktop font: + True + right + desktop_font + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + _Window title font: + True + right + window_title_font + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Fixed width font: + True + right + monospace_font + + + 4 + 5 + GTK_FILL + + + + + + True + True + False + True + + + 1 + 2 + + + + + + True + 0 + _Application font: + True + right + application_font + + + GTK_FILL + + + + + + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Rendering + + + + + + False + False + 0 + + + + + True + 12 + + + True + 2 + 2 + 12 + 6 + + + True + vertical + 6 + + + Sub_pixel smoothing (LCDs) + True + True + False + True + True + monochrome_radio + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + 1 + 2 + 1 + 2 + + + + + True + vertical + 6 + + + Best co_ntrast + True + True + False + True + True + monochrome_radio + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + 1 + 2 + + + + + True + vertical + 6 + + + Best _shapes + True + True + False + True + True + monochrome_radio + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + 1 + 2 + + + + + True + vertical + 6 + + + _Monochrome + True + True + False + True + True + + + False + False + 0 + + + + + True + + + True + + + + + False + 1 + + + + + + + + + False + 1 + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + D_etails... + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 2 + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Fonts + + + 2 + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + gtk-help + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + True + + + + + gtk-close + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + help_button + close_button + + + + + + + + + + Tile + + + Zoom + + + Center + + + Scale + + + Stretch + + + Span + + + + + + + + + + + Solid color + + + Horizontal gradient + + + Vertical gradient + + + + + + + + + + + Text below items + + + Text beside items + + + Icons only + + + Text only + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Customize Theme + center-on-parent + dialog + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + 300 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 0 + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Controls + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 18 + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + gtk-dialog-info + 5 + + + False + 0 + + + + + 280 + True + The current controls theme does not support color schemes. + True + + + False + False + 1 + + + + + False + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + 3 + 12 + 12 + + + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 2 + 3 + 4 + 5 + + + + + + + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + 4 + 5 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + _Tooltips: + True + tooltip_bg_color + + + 4 + 5 + + + + + True + True + False + + + 2 + 3 + 3 + 4 + + + + + + + True + True + False + + + 2 + 3 + 2 + 3 + + + + + + + True + True + False + + + 2 + 3 + 1 + 2 + + + + + + + True + 0 + _Selected items: + True + selected_bg_color + + + 3 + 4 + + + + + True + True + False + + + 1 + 2 + 3 + 4 + + + + + + + True + True + False + + + 1 + 2 + 2 + 3 + + + + + + + True + True + False + + + 1 + 2 + 1 + 2 + + + + + + + True + 0 + _Input boxes: + True + base_color + + + 2 + 3 + + + + + True + 0 + _Windows: + True + bg_color + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Text + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Background + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + _Reset to Defaults + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Colors + + + 1 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 1 + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Window Border + + + 2 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 1 + + + + + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Icons + + + 3 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 6 + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + 6 + + + True + gtk-dialog-info + 5 + + + False + 0 + + + + + 280 + True + Changing your cursor theme takes effect the next time you log in. + True + + + False + False + 1 + + + + + False + False + 0 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + _Size: + True + cursor_size_scale + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Small + True + + + False + 0 + + + + + 100 + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + False + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Large + True + + + False + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + end + + + gtk-delete + True + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + 2 + + + + + False + 2 + + + + + 4 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Pointer + + + 4 + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + gtk-help + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + gtk-close + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + theme_help_button + theme_close_button + + + + 6 + Save Theme As... + True + dialog + False + + + True + vertical + 2 + + + True + 6 + 3 + 2 + 12 + 6 + + + True + True + True + + + 1 + 2 + + + + + + True + 0 + _Name: + True + save_dialog_entry + + + GTK_FILL + GTK_FILL + + + + + True + True + never + automatic + in + + + True + True + word + False + + + + + 1 + 2 + 1 + 2 + + + + + True + 0 + 0 + _Description: + True + save_dialog_textview + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + Save _background image + True + True + False + True + True + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + + + + 1 + + + + + True + end + + + gtk-cancel + True + True + True + False + True + + + False + False + 0 + + + + + gtk-save + True + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + save_dialog_cancel_button + save_dialog_save_button + + + diff --git a/capplets/appearance/data/cursor-large-white.pcf b/capplets/appearance/data/cursor-large-white.pcf new file mode 100644 index 00000000..e1d7b631 Binary files /dev/null and b/capplets/appearance/data/cursor-large-white.pcf differ diff --git a/capplets/appearance/data/cursor-large.pcf b/capplets/appearance/data/cursor-large.pcf new file mode 100644 index 00000000..6580b33d Binary files /dev/null and b/capplets/appearance/data/cursor-large.pcf differ diff --git a/capplets/appearance/data/cursor-white.pcf b/capplets/appearance/data/cursor-white.pcf new file mode 100644 index 00000000..bc9932c3 Binary files /dev/null and b/capplets/appearance/data/cursor-white.pcf differ diff --git a/capplets/appearance/data/gtk-theme-thumbnailing.png b/capplets/appearance/data/gtk-theme-thumbnailing.png new file mode 100644 index 00000000..52065c7f Binary files /dev/null and b/capplets/appearance/data/gtk-theme-thumbnailing.png differ diff --git a/capplets/appearance/data/icon-theme-thumbnailing.png b/capplets/appearance/data/icon-theme-thumbnailing.png new file mode 100644 index 00000000..6ede83f2 Binary files /dev/null and b/capplets/appearance/data/icon-theme-thumbnailing.png differ diff --git a/capplets/appearance/data/mate-appearance-properties.desktop.in.in b/capplets/appearance/data/mate-appearance-properties.desktop.in.in new file mode 100644 index 00000000..f6362193 --- /dev/null +++ b/capplets/appearance/data/mate-appearance-properties.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Appearance +_Comment=Customize the look of the desktop +Exec=mate-appearance-properties %F +Icon=preferences-desktop-theme +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;DesktopSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Appearance +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/appearance/data/mate-theme-installer.desktop.in.in b/capplets/appearance/data/mate-theme-installer.desktop.in.in new file mode 100644 index 00000000..3bf6aab5 --- /dev/null +++ b/capplets/appearance/data/mate-theme-installer.desktop.in.in @@ -0,0 +1,16 @@ +[Desktop Entry] +_Name=Theme Installer +_Comment=Installs themes packages for various parts of the desktop +Exec=mate-appearance-properties -i %u +Icon=preferences-desktop-theme +Terminal=false +Type=Application +MimeType=application/x-mate-theme-package; +StartupNotify=true +Categories=MATE;GTK; +NoDisplay=true +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Appearance +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/appearance/data/mate-theme-package.xml.in b/capplets/appearance/data/mate-theme-package.xml.in new file mode 100644 index 00000000..58cbdf43 --- /dev/null +++ b/capplets/appearance/data/mate-theme-package.xml.in @@ -0,0 +1,9 @@ + + + + + <_comment>Mate Theme Package + + + + diff --git a/capplets/appearance/data/mouse-cursor-normal-large.png b/capplets/appearance/data/mouse-cursor-normal-large.png new file mode 100644 index 00000000..afef4ea8 Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-normal-large.png differ diff --git a/capplets/appearance/data/mouse-cursor-normal.png b/capplets/appearance/data/mouse-cursor-normal.png new file mode 100644 index 00000000..1d9351c8 Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-normal.png differ diff --git a/capplets/appearance/data/mouse-cursor-white-large.png b/capplets/appearance/data/mouse-cursor-white-large.png new file mode 100644 index 00000000..8f98438a Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-white-large.png differ diff --git a/capplets/appearance/data/mouse-cursor-white.png b/capplets/appearance/data/mouse-cursor-white.png new file mode 100644 index 00000000..2be63acd Binary files /dev/null and b/capplets/appearance/data/mouse-cursor-white.png differ diff --git a/capplets/appearance/data/subpixel-bgr.png b/capplets/appearance/data/subpixel-bgr.png new file mode 100644 index 00000000..7efd2624 Binary files /dev/null and b/capplets/appearance/data/subpixel-bgr.png differ diff --git a/capplets/appearance/data/subpixel-rgb.png b/capplets/appearance/data/subpixel-rgb.png new file mode 100644 index 00000000..58ac1eca Binary files /dev/null and b/capplets/appearance/data/subpixel-rgb.png differ diff --git a/capplets/appearance/data/subpixel-vbgr.png b/capplets/appearance/data/subpixel-vbgr.png new file mode 100644 index 00000000..abd8df01 Binary files /dev/null and b/capplets/appearance/data/subpixel-vbgr.png differ diff --git a/capplets/appearance/data/subpixel-vrgb.png b/capplets/appearance/data/subpixel-vrgb.png new file mode 100644 index 00000000..6e609060 Binary files /dev/null and b/capplets/appearance/data/subpixel-vrgb.png differ diff --git a/capplets/appearance/data/theme-thumbnailing.png b/capplets/appearance/data/theme-thumbnailing.png new file mode 100644 index 00000000..938edb0e Binary files /dev/null and b/capplets/appearance/data/theme-thumbnailing.png differ diff --git a/capplets/appearance/data/window-theme-thumbnailing.png b/capplets/appearance/data/window-theme-thumbnailing.png new file mode 100644 index 00000000..ce3bd1ab Binary files /dev/null and b/capplets/appearance/data/window-theme-thumbnailing.png differ diff --git a/capplets/appearance/mate-wp-info.c b/capplets/appearance/mate-wp-info.c new file mode 100644 index 00000000..5c799eab --- /dev/null +++ b/capplets/appearance/mate-wp-info.c @@ -0,0 +1,87 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include "mate-wp-info.h" + +MateWPInfo * mate_wp_info_new (const gchar * uri, + MateDesktopThumbnailFactory * thumbs) { + MateWPInfo *wp; + GFile *file; + GFileInfo *info; + + file = g_file_new_for_commandline_arg (uri); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_SIZE "," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (info == NULL || g_file_info_get_content_type (info) == NULL) { + if (!strcmp (uri, "(none)")) { + wp = g_new0 (MateWPInfo, 1); + + wp->mime_type = g_strdup ("image/x-no-data"); + wp->uri = g_strdup (uri); + wp->name = g_strdup (_("No Desktop Background")); + wp->size = 0; + } else { + wp = NULL; + } + } else { + wp = g_new0 (MateWPInfo, 1); + + wp->uri = g_strdup (uri); + + wp->name = g_strdup (g_file_info_get_name (info)); + if (g_file_info_get_content_type (info) != NULL) + wp->mime_type = g_strdup (g_file_info_get_content_type (info)); + wp->size = g_file_info_get_size (info); + wp->mtime = g_file_info_get_attribute_uint64 (info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + + wp->thumburi = mate_desktop_thumbnail_factory_lookup (thumbs, + uri, + wp->mtime); + } + + if (info != NULL) + g_object_unref (info); + + return wp; +} + +void mate_wp_info_free (MateWPInfo * info) { + if (info == NULL) { + return; + } + + g_free (info->uri); + g_free (info->thumburi); + g_free (info->name); + g_free (info->mime_type); +} diff --git a/capplets/appearance/mate-wp-info.h b/capplets/appearance/mate-wp-info.h new file mode 100644 index 00000000..95c94f89 --- /dev/null +++ b/capplets/appearance/mate-wp-info.h @@ -0,0 +1,45 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_WP_INFO_H_ +#define _MATE_WP_INFO_H_ + +#include +#include + +typedef struct _MateWPInfo MateWPInfo; + +struct _MateWPInfo { + gchar * uri; + gchar * thumburi; + gchar * name; + gchar * mime_type; + + goffset size; + + time_t mtime; +}; + +MateWPInfo * mate_wp_info_new (const gchar * uri, + MateDesktopThumbnailFactory * thumbs); +void mate_wp_info_free (MateWPInfo * info); + +#endif + diff --git a/capplets/appearance/mate-wp-item.c b/capplets/appearance/mate-wp-item.c new file mode 100644 index 00000000..de9623f1 --- /dev/null +++ b/capplets/appearance/mate-wp-item.c @@ -0,0 +1,307 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include +#include +#include +#include "mate-wp-item.h" + +static MateConfEnumStringPair options_lookup[] = { + { MATE_BG_PLACEMENT_CENTERED, "centered" }, + { MATE_BG_PLACEMENT_FILL_SCREEN, "stretched" }, + { MATE_BG_PLACEMENT_SCALED, "scaled" }, + { MATE_BG_PLACEMENT_ZOOMED, "zoom" }, + { MATE_BG_PLACEMENT_TILED, "wallpaper" }, + { MATE_BG_PLACEMENT_SPANNED, "spanned" }, + { 0, NULL } +}; + +static MateConfEnumStringPair shade_lookup[] = { + { MATE_BG_COLOR_SOLID, "solid" }, + { MATE_BG_COLOR_H_GRADIENT, "horizontal-gradient" }, + { MATE_BG_COLOR_V_GRADIENT, "vertical-gradient" }, + { 0, NULL } +}; + +const gchar *wp_item_option_to_string (MateBGPlacement type) +{ + return mateconf_enum_to_string (options_lookup, type); +} + +const gchar *wp_item_shading_to_string (MateBGColorType type) +{ + return mateconf_enum_to_string (shade_lookup, type); +} + +MateBGPlacement wp_item_string_to_option (const gchar *option) +{ + int i = MATE_BG_PLACEMENT_SCALED; + mateconf_string_to_enum (options_lookup, option, &i); + return i; +} + +MateBGColorType wp_item_string_to_shading (const gchar *shade_type) +{ + int i = MATE_BG_COLOR_SOLID; + mateconf_string_to_enum (shade_lookup, shade_type, &i); + return i; +} + +static void set_bg_properties (MateWPItem *item) +{ + if (item->filename) + mate_bg_set_filename (item->bg, item->filename); + + mate_bg_set_color (item->bg, item->shade_type, item->pcolor, item->scolor); + mate_bg_set_placement (item->bg, item->options); +} + +void mate_wp_item_ensure_mate_bg (MateWPItem *item) +{ + if (!item->bg) { + item->bg = mate_bg_new (); + + set_bg_properties (item); + } +} + +void mate_wp_item_update (MateWPItem *item) { + MateConfClient *client; + GdkColor color1 = { 0, 0, 0, 0 }, color2 = { 0, 0, 0, 0 }; + gchar *s; + + client = mateconf_client_get_default (); + + s = mateconf_client_get_string (client, WP_OPTIONS_KEY, NULL); + item->options = wp_item_string_to_option (s); + g_free (s); + + s = mateconf_client_get_string (client, WP_SHADING_KEY, NULL); + item->shade_type = wp_item_string_to_shading (s); + g_free (s); + + s = mateconf_client_get_string (client, WP_PCOLOR_KEY, NULL); + if (s != NULL) { + gdk_color_parse (s, &color1); + g_free (s); + } + + s = mateconf_client_get_string (client, WP_SCOLOR_KEY, NULL); + if (s != NULL) { + gdk_color_parse (s, &color2); + g_free (s); + } + + g_object_unref (client); + + if (item->pcolor != NULL) + gdk_color_free (item->pcolor); + + if (item->scolor != NULL) + gdk_color_free (item->scolor); + + item->pcolor = gdk_color_copy (&color1); + item->scolor = gdk_color_copy (&color2); +} + +MateWPItem * mate_wp_item_new (const gchar * filename, + GHashTable * wallpapers, + MateDesktopThumbnailFactory * thumbnails) { + MateWPItem *item = g_new0 (MateWPItem, 1); + + item->filename = g_strdup (filename); + item->fileinfo = mate_wp_info_new (filename, thumbnails); + + if (item->fileinfo != NULL && item->fileinfo->mime_type != NULL && + (g_str_has_prefix (item->fileinfo->mime_type, "image/") || + strcmp (item->fileinfo->mime_type, "application/xml") == 0)) { + + if (g_utf8_validate (item->fileinfo->name, -1, NULL)) + item->name = g_strdup (item->fileinfo->name); + else + item->name = g_filename_to_utf8 (item->fileinfo->name, -1, NULL, + NULL, NULL); + + mate_wp_item_update (item); + mate_wp_item_ensure_mate_bg (item); + mate_wp_item_update_description (item); + + g_hash_table_insert (wallpapers, item->filename, item); + } else { + mate_wp_item_free (item); + item = NULL; + } + + return item; +} + +void mate_wp_item_free (MateWPItem * item) { + if (item == NULL) { + return; + } + + g_free (item->name); + g_free (item->filename); + g_free (item->description); + + if (item->pcolor != NULL) + gdk_color_free (item->pcolor); + + if (item->scolor != NULL) + gdk_color_free (item->scolor); + + mate_wp_info_free (item->fileinfo); + if (item->bg) + g_object_unref (item->bg); + + gtk_tree_row_reference_free (item->rowref); + + g_free (item); +} + +static GdkPixbuf * +add_slideshow_frame (GdkPixbuf *pixbuf) +{ + GdkPixbuf *sheet, *sheet2; + GdkPixbuf *tmp; + gint w, h; + + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + + sheet = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w, h); + gdk_pixbuf_fill (sheet, 0x00000000); + sheet2 = gdk_pixbuf_new_subpixbuf (sheet, 1, 1, w - 2, h - 2); + gdk_pixbuf_fill (sheet2, 0xffffffff); + g_object_unref (sheet2); + + tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, w + 6, h + 6); + + gdk_pixbuf_fill (tmp, 0x00000000); + gdk_pixbuf_composite (sheet, tmp, 6, 6, w, h, 6.0, 6.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + gdk_pixbuf_composite (sheet, tmp, 3, 3, w, h, 3.0, 3.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + gdk_pixbuf_composite (pixbuf, tmp, 0, 0, w, h, 0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255); + + g_object_unref (sheet); + + return tmp; +} + +GdkPixbuf * mate_wp_item_get_frame_thumbnail (MateWPItem * item, + MateDesktopThumbnailFactory * thumbs, + int width, + int height, + gint frame) { + GdkPixbuf *pixbuf = NULL; + + set_bg_properties (item); + + if (frame != -1) + pixbuf = mate_bg_create_frame_thumbnail (item->bg, thumbs, gdk_screen_get_default (), width, height, frame); + else + pixbuf = mate_bg_create_thumbnail (item->bg, thumbs, gdk_screen_get_default(), width, height); + + if (pixbuf && mate_bg_changes_with_time (item->bg)) + { + GdkPixbuf *tmp; + + tmp = add_slideshow_frame (pixbuf); + g_object_unref (pixbuf); + pixbuf = tmp; + } + + mate_bg_get_image_size (item->bg, thumbs, width, height, &item->width, &item->height); + + return pixbuf; +} + + +GdkPixbuf * mate_wp_item_get_thumbnail (MateWPItem * item, + MateDesktopThumbnailFactory * thumbs, + gint width, + gint height) { + return mate_wp_item_get_frame_thumbnail (item, thumbs, width, height, -1); +} + +void mate_wp_item_update_description (MateWPItem * item) { + g_free (item->description); + + if (!strcmp (item->filename, "(none)")) { + item->description = g_strdup (item->name); + } else { + const gchar *description; + gchar *size; + gchar *dirname = g_path_get_dirname (item->filename); + + description = NULL; + size = NULL; + + if (strcmp (item->fileinfo->mime_type, "application/xml") == 0) + { + if (mate_bg_changes_with_time (item->bg)) + description = _("Slide Show"); + else if (item->width > 0 && item->height > 0) + description = _("Image"); + } + else + description = g_content_type_get_description (item->fileinfo->mime_type); + + if (mate_bg_has_multiple_sizes (item->bg)) + size = g_strdup (_("multiple sizes")); + else if (item->width > 0 && item->height > 0) { + /* translators: x pixel(s) by y pixel(s) */ + size = g_strdup_printf (_("%d %s by %d %s"), + item->width, + ngettext ("pixel", "pixels", item->width), + item->height, + ngettext ("pixel", "pixels", item->height)); + } + + if (description && size) { + /* translators: wallpaper name + * mime type, size + * Folder: /path/to/file + */ + item->description = g_markup_printf_escaped (_("%s\n" + "%s, %s\n" + "Folder: %s"), + item->name, + description, + size, + dirname); + } else { + /* translators: wallpaper name + * Image missing + * Folder: /path/to/file + */ + item->description = g_markup_printf_escaped (_("%s\n" + "%s\n" + "Folder: %s"), + item->name, + _("Image missing"), + dirname); + } + + g_free (size); + g_free (dirname); + } +} diff --git a/capplets/appearance/mate-wp-item.h b/capplets/appearance/mate-wp-item.h new file mode 100644 index 00000000..1dd726a3 --- /dev/null +++ b/capplets/appearance/mate-wp-item.h @@ -0,0 +1,91 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "mate-wp-info.h" + +#ifndef _MATE_WP_ITEM_H_ +#define _MATE_WP_ITEM_H_ + +#define WP_PATH_KEY "/desktop/mate/background" +#define WP_FILE_KEY WP_PATH_KEY "/picture_filename" +#define WP_OPTIONS_KEY WP_PATH_KEY "/picture_options" +#define WP_SHADING_KEY WP_PATH_KEY "/color_shading_type" +#define WP_PCOLOR_KEY WP_PATH_KEY "/primary_color" +#define WP_SCOLOR_KEY WP_PATH_KEY "/secondary_color" + +typedef struct _MateWPItem MateWPItem; + +struct _MateWPItem { + MateBG *bg; + + gchar * name; + gchar * filename; + gchar * description; + MateBGPlacement options; + MateBGColorType shade_type; + + /* Where the Item is in the List */ + GtkTreeRowReference * rowref; + + /* Real colors */ + GdkColor * pcolor; + GdkColor * scolor; + + MateWPInfo * fileinfo; + + /* Did the user remove us? */ + gboolean deleted; + + /* Width and Height of the original image */ + gint width; + gint height; +}; + +MateWPItem * mate_wp_item_new (const gchar *filename, + GHashTable *wallpapers, + MateDesktopThumbnailFactory *thumbnails); + +void mate_wp_item_free (MateWPItem *item); +GdkPixbuf * mate_wp_item_get_thumbnail (MateWPItem *item, + MateDesktopThumbnailFactory *thumbs, + gint width, + gint height); +GdkPixbuf * mate_wp_item_get_frame_thumbnail (MateWPItem *item, + MateDesktopThumbnailFactory *thumbs, + gint width, + gint height, + gint frame); +void mate_wp_item_update (MateWPItem *item); +void mate_wp_item_update_description (MateWPItem *item); +void mate_wp_item_ensure_mate_bg (MateWPItem *item); + +const gchar *wp_item_option_to_string (MateBGPlacement type); +const gchar *wp_item_shading_to_string (MateBGColorType type); +MateBGPlacement wp_item_string_to_option (const gchar *option); +MateBGColorType wp_item_string_to_shading (const gchar *shade_type); + +#endif diff --git a/capplets/appearance/mate-wp-xml.c b/capplets/appearance/mate-wp-xml.c new file mode 100644 index 00000000..2157acf7 --- /dev/null +++ b/capplets/appearance/mate-wp-xml.c @@ -0,0 +1,451 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include "appearance.h" +#include "mate-wp-item.h" +#include +#include +#include + +static gboolean mate_wp_xml_get_bool (const xmlNode * parent, + const gchar * prop_name) { + xmlChar * prop; + gboolean ret_val = FALSE; + + g_return_val_if_fail (parent != NULL, FALSE); + g_return_val_if_fail (prop_name != NULL, FALSE); + + prop = xmlGetProp ((xmlNode *) parent, (xmlChar*)prop_name); + if (prop != NULL) { + if (!g_ascii_strcasecmp ((gchar *)prop, "true") || !g_ascii_strcasecmp ((gchar *)prop, "1")) { + ret_val = TRUE; + } else { + ret_val = FALSE; + } + g_free (prop); + } + + return ret_val; +} + +static void mate_wp_xml_set_bool (const xmlNode * parent, + const xmlChar * prop_name, gboolean value) { + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + if (value) { + xmlSetProp ((xmlNode *) parent, prop_name, (xmlChar *)"true"); + } else { + xmlSetProp ((xmlNode *) parent, prop_name, (xmlChar *)"false"); + } +} + +static void mate_wp_load_legacy (AppearanceData *data) { + FILE * fp; + gchar * foo, * filename; + + filename = g_build_filename (g_get_home_dir (), ".mate2", + "wallpapers.list", NULL); + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + if ((fp = fopen (filename, "r")) != NULL) { + foo = (gchar *) g_malloc (sizeof (gchar) * 4096); + while (fgets (foo, 4096, fp)) { + MateWPItem * item; + + if (foo[strlen (foo) - 1] == '\n') { + foo[strlen (foo) - 1] = '\0'; + } + + item = g_hash_table_lookup (data->wp_hash, foo); + if (item != NULL) { + continue; + } + + if (!g_file_test (foo, G_FILE_TEST_EXISTS)) { + continue; + } + + item = mate_wp_item_new (foo, data->wp_hash, data->thumb_factory); + if (item != NULL && item->fileinfo == NULL) { + mate_wp_item_free (item); + } + } + fclose (fp); + g_free (foo); + } + } + + g_free (filename); +} + +static void mate_wp_xml_load_xml (AppearanceData *data, + const gchar * filename) { + xmlDoc * wplist; + xmlNode * root, * list, * wpa; + xmlChar * nodelang; + const gchar * const * syslangs; + GdkColor color1, color2; + gint i; + + wplist = xmlParseFile (filename); + + if (!wplist) + return; + + syslangs = g_get_language_names (); + + root = xmlDocGetRootElement (wplist); + + for (list = root->children; list != NULL; list = list->next) { + if (!strcmp ((gchar *)list->name, "wallpaper")) { + MateWPItem * wp; + gchar *pcolor = NULL, *scolor = NULL; + gchar *s; + gboolean have_scale = FALSE, have_shade = FALSE; + + wp = g_new0 (MateWPItem, 1); + + wp->deleted = mate_wp_xml_get_bool (list, "deleted"); + + for (wpa = list->children; wpa != NULL; wpa = wpa->next) { + if (wpa->type == XML_COMMENT_NODE) { + continue; + } else if (!strcmp ((gchar *)wpa->name, "filename")) { + if (wpa->last != NULL && wpa->last->content != NULL) { + const char * none = "(none)"; + gchar *content = g_strstrip ((gchar *)wpa->last->content); + + if (!strcmp (content, none)) + wp->filename = g_strdup (content); + else if (g_utf8_validate (content, -1, NULL) && + g_file_test (content, G_FILE_TEST_EXISTS)) + wp->filename = g_strdup (content); + else + wp->filename = g_filename_from_utf8 (content, -1, NULL, NULL, NULL); + } else { + break; + } + } else if (!strcmp ((gchar *)wpa->name, "name")) { + if (wpa->last != NULL && wpa->last->content != NULL) { + nodelang = xmlNodeGetLang (wpa->last); + + if (wp->name == NULL && nodelang == NULL) { + wp->name = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + } else { + for (i = 0; syslangs[i] != NULL; i++) { + if (!strcmp (syslangs[i], (gchar *)nodelang)) { + g_free (wp->name); + wp->name = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + break; + } + } + } + + xmlFree (nodelang); + } else { + break; + } + } else if (!strcmp ((gchar *)wpa->name, "options")) { + if (wpa->last != NULL) { + wp->options = wp_item_string_to_option (g_strstrip ((gchar *)wpa->last->content)); + have_scale = TRUE; + } + } else if (!strcmp ((gchar *)wpa->name, "shade_type")) { + if (wpa->last != NULL) { + wp->shade_type = wp_item_string_to_shading (g_strstrip ((gchar *)wpa->last->content)); + have_shade = TRUE; + } + } else if (!strcmp ((gchar *)wpa->name, "pcolor")) { + if (wpa->last != NULL) { + pcolor = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + } + } else if (!strcmp ((gchar *)wpa->name, "scolor")) { + if (wpa->last != NULL) { + scolor = g_strdup (g_strstrip ((gchar *)wpa->last->content)); + } + } else if (!strcmp ((gchar *)wpa->name, "text")) { + /* Do nothing here, libxml2 is being weird */ + } else { + g_warning ("Unknown Tag: %s", wpa->name); + } + } + + /* Make sure we don't already have this one and that filename exists */ + if (wp->filename == NULL || + g_hash_table_lookup (data->wp_hash, wp->filename) != NULL) { + + mate_wp_item_free (wp); + g_free (pcolor); + g_free (scolor); + continue; + } + + /* Verify the colors and alloc some GdkColors here */ + if (!have_scale) { + s = mateconf_client_get_string (data->client, WP_OPTIONS_KEY, NULL); + wp->options = wp_item_string_to_option (s); + g_free (s); + } + + if (!have_shade) { + s = mateconf_client_get_string (data->client, WP_SHADING_KEY, NULL); + wp->shade_type = wp_item_string_to_shading (s); + g_free (s); + } + + if (pcolor == NULL) { + pcolor = mateconf_client_get_string (data->client, + WP_PCOLOR_KEY, NULL); + } + if (scolor == NULL) { + scolor = mateconf_client_get_string (data->client, + WP_SCOLOR_KEY, NULL); + } + gdk_color_parse (pcolor, &color1); + gdk_color_parse (scolor, &color2); + g_free (pcolor); + g_free (scolor); + + wp->pcolor = gdk_color_copy (&color1); + wp->scolor = gdk_color_copy (&color2); + + if ((wp->filename != NULL && + g_file_test (wp->filename, G_FILE_TEST_EXISTS)) || + !strcmp (wp->filename, "(none)")) { + wp->fileinfo = mate_wp_info_new (wp->filename, data->thumb_factory); + + if (wp->name == NULL || !strcmp (wp->filename, "(none)")) { + g_free (wp->name); + wp->name = g_strdup (wp->fileinfo->name); + } + + mate_wp_item_ensure_mate_bg (wp); + mate_wp_item_update_description (wp); + g_hash_table_insert (data->wp_hash, wp->filename, wp); + } else { + mate_wp_item_free (wp); + wp = NULL; + } + } + } + xmlFreeDoc (wplist); +} + +static void mate_wp_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + AppearanceData *data) { + gchar * filename; + + switch (event_type) { + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CREATED: + filename = g_file_get_path (file); + mate_wp_xml_load_xml (data, filename); + g_free (filename); + break; + default: + break; + } +} + +static void mate_wp_xml_add_monitor (GFile *directory, + AppearanceData *data) { + GFileMonitor *monitor; + GError *error = NULL; + + monitor = g_file_monitor_directory (directory, + G_FILE_MONITOR_NONE, + NULL, + &error); + if (error != NULL) { + gchar *path; + + path = g_file_get_parse_name (directory); + g_warning ("Unable to monitor directory %s: %s", + path, error->message); + g_error_free (error); + g_free (path); + return; + } + + g_signal_connect (monitor, "changed", + G_CALLBACK (mate_wp_file_changed), + data); +} + +static void mate_wp_xml_load_from_dir (const gchar *path, + AppearanceData *data) { + GFile *directory; + GFileEnumerator *enumerator; + GError *error = NULL; + GFileInfo *info; + + if (!g_file_test (path, G_FILE_TEST_IS_DIR)) { + return; + } + + directory = g_file_new_for_path (path); + enumerator = g_file_enumerate_children (directory, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + &error); + if (error != NULL) { + g_warning ("Unable to check directory %s: %s", path, error->message); + g_error_free (error); + g_object_unref (directory); + return; + } + + while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { + const gchar *filename; + gchar *fullpath; + + filename = g_file_info_get_name (info); + fullpath = g_build_filename (path, filename, NULL); + g_object_unref (info); + + mate_wp_xml_load_xml (data, fullpath); + g_free (fullpath); + } + g_file_enumerator_close (enumerator, NULL, NULL); + + mate_wp_xml_add_monitor (directory, data); + + g_object_unref (directory); +} + +void mate_wp_xml_load_list (AppearanceData *data) { + const char * const *system_data_dirs; + gchar * datadir; + gchar * wpdbfile; + gint i; + + wpdbfile = g_build_filename (g_get_home_dir (), + ".mate2", + "backgrounds.xml", + NULL); + + if (g_file_test (wpdbfile, G_FILE_TEST_EXISTS)) { + mate_wp_xml_load_xml (data, wpdbfile); + } else { + g_free (wpdbfile); + wpdbfile = g_build_filename (g_get_home_dir (), + ".mate2", + "wp-list.xml", + NULL); + if (g_file_test (wpdbfile, G_FILE_TEST_EXISTS)) { + mate_wp_xml_load_xml (data, wpdbfile); + } + } + g_free (wpdbfile); + + datadir = g_build_filename (g_get_user_data_dir (), + "mate-background-properties", + NULL); + mate_wp_xml_load_from_dir (datadir, data); + g_free (datadir); + + system_data_dirs = g_get_system_data_dirs (); + for (i = 0; system_data_dirs[i]; i++) { + datadir = g_build_filename (system_data_dirs[i], + "mate-background-properties", + NULL); + mate_wp_xml_load_from_dir (datadir, data); + g_free (datadir); + } + + mate_wp_xml_load_from_dir (WALLPAPER_DATADIR, data); + + mate_wp_load_legacy (data); +} + +static void mate_wp_list_flatten (const gchar * key, MateWPItem * item, + GSList ** list) { + g_return_if_fail (key != NULL); + g_return_if_fail (item != NULL); + + *list = g_slist_prepend (*list, item); +} + +void mate_wp_xml_save_list (AppearanceData *data) { + xmlDoc * wplist; + xmlNode * root, * wallpaper, * item; + GSList * list = NULL; + gchar * wpfile; + + g_hash_table_foreach (data->wp_hash, + (GHFunc) mate_wp_list_flatten, &list); + g_hash_table_destroy (data->wp_hash); + list = g_slist_reverse (list); + + wpfile = g_build_filename (g_get_home_dir (), + "/.mate2", + "backgrounds.xml", + NULL); + + xmlKeepBlanksDefault (0); + + wplist = xmlNewDoc ((xmlChar *)"1.0"); + xmlCreateIntSubset (wplist, (xmlChar *)"wallpapers", NULL, (xmlChar *)"mate-wp-list.dtd"); + root = xmlNewNode (NULL, (xmlChar *)"wallpapers"); + xmlDocSetRootElement (wplist, root); + + while (list != NULL) { + MateWPItem * wpitem = list->data; + const char * none = "(none)"; + gchar * filename; + const gchar * scale, * shade; + gchar * pcolor, * scolor; + + if (!strcmp (wpitem->filename, none) || + (g_utf8_validate (wpitem->filename, -1, NULL) && + g_file_test (wpitem->filename, G_FILE_TEST_EXISTS))) + filename = g_strdup (wpitem->filename); + else + filename = g_filename_to_utf8 (wpitem->filename, -1, NULL, NULL, NULL); + + pcolor = gdk_color_to_string (wpitem->pcolor); + scolor = gdk_color_to_string (wpitem->scolor); + scale = wp_item_option_to_string (wpitem->options); + shade = wp_item_shading_to_string (wpitem->shade_type); + + wallpaper = xmlNewChild (root, NULL, (xmlChar *)"wallpaper", NULL); + mate_wp_xml_set_bool (wallpaper, (xmlChar *)"deleted", wpitem->deleted); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"name", (xmlChar *)wpitem->name); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"filename", (xmlChar *)filename); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"options", (xmlChar *)scale); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"shade_type", (xmlChar *)shade); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"pcolor", (xmlChar *)pcolor); + item = xmlNewTextChild (wallpaper, NULL, (xmlChar *)"scolor", (xmlChar *)scolor); + g_free (pcolor); + g_free (scolor); + g_free (filename); + + list = g_slist_delete_link (list, list); + mate_wp_item_free (wpitem); + } + xmlSaveFormatFile (wpfile, wplist, 1); + xmlFreeDoc (wplist); + g_free (wpfile); +} diff --git a/capplets/appearance/mate-wp-xml.h b/capplets/appearance/mate-wp-xml.h new file mode 100644 index 00000000..795487ff --- /dev/null +++ b/capplets/appearance/mate-wp-xml.h @@ -0,0 +1,28 @@ +/* + * Authors: Rodney Dawes + * + * Copyright 2003-2006 Novell, Inc. (www.novell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_WP_XML_H_ +#define _MATE_WP_XML_H_ + +void mate_wp_xml_load_list (AppearanceData *data); +void mate_wp_xml_save_list (AppearanceData *data); + +#endif + diff --git a/capplets/appearance/theme-installer.c b/capplets/appearance/theme-installer.c new file mode 100644 index 00000000..fec1f001 --- /dev/null +++ b/capplets/appearance/theme-installer.c @@ -0,0 +1,801 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include +#include +#include +#include + +#include "capplet-util.h" +#include "file-transfer-dialog.h" +#include "theme-installer.h" +#include "theme-util.h" + +enum { + THEME_INVALID, + THEME_ICON, + THEME_MATE, + THEME_GTK, + THEME_ENGINE, + THEME_MARCO, + THEME_CURSOR, + THEME_ICON_CURSOR +}; + +enum { + TARGZ, + TARBZ, + DIRECTORY +}; + +static gboolean +cleanup_tmp_dir (GIOSchedulerJob *job, + GCancellable *cancellable, + const gchar *tmp_dir) +{ + GFile *directory; + + directory = g_file_new_for_path (tmp_dir); + capplet_file_delete_recursive (directory, NULL); + g_object_unref (directory); + + return FALSE; +} + +static int +file_theme_type (const gchar *dir) +{ + gchar *filename = NULL; + gboolean exists; + + if (!dir) + return THEME_INVALID; + + filename = g_build_filename (dir, "index.theme", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + + if (exists) { + GPatternSpec *pattern; + gchar *file_contents = NULL; + gsize file_size; + gboolean match; + + g_file_get_contents (filename, &file_contents, &file_size, NULL); + g_free (filename); + + pattern = g_pattern_spec_new ("*[Icon Theme]*"); + match = g_pattern_match_string (pattern, file_contents); + g_pattern_spec_free (pattern); + + if (match) { + pattern = g_pattern_spec_new ("*Directories=*"); + match = g_pattern_match_string (pattern, file_contents); + g_pattern_spec_free (pattern); + g_free (file_contents); + + if (match) { + /* check if we have a cursor, too */ + filename = g_build_filename (dir, "cursors", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_DIR); + g_free (filename); + + if (exists) + return THEME_ICON_CURSOR; + else + return THEME_ICON; + } + return THEME_CURSOR; + } + + pattern = g_pattern_spec_new ("*[X-GNOME-Metatheme]*"); + match = g_pattern_match_string (pattern, file_contents); + g_pattern_spec_free (pattern); + g_free (file_contents); + + if (match) + return THEME_MATE; + } else { + g_free (filename); + } + + filename = g_build_filename (dir, "gtk-2.0", "gtkrc", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + g_free (filename); + + if (exists) + return THEME_GTK; + + filename = g_build_filename (dir, "metacity-1", "metacity-theme-1.xml", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + g_free (filename); + + if (exists) + return THEME_MARCO; + + /* cursor themes don't necessarily have an index.theme */ + filename = g_build_filename (dir, "cursors", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_DIR); + g_free (filename); + + if (exists) + return THEME_CURSOR; + + filename = g_build_filename (dir, "configure", NULL); + exists = g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE); + g_free (filename); + + if (exists) + return THEME_ENGINE; + + return THEME_INVALID; +} + +static void +transfer_cancel_cb (GtkWidget *dialog, + gchar *path) +{ + GFile *todelete; + + todelete = g_file_new_for_path (path); + capplet_file_delete_recursive (todelete, NULL); + + g_object_unref (todelete); + g_free (path); + gtk_widget_destroy (dialog); +} + +static void +missing_utility_message_dialog (GtkWindow *parent, + const gchar *utility) +{ + GtkWidget *dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot install theme")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The %s utility is not installed."), utility); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/* this works around problems when doing fork/exec in a threaded app + * with some locks being held/waited on in different threads. + * + * we do the idle callback so that the async xfer has finished and + * cleaned up its vfs job. otherwise it seems the slave thread gets + * woken up and it removes itself from the job queue before it is + * supposed to. very strange. + * + * see bugzilla.gnome.org #86141 for details + */ +static gboolean +process_local_theme_tgz_tbz (GtkWindow *parent, + const gchar *util, + const gchar *tmp_dir, + const gchar *archive) +{ + gboolean rc; + int status; + gchar *command, *filename, *zip, *tar; + + if (!(zip = g_find_program_in_path (util))) { + missing_utility_message_dialog (parent, util); + return FALSE; + } + if (!(tar = g_find_program_in_path ("tar"))) { + missing_utility_message_dialog (parent, "tar"); + g_free (zip); + return FALSE; + } + + filename = g_shell_quote (archive); + + /* this should be something more clever and nonblocking */ + command = g_strdup_printf ("sh -c 'cd \"%s\"; %s -d -c < \"%s\" | %s xf - '", + tmp_dir, zip, filename, tar); + g_free (zip); + g_free (tar); + g_free (filename); + + rc = (g_spawn_command_line_sync (command, NULL, NULL, &status, NULL) && status == 0); + g_free (command); + + if (rc == FALSE) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot install theme")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("There was a problem while extracting the theme.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + + return rc; +} + +static gboolean +process_local_theme_archive (GtkWindow *parent, + gint filetype, + const gchar *tmp_dir, + const gchar *archive) +{ + if (filetype == TARGZ) + return process_local_theme_tgz_tbz (parent, "gzip", tmp_dir, archive); + else if (filetype == TARBZ) + return process_local_theme_tgz_tbz (parent, "bzip2", tmp_dir, archive); + else + return FALSE; +} + +static void +invalid_theme_dialog (GtkWindow *parent, + const gchar *filename, + gboolean maybe_theme_engine) +{ + GtkWidget *dialog; + const gchar *primary = _("There was an error installing the selected file"); + const gchar *secondary = _("\"%s\" does not appear to be a valid theme."); + const gchar *engine = _("\"%s\" does not appear to be a valid theme. It may be a theme engine which you need to compile."); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", primary); + if (maybe_theme_engine) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), engine, filename); + else + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), secondary, filename); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static gboolean +mate_theme_install_real (GtkWindow *parent, + gint filetype, + const gchar *tmp_dir, + const gchar *theme_name, + gboolean ask_user) +{ + gboolean success = TRUE; + GtkWidget *dialog, *apply_button; + GFile *theme_source_dir, *theme_dest_dir; + GError *error = NULL; + gint theme_type; + gchar *target_dir = NULL; + + /* What type of theme is it? */ + theme_type = file_theme_type (tmp_dir); + switch (theme_type) { + case THEME_ICON: + case THEME_CURSOR: + case THEME_ICON_CURSOR: + target_dir = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), ".icons", + theme_name, NULL); + break; + case THEME_MATE: + target_dir = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), ".themes", + theme_name, NULL); + break; + case THEME_MARCO: + case THEME_GTK: + target_dir = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), ".themes", + theme_name, NULL); + break; + case THEME_ENGINE: + invalid_theme_dialog (parent, theme_name, TRUE); + return FALSE; + default: + invalid_theme_dialog (parent, theme_name, FALSE); + return FALSE; + } + + /* see if there is an icon theme lurking in this package */ + if (theme_type == THEME_MATE) { + gchar *path; + + path = g_build_path (G_DIR_SEPARATOR_S, + tmp_dir, "icons", NULL); + if (g_file_test (path, G_FILE_TEST_IS_DIR) + && (file_theme_type (path) == THEME_ICON)) { + gchar *new_path, *update_icon_cache; + GFile *new_file; + GFile *src_file; + + src_file = g_file_new_for_path (path); + new_path = g_build_path (G_DIR_SEPARATOR_S, + g_get_home_dir (), + ".icons", + theme_name, NULL); + new_file = g_file_new_for_path (new_path); + + if (!g_file_move (src_file, new_file, G_FILE_COPY_NONE, + NULL, NULL, NULL, &error)) { + g_warning ("Error while moving from `%s' to `%s': %s", + path, new_path, error->message); + g_error_free (error); + error = NULL; + } + g_object_unref (new_file); + g_object_unref (src_file); + + /* update icon cache - shouldn't really matter if this fails */ + update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", new_path); + g_spawn_command_line_async (update_icon_cache, NULL); + g_free (update_icon_cache); + + g_free (new_path); + } + g_free (path); + } + + /* Move the dir to the target dir */ + theme_source_dir = g_file_new_for_path (tmp_dir); + theme_dest_dir = g_file_new_for_path (target_dir); + + if (!g_file_move (theme_source_dir, theme_dest_dir, + G_FILE_COPY_OVERWRITE, NULL, NULL, + NULL, &error)) { + gchar *str; + + str = g_strdup_printf (_("Installation for theme \"%s\" failed."), theme_name); + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + str); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + + g_free (str); + g_error_free (error); + error = NULL; + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + success = FALSE; + } else { + if (theme_type == THEME_ICON || theme_type == THEME_ICON_CURSOR) + { + gchar *update_icon_cache; + + /* update icon cache - shouldn't really matter if this fails */ + update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", target_dir); + g_spawn_command_line_async (update_icon_cache, NULL); + + g_free (update_icon_cache); + } + + if (ask_user) { + /* Ask to apply theme (if we can) */ + if (theme_type == THEME_GTK + || theme_type == THEME_MARCO + || theme_type == THEME_ICON + || theme_type == THEME_CURSOR + || theme_type == THEME_ICON_CURSOR) { + /* TODO: currently cannot apply "mate themes" */ + gchar *str; + + str = g_strdup_printf (_("The theme \"%s\" has been installed."), theme_name); + dialog = gtk_message_dialog_new_with_markup (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_NONE, + "%s", + str); + g_free (str); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Would you like to apply it now, or keep your current theme?")); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("Keep Current Theme"), + GTK_RESPONSE_CLOSE); + + apply_button = gtk_button_new_with_label (_("Apply New Theme")); + gtk_button_set_image (GTK_BUTTON (apply_button), + gtk_image_new_from_stock (GTK_STOCK_APPLY, + GTK_ICON_SIZE_BUTTON)); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), apply_button, GTK_RESPONSE_APPLY); + gtk_widget_set_can_default (apply_button, TRUE); + gtk_widget_show (apply_button); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) { + /* apply theme here! */ + MateConfClient *mateconf_client; + + mateconf_client = mateconf_client_get_default (); + + switch (theme_type) { + case THEME_GTK: + mateconf_client_set_string (mateconf_client, GTK_THEME_KEY, theme_name, NULL); + break; + case THEME_MARCO: + mateconf_client_set_string (mateconf_client, MARCO_THEME_KEY, theme_name, NULL); + break; + case THEME_ICON: + mateconf_client_set_string (mateconf_client, ICON_THEME_KEY, theme_name, NULL); + break; + case THEME_CURSOR: + mateconf_client_set_string (mateconf_client, CURSOR_THEME_KEY, theme_name, NULL); + break; + case THEME_ICON_CURSOR: + mateconf_client_set_string (mateconf_client, ICON_THEME_KEY, theme_name, NULL); + mateconf_client_set_string (mateconf_client, CURSOR_THEME_KEY, theme_name, NULL); + break; + default: + break; + } + + g_object_unref (mateconf_client); + } + } else { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("MATE Theme %s correctly installed"), + theme_name); + gtk_dialog_run (GTK_DIALOG (dialog)); + } + gtk_widget_destroy (dialog); + } + } + + g_free (target_dir); + + return success; +} + +static void +process_local_theme (GtkWindow *parent, + const char *path) +{ + GtkWidget *dialog; + gint filetype; + + if (g_str_has_suffix (path, ".tar.gz") + || g_str_has_suffix (path, ".tgz") + || g_str_has_suffix(path, ".gtp")) { + filetype = TARGZ; + } else if (g_str_has_suffix (path, ".tar.bz2")) { + filetype = TARBZ; + } else if (g_file_test (path, G_FILE_TEST_IS_DIR)) { + filetype = DIRECTORY; + } else { + gchar *filename; + filename = g_path_get_basename (path); + invalid_theme_dialog (parent, filename, FALSE); + g_free (filename); + return; + } + + if (filetype == DIRECTORY) { + gchar *name = g_path_get_basename (path); + mate_theme_install_real (parent, + filetype, + path, + name, + TRUE); + g_free (name); + } else { + /* Create a temp directory and uncompress file there */ + GDir *dir; + const gchar *name; + gchar *tmp_dir; + gboolean ok; + gint n_themes; + GFile *todelete; + + tmp_dir = g_strdup_printf ("%s/.themes/.theme-%u", + g_get_home_dir (), + g_random_int ()); + + if ((g_mkdir (tmp_dir, 0700)) != 0) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Failed to create temporary directory")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (tmp_dir); + return; + } + + if (!process_local_theme_archive (parent, filetype, tmp_dir, path) + || ((dir = g_dir_open (tmp_dir, 0, NULL)) == NULL)) { + g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir, + g_strdup (tmp_dir), + g_free, + G_PRIORITY_DEFAULT, + NULL); + g_free (tmp_dir); + return; + } + + todelete = g_file_new_for_path (path); + g_file_delete (todelete, NULL, NULL); + g_object_unref (todelete); + + /* See whether we have multiple themes to install. If so, + * we won't ask the user whether to apply the new theme + * after installation. */ + n_themes = 0; + for (name = g_dir_read_name (dir); + name && n_themes <= 1; + name = g_dir_read_name (dir)) { + gchar *theme_dir; + + theme_dir = g_build_filename (tmp_dir, name, NULL); + + if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR)) + ++n_themes; + + g_free (theme_dir); + } + g_dir_rewind (dir); + + ok = TRUE; + for (name = g_dir_read_name (dir); name && ok; + name = g_dir_read_name (dir)) { + gchar *theme_dir; + + theme_dir = g_build_filename (tmp_dir, name, NULL); + + if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR)) + ok = mate_theme_install_real (parent, + filetype, + theme_dir, + name, + n_themes == 1); + + g_free (theme_dir); + } + g_dir_close (dir); + + if (ok && n_themes > 1) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("New themes have been successfully installed.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir, + tmp_dir, g_free, + G_PRIORITY_DEFAULT, NULL); + } +} + +typedef struct +{ + GtkWindow *parent; + char *path; +} TransferData; + +static void +transfer_done_cb (GtkWidget *dialog, + TransferData *tdata) +{ + gdk_threads_enter (); + /* XXX: path should be on the local filesystem by now? */ + + if (dialog != NULL) { + gtk_widget_destroy (dialog); + } + + process_local_theme (tdata->parent, tdata->path); + + g_free (tdata->path); + g_free (tdata); + + gdk_threads_leave (); +} + +void +mate_theme_install (GFile *file, + GtkWindow *parent) +{ + GtkWidget *dialog; + gchar *path, *base; + GList *src, *target; + const gchar *template; + TransferData *tdata; + + if (file == NULL) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("No theme file location specified to install")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + return; + } + + /* see if someone dropped a local directory */ + if (g_file_is_native (file) + && g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) { + path = g_file_get_path (file); + process_local_theme (parent, path); + g_free (path); + return; + } + + /* we can't tell if this is an icon theme yet, so just make a + * temporary copy in .themes */ + path = g_build_filename (g_get_home_dir (), ".themes", NULL); + + if (access (path, X_OK | W_OK) != 0) { + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Insufficient permissions to install the theme in:\n%s"), path); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (path); + return; + } + + base = g_file_get_basename (file); + + if (g_str_has_suffix (base, ".tar.gz") + || g_str_has_suffix (base, ".tgz") + || g_str_has_suffix (base, ".gtp")) + template = "mate-theme-%d.gtp"; + else if (g_str_has_suffix (base, ".tar.bz2")) + template = "mate-theme-%d.tar.bz2"; + else { + invalid_theme_dialog (parent, base, FALSE); + g_free (base); + return; + } + g_free (base); + + src = g_list_append (NULL, g_object_ref (file)); + + path = NULL; + do { + gchar *file_tmp; + + g_free (path); + file_tmp = g_strdup_printf (template, g_random_int ()); + path = g_build_filename (g_get_home_dir (), ".themes", file_tmp, NULL); + g_free (file_tmp); + } while (g_file_test (path, G_FILE_TEST_EXISTS)); + + tdata = g_new0 (TransferData, 1); + tdata->parent = parent; + tdata->path = path; + + dialog = file_transfer_dialog_new_with_parent (parent); + g_signal_connect (dialog, + "cancel", + (GCallback) transfer_cancel_cb, path); + g_signal_connect (dialog, + "done", + (GCallback) transfer_done_cb, tdata); + + target = g_list_append (NULL, g_file_new_for_path (path)); + file_transfer_dialog_copy_async (FILE_TRANSFER_DIALOG (dialog), + src, + target, + FILE_TRANSFER_DIALOG_DEFAULT, + G_PRIORITY_DEFAULT); + gtk_widget_show (dialog); + + /* don't free the path since we're using that for the signals */ + g_list_foreach (src, (GFunc) g_object_unref, NULL); + g_list_free (src); + g_list_foreach (target, (GFunc) g_object_unref, NULL); + g_list_free (target); +} + +void +mate_theme_installer_run (GtkWindow *parent, + const gchar *filename) +{ + static gboolean running_theme_install = FALSE; + static gchar old_folder[512] = ""; + GtkWidget *dialog; + GtkFileFilter *filter; + + if (running_theme_install) + return; + + running_theme_install = TRUE; + + if (filename == NULL) + filename = old_folder; + + dialog = gtk_file_chooser_dialog_new (_("Select Theme"), + parent, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Theme Packages")); + gtk_file_filter_add_mime_type (filter, "application/x-bzip-compressed-tar"); + gtk_file_filter_add_mime_type (filter, "application/x-compressed-tar"); + gtk_file_filter_add_mime_type (filter, "application/x-mate-theme-package"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + if (strcmp (old_folder, "")) + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), old_folder); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *uri_selected, *folder; + + uri_selected = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog)); + + folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); + g_strlcpy (old_folder, folder, 255); + g_free (folder); + + gtk_widget_destroy (dialog); + + if (uri_selected != NULL) { + GFile *file = g_file_new_for_uri (uri_selected); + g_free (uri_selected); + + mate_theme_install (file, parent); + g_object_unref (file); + } + } else { + gtk_widget_destroy (dialog); + } + + /* + * we're relying on the mate theme info module to pick up changes + * to the themes so we don't need to update the model here + */ + + running_theme_install = FALSE; +} diff --git a/capplets/appearance/theme-installer.h b/capplets/appearance/theme-installer.h new file mode 100644 index 00000000..c0b3f695 --- /dev/null +++ b/capplets/appearance/theme-installer.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef THEME_INSTALLER_H +#define THEME_INSTALLER_H + +void mate_theme_install (GFile *file, GtkWindow *parent); +void mate_theme_installer_run (GtkWindow *parent, const gchar *filename); + +#endif /* THEME_INSTALLER_H */ diff --git a/capplets/appearance/theme-save.c b/capplets/appearance/theme-save.c new file mode 100644 index 00000000..f4981468 --- /dev/null +++ b/capplets/appearance/theme-save.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include + +#include "theme-save.h" +#include "theme-util.h" +#include "capplet-util.h" + +static GQuark error_quark; +enum { + INVALID_THEME_NAME +}; + +/* taken from mate-desktop-item.c */ +static gchar * +escape_string_and_dup (const gchar *s) +{ + gchar *return_value, *p; + const gchar *q; + int len = 0; + + if (s == NULL) + return g_strdup(""); + + q = s; + while (*q) + { + len++; + if (strchr ("\n\r\t\\", *q) != NULL) + len++; + q++; + } + return_value = p = (gchar *) g_malloc (len + 1); + do + { + switch (*s) + { + case '\t': + *p++ = '\\'; + *p++ = 't'; + break; + case '\n': + *p++ = '\\'; + *p++ = 'n'; + break; + case '\r': + *p++ = '\\'; + *p++ = 'r'; + break; + case '\\': + *p++ = '\\'; + *p++ = '\\'; + break; + default: + *p++ = *s; + } + } + while (*s++); + return return_value; +} + +static gboolean +check_theme_name (const gchar *theme_name, + GError **error) +{ + if (theme_name == NULL) { + g_set_error (error, + error_quark, + INVALID_THEME_NAME, + _("Theme name must be present")); + return FALSE; + } + return TRUE; +} + +static gchar * +str_remove_slash (const gchar *src) +{ + const gchar *i; + gchar *rtn; + gint len = 0; + i = src; + + while (*i) { + if (*i != '/') + len++; + i++; + } + + rtn = (gchar *) g_malloc (len + 1); + while (*src) { + if (*src != '/') { + *rtn = *src; + rtn++; + } + src++; + } + *rtn = '\0'; + return rtn - len; +} + +static gboolean +setup_directory_structure (const gchar *theme_name, + GError **error) +{ + gchar *dir, *theme_name_dir; + gboolean retval = TRUE; + + theme_name_dir = str_remove_slash (theme_name); + + dir = g_build_filename (g_get_home_dir (), ".themes", NULL); + if (!g_file_test (dir, G_FILE_TEST_EXISTS)) + g_mkdir (dir, 0775); + g_free (dir); + + dir = g_build_filename (g_get_home_dir (), ".themes", theme_name_dir, NULL); + if (!g_file_test (dir, G_FILE_TEST_EXISTS)) + g_mkdir (dir, 0775); + g_free (dir); + + dir = g_build_filename (g_get_home_dir (), ".themes", theme_name_dir, "index.theme", NULL); + g_free (theme_name_dir); + + if (g_file_test (dir, G_FILE_TEST_EXISTS)) { + GtkDialog *dialog; + GtkWidget *button; + gint response; + + dialog = (GtkDialog *) gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("The theme already exists. Would you like to replace it?")); + button = gtk_dialog_add_button (dialog, _("_Overwrite"), GTK_RESPONSE_ACCEPT); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON)); + response = gtk_dialog_run (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + retval = (response != GTK_RESPONSE_CANCEL); + } + g_free (dir); + + return retval; +} + +static gboolean +write_theme_to_disk (MateThemeMetaInfo *theme_info, + const gchar *theme_name, + const gchar *theme_description, + gboolean save_background, + GError **error) +{ + gchar* dir; + gchar* theme_name_dir; + GFile* tmp_file; + GFile* target_file; + GOutputStream* output; + + gchar* str; + gchar* current_background; + + MateConfClient* client; + const gchar* theme_header = "" + "[Desktop Entry]\n" + "Name=%s\n" + "Type=X-GNOME-Metatheme\n" + "Comment=%s\n" + "\n" + "[X-GNOME-Metatheme]\n" + "GtkTheme=%s\n" + "MetacityTheme=%s\n" + "IconTheme=%s\n"; + + theme_name_dir = str_remove_slash (theme_name); + dir = g_build_filename (g_get_home_dir (), ".themes", theme_name_dir, "index.theme~", NULL); + g_free (theme_name_dir); + + tmp_file = g_file_new_for_path (dir); + dir [strlen (dir) - 1] = '\000'; + target_file = g_file_new_for_path (dir); + g_free (dir); + + /* start making the theme file */ + str = g_strdup_printf(theme_header, theme_name, theme_description, theme_info->gtk_theme_name, theme_info->marco_theme_name, theme_info->icon_theme_name); + + output = G_OUTPUT_STREAM (g_file_replace (tmp_file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL)); + g_output_stream_write (output, str, strlen (str), NULL, NULL); + g_free (str); + + if (theme_info->gtk_color_scheme) { + gchar *a, *tmp; + tmp = g_strdup (theme_info->gtk_color_scheme); + for (a = tmp; *a != '\0'; a++) + if (*a == '\n') + *a = ','; + str = g_strdup_printf ("GtkColorScheme=%s\n", tmp); + g_output_stream_write (output, str, strlen (str), NULL, NULL); + + g_free (str); + g_free (tmp); + } + + if (theme_info->cursor_theme_name) { +#ifdef HAVE_XCURSOR + str = g_strdup_printf ("CursorTheme=%s\n" + "CursorSize=%i\n", + theme_info->cursor_theme_name, + theme_info->cursor_size); +#else + str = g_strdup_printf ("CursorFont=%s\n", theme_info->cursor_theme_name); +#endif + g_output_stream_write (output, str, strlen (str), NULL, NULL); + g_free (str); + } + + if (theme_info->notification_theme_name) { + str = g_strdup_printf ("NotificationTheme=%s\n", theme_info->notification_theme_name); + g_output_stream_write (output, str, strlen (str), NULL, NULL); + g_free (str); + } + + if (save_background) { + client = mateconf_client_get_default (); + current_background = mateconf_client_get_string (client, BACKGROUND_KEY, NULL); + + if (current_background != NULL) { + str = g_strdup_printf ("BackgroundImage=%s\n", current_background); + + g_output_stream_write (output, str, strlen (str), NULL, NULL); + + g_free (current_background); + g_free (str); + } + g_object_unref (client); + } + + g_file_move (tmp_file, target_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL); + g_output_stream_close (output, NULL, NULL); + + g_object_unref (tmp_file); + g_object_unref (target_file); + + return TRUE; +} + +static gboolean +save_theme_to_disk (MateThemeMetaInfo *theme_info, + const gchar *theme_name, + const gchar *theme_description, + gboolean save_background, + GError **error) +{ + if (!check_theme_name (theme_name, error)) + return FALSE; + + if (!setup_directory_structure (theme_name, error)) + return FALSE; + + if (!write_theme_to_disk (theme_info, theme_name, theme_description, save_background, error)) + return FALSE; + + return TRUE; +} + +static void +save_dialog_response (GtkWidget *save_dialog, + gint response_id, + AppearanceData *data) +{ + if (response_id == GTK_RESPONSE_OK) { + GtkWidget *entry; + GtkWidget *text_view; + GtkTextBuffer *buffer; + GtkTextIter start_iter; + GtkTextIter end_iter; + gchar *buffer_text; + MateThemeMetaInfo *theme_info; + gchar *theme_description = NULL; + gchar *theme_name = NULL; + gboolean save_background; + GError *error = NULL; + + entry = appearance_capplet_get_widget (data, "save_dialog_entry"); + theme_name = escape_string_and_dup (gtk_entry_get_text (GTK_ENTRY (entry))); + + text_view = appearance_capplet_get_widget (data, "save_dialog_textview"); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + gtk_text_buffer_get_start_iter (buffer, &start_iter); + gtk_text_buffer_get_end_iter (buffer, &end_iter); + buffer_text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE); + theme_description = escape_string_and_dup (buffer_text); + g_free (buffer_text); + theme_info = (MateThemeMetaInfo *) g_object_get_data (G_OBJECT (save_dialog), "meta-theme-info"); + save_background = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON ( + appearance_capplet_get_widget (data, "save_background_checkbutton"))); + + if (save_theme_to_disk (theme_info, theme_name, theme_description, save_background, &error)) { + /* remove the custom theme */ + GtkTreeIter iter; + + if (theme_find_in_model (GTK_TREE_MODEL (data->theme_store), "__custom__", &iter)) + gtk_list_store_remove (data->theme_store, &iter); + } + + g_free (theme_name); + g_free (theme_description); + g_clear_error (&error); + } + + gtk_widget_hide (save_dialog); +} + +static void +entry_text_changed (GtkEditable *editable, + AppearanceData *data) +{ + const gchar *text; + GtkWidget *button; + + text = gtk_entry_get_text (GTK_ENTRY (editable)); + button = appearance_capplet_get_widget (data, "save_dialog_save_button"); + + gtk_widget_set_sensitive (button, text != NULL && text[0] != '\000'); +} + +void +theme_save_dialog_run (MateThemeMetaInfo *theme_info, + AppearanceData *data) +{ + GtkWidget *entry; + GtkWidget *text_view; + GtkTextBuffer *text_buffer; + + entry = appearance_capplet_get_widget (data, "save_dialog_entry"); + text_view = appearance_capplet_get_widget (data, "save_dialog_textview"); + + if (data->theme_save_dialog == NULL) { + data->theme_save_dialog = appearance_capplet_get_widget (data, "theme_save_dialog"); + + g_signal_connect (data->theme_save_dialog, "response", (GCallback) save_dialog_response, data); + g_signal_connect (data->theme_save_dialog, "delete-event", (GCallback) gtk_true, NULL); + g_signal_connect (entry, "changed", (GCallback) entry_text_changed, data); + + error_quark = g_quark_from_string ("mate-theme-save"); + gtk_widget_set_size_request (text_view, 300, 100); + } + + gtk_entry_set_text (GTK_ENTRY (entry), ""); + entry_text_changed (GTK_EDITABLE (entry), data); + gtk_widget_grab_focus (entry); + + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + gtk_text_buffer_set_text (text_buffer, "", 0); + g_object_set_data (G_OBJECT (data->theme_save_dialog), "meta-theme-info", theme_info); + gtk_window_set_transient_for (GTK_WINDOW (data->theme_save_dialog), + GTK_WINDOW (appearance_capplet_get_widget (data, "appearance_window"))); + gtk_widget_show (data->theme_save_dialog); +} diff --git a/capplets/appearance/theme-save.h b/capplets/appearance/theme-save.h new file mode 100644 index 00000000..e56f4041 --- /dev/null +++ b/capplets/appearance/theme-save.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void theme_save_dialog_run (MateThemeMetaInfo *theme_info, + AppearanceData *data); diff --git a/capplets/appearance/theme-util.c b/capplets/appearance/theme-util.c new file mode 100644 index 00000000..2305b0f9 --- /dev/null +++ b/capplets/appearance/theme-util.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "appearance.h" + +#include +#include +#include +#include + +#include "capplet-util.h" +#include "theme-util.h" + +gboolean +theme_is_writable (const gpointer theme) +{ + MateThemeCommonInfo *info = theme; + GFile *file; + GFileInfo *file_info; + gboolean writable; + + if (info == NULL || info->path == NULL) + return FALSE; + + file = g_file_new_for_path (info->path); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + g_object_unref (file); + + if (file_info == NULL) + return FALSE; + + writable = g_file_info_get_attribute_boolean (file_info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); + g_object_unref (file_info); + + return writable; +} + +gboolean +theme_delete (const gchar *name, ThemeType type) +{ + gboolean rc; + GtkDialog *dialog; + gchar *theme_dir; + gint response; + MateThemeCommonInfo *theme; + GFile *dir; + gboolean del_empty_parent; + + dialog = (GtkDialog *) gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("Would you like to delete this theme?")); + gtk_dialog_add_button (dialog, GTK_STOCK_DELETE, GTK_RESPONSE_ACCEPT); + response = gtk_dialog_run (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + if (response != GTK_RESPONSE_ACCEPT) + return FALSE; + + /* Most theme types are put into separate subdirectories. For those + we want to delete those directories as well. */ + del_empty_parent = TRUE; + + switch (type) { + case THEME_TYPE_GTK: + theme = (MateThemeCommonInfo *) mate_theme_info_find (name); + theme_dir = g_build_filename (theme->path, "gtk-2.0", NULL); + break; + + case THEME_TYPE_ICON: + theme = (MateThemeCommonInfo *) mate_theme_icon_info_find (name); + theme_dir = g_path_get_dirname (theme->path); + del_empty_parent = FALSE; + break; + + case THEME_TYPE_WINDOW: + theme = (MateThemeCommonInfo *) mate_theme_info_find (name); + theme_dir = g_build_filename (theme->path, "marco-1", NULL); + break; + + case THEME_TYPE_META: + theme = (MateThemeCommonInfo *) mate_theme_meta_info_find (name); + theme_dir = g_strdup (theme->path); + break; + + case THEME_TYPE_CURSOR: + theme = (MateThemeCommonInfo *) mate_theme_cursor_info_find (name); + theme_dir = g_build_filename (theme->path, "cursors", NULL); + break; + + default: + return FALSE; + } + + dir = g_file_new_for_path (theme_dir); + g_free (theme_dir); + + if (!capplet_file_delete_recursive (dir, NULL)) { + GtkWidget *info_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Theme cannot be deleted")); + gtk_dialog_run (GTK_DIALOG (info_dialog)); + gtk_widget_destroy (info_dialog); + rc = FALSE; + } else { + if (del_empty_parent) { + /* also delete empty parent directories */ + GFile *parent = g_file_get_parent (dir); + g_file_delete (parent, NULL, NULL); + g_object_unref (parent); + } + rc = TRUE; + } + + g_object_unref (dir); + return rc; +} + +gboolean +theme_model_iter_last (GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter walk, prev; + gboolean valid; + + valid = gtk_tree_model_get_iter_first (model, &walk); + + if (valid) { + do { + prev = walk; + valid = gtk_tree_model_iter_next (model, &walk); + } while (valid); + + *iter = prev; + return TRUE; + } + return FALSE; +} + +gboolean +theme_find_in_model (GtkTreeModel *model, const gchar *name, GtkTreeIter *iter) +{ + GtkTreeIter walk; + gboolean valid; + gchar *test; + + if (!name) + return FALSE; + + for (valid = gtk_tree_model_get_iter_first (model, &walk); valid; + valid = gtk_tree_model_iter_next (model, &walk)) + { + gtk_tree_model_get (model, &walk, COL_NAME, &test, -1); + + if (test) { + gint cmp = strcmp (test, name); + g_free (test); + + if (!cmp) { + if (iter) + *iter = walk; + return TRUE; + } + } + } + + return FALSE; +} + +gboolean +packagekit_available (void) +{ + DBusGConnection *connection; + DBusGProxy *proxy; + gboolean available = FALSE; + + connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (connection == NULL) { + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + org_freedesktop_DBus_name_has_owner (proxy, + "org.freedesktop.PackageKit", + &available, + NULL); + + g_object_unref (proxy); + dbus_g_connection_unref (connection); + + return available; +} + +void theme_install_file(GtkWindow* parent, const gchar* path) +{ + DBusGConnection* connection; + DBusGProxy* proxy; + GError* error = NULL; + gboolean ret; + + connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + + if (connection == NULL) + { + g_warning("Could not get session bus"); + return; + } + + proxy = dbus_g_proxy_new_for_name(connection, + "org.freedesktop.PackageKit", + "/org/freedesktop/PackageKit", + "org.freedesktop.PackageKit"); + + + ret = dbus_g_proxy_call(proxy, "InstallProvideFile", &error, + G_TYPE_STRING, path, + G_TYPE_INVALID, G_TYPE_INVALID); + + g_object_unref(proxy); + + if (!ret) + { + GtkWidget* dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Could not install theme engine")); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG (dialog), "%s", error->message); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_error_free(error); + } + + dbus_g_connection_unref(connection); +} diff --git a/capplets/appearance/theme-util.h b/capplets/appearance/theme-util.h new file mode 100644 index 00000000..8bf91302 --- /dev/null +++ b/capplets/appearance/theme-util.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define GTK_THEME_KEY "/desktop/mate/interface/gtk_theme" +#define MARCO_THEME_KEY "/apps/marco/general/theme" +#define ICON_THEME_KEY "/desktop/mate/interface/icon_theme" +#define NOTIFICATION_THEME_KEY "/apps/notification-daemon/theme" +#define COLOR_SCHEME_KEY "/desktop/mate/interface/gtk_color_scheme" +#define LOCKDOWN_KEY "/desktop/mate/lockdown/disable_theme_settings" +#define BACKGROUND_KEY "/desktop/mate/background/picture_filename" +#define APPLICATION_FONT_KEY "/desktop/mate/interface/font_name" +#define DOCUMENTS_FONT_KEY "/desktop/mate/interface/document_font_name" +#define DESKTOP_FONT_KEY "/apps/caja/preferences/desktop_font" +#define WINDOWTITLE_FONT_KEY "/apps/marco/general/titlebar_font" +#define MONOSPACE_FONT_KEY "/desktop/mate/interface/monospace_font_name" + +#ifdef HAVE_XCURSOR + #define CURSOR_THEME_KEY "/desktop/mate/peripherals/mouse/cursor_theme" + #define CURSOR_SIZE_KEY "/desktop/mate/peripherals/mouse/cursor_size" +#else + #define CURSOR_THEME_KEY "/desktop/mate/peripherals/mouse/cursor_font" +#endif + +enum { + COL_THUMBNAIL, + COL_LABEL, + COL_NAME, + NUM_COLS +}; + +typedef enum { + THEME_TYPE_GTK, + THEME_TYPE_WINDOW, + THEME_TYPE_ICON, + THEME_TYPE_META, + THEME_TYPE_CURSOR +} ThemeType; + +gboolean theme_is_writable(const gpointer theme); +gboolean theme_delete(const gchar* name, ThemeType type); + +gboolean theme_model_iter_last(GtkTreeModel* model, GtkTreeIter* iter); +gboolean theme_find_in_model(GtkTreeModel* model, const gchar* name, GtkTreeIter* iter); + +void theme_install_file(GtkWindow* parent, const gchar* path); +gboolean packagekit_available(void); diff --git a/capplets/common/Makefile.am b/capplets/common/Makefile.am new file mode 100644 index 00000000..127af950 --- /dev/null +++ b/capplets/common/Makefile.am @@ -0,0 +1,63 @@ +EXTRA_DIST = + +INCLUDES = \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DGTK_ENGINE_DIR="\"$(GTK_ENGINE_DIR)\"" \ + -DG_LOG_DOMAIN=\"capplet-common\" \ + -DINSTALL_PREFIX=\"$(prefix)\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libwindow-settings \ + -DPIXMAP_DIR=\""$(datadir)/mate-control-center/pixmaps"\" \ + $(CAPPLET_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(MATE_DESKTOP_CFLAGS) \ + $(MARCO_CFLAGS) \ + $(GSD_DBUS_CFLAGS) \ + $(GIO_CFLAGS) + + +noinst_LTLIBRARIES = libcommon.la + +libcommon_la_SOURCES = \ + activate-settings-daemon.c \ + activate-settings-daemon.h \ + capplet-stock-icons.c \ + capplet-stock-icons.h \ + capplet-util.c \ + capplet-util.h \ + file-transfer-dialog.c \ + file-transfer-dialog.h \ + mateconf-property-editor.c \ + mateconf-property-editor.h \ + mateconf-property-editor-marshal.c \ + mateconf-property-editor-marshal.h \ + mate-theme-apply.c \ + mate-theme-apply.h \ + mate-theme-info.c \ + mate-theme-info.h \ + gtkrc-utils.c \ + gtkrc-utils.h \ + theme-thumbnail.c \ + theme-thumbnail.h \ + wm-common.c \ + wm-common.h + +libcommon_la_LIBADD = \ + $(top_builddir)/libwindow-settings/libmate-window-settings.la \ + $(MARCO_LIBS) \ + $(DBUS_LIBS) \ + $(MATE_DESKTOP_LIBS) \ + $(GIO_LIBS) + +mate_theme_test_SOURCES = \ + mate-theme-test.c + +mate_theme_test_LDADD = \ + libcommon.la \ + $(MATECC_CAPPLETS_LIBS) + +noinst_PROGRAMS = \ + mate-theme-test + +-include $(top_srcdir)/git.mk diff --git a/capplets/common/activate-settings-daemon.c b/capplets/common/activate-settings-daemon.c new file mode 100644 index 00000000..794f1098 --- /dev/null +++ b/capplets/common/activate-settings-daemon.c @@ -0,0 +1,60 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "activate-settings-daemon.h" + +static void popup_error_message (void) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, _("Unable to start the settings manager 'mate-settings-daemon'.\n" + "Without the MATE settings manager running, some preferences may not take effect. This could " + "indicate a problem with DBus, or a non-MATE (e.g. KDE) settings manager may already " + "be active and conflicting with the MATE settings manager.")); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/* Returns FALSE if activation failed, else TRUE */ +gboolean +activate_settings_daemon (void) +{ + DBusGConnection *connection = NULL; + DBusGProxy *proxy = NULL; + GError *error = NULL; + + connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (connection == NULL) + { + popup_error_message (); + g_error_free (error); + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (connection, + "org.mate.SettingsDaemon", + "/org/mate/SettingsDaemon", + "org.mate.SettingsDaemon"); + + if (proxy == NULL) + { + popup_error_message (); + return FALSE; + } + + if (!org_mate_SettingsDaemon_awake(proxy, &error)) + { + popup_error_message (); + g_error_free (error); + return FALSE; + } + + return TRUE; +} diff --git a/capplets/common/activate-settings-daemon.h b/capplets/common/activate-settings-daemon.h new file mode 100644 index 00000000..fc1558d8 --- /dev/null +++ b/capplets/common/activate-settings-daemon.h @@ -0,0 +1,9 @@ +#ifndef ACTIVATE_SETINGS_DAEMON +#define ACTIVATE_SETINGS_DAEMON + +#include + +/* Returns FALSE if activation failed, else TRUE */ +gboolean activate_settings_daemon (void); + +#endif diff --git a/capplets/common/capplet-stock-icons.c b/capplets/common/capplet-stock-icons.c new file mode 100644 index 00000000..29b440f5 --- /dev/null +++ b/capplets/common/capplet-stock-icons.c @@ -0,0 +1,101 @@ +/* + * capplet-stock-icons.c + * + * Copyright (C) 2002 Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Rajkumar Sivasamy + * Taken bits of code from panel-stock-icons.c, Thanks Mark + */ + +#include +#include + +#include "capplet-stock-icons.h" + +static GtkIconSize mouse_capplet_dblclck_icon_size = 0; + +GtkIconSize +mouse_capplet_dblclck_icon_get_size (void) +{ + return mouse_capplet_dblclck_icon_size; +} + +typedef struct +{ + char *stock_id; + char *name; +} CappletStockIcon; + + +static CappletStockIcon items [] = { + { MOUSE_DBLCLCK_MAYBE, "double-click-maybe.png"}, + { MOUSE_DBLCLCK_ON, "double-click-on.png"}, + { MOUSE_DBLCLCK_OFF, "double-click-off.png"} +}; + +static void +capplet_register_stock_icons (GtkIconFactory *factory) +{ + gint i; + GtkIconSource *source; + + source = gtk_icon_source_new (); + + for (i = 0; i < G_N_ELEMENTS (items); ++i) { + GtkIconSet *icon_set; + char *filename; + filename = g_build_filename (PIXMAP_DIR, items[i].name, NULL); + + if (!filename) { + g_warning (_("Unable to load stock icon '%s'\n"), items[i].name); + icon_set = gtk_icon_factory_lookup_default (GTK_STOCK_MISSING_IMAGE); + gtk_icon_factory_add (factory, items[i].stock_id, icon_set); + continue; + } + + gtk_icon_source_set_filename (source, filename); + g_free (filename); + + icon_set = gtk_icon_set_new (); + gtk_icon_set_add_source (icon_set, source); + gtk_icon_factory_add (factory, items[i].stock_id, icon_set); + gtk_icon_set_unref (icon_set); + } + gtk_icon_source_free (source); +} + +void +capplet_init_stock_icons (void) +{ + GtkIconFactory *factory; + static gboolean initialized = FALSE; + + if (initialized) + return; + initialized = TRUE; + + factory = gtk_icon_factory_new (); + gtk_icon_factory_add_default (factory); + capplet_register_stock_icons (factory); + + mouse_capplet_dblclck_icon_size = gtk_icon_size_register ("mouse-capplet-dblclck-icon", + MOUSE_CAPPLET_DBLCLCK_ICON_SIZE, + MOUSE_CAPPLET_DBLCLCK_ICON_SIZE); + g_object_unref (factory); +} diff --git a/capplets/common/capplet-stock-icons.h b/capplets/common/capplet-stock-icons.h new file mode 100644 index 00000000..8c954cf4 --- /dev/null +++ b/capplets/common/capplet-stock-icons.h @@ -0,0 +1,66 @@ +/* + * capplet-stock-icons.h + * + * Copyright (C) 2002 Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Rajkumar Sivasamy + * Taken bits of code from panel-stock-icons.h, Thanks Mark + */ + +#ifndef __CAPPLET_STOCK_ICONS_H__ +#define __CAPPLET_STOCK_ICONS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define KEYBOARD_CAPPLET_DEFAULT_ICON_SIZE 48 +#define MOUSE_CAPPLET_DEFAULT_WIDTH 120 +#define MOUSE_CAPPLET_DEFAULT_HEIGHT 100 +#define MOUSE_CAPPLET_DBLCLCK_ICON_SIZE 100 + +/* stock icons */ +#define KEYBOARD_REPEAT "keyboard-repeat" +#define KEYBOARD_CURSOR "keyboard-cursor" +#define KEYBOARD_VOLUME "keyboard-volume" +#define KEYBOARD_BELL "keyboard-bell" +#define ACCESSX_KEYBOARD_BOUNCE "accessibility-keyboard-bouncekey" +#define ACCESSX_KEYBOARD_SLOW "accessibility-keyboard-slowkey" +#define ACCESSX_KEYBOARD_MOUSE "accessibility-keyboard-mousekey" +#define ACCESSX_KEYBOARD_STICK "accessibility-keyboard-stickykey" +#define ACCESSX_KEYBOARD_TOGGLE "accessibility-keyboard-togglekey" +#define MOUSE_DBLCLCK_MAYBE "mouse-dblclck-maybe" +#define MOUSE_DBLCLCK_ON "mouse-dblclck-on" +#define MOUSE_DBLCLCK_OFF "mouse-dblclck-off" +#define MOUSE_RIGHT_HANDED "mouse-right-handed" +#define MOUSE_LEFT_HANDED "mouse-left-handed" + +void capplet_init_stock_icons (void); +GtkIconSize keyboard_capplet_icon_get_size (void); +GtkIconSize mouse_capplet_icon_get_size (void); +GtkIconSize mouse_capplet_dblclck_icon_get_size (void); + +#ifdef __cplusplus +} +#endif + +#endif /* __CAPPLET_STOCK_ICONS_H__ */ diff --git a/capplets/common/capplet-util.c b/capplets/common/capplet-util.c new file mode 100644 index 00000000..43d9485f --- /dev/null +++ b/capplets/common/capplet-util.c @@ -0,0 +1,205 @@ +/* -*- mode: c; style: linux -*- */ + +/* capplet-util.c + * Copyright (C) 2001 Ximian, Inc. + * + * Written by Bradford Hovinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* For stat */ +#include +#include +#include +#include +#include + +#include "capplet-util.h" + +static void +capplet_error_dialog (GtkWindow *parent, char const *msg, GError *err) +{ + if (err != NULL) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + msg, err->message); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_widget_show (dialog); + g_error_free (err); + } +} + +/** + * capplet_help : + * @parent : + * @helpfile : + * @section : + * + * A quick utility routine to display help for capplets, and handle errors in a + * Havoc happy way. + **/ +void +capplet_help (GtkWindow *parent, char const *section) +{ + GError *error = NULL; + char *uri; + GdkScreen *screen; + + g_return_if_fail (section != NULL); + + if (!parent) + screen = gdk_screen_get_default (); + else + screen = gtk_widget_get_screen (GTK_WIDGET (parent)); + + uri = g_strdup_printf ("ghelp:user-guide#%s", section); + + if (!gtk_show_uri (screen, uri, gtk_get_current_event_time (), &error)) { + capplet_error_dialog ( + parent, + _("There was an error displaying help: %s"), + error); + } + + g_free (uri); +} + +/** + * capplet_set_icon : + * @window : + * @file_name : + * + * A quick utility routine to avoid the cut-n-paste of bogus code + * that caused several bugs. + **/ +void +capplet_set_icon (GtkWidget *window, char const *icon_file_name) +{ + /* Make sure that every window gets an icon */ + gtk_window_set_default_icon_name (icon_file_name); + gtk_window_set_icon_name (GTK_WINDOW (window), icon_file_name); +} + +static gboolean +directory_delete_recursive (GFile *directory, GError **error) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + gboolean success = TRUE; + + enumerator = g_file_enumerate_children (directory, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (enumerator == NULL) + return FALSE; + + while (success && + (info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { + GFile *child; + + child = g_file_get_child (directory, g_file_info_get_name (info)); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { + success = directory_delete_recursive (child, error); + } else { + success = g_file_delete (child, NULL, error); + } + g_object_unref (info); + } + g_file_enumerator_close (enumerator, NULL, NULL); + + if (success) + success = g_file_delete (directory, NULL, error); + + return success; +} + +/** + * capplet_file_delete_recursive : + * @file : + * @error : + * + * A utility routine to delete files and/or directories, + * including non-empty directories. + **/ +gboolean +capplet_file_delete_recursive (GFile *file, GError **error) +{ + GFileInfo *info; + GFileType type; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, error); + if (info == NULL) + return FALSE; + + type = g_file_info_get_file_type (info); + g_object_unref (info); + + if (type == G_FILE_TYPE_DIRECTORY) + return directory_delete_recursive (file, error); + else + return g_file_delete (file, NULL, error); +} + +void +capplet_init (GOptionContext *context, + int *argc, + char ***argv) +{ + GError *err = NULL; + +#ifdef ENABLE_NLS + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + if (context) { +#if GLIB_CHECK_VERSION (2, 12, 0) + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); +#endif + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + + if (!g_option_context_parse (context, argc, argv, &err)) { + g_printerr ("%s\n", err->message); + exit (1); + } + } + + gtk_init (argc, argv); +} diff --git a/capplets/common/capplet-util.h b/capplets/common/capplet-util.h new file mode 100644 index 00000000..b7caf4f0 --- /dev/null +++ b/capplets/common/capplet-util.h @@ -0,0 +1,46 @@ +/* -*- mode: c; style: linux -*- */ + +/* capplet-util.h + * Copyright (C) 2001 Ximian, Inc. + * + * Written by Bradford Hovinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __CAPPLET_UTIL_H +#define __CAPPLET_UTIL_H + +#include +#include +#include +#include +#include + +/* Macros to make certain repetitive tasks a bit easier */ + +/* Retrieve a widget from the UI object */ + +#define WID(s) GTK_WIDGET (gtk_builder_get_object (dialog, s)) + +/* Some miscellaneous functions useful to all capplets */ + +void capplet_help (GtkWindow *parent, char const *section); +void capplet_set_icon (GtkWidget *window, char const *icon_file_name); +gboolean capplet_file_delete_recursive (GFile *directory, GError **error); +void capplet_init (GOptionContext *context, int *argc, char ***argv); + +#endif /* __CAPPLET_UTIL_H */ diff --git a/capplets/common/file-transfer-dialog.c b/capplets/common/file-transfer-dialog.c new file mode 100644 index 00000000..a698520f --- /dev/null +++ b/capplets/common/file-transfer-dialog.c @@ -0,0 +1,608 @@ +/* file-transfer-dialog.c + * Copyright (C) 2002 Ximian, Inc. + * + * Written by Rachel Hestilow + * Jens Granseuer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "file-transfer-dialog.h" + +enum +{ + PROP_0, + PROP_FROM_URI, + PROP_TO_URI, + PROP_FRACTION_COMPLETE, + PROP_NTH_URI, + PROP_TOTAL_URIS, + PROP_PARENT +}; + +enum +{ + CANCEL, + DONE, + LAST_SIGNAL +}; + +guint file_transfer_dialog_signals[LAST_SIGNAL] = {0, }; + +struct _FileTransferDialogPrivate +{ + GtkWidget *progress; + GtkWidget *status; + guint nth; + guint total; + GCancellable *cancellable; +}; + +#define FILE_TRANSFER_DIALOG_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), file_transfer_dialog_get_type (), FileTransferDialogPrivate)) + +typedef struct _FileTransferJob +{ + FileTransferDialog *dialog; + GtkDialog *overwrite_dialog; + GSList *source_files; + GSList *target_files; + FileTransferDialogOptions options; +} FileTransferJob; + +/* structure passed to the various callbacks */ +typedef struct { + FileTransferDialog *dialog; + gchar *source; + gchar *target; + guint current_file; + guint total_files; + goffset current_bytes; + goffset total_bytes; + gint response; + GtkDialog *overwrite_dialog; +} FileTransferData; + +G_DEFINE_TYPE (FileTransferDialog, file_transfer_dialog, GTK_TYPE_DIALOG) + +static void +file_transfer_dialog_update_num_files (FileTransferDialog *dlg) +{ + gchar *str; + + if (dlg->priv->total <= 1) + return; + + str = g_strdup_printf (_("Copying file: %u of %u"), + dlg->priv->nth, dlg->priv->total); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dlg->priv->progress), str); + g_free (str); +} + +static void +file_transfer_dialog_response (GtkDialog *dlg, gint response_id) +{ + FileTransferDialog *dialog = FILE_TRANSFER_DIALOG (dlg); + + g_cancellable_cancel (dialog->priv->cancellable); +} + +static void +file_transfer_dialog_finalize (GObject *object) +{ + FileTransferDialog *dlg = FILE_TRANSFER_DIALOG (object); + + if (dlg->priv->cancellable) + { + g_object_unref (dlg->priv->cancellable); + dlg->priv->cancellable = NULL; + } + + G_OBJECT_CLASS (file_transfer_dialog_parent_class)->finalize (object); +} + +static void +file_transfer_dialog_set_prop (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FileTransferDialog *dlg = FILE_TRANSFER_DIALOG (object); + GFile *file; + gchar *str; + gchar *str2; + gchar *base; + gchar *escaped; + GtkWindow *parent; + guint n; + + switch (prop_id) + { + case PROP_FROM_URI: + file = g_file_new_for_uri (g_value_get_string (value)); + base = g_file_get_basename (file); + escaped = g_uri_escape_string (base, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE); + + str = g_strdup_printf (_("Copying '%s'"), escaped); + str2 = g_strdup_printf ("%s", str); + gtk_label_set_markup (GTK_LABEL (dlg->priv->status), str2); + + g_free (base); + g_free (escaped); + g_free (str); + g_free (str2); + g_object_unref (file); + break; + case PROP_TO_URI: + break; + case PROP_FRACTION_COMPLETE: + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dlg->priv->progress), g_value_get_double (value)); + break; + case PROP_NTH_URI: + n = g_value_get_uint (value); + if (n != dlg->priv->nth) + { + dlg->priv->nth = g_value_get_uint (value); + file_transfer_dialog_update_num_files (dlg); + } + break; + case PROP_TOTAL_URIS: + n = g_value_get_uint (value); + if (n != dlg->priv->nth) + { + dlg->priv->total = g_value_get_uint (value); + file_transfer_dialog_update_num_files (dlg); + } + break; + case PROP_PARENT: + parent = g_value_get_pointer (value); + if (parent) + { + gtk_window_set_title (GTK_WINDOW (dlg), gtk_window_get_title (parent)); + gtk_window_set_transient_for (GTK_WINDOW (dlg), parent); + } + else + gtk_window_set_title (GTK_WINDOW (dlg), + _("Copying files")); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +file_transfer_dialog_get_prop (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FileTransferDialog *dlg = FILE_TRANSFER_DIALOG (object); + + switch (prop_id) + { + case PROP_NTH_URI: + g_value_set_uint (value, dlg->priv->nth); + break; + case PROP_TOTAL_URIS: + g_value_set_uint (value, dlg->priv->total); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +file_transfer_dialog_class_init (FileTransferDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = file_transfer_dialog_finalize; + object_class->get_property = file_transfer_dialog_get_prop; + object_class->set_property = file_transfer_dialog_set_prop; + + GTK_DIALOG_CLASS (klass)->response = file_transfer_dialog_response; + + g_object_class_install_property + (object_class, PROP_PARENT, + g_param_spec_pointer ("parent", + _("Parent Window"), + _("Parent window of the dialog"), + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_FROM_URI, + g_param_spec_string ("from_uri", + _("From URI"), + _("URI currently transferring from"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_TO_URI, + g_param_spec_string ("to_uri", + _("To URI"), + _("URI currently transferring to"), + NULL, + G_PARAM_WRITABLE)); + + g_object_class_install_property + (object_class, PROP_FRACTION_COMPLETE, + g_param_spec_double ("fraction_complete", + _("Fraction completed"), + _("Fraction of transfer currently completed"), + 0, 1, 0, + G_PARAM_WRITABLE)); + + g_object_class_install_property + (object_class, PROP_NTH_URI, + g_param_spec_uint ("nth_uri", + _("Current URI index"), + _("Current URI index - starts from 1"), + 1, INT_MAX, 1, + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_TOTAL_URIS, + g_param_spec_uint ("total_uris", + _("Total URIs"), + _("Total number of URIs"), + 1, INT_MAX, 1, + G_PARAM_READWRITE)); + + file_transfer_dialog_signals[CANCEL] = + g_signal_new ("cancel", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + file_transfer_dialog_signals[DONE] = + g_signal_new ("done", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (FileTransferDialogPrivate)); +} + +static void +file_transfer_dialog_init (FileTransferDialog *dlg) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *progress_vbox; + GtkWidget *table; + char *markup; + GtkWidget *content_area; + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dlg)); + dlg->priv = FILE_TRANSFER_DIALOG_GET_PRIVATE (dlg); + dlg->priv->cancellable = g_cancellable_new (); + + gtk_container_set_border_width (GTK_CONTAINER (content_area), 4); + gtk_box_set_spacing (GTK_BOX (content_area), 4); + + gtk_widget_set_size_request (GTK_WIDGET (dlg), 350, -1); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0); + + dlg->priv->status = gtk_label_new (NULL); + markup = g_strconcat ("", _("Copying files"), "", NULL); + gtk_label_set_markup (GTK_LABEL (dlg->priv->status), markup); + g_free (markup); + + gtk_misc_set_alignment (GTK_MISC (dlg->priv->status), 0.0, 0.0); + + gtk_box_pack_start (GTK_BOX (vbox), dlg->priv->status, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + + gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (table), FALSE, FALSE, 0); + + progress_vbox = gtk_vbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), progress_vbox, FALSE, FALSE, 0); + + dlg->priv->progress = gtk_progress_bar_new (); + gtk_box_pack_start (GTK_BOX (progress_vbox), + dlg->priv->progress, FALSE, FALSE, 0); + + gtk_dialog_add_button (GTK_DIALOG (dlg), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dlg), 6); + + gtk_widget_show_all (content_area); +} + +GtkWidget* +file_transfer_dialog_new (void) +{ + return GTK_WIDGET (g_object_new (file_transfer_dialog_get_type (), + NULL)); +} + +GtkWidget* +file_transfer_dialog_new_with_parent (GtkWindow *parent) +{ + return GTK_WIDGET (g_object_new (file_transfer_dialog_get_type (), + "parent", parent, NULL)); +} + +static gboolean +file_transfer_job_update (gpointer user_data) +{ + FileTransferData *data = user_data; + gdouble fraction; + gdouble current_fraction; + + if (data->total_bytes == 0) + current_fraction = 0.0; + else + current_fraction = ((gdouble) data->current_bytes) / data->total_bytes; + + fraction = ((gdouble) data->current_file - 1) / data->total_files + + (1.0 / data->total_files) * current_fraction; + + g_object_set (data->dialog, + "from_uri", data->source, + "to_uri", data->target, + "nth_uri", data->current_file, + "fraction_complete", fraction, + NULL); + return FALSE; +} + +static void +file_transfer_job_progress (goffset current_bytes, + goffset total_bytes, + gpointer user_data) +{ + FileTransferData *data = user_data; + + data->current_bytes = current_bytes; + data->total_bytes = total_bytes; + + gdk_threads_enter (); + file_transfer_job_update (data); + gdk_threads_leave (); +} + +static void +file_transfer_job_destroy (FileTransferJob *job) +{ + g_object_unref (job->dialog); + g_slist_foreach (job->source_files, (GFunc) g_object_unref, NULL); + g_slist_foreach (job->target_files, (GFunc) g_object_unref, NULL); + g_slist_free (job->source_files); + g_slist_free (job->target_files); + if (job->overwrite_dialog != NULL) + gtk_widget_destroy (GTK_WIDGET (job->overwrite_dialog)); + g_free (job); +} + +static gboolean +file_transfer_dialog_done (FileTransferDialog *dialog) +{ + g_signal_emit (dialog, + file_transfer_dialog_signals[DONE], + 0, NULL); + return FALSE; +} + +static gboolean +file_transfer_dialog_cancel (FileTransferDialog *dialog) +{ + g_signal_emit (dialog, + file_transfer_dialog_signals[CANCEL], + 0, NULL); + return FALSE; +} + +static gboolean +file_transfer_dialog_overwrite (gpointer user_data) +{ + FileTransferData *data = user_data; + GtkDialog *dialog; + + dialog = data->overwrite_dialog; + + if (dialog != NULL) { + } else { + GtkWidget *button; + + dialog = GTK_DIALOG (gtk_message_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("File '%s' already exists. Do you want to overwrite it?"), + data->target)); + + gtk_dialog_add_button (dialog, _("_Skip"), GTK_RESPONSE_NO); + gtk_dialog_add_button (dialog, _("Overwrite _All"), GTK_RESPONSE_APPLY); + + button = gtk_button_new_with_label (_("_Overwrite")); + gtk_button_set_image (GTK_BUTTON (button), + gtk_image_new_from_stock (GTK_STOCK_APPLY, + GTK_ICON_SIZE_BUTTON)); + gtk_dialog_add_action_widget (dialog, button, GTK_RESPONSE_YES); + gtk_widget_show (button); + + data->overwrite_dialog = dialog; + } + + data->response = gtk_dialog_run (dialog); + + gtk_widget_hide (GTK_WIDGET (dialog)); + return FALSE; +} + +/* TODO: support transferring directories recursively? */ +static gboolean +file_transfer_job_schedule (GIOSchedulerJob *io_job, + GCancellable *cancellable, + FileTransferJob *job) +{ + GFile *source, *target; + gboolean success; + GFileCopyFlags copy_flags = G_FILE_COPY_NONE; + FileTransferData data; + GError *error; + gboolean retry; + + /* take the first file from the list and copy it */ + source = job->source_files->data; + job->source_files = g_slist_delete_link (job->source_files, job->source_files); + + target = job->target_files->data; + job->target_files = g_slist_delete_link (job->target_files, job->target_files); + + data.dialog = job->dialog; + data.overwrite_dialog = job->overwrite_dialog; + data.current_file = job->dialog->priv->nth + 1; + data.total_files = job->dialog->priv->total; + data.current_bytes = data.total_bytes = 0; + data.source = g_file_get_basename (source); + data.target = g_file_get_basename (target); + + g_io_scheduler_job_send_to_mainloop (io_job, + file_transfer_job_update, + &data, + NULL); + + if (job->options & FILE_TRANSFER_DIALOG_OVERWRITE) + copy_flags |= G_FILE_COPY_OVERWRITE; + + do { + retry = FALSE; + error = NULL; + success = g_file_copy (source, target, + copy_flags, + job->dialog->priv->cancellable, + file_transfer_job_progress, + &data, + &error); + + if (error != NULL) + { + if (error->domain == G_IO_ERROR && + error->code == G_IO_ERROR_EXISTS) + { + /* since the job is run in a thread, we cannot simply run + * a dialog here and need to defer it to the mainloop */ + data.response = GTK_RESPONSE_NONE; + g_io_scheduler_job_send_to_mainloop (io_job, + file_transfer_dialog_overwrite, + &data, + NULL); + + if (data.response == GTK_RESPONSE_YES) { + retry = TRUE; + copy_flags |= G_FILE_COPY_OVERWRITE; + } else if (data.response == GTK_RESPONSE_APPLY) { + retry = TRUE; + job->options |= FILE_TRANSFER_DIALOG_OVERWRITE; + copy_flags |= G_FILE_COPY_OVERWRITE; + } else { + success = TRUE; + } + + job->overwrite_dialog = data.overwrite_dialog; + } + g_error_free (error); + } + } while (retry); + + g_object_unref (source); + g_object_unref (target); + + g_free (data.source); + g_free (data.target); + + if (success) + { + if (job->source_files == NULL) + { + g_io_scheduler_job_send_to_mainloop_async (io_job, + (GSourceFunc) file_transfer_dialog_done, + g_object_ref (job->dialog), + g_object_unref); + return FALSE; + } + } + else /* error on copy or cancelled */ + { + g_io_scheduler_job_send_to_mainloop_async (io_job, + (GSourceFunc) file_transfer_dialog_cancel, + g_object_ref (job->dialog), + g_object_unref); + return FALSE; + } + + /* more work to do... */ + return TRUE; +} + +void +file_transfer_dialog_copy_async (FileTransferDialog *dlg, + GList *source_files, + GList *target_files, + FileTransferDialogOptions options, + int priority) +{ + FileTransferJob *job; + GList *l; + guint n; + + job = g_new0 (FileTransferJob, 1); + job->dialog = g_object_ref (dlg); + job->options = options; + + /* we need to copy the list contents for private use */ + n = 0; + for (l = g_list_last (source_files); l; l = l->prev, ++n) + { + job->source_files = g_slist_prepend (job->source_files, + g_object_ref (l->data)); + } + for (l = g_list_last (target_files); l; l = l->prev) + { + job->target_files = g_slist_prepend (job->target_files, + g_object_ref (l->data)); + } + + g_object_set (dlg, "total_uris", n, NULL); + + g_io_scheduler_push_job ((GIOSchedulerJobFunc) file_transfer_job_schedule, + job, + (GDestroyNotify) file_transfer_job_destroy, + priority, + dlg->priv->cancellable); +} diff --git a/capplets/common/file-transfer-dialog.h b/capplets/common/file-transfer-dialog.h new file mode 100644 index 00000000..0b867005 --- /dev/null +++ b/capplets/common/file-transfer-dialog.h @@ -0,0 +1,73 @@ +/* -*- mode: c; style: linux -*- */ + +/* file-transfer-dialog.h + * Copyright (C) 2002 Ximian, Inc. + * + * Written by Rachel Hestilow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __FILE_TRANSFER_DIALOG_H__ +#define __FILE_TRANSFER_DIALOG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FILE_TRANSFER_DIALOG(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, file_transfer_dialog_get_type (), FileTransferDialog) +#define FILE_TRANSFER_DIALOG_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, file_transfer_dialog_get_type (), FileTransferDialogClass) +#define IS_FILE_TRANSFER_DIALOG(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, file_transfer_dialog_get_type ()) + +typedef struct _FileTransferDialog FileTransferDialog; +typedef struct _FileTransferDialogClass FileTransferDialogClass; +typedef struct _FileTransferDialogPrivate FileTransferDialogPrivate; + +typedef enum { + FILE_TRANSFER_DIALOG_DEFAULT = 1 << 0, + FILE_TRANSFER_DIALOG_OVERWRITE = 1 << 1 +} FileTransferDialogOptions; + +struct _FileTransferDialog +{ + GtkDialog dialog; + + FileTransferDialogPrivate *priv; +}; + +struct _FileTransferDialogClass +{ + GtkDialogClass parent_class; +}; + +GType file_transfer_dialog_get_type (void); +GtkWidget* file_transfer_dialog_new (void); +GtkWidget* file_transfer_dialog_new_with_parent (GtkWindow *parent); + +void file_transfer_dialog_copy_async (FileTransferDialog *dlg, + GList *source_files, + GList *target_files, + FileTransferDialogOptions options, + int priority); + + +#ifdef __cplusplus +} +#endif + +#endif /* __FILE_TRANSFER_DIALOG_H__ */ diff --git a/capplets/common/gtkrc-utils.c b/capplets/common/gtkrc-utils.c new file mode 100644 index 00000000..be03faa5 --- /dev/null +++ b/capplets/common/gtkrc-utils.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#define INCLUDE_SYMBOL ((gpointer) 1) +#define ENGINE_SYMBOL ((gpointer) 2) +#define COLOR_SCHEME_SYMBOL ((gpointer) 3) + +gchar* gtkrc_find_named(const gchar* name) +{ + /* find the gtkrc of the named theme + * taken from gtkrc.c (gtk_rc_parse_named) + */ + gchar* path = NULL; + const gchar* home_dir; + const gchar* subpath = "gtk-2.0" G_DIR_SEPARATOR_S "gtkrc"; + + /* First look in the users home directory + */ + home_dir = g_get_home_dir(); + + if (home_dir) + { + path = g_build_filename(home_dir, ".themes", name, subpath, NULL); + + if (!g_file_test (path, G_FILE_TEST_EXISTS)) + { + g_free (path); + path = NULL; + } + } + + if (!path) + { + gchar* theme_dir = gtk_rc_get_theme_dir(); + path = g_build_filename(theme_dir, name, subpath, NULL); + g_free(theme_dir); + + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + { + g_free (path); + path = NULL; + } + } + + return path; + +} + +void gtkrc_get_details(gchar* filename, GSList** engines, GSList** symbolic_colors) +{ + gint file = -1; + GSList* files = NULL; + GSList* read_files = NULL; + GTokenType token; + GScanner *scanner = g_scanner_new (NULL); + + g_scanner_scope_add_symbol (scanner, 0, "include", INCLUDE_SYMBOL); + + if (engines != NULL) + { + g_scanner_scope_add_symbol (scanner, 0, "engine", ENGINE_SYMBOL); + } + + files = g_slist_prepend (files, g_strdup (filename)); + + while (files != NULL) + { + filename = files->data; + files = g_slist_delete_link (files, files); + + if (filename == NULL) + continue; + + if (g_slist_find_custom (read_files, filename, (GCompareFunc) strcmp)) + { + g_warning ("Recursion in the gtkrc detected!"); + g_free (filename); + continue; /* skip this file since we've done it before... */ + } + + read_files = g_slist_prepend (read_files, filename); + + file = g_open (filename, O_RDONLY); + if (file == -1) + { + g_warning ("Could not open file \"%s\"", filename); + } + else + { + g_scanner_input_file (scanner, file); + while ((token = g_scanner_get_next_token (scanner)) != G_TOKEN_EOF) + { + GTokenType string_token; + if (token == '@') + { + if (symbolic_colors == NULL) + continue; + token = g_scanner_get_next_token (scanner); + if (token != G_TOKEN_IDENTIFIER) + continue; + if (!g_slist_find_custom (*symbolic_colors, scanner->value.v_identifier, (GCompareFunc) strcmp)) + *symbolic_colors = g_slist_append (*symbolic_colors, g_strdup (scanner->value.v_identifier)); + continue; + } + + if (token != G_TOKEN_SYMBOL) + continue; + + if (scanner->value.v_symbol == INCLUDE_SYMBOL) + { + string_token = g_scanner_get_next_token (scanner); + if (string_token != G_TOKEN_STRING) + continue; + if (g_path_is_absolute (scanner->value.v_string)) + { + files = g_slist_prepend (files, g_strdup (scanner->value.v_string)); + } + else + { + gchar *basedir = g_path_get_dirname (filename); + files = g_slist_prepend (files, g_build_path (G_DIR_SEPARATOR_S, basedir, scanner->value.v_string, NULL)); + g_free (basedir); + } + } + else if (scanner->value.v_symbol == ENGINE_SYMBOL) + { + string_token = g_scanner_get_next_token (scanner); + if (string_token != G_TOKEN_STRING || scanner->value.v_string[0] == '\0') + continue; + if (!g_slist_find_custom (*engines, scanner->value.v_string, (GCompareFunc) strcmp)) + *engines = g_slist_append (*engines, g_strdup (scanner->value.v_string)); + } + + } + close (file); + } + } + + g_slist_foreach (read_files, (GFunc) g_free, NULL); + g_slist_free (read_files); + + g_scanner_destroy (scanner); +} + + +gchar * +gtkrc_get_color_scheme (const gchar *gtkrc_file) +{ + gint file = -1; + gchar *result = NULL; + GSList *files = NULL; + GSList *read_files = NULL; + GTokenType token; + GScanner *scanner = gtk_rc_scanner_new (); + + g_scanner_scope_add_symbol (scanner, 0, "include", INCLUDE_SYMBOL); + g_scanner_scope_add_symbol (scanner, 0, "gtk_color_scheme", COLOR_SCHEME_SYMBOL); + g_scanner_scope_add_symbol (scanner, 0, "gtk-color-scheme", COLOR_SCHEME_SYMBOL); + + files = g_slist_prepend (files, g_strdup (gtkrc_file)); + while (files != NULL) + { + gchar *filename = files->data; + files = g_slist_delete_link (files, files); + + if (filename == NULL) + continue; + + if (g_slist_find_custom (read_files, filename, (GCompareFunc) strcmp)) + { + g_warning ("Recursion in the gtkrc detected!"); + g_free (filename); + continue; /* skip this file since we've done it before... */ + } + + read_files = g_slist_prepend (read_files, filename); + + file = g_open (filename, O_RDONLY); + if (file == -1) + { + g_warning ("Could not open file \"%s\"", filename); + } + else + { + g_scanner_input_file (scanner, file); + while ((token = g_scanner_get_next_token (scanner)) != G_TOKEN_EOF) + { + if (GINT_TO_POINTER (token) == COLOR_SCHEME_SYMBOL) + { + if (g_scanner_get_next_token (scanner) == '=') + { + token = g_scanner_get_next_token (scanner); + if (token == G_TOKEN_STRING) + { + g_free (result); + result = g_strdup (scanner->value.v_string); + } + } + } + } + close (file); + } + } + + g_slist_foreach (read_files, (GFunc) g_free, NULL); + g_slist_free (read_files); + + g_scanner_destroy (scanner); + return result; +} + +gchar* gtkrc_get_color_scheme_for_theme(const gchar* theme_name) +{ + /* try to find the color scheme from the gtkrc */ + gchar* gtkrc_file; + gchar* scheme = NULL; + + gtkrc_file = gtkrc_find_named(theme_name); + + if (gtkrc_file) + { + scheme = gtkrc_get_color_scheme(gtkrc_file); + g_free(gtkrc_file); + } + + return scheme; +} diff --git a/capplets/common/gtkrc-utils.h b/capplets/common/gtkrc-utils.h new file mode 100644 index 00000000..1023fe14 --- /dev/null +++ b/capplets/common/gtkrc-utils.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Thomas Wood + * Jens Granseuer + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void gtkrc_get_details (gchar *filename, GSList **engines, GSList **symbolic_colors); +gchar * gtkrc_find_named (const gchar *name); +gchar * gtkrc_get_color_scheme (const gchar *filename); +gchar * gtkrc_get_color_scheme_for_theme (const gchar *theme_name); diff --git a/capplets/common/mate-theme-apply.c b/capplets/common/mate-theme-apply.c new file mode 100644 index 00000000..b1c772e8 --- /dev/null +++ b/capplets/common/mate-theme-apply.c @@ -0,0 +1,136 @@ +/* -*- mode: C; c-basic-offset: 4 -*- + * themus - utilities for MATE themes + * Copyright (C) 2002 Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "mate-theme-apply.h" +#include "gtkrc-utils.h" + +#define GTK_THEME_KEY "/desktop/mate/interface/gtk_theme" +#define COLOR_SCHEME_KEY "/desktop/mate/interface/gtk_color_scheme" +#define ICON_THEME_KEY "/desktop/mate/interface/icon_theme" +#define FONT_KEY "/desktop/mate/interface/font_name" +#define CURSOR_FONT_KEY "/desktop/mate/peripherals/mouse/cursor_font" +#define CURSOR_THEME_KEY "/desktop/mate/peripherals/mouse/cursor_theme" +#define CURSOR_SIZE_KEY "/desktop/mate/peripherals/mouse/cursor_size" +#define NOTIFICATION_THEME_KEY "/apps/notification-daemon/theme" + +#define compare(x,y) (!x && y) || (x && !y) || (x && y && strcmp (x, y)) + +void +mate_meta_theme_set (MateThemeMetaInfo *meta_theme_info) +{ + MateConfClient *client; + gchar *old_key; + gint old_key_int; + MateWindowManager *window_manager; + MateWMSettings wm_settings; + + mate_wm_manager_init (); + + window_manager = mate_wm_manager_get_current (gdk_display_get_default_screen (gdk_display_get_default ())); + + client = mateconf_client_get_default (); + + /* Set the gtk+ key */ + old_key = mateconf_client_get_string (client, GTK_THEME_KEY, NULL); + if (compare (old_key, meta_theme_info->gtk_theme_name)) + { + mateconf_client_set_string (client, GTK_THEME_KEY, meta_theme_info->gtk_theme_name, NULL); + } + g_free (old_key); + + /* Set the color scheme key */ + old_key = mateconf_client_get_string (client, COLOR_SCHEME_KEY, NULL); + if (compare (old_key, meta_theme_info->gtk_color_scheme)) + { + /* only save the color scheme if it differs from the default + scheme for the selected gtk theme */ + gchar *newval, *gtkcols; + + newval = meta_theme_info->gtk_color_scheme; + gtkcols = gtkrc_get_color_scheme_for_theme (meta_theme_info->gtk_theme_name); + + if (newval == NULL || !strcmp (newval, "") || + mate_theme_color_scheme_equal (newval, gtkcols)) + { + mateconf_client_unset (client, COLOR_SCHEME_KEY, NULL); + } + else + { + mateconf_client_set_string (client, COLOR_SCHEME_KEY, newval, NULL); + } + g_free (gtkcols); + } + g_free (old_key); + + /* Set the wm key */ + wm_settings.flags = MATE_WM_SETTING_THEME; + wm_settings.theme = meta_theme_info->marco_theme_name; + if (window_manager) + mate_window_manager_change_settings (window_manager, &wm_settings); + + /* set the icon theme */ + old_key = mateconf_client_get_string (client, ICON_THEME_KEY, NULL); + if (compare (old_key, meta_theme_info->icon_theme_name)) + { + mateconf_client_set_string (client, ICON_THEME_KEY, meta_theme_info->icon_theme_name, NULL); + } + g_free (old_key); + + /* set the notification theme */ + if (meta_theme_info->notification_theme_name != NULL) + { + old_key = mateconf_client_get_string (client, NOTIFICATION_THEME_KEY, NULL); + if (compare (old_key, meta_theme_info->notification_theme_name)) + { + mateconf_client_set_string (client, NOTIFICATION_THEME_KEY, meta_theme_info->notification_theme_name, NULL); + } + g_free (old_key); + } + + /* Set the cursor theme key */ +#ifdef HAVE_XCURSOR + old_key = mateconf_client_get_string (client, CURSOR_THEME_KEY, NULL); + if (compare (old_key, meta_theme_info->cursor_theme_name)) + { + mateconf_client_set_string (client, CURSOR_THEME_KEY, meta_theme_info->cursor_theme_name, NULL); + } + + old_key_int = mateconf_client_get_int (client, CURSOR_SIZE_KEY, NULL); + if (old_key_int != meta_theme_info->cursor_size) + { + mateconf_client_set_int (client, CURSOR_SIZE_KEY, meta_theme_info->cursor_size, NULL); + } +#else + old_key = mateconf_client_get_string (client, CURSOR_FONT_KEY, NULL); + if (compare (old_key, meta_theme_info->cursor_theme_name)) + { + mateconf_client_set_string (client, CURSOR_FONT_KEY, meta_theme_info->cursor_theme_name, NULL); + } +#endif + + g_free (old_key); + g_object_unref (client); +} diff --git a/capplets/common/mate-theme-apply.h b/capplets/common/mate-theme-apply.h new file mode 100644 index 00000000..d89d8d06 --- /dev/null +++ b/capplets/common/mate-theme-apply.h @@ -0,0 +1,33 @@ +/* mate-theme-info.h - MATE Theme information + + Copyright (C) 2002 Jonathan Blandford + All rights reserved. + + This file is part of the Mate Library. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ +/* + @NOTATION@ + */ + +#ifndef THEME_APPLY_H +#define THEME_APPLY_H + +#include "mate-theme-info.h" + +void mate_meta_theme_set (MateThemeMetaInfo *meta_theme_info); + +#endif /* THEME_APPLY_H */ diff --git a/capplets/common/mate-theme-info.c b/capplets/common/mate-theme-info.c new file mode 100644 index 00000000..aead0b34 --- /dev/null +++ b/capplets/common/mate-theme-info.c @@ -0,0 +1,1988 @@ +/* mate-theme-info.c - MATE Theme information + * + * Copyright (C) 2002 Jonathan Blandford + * Copyright (C) 2011 Perberos + * All rights reserved. + * + * This file is part of the Mate Library. + * + * The Mate Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Mate Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Mate Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mate-theme-info.h" +#include "gtkrc-utils.h" + +#ifdef HAVE_XCURSOR + #include +#endif + +#define THEME_NAME "X-GNOME-Metatheme/Name" +#define THEME_COMMENT "X-GNOME-Metatheme/Comment" +#define GTK_THEME_KEY "X-GNOME-Metatheme/GtkTheme" +#define GTK_COLOR_SCHEME_KEY "X-GNOME-Metatheme/GtkColorScheme" +#define MARCO_THEME_KEY "X-GNOME-Metatheme/MetacityTheme" +#define ICON_THEME_KEY "X-GNOME-Metatheme/IconTheme" +#define CURSOR_THEME_KEY "X-GNOME-Metatheme/CursorTheme" +#define NOTIFICATION_THEME_KEY "X-GNOME-Metatheme/NotificationTheme" +#define CURSOR_SIZE_KEY "X-GNOME-Metatheme/CursorSize" +#define SOUND_THEME_KEY "X-GNOME-Metatheme/SoundTheme" +#define APPLICATION_FONT_KEY "X-GNOME-Metatheme/ApplicationFont" +#define DOCUMENTS_FONT_KEY "X-GNOME-Metatheme/DocumentsFont" +#define DESKTOP_FONT_KEY "X-GNOME-Metatheme/DesktopFont" +#define WINDOWTITLE_FONT_KEY "X-GNOME-Metatheme/WindowTitleFont" +#define MONOSPACE_FONT_KEY "X-GNOME-Metatheme/MonospaceFont" +#define BACKGROUND_IMAGE_KEY "X-GNOME-Metatheme/BackgroundImage" +#define HIDDEN_KEY "X-GNOME-Metatheme/Hidden" + +/* Terminology used in this lib: + * + * /usr/share/themes, ~/.themes -- top_theme_dir + * top_theme_dir/theme_name/ -- common_theme_dir + * /usr/share/icons, ~/.icons -- top_icon_theme_dir + * top_icon_theme_dir/theme_name/ -- icon_common_theme_dir + * + */ + +typedef struct _ThemeCallbackData { + ThemeChangedCallback func; + gpointer data; +} ThemeCallbackData; + +typedef struct { + GFileMonitor* common_theme_dir_handle; + GFileMonitor* gtk2_dir_handle; + GFileMonitor* keybinding_dir_handle; + GFileMonitor* marco_dir_handle; + gint priority; +} CommonThemeDirMonitorData; + +typedef struct { + GFileMonitor* common_icon_theme_dir_handle; + gint priority; +} CommonIconThemeDirMonitorData; + +typedef struct { + GHashTable* handle_hash; + gint priority; +} CallbackTuple; + + +/* Hash tables */ + +/* The hashes_by_dir are indexed by an escaped uri of the common_theme_dir that + * that particular theme is part of. The data pointed to by them is a + * MateTheme{Meta,Icon,}Info struct. Note that the uri is of the form + * "file:///home/username/.themes/foo", and not "/home/username/.themes/foo" + */ + +/* The hashes_by_name are hashed by the index of the theme. The data pointed to + * by them is a GList whose data elements are MateTheme{Meta,Icon,}Info + * structs. This is because a theme can be found both in the users ~/.theme as + * well as globally in $prefix. All access to them must be done via helper + * functions. + */ +static GList* callbacks = NULL; + +static GHashTable* meta_theme_hash_by_uri; +static GHashTable* meta_theme_hash_by_name; +static GHashTable* icon_theme_hash_by_uri; +static GHashTable* icon_theme_hash_by_name; +static GHashTable* cursor_theme_hash_by_uri; +static GHashTable* cursor_theme_hash_by_name; +static GHashTable* theme_hash_by_uri; +static GHashTable* theme_hash_by_name; +static gboolean initting = FALSE; + +/* private functions */ +static gint safe_strcmp(const gchar* a_str, const gchar* b_str) +{ + if (a_str && b_str) + { + return strcmp(a_str, b_str); + } + else + { + return a_str - b_str; + } +} + +static GFileType +get_file_type (GFile *file) +{ + GFileType file_type = G_FILE_TYPE_UNKNOWN; + GFileInfo *file_info; + + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + if (file_info != NULL) { + file_type = g_file_info_get_file_type (file_info); + g_object_unref (file_info); + } + + return file_type; +} + +static void +add_theme_to_hash_by_name (GHashTable *hash_table, + gpointer data) +{ + MateThemeCommonInfo *info = data; + GList *list; + + list = g_hash_table_lookup (hash_table, info->name); + if (list == NULL) { + list = g_list_append (list, info); + } else { + GList *list_ptr = list; + gboolean added = FALSE; + + while (list_ptr) { + gint theme_priority; + + theme_priority = ((MateThemeCommonInfo *) list_ptr->data)->priority; + + if (theme_priority == info->priority) { + /* Swap it in */ + list_ptr->data = info; + added = TRUE; + break; + } else if (theme_priority > info->priority) { + list = g_list_insert_before (list, list_ptr, info); + added = TRUE; + break; + } + list_ptr = list_ptr->next; + } + if (!added) + list = g_list_append (list, info); + } + g_hash_table_insert (hash_table, g_strdup (info->name), list); +} + +static void +remove_theme_from_hash_by_name (GHashTable *hash_table, + gpointer data) +{ + MateThemeCommonInfo *info = data; + GList *list; + + list = g_hash_table_lookup (hash_table, info->name); + + list = g_list_remove (list, info); + if (list == NULL) + g_hash_table_remove (hash_table, info->name); + else + g_hash_table_insert (hash_table, g_strdup (info->name), list); +} + +static MateThemeCommonInfo * +get_theme_from_hash_by_name (GHashTable *hash_table, + const gchar *name, + gint priority) +{ + GList *list; + + list = g_hash_table_lookup (hash_table, name); + + /* -1 implies return the first one */ + if (priority == -1) { + return list ? list->data : NULL; + } + + while (list) { + MateThemeCommonInfo *info = (MateThemeCommonInfo *) list->data; + + if (info->priority == priority) + return info; + + list = list->next; + } + return NULL; +} + +static gint +theme_compare (MateThemeCommonInfo *a, + MateThemeCommonInfo *b) +{ + gint cmp; + + g_return_val_if_fail (a->type == b->type, a->type - b->type); + + switch (a->type) { + case MATE_THEME_TYPE_METATHEME: + cmp = mate_theme_meta_info_compare ( + (MateThemeMetaInfo *) a, (MateThemeMetaInfo *) b); + break; + case MATE_THEME_TYPE_ICON: + cmp = mate_theme_icon_info_compare ( + (MateThemeIconInfo *) a, (MateThemeIconInfo *) b); + break; + case MATE_THEME_TYPE_CURSOR: + cmp = mate_theme_cursor_info_compare ( + (MateThemeCursorInfo *) a, (MateThemeCursorInfo *) b); + break; + default: + /* not supported at this time */ + g_assert_not_reached (); + } + + return cmp; +} + +static void +theme_free (MateThemeCommonInfo *info) +{ + switch (info->type) { + case MATE_THEME_TYPE_METATHEME: + mate_theme_meta_info_free ((MateThemeMetaInfo *) info); + break; + case MATE_THEME_TYPE_ICON: + mate_theme_icon_info_free ((MateThemeIconInfo *) info); + break; + case MATE_THEME_TYPE_REGULAR: + mate_theme_info_free ((MateThemeInfo *) info); + break; + case MATE_THEME_TYPE_CURSOR: + mate_theme_cursor_info_free ((MateThemeCursorInfo *) info); + break; + default: + g_assert_not_reached (); + } +} + +GQuark mate_theme_info_error_quark(void) +{ + return g_quark_from_static_string("mate-theme-info-error-quark"); +} + +MateThemeMetaInfo* mate_theme_read_meta_theme(GFile* meta_theme_uri) +{ + MateThemeMetaInfo* meta_theme_info; + GFile* common_theme_dir_uri; + MateDesktopItem* meta_theme_ditem; + gchar* meta_theme_file; + const gchar* str; + gchar* scheme; + + meta_theme_file = g_file_get_uri(meta_theme_uri); + meta_theme_ditem = mate_desktop_item_new_from_uri(meta_theme_file, 0, NULL); + g_free(meta_theme_file); + + if (meta_theme_ditem == NULL) + return NULL; + + common_theme_dir_uri = g_file_get_parent(meta_theme_uri); + meta_theme_info = mate_theme_meta_info_new(); + meta_theme_info->path = g_file_get_path(meta_theme_uri); + meta_theme_info->name = g_file_get_basename(common_theme_dir_uri); + g_object_unref(common_theme_dir_uri); + + str = mate_desktop_item_get_localestring(meta_theme_ditem, THEME_NAME); + + if (!str) + { + str = mate_desktop_item_get_localestring(meta_theme_ditem, MATE_DESKTOP_ITEM_NAME); + if (!str) + { /* shouldn't reach */ + mate_theme_meta_info_free(meta_theme_info); + return NULL; + } + } + + meta_theme_info->readable_name = g_strdup(str); + + str = mate_desktop_item_get_localestring(meta_theme_ditem, THEME_COMMENT); + + if (str == NULL) + str = mate_desktop_item_get_localestring(meta_theme_ditem, MATE_DESKTOP_ITEM_COMMENT); + + if (str != NULL) + meta_theme_info->comment = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, MATE_DESKTOP_ITEM_ICON); + + if (str != NULL) + meta_theme_info->icon_file = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, GTK_THEME_KEY); + + if (str == NULL) + { + mate_theme_meta_info_free(meta_theme_info); + return NULL; + } + meta_theme_info->gtk_theme_name = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, GTK_COLOR_SCHEME_KEY); + + if (str == NULL || str[0] == '\0') + scheme = gtkrc_get_color_scheme_for_theme(meta_theme_info->gtk_theme_name); + else + scheme = g_strdup(str); + + if (scheme != NULL) + { + meta_theme_info->gtk_color_scheme = scheme; + + for (; *scheme != '\0'; scheme++) + if (*scheme == ',') + *scheme = '\n'; + } + + str = mate_desktop_item_get_string (meta_theme_ditem, MARCO_THEME_KEY); + + if (str == NULL) + { + mate_theme_meta_info_free (meta_theme_info); + return NULL; + } + + meta_theme_info->marco_theme_name = g_strdup (str); + + str = mate_desktop_item_get_string(meta_theme_ditem, ICON_THEME_KEY); + + if (str == NULL) + { + mate_theme_meta_info_free(meta_theme_info); + return NULL; + } + + meta_theme_info->icon_theme_name = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, NOTIFICATION_THEME_KEY); + + if (str != NULL) + meta_theme_info->notification_theme_name = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, CURSOR_THEME_KEY); + + if (str != NULL) + { + meta_theme_info->cursor_theme_name = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, CURSOR_SIZE_KEY); + + if (str) + meta_theme_info->cursor_size = (int) g_ascii_strtoll(str, NULL, 10); + else + meta_theme_info->cursor_size = 18; + } + else + { + meta_theme_info->cursor_theme_name = g_strdup("default"); + meta_theme_info->cursor_size = 18; + } + + str = mate_desktop_item_get_string(meta_theme_ditem, APPLICATION_FONT_KEY); + + if (str != NULL) + meta_theme_info->application_font = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, DOCUMENTS_FONT_KEY); + + if (str != NULL) + meta_theme_info->documents_font = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, DESKTOP_FONT_KEY); + + if (str != NULL) + meta_theme_info->desktop_font = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, WINDOWTITLE_FONT_KEY); + + if (str != NULL) + meta_theme_info->windowtitle_font = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, MONOSPACE_FONT_KEY); + + if (str != NULL) + meta_theme_info->monospace_font = g_strdup(str); + + str = mate_desktop_item_get_string(meta_theme_ditem, BACKGROUND_IMAGE_KEY); + + if (str != NULL) + meta_theme_info->background_image = g_strdup(str); + + meta_theme_info->hidden = mate_desktop_item_get_boolean(meta_theme_ditem, HIDDEN_KEY); + + mate_desktop_item_unref(meta_theme_ditem); + + return meta_theme_info; +} + +static MateThemeIconInfo * +read_icon_theme (GFile *icon_theme_uri) +{ + MateThemeIconInfo *icon_theme_info; + MateDesktopItem *icon_theme_ditem; + gchar *icon_theme_file; + gchar *dir_name; + const gchar *name; + const gchar *directories; + + icon_theme_file = g_file_get_uri (icon_theme_uri); + icon_theme_ditem = mate_desktop_item_new_from_uri (icon_theme_file, 0, NULL); + g_free (icon_theme_file); + + if (icon_theme_ditem == NULL) + return NULL; + + name = mate_desktop_item_get_localestring (icon_theme_ditem, "Icon Theme/Name"); + if (!name) { + name = mate_desktop_item_get_localestring (icon_theme_ditem, MATE_DESKTOP_ITEM_NAME); + if (!name) { + mate_desktop_item_unref (icon_theme_ditem); + return NULL; + } + } + + /* If index.theme has no Directories entry, it is only a cursor theme */ + directories = mate_desktop_item_get_string (icon_theme_ditem, "Icon Theme/Directories"); + if (directories == NULL) { + mate_desktop_item_unref (icon_theme_ditem); + return NULL; + } + + icon_theme_info = mate_theme_icon_info_new (); + icon_theme_info->readable_name = g_strdup (name); + icon_theme_info->path = g_file_get_path (icon_theme_uri); + icon_theme_info->hidden = mate_desktop_item_get_boolean (icon_theme_ditem, "Icon Theme/Hidden"); + dir_name = g_path_get_dirname (icon_theme_info->path); + icon_theme_info->name = g_path_get_basename (dir_name); + g_free (dir_name); + + mate_desktop_item_unref (icon_theme_ditem); + + return icon_theme_info; +} + +#ifdef HAVE_XCURSOR +static void +add_default_cursor_theme () +{ + MateThemeCursorInfo *theme_info; + + theme_info = mate_theme_cursor_info_new (); + theme_info->path = g_strdup ("builtin"); + theme_info->name = g_strdup ("default"); + theme_info->readable_name = g_strdup (_("Default Pointer")); + theme_info->sizes = g_array_sized_new (FALSE, FALSE, sizeof (gint), 0); + + g_hash_table_insert (cursor_theme_hash_by_uri, theme_info->path, theme_info); + add_theme_to_hash_by_name (cursor_theme_hash_by_name, theme_info); +} + +static GdkPixbuf * +gdk_pixbuf_from_xcursor_image (XcursorImage *cursor) +{ + GdkPixbuf *pixbuf; +#define BUF_SIZE sizeof(guint32) * cursor->width * cursor->height + guchar *buf = g_malloc0 (BUF_SIZE); + guchar *it; + + for (it = buf; it < (buf + BUF_SIZE); it += 4) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* on little endianess it's BGRA to RGBA */ + it[0] = ((guchar *) (cursor->pixels))[it - buf + 2]; + it[1] = ((guchar *) (cursor->pixels))[it - buf + 1]; + it[2] = ((guchar *) (cursor->pixels))[it - buf + 0]; + it[3] = ((guchar *) (cursor->pixels))[it - buf + 3]; +#else + /* on big endianess it's ARGB to RGBA */ + it[0] = ((guchar *) cursor->pixels)[it - buf + 1]; + it[1] = ((guchar *) cursor->pixels)[it - buf + 2]; + it[2] = ((guchar *) cursor->pixels)[it - buf + 3]; + it[3] = ((guchar *) cursor->pixels)[it - buf + 0]; +#endif + } + + pixbuf = gdk_pixbuf_new_from_data ((const guchar *) buf, + GDK_COLORSPACE_RGB, TRUE, 8, + cursor->width, cursor->height, + cursor->width * 4, + (GdkPixbufDestroyNotify) g_free, + NULL); + + if (!pixbuf) + g_free (buf); + + return pixbuf; +} + +static MateThemeCursorInfo * +read_cursor_theme (GFile *cursor_theme_uri) +{ + MateThemeCursorInfo *cursor_theme_info = NULL; + GFile *parent_uri, *cursors_uri; + + const gint filter_sizes[] = { 12, 16, 24, 32, 36, 40, 48, 64 }; + const gint num_sizes = G_N_ELEMENTS (filter_sizes); + + parent_uri = g_file_get_parent (cursor_theme_uri); + cursors_uri = g_file_get_child (parent_uri, "cursors"); + + if (get_file_type (cursors_uri) == G_FILE_TYPE_DIRECTORY) { + GArray *sizes; + XcursorImage *cursor; + GdkPixbuf *thumbnail = NULL; + gchar *name; + gint i; + + name = g_file_get_basename (parent_uri); + + sizes = g_array_sized_new (FALSE, FALSE, sizeof (gint), num_sizes); + + for (i = 0; i < num_sizes; ++i) { + cursor = XcursorLibraryLoadImage ("left_ptr", name, filter_sizes[i]); + + if (cursor) { + if (cursor->size == filter_sizes[i]) { + g_array_append_val (sizes, filter_sizes[i]); + + if (thumbnail == NULL && i >= 1) + thumbnail = gdk_pixbuf_from_xcursor_image (cursor); + } + + XcursorImageDestroy (cursor); + } + } + + if (sizes->len == 0) { + g_array_free (sizes, TRUE); + g_free (name); + } else { + MateDesktopItem *cursor_theme_ditem; + gchar *cursor_theme_file; + + if (!thumbnail) { + cursor = XcursorLibraryLoadImage ("left_ptr", name, + g_array_index (sizes, gint, 0)); + if (cursor) { + thumbnail = gdk_pixbuf_from_xcursor_image (cursor); + XcursorImageDestroy (cursor); + } + } + + cursor_theme_info = mate_theme_cursor_info_new (); + cursor_theme_info->path = g_file_get_path (parent_uri); + cursor_theme_info->name = name; + cursor_theme_info->sizes = sizes; + cursor_theme_info->thumbnail = thumbnail; + + cursor_theme_file = g_file_get_path (cursor_theme_uri); + cursor_theme_ditem = mate_desktop_item_new_from_file (cursor_theme_file, 0, NULL); + g_free (cursor_theme_file); + + if (cursor_theme_ditem != NULL) { + const gchar *readable_name; + + readable_name = mate_desktop_item_get_string (cursor_theme_ditem, + "Icon Theme/Name"); + if (readable_name) + cursor_theme_info->readable_name = g_strdup (readable_name); + else + cursor_theme_info->readable_name = g_strdup (name); + + cursor_theme_info->hidden = mate_desktop_item_get_boolean (cursor_theme_ditem, + "Icon Theme/Hidden"); + + mate_desktop_item_unref (cursor_theme_ditem); + } else { + cursor_theme_info->readable_name = g_strdup (name); + } + } + } + + g_object_unref (cursors_uri); + g_object_unref (parent_uri); + + return cursor_theme_info; +} + +#else /* !HAVE_XCURSOR */ + +static gchar * +read_current_cursor_font (void) +{ + DIR *dir; + gchar *dir_name; + struct dirent *file_dirent; + + dir_name = g_build_filename (g_get_home_dir (), ".mate2/share/cursor-fonts", NULL); + if (! g_file_test (dir_name, G_FILE_TEST_EXISTS)) { + g_free (dir_name); + return NULL; + } + + dir = opendir (dir_name); + + while ((file_dirent = readdir (dir)) != NULL) { + struct stat st; + gchar *link_name; + + link_name = g_build_filename (dir_name, file_dirent->d_name, NULL); + if (lstat (link_name, &st)) { + g_free (link_name); + continue; + } + + if (S_ISLNK (st.st_mode)) { + gint length; + gchar target[256]; + + length = readlink (link_name, target, 255); + if (length > 0) { + gchar *retval; + target[length] = '\0'; + retval = g_strdup (target); + g_free (link_name); + closedir (dir); + return retval; + } + + } + g_free (link_name); + } + g_free (dir_name); + closedir (dir); + return NULL; +} + +static void +read_cursor_fonts (void) +{ + gchar *cursor_font; + gint i; + + const gchar *builtins[][4] = { + { + "mate/cursor-fonts/cursor-normal.pcf", + N_("Default Pointer"), + N_("Default Pointer - Current"), + "mouse-cursor-normal.png" + }, { + "mate/cursor-fonts/cursor-white.pcf", + N_("White Pointer"), + N_("White Pointer - Current"), + "mouse-cursor-white.png" + }, { + "mate/cursor-fonts/cursor-large.pcf", + N_("Large Pointer"), + N_("Large Pointer - Current"), + "mouse-cursor-normal-large.png" + }, { + "mate/cursor-fonts/cursor-large-white.pcf", + N_("Large White Pointer - Current"), + N_("Large White Pointer"), + "mouse-cursor-white-large.png" + } + }; + + cursor_font = read_current_cursor_font(); + + if (!cursor_font) + cursor_font = g_strdup (builtins[0][0]); + + for (i = 0; i < G_N_ELEMENTS (builtins); i++) { + MateThemeCursorInfo *theme_info; + gchar *filename; + + theme_info = mate_theme_cursor_info_new (); + + filename = g_build_filename (MATECC_DATA_DIR, "pixmaps", builtins[i][3], NULL); + theme_info->thumbnail = gdk_pixbuf_new_from_file (filename, NULL); + g_free (filename); + + theme_info->path = g_build_filename (MATECC_DATA_DIR, builtins[i][0], NULL); + theme_info->name = g_strdup (theme_info->path); + + if (!strcmp (theme_info->path, cursor_font)) + theme_info->readable_name = g_strdup (_(builtins[i][2])); + else + theme_info->readable_name = g_strdup (_(builtins[i][1])); + + g_hash_table_insert (cursor_theme_hash_by_uri, theme_info->path, theme_info); + add_theme_to_hash_by_name (cursor_theme_hash_by_name, theme_info); + } + + g_free (cursor_font); +} +#endif /* HAVE_XCURSOR */ + +static void +handle_change_signal (gpointer data, + MateThemeChangeType change_type, + MateThemeElement element_type) +{ +#ifdef DEBUG + gchar *type_str = NULL; + gchar *change_str = NULL; + gchar *element_str = NULL; +#endif + MateThemeCommonInfo *theme = data; + GList *list; + + if (initting) + return; + + for (list = callbacks; list; list = list->next) { + ThemeCallbackData *callback_data = list->data; + (* callback_data->func) (theme, change_type, element_type, callback_data->data); + } + +#ifdef DEBUG + if (theme->type == MATE_THEME_TYPE_METATHEME) + type_str = "meta"; + else if (theme->type == MATE_THEME_TYPE_ICON) + type_str = "icon"; + else if (theme->type == MATE_THEME_TYPE_CURSOR) + type_str = "cursor"; + else if (theme->type == MATE_THEME_TYPE_REGULAR) { + if (element_type & MATE_THEME_GTK_2) + element_str = "gtk-2"; + else if (element_type & MATE_THEME_GTK_2_KEYBINDING) + element_str = "keybinding"; + else if (element_type & MATE_THEME_MARCO) + element_str = "marco"; + } + + if (change_type == MATE_THEME_CHANGE_CREATED) + change_str = "created"; + else if (change_type == MATE_THEME_CHANGE_CHANGED) + change_str = "changed"; + else if (change_type == MATE_THEME_CHANGE_DELETED) + change_str = "deleted"; + + if (type == MATE_THEME_TYPE_REGULAR) { + g_print ("theme \"%s\" has a theme of type %s (priority %d) has been %s\n", + theme->name, + element_str, + theme->priority, + type_str); + } else if (type_str != NULL) { + g_print ("%s theme \"%s\" (priority %d) has been %s\n", + type_str, + theme->name, + theme->priority, + type_str); + } +#endif +} + +/* index_uri should point to the gtkrc file that was modified */ +static void +update_theme_index (GFile *index_uri, + MateThemeElement key_element, + gint priority) +{ + gboolean theme_exists; + MateThemeInfo *theme_info; + GFile *parent; + GFile *common_theme_dir_uri; + gchar *common_theme_dir; + + /* First, we determine the new state of the file. We do no more + * sophisticated a test than "files exists and is a file" */ + theme_exists = (get_file_type (index_uri) == G_FILE_TYPE_REGULAR); + + /* Next, we see what currently exists */ + parent = g_file_get_parent (index_uri); + common_theme_dir_uri = g_file_get_parent (parent); + common_theme_dir = g_file_get_path (common_theme_dir_uri); + + theme_info = g_hash_table_lookup (theme_hash_by_uri, common_theme_dir); + if (theme_info == NULL) { + if (theme_exists) { + theme_info = mate_theme_info_new (); + theme_info->path = g_strdup (common_theme_dir); + theme_info->name = g_file_get_basename (common_theme_dir_uri); + theme_info->readable_name = g_strdup (theme_info->name); + theme_info->priority = priority; + if (key_element & MATE_THEME_GTK_2) + theme_info->has_gtk = TRUE; + else if (key_element & MATE_THEME_GTK_2_KEYBINDING) + theme_info->has_keybinding = TRUE; + else if (key_element & MATE_THEME_MARCO) + theme_info->has_marco = TRUE; + + g_hash_table_insert (theme_hash_by_uri, g_strdup (common_theme_dir), theme_info); + add_theme_to_hash_by_name (theme_hash_by_name, theme_info); + handle_change_signal (theme_info, MATE_THEME_CHANGE_CREATED, key_element); + } + } else { + gboolean theme_used_to_exist = FALSE; + + if (key_element & MATE_THEME_GTK_2) { + theme_used_to_exist = theme_info->has_gtk; + theme_info->has_gtk = theme_exists; + } else if (key_element & MATE_THEME_GTK_2_KEYBINDING) { + theme_used_to_exist = theme_info->has_keybinding; + theme_info->has_keybinding = theme_exists; + } else if (key_element & MATE_THEME_MARCO) { + theme_used_to_exist = theme_info->has_marco; + theme_info->has_marco = theme_exists; + } + + if (!theme_info->has_marco && !theme_info->has_keybinding && !theme_info->has_gtk) { + g_hash_table_remove (theme_hash_by_uri, common_theme_dir); + remove_theme_from_hash_by_name (theme_hash_by_name, theme_info); + } + + if (theme_exists && theme_used_to_exist) { + handle_change_signal (theme_info, MATE_THEME_CHANGE_CHANGED, key_element); + } else if (theme_exists && !theme_used_to_exist) { + handle_change_signal (theme_info, MATE_THEME_CHANGE_CREATED, key_element); + } else if (!theme_exists && theme_used_to_exist) { + handle_change_signal (theme_info, MATE_THEME_CHANGE_DELETED, key_element); + } + + if (!theme_info->has_marco && !theme_info->has_keybinding && !theme_info->has_gtk) { + mate_theme_info_free (theme_info); + } + } + + g_free (common_theme_dir); + g_object_unref (parent); + g_object_unref (common_theme_dir_uri); +} + +static void +update_gtk2_index (GFile *gtk2_index_uri, + gint priority) +{ + update_theme_index (gtk2_index_uri, MATE_THEME_GTK_2, priority); +} + +static void +update_keybinding_index (GFile *keybinding_index_uri, + gint priority) +{ + update_theme_index (keybinding_index_uri, MATE_THEME_GTK_2_KEYBINDING, priority); +} + +static void +update_marco_index (GFile *marco_index_uri, + gint priority) +{ + update_theme_index (marco_index_uri, MATE_THEME_MARCO, priority); +} + +static void +update_common_theme_dir_index (GFile *theme_index_uri, + MateThemeType type, + gint priority) +{ + gboolean theme_exists; + MateThemeCommonInfo *theme_info = NULL; + MateThemeCommonInfo *old_theme_info; + GFile *common_theme_dir_uri; + gchar *common_theme_dir; + GHashTable *hash_by_uri; + GHashTable *hash_by_name; + + if (type == MATE_THEME_TYPE_ICON) { + hash_by_uri = icon_theme_hash_by_uri; + hash_by_name = icon_theme_hash_by_name; + } else if (type == MATE_THEME_TYPE_CURSOR) { + hash_by_uri = cursor_theme_hash_by_uri; + hash_by_name = cursor_theme_hash_by_name; + } else { + hash_by_uri = meta_theme_hash_by_uri; + hash_by_name = meta_theme_hash_by_name; + } + + if (type != MATE_THEME_TYPE_CURSOR) { + /* First, we determine the new state of the file. */ + if (get_file_type (theme_index_uri) == G_FILE_TYPE_REGULAR) { + /* It's an interesting file. Let's try to load it. */ + if (type == MATE_THEME_TYPE_ICON) + theme_info = (MateThemeCommonInfo *) read_icon_theme (theme_index_uri); + else + theme_info = (MateThemeCommonInfo *) mate_theme_read_meta_theme (theme_index_uri); + } else { + theme_info = NULL; + } + + } +#ifdef HAVE_XCURSOR + /* cursor themes don't necessarily have an index file, so try those in any case */ + else { + theme_info = (MateThemeCommonInfo *) read_cursor_theme (theme_index_uri); + } +#endif + + if (theme_info) { + theme_info->priority = priority; + theme_exists = TRUE; + } else { + theme_exists = FALSE; + } + + /* Next, we see what currently exists */ + common_theme_dir_uri = g_file_get_parent (theme_index_uri); + common_theme_dir = g_file_get_path (common_theme_dir_uri); + g_object_unref (common_theme_dir_uri); + + old_theme_info = (MateThemeCommonInfo *) g_hash_table_lookup (hash_by_uri, common_theme_dir); + + if (old_theme_info == NULL) { + if (theme_exists) { + g_hash_table_insert (hash_by_uri, g_strdup (common_theme_dir), theme_info); + add_theme_to_hash_by_name (hash_by_name, theme_info); + handle_change_signal (theme_info, MATE_THEME_CHANGE_CREATED, 0); + } + } else { + if (theme_exists) { + if (theme_compare (theme_info, old_theme_info) != 0) { + /* Remove old theme */ + g_hash_table_remove (hash_by_uri, common_theme_dir); + remove_theme_from_hash_by_name (hash_by_name, old_theme_info); + g_hash_table_insert (hash_by_uri, g_strdup (common_theme_dir), theme_info); + add_theme_to_hash_by_name (hash_by_name, theme_info); + handle_change_signal (theme_info, MATE_THEME_CHANGE_CHANGED, 0); + theme_free (old_theme_info); + } else { + theme_free (theme_info); + } + } else { + g_hash_table_remove (hash_by_uri, common_theme_dir); + remove_theme_from_hash_by_name (hash_by_name, old_theme_info); + + handle_change_signal (old_theme_info, MATE_THEME_CHANGE_DELETED, 0); + theme_free (old_theme_info); + } + } + + g_free (common_theme_dir); +} + +static void +update_meta_theme_index (GFile *meta_theme_index_uri, + gint priority) +{ + update_common_theme_dir_index (meta_theme_index_uri, MATE_THEME_TYPE_METATHEME, priority); +} + +static void +update_icon_theme_index (GFile *icon_theme_index_uri, + gint priority) +{ + update_common_theme_dir_index (icon_theme_index_uri, MATE_THEME_TYPE_ICON, priority); +} + +static void +update_cursor_theme_index (GFile *cursor_theme_index_uri, + gint priority) +{ +#ifdef HAVE_XCURSOR + update_common_theme_dir_index (cursor_theme_index_uri, MATE_THEME_TYPE_CURSOR, priority); +#endif +} + +static void +gtk2_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CommonThemeDirMonitorData *monitor_data) +{ + gchar *affected_file; + + affected_file = g_file_get_basename (file); + + /* The only file we care about is gtkrc */ + if (!strcmp (affected_file, "gtkrc")) { + update_gtk2_index (file, monitor_data->priority); + } + + g_free (affected_file); +} + +static void +keybinding_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CommonThemeDirMonitorData *monitor_data) +{ + gchar *affected_file; + + affected_file = g_file_get_basename (file); + + /* The only file we care about is gtkrc */ + if (!strcmp (affected_file, "gtkrc")) { + update_keybinding_index (file, monitor_data->priority); + } + + g_free (affected_file); +} + +static void +marco_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CommonThemeDirMonitorData *monitor_data) +{ + gchar *affected_file; + + affected_file = g_file_get_basename (file); + + /* The only file we care about is marco-theme-1.xml */ + if (!strcmp (affected_file, "metacity-theme-1.xml")) { + update_marco_index (file, monitor_data->priority); + } + + g_free (affected_file); +} + +static void +common_theme_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CommonThemeDirMonitorData *monitor_data) +{ + gchar *affected_file; + + affected_file = g_file_get_basename (file); + + /* The only file we care about is index.theme */ + if (!strcmp (affected_file, "index.theme")) { + update_meta_theme_index (file, monitor_data->priority); + } + + g_free (affected_file); +} + +static void +common_icon_theme_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CommonIconThemeDirMonitorData *monitor_data) +{ + gchar *affected_file; + + affected_file = g_file_get_basename (file); + + /* The only file we care about is index.theme */ + if (!strcmp (affected_file, "index.theme")) { + update_icon_theme_index (file, monitor_data->priority); + update_cursor_theme_index (file, monitor_data->priority); + } + /* and the cursors subdir for cursor themes */ + else if (!strcmp (affected_file, "cursors")) { + /* always call update_cursor_theme_index with the index.theme URI */ + GFile *parent, *index; + + parent = g_file_get_parent (file); + index = g_file_get_child (parent, "index.theme"); + g_object_unref (parent); + + update_cursor_theme_index (index, monitor_data->priority); + + g_object_unref (index); + } + + g_free (affected_file); +} + +/* Add a monitor to a common_theme_dir. */ +static gboolean +add_common_theme_dir_monitor (GFile *theme_dir_uri, + CommonThemeDirMonitorData *monitor_data, + GError **error) +{ + GFile *uri, *subdir; + GFileMonitor *monitor; + + uri = g_file_get_child (theme_dir_uri, "index.theme"); + update_meta_theme_index (uri, monitor_data->priority); + g_object_unref (uri); + + /* Add the handle for this directory */ + monitor = g_file_monitor_file (theme_dir_uri, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor == NULL) + return FALSE; + + g_signal_connect (monitor, "changed", + (GCallback) common_theme_dir_changed, monitor_data); + + monitor_data->common_theme_dir_handle = monitor; + + + /* gtk-2 theme subdir */ + subdir = g_file_get_child (theme_dir_uri, "gtk-2.0"); + uri = g_file_get_child (subdir, "gtkrc"); + if (g_file_query_exists (uri, NULL)) { + update_gtk2_index (uri, monitor_data->priority); + } + g_object_unref (uri); + + monitor = g_file_monitor_directory (subdir, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor != NULL) { + g_signal_connect (monitor, "changed", + (GCallback) gtk2_dir_changed, monitor_data); + } + monitor_data->gtk2_dir_handle = monitor; + g_object_unref (subdir); + + /* keybinding theme subdir */ + subdir = g_file_get_child (theme_dir_uri, "gtk-2.0-key"); + uri = g_file_get_child (subdir, "gtkrc"); + if (g_file_query_exists (uri, NULL)) { + update_keybinding_index (uri, monitor_data->priority); + } + g_object_unref (uri); + + monitor = g_file_monitor_directory (subdir, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor != NULL) { + g_signal_connect (monitor, "changed", + (GCallback) keybinding_dir_changed, monitor_data); + } + monitor_data->keybinding_dir_handle = monitor; + g_object_unref (subdir); + + /* marco theme subdir */ + subdir = g_file_get_child (theme_dir_uri, "metacity-1"); + uri = g_file_get_child (subdir, "metacity-theme-1.xml"); + if (g_file_query_exists (uri, NULL)) { + update_marco_index (uri, monitor_data->priority); + } + g_object_unref (uri); + + monitor = g_file_monitor_directory (subdir, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor != NULL) { + g_signal_connect (monitor, "changed", + (GCallback) marco_dir_changed, monitor_data); + } + monitor_data->marco_dir_handle = monitor; + g_object_unref (subdir); + + return TRUE; +} + +static gboolean +add_common_icon_theme_dir_monitor (GFile *theme_dir_uri, + CommonIconThemeDirMonitorData *monitor_data, + GError **error) +{ + GFile *index_uri; + GFileMonitor *monitor; + + /* Add the handle for this directory */ + index_uri = g_file_get_child (theme_dir_uri, "index.theme"); + update_icon_theme_index (index_uri, monitor_data->priority); + update_cursor_theme_index (index_uri, monitor_data->priority); + g_object_unref (index_uri); + + monitor = g_file_monitor_file (theme_dir_uri, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor == NULL) + return FALSE; + + g_signal_connect (monitor, "changed", + (GCallback) common_icon_theme_dir_changed, monitor_data); + + monitor_data->common_icon_theme_dir_handle = monitor; + return TRUE; +} + +static void +remove_common_theme_dir_monitor (CommonThemeDirMonitorData *monitor_data) +{ + g_file_monitor_cancel (monitor_data->common_theme_dir_handle); + g_file_monitor_cancel (monitor_data->gtk2_dir_handle); + g_file_monitor_cancel (monitor_data->keybinding_dir_handle); + g_file_monitor_cancel (monitor_data->marco_dir_handle); +} + +static void +remove_common_icon_theme_dir_monitor (CommonIconThemeDirMonitorData *monitor_data) +{ + g_file_monitor_cancel (monitor_data->common_icon_theme_dir_handle); +} + +static void +top_theme_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CallbackTuple *tuple) +{ + GHashTable *handle_hash; + CommonThemeDirMonitorData *monitor_data; + gint priority; + + handle_hash = tuple->handle_hash; + priority = tuple->priority; + + if (event_type == G_FILE_MONITOR_EVENT_CREATED) { + if (get_file_type (file) == G_FILE_TYPE_DIRECTORY) { + monitor_data = g_new0 (CommonThemeDirMonitorData, 1); + monitor_data->priority = priority; + add_common_theme_dir_monitor (file, monitor_data, NULL); + g_hash_table_insert (handle_hash, g_file_get_basename (file), monitor_data); + } + + } else if (event_type == G_FILE_MONITOR_EVENT_DELETED) { + gchar *name; + + name = g_file_get_basename (file); + monitor_data = g_hash_table_lookup (handle_hash, name); + if (monitor_data != NULL) { + remove_common_theme_dir_monitor (monitor_data); + g_hash_table_remove (handle_hash, name); + } + g_free (name); + } +} + +static void +top_icon_theme_dir_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + CallbackTuple *tuple) +{ + GHashTable *handle_hash; + CommonIconThemeDirMonitorData *monitor_data; + gint priority; + + handle_hash = tuple->handle_hash; + priority = tuple->priority; + + if (event_type == G_FILE_MONITOR_EVENT_CREATED) { + if (get_file_type (file) == G_FILE_TYPE_DIRECTORY) { + monitor_data = g_new0 (CommonIconThemeDirMonitorData, 1); + monitor_data->priority = priority; + add_common_icon_theme_dir_monitor (file, monitor_data, NULL); + g_hash_table_insert (handle_hash, g_file_get_basename (file), monitor_data); + } + + } else if (event_type == G_FILE_MONITOR_EVENT_DELETED) { + gchar *name; + + name = g_file_get_basename (file); + monitor_data = g_hash_table_lookup (handle_hash, name); + if (monitor_data != NULL) { + remove_common_icon_theme_dir_monitor (monitor_data); + g_hash_table_remove (handle_hash, name); + } + g_free (name); + } +} + +/* Add a monitor to a top dir. These monitors persist for the duration of the + * lib. + */ +static gboolean +real_add_top_theme_dir_monitor (GFile *uri, + gint priority, + gboolean icon_theme, + GError **error) +{ + GFileInfo *file_info; + GFileMonitor *monitor; + GFileEnumerator *enumerator; + CallbackTuple *tuple; + + /* Check the URI */ + if (get_file_type (uri) != G_FILE_TYPE_DIRECTORY) + return FALSE; + + /* handle_hash is a hash of common_theme_dir names to their monitor_data. We + * use it to remove the monitor handles when a dir is removed. + */ + tuple = g_new (CallbackTuple, 1); + tuple->handle_hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_free); + tuple->priority = priority; + + /* Monitor the top directory */ + monitor = g_file_monitor_directory (uri, G_FILE_MONITOR_NONE, NULL, NULL); + if (monitor != NULL) { + g_signal_connect (monitor, "changed", + (GCallback) (icon_theme ? top_icon_theme_dir_changed : top_theme_dir_changed), + tuple); + } + + /* Go through the directory to add monitoring */ + enumerator = g_file_enumerate_children (uri, + G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + if (enumerator == NULL) + return FALSE; + + while ((file_info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { + GFileType type = g_file_info_get_file_type (file_info); + + if (type == G_FILE_TYPE_DIRECTORY || type == G_FILE_TYPE_SYMBOLIC_LINK) { + GFile *child; + const gchar *name; + gpointer data; + + /* Add the directory */ + name = g_file_info_get_name (file_info); + child = g_file_get_child (uri, name); + + if (icon_theme) { + CommonIconThemeDirMonitorData *monitor_data; + monitor_data = g_new0 (CommonIconThemeDirMonitorData, 1); + monitor_data->priority = priority; + add_common_icon_theme_dir_monitor (child, monitor_data, error); + data = monitor_data; + } else { + CommonThemeDirMonitorData *monitor_data; + monitor_data = g_new0 (CommonThemeDirMonitorData, 1); + monitor_data->priority = priority; + add_common_theme_dir_monitor (child, monitor_data, error); + data = monitor_data; + } + g_object_unref (child); + + g_hash_table_insert (tuple->handle_hash, g_strdup (name), data); + } + g_object_unref (file_info); + } + g_file_enumerator_close (enumerator, NULL, NULL); + + return TRUE; +} + +static gboolean +add_top_theme_dir_monitor (GFile *uri, + gint priority, + GError **error) +{ + return real_add_top_theme_dir_monitor (uri, priority, FALSE, error); +} + +static gboolean +add_top_icon_theme_dir_monitor (GFile *uri, + gint priority, + GError **error) +{ + return real_add_top_theme_dir_monitor (uri, priority, TRUE, error); +} + +/* Public functions */ + +/* GTK/Marco/keybinding Themes */ +MateThemeInfo * +mate_theme_info_new (void) +{ + MateThemeInfo *theme_info; + + theme_info = g_new0 (MateThemeInfo, 1); + theme_info->type = MATE_THEME_TYPE_REGULAR; + + return theme_info; +} + +void +mate_theme_info_free (MateThemeInfo *theme_info) +{ + g_free (theme_info->path); + g_free (theme_info->name); + g_free (theme_info->readable_name); + g_free (theme_info); +} + +MateThemeInfo * +mate_theme_info_find (const gchar *theme_name) +{ + return (MateThemeInfo *) + get_theme_from_hash_by_name (theme_hash_by_name, theme_name, -1); +} + +struct MateThemeInfoHashData +{ + gconstpointer user_data; + GList *list; +}; + +static void +mate_theme_info_find_by_type_helper (gpointer key, + GList *list, + struct MateThemeInfoHashData *hash_data) +{ + guint elements = GPOINTER_TO_INT (hash_data->user_data); + + do { + MateThemeInfo *theme_info = list->data; + + if ((elements & MATE_THEME_MARCO && theme_info->has_marco) || + (elements & MATE_THEME_GTK_2 && theme_info->has_gtk) || + (elements & MATE_THEME_GTK_2_KEYBINDING && theme_info->has_keybinding)) { + hash_data->list = g_list_prepend (hash_data->list, theme_info); + return; + } + + list = list->next; + } while (list); +} + +GList * +mate_theme_info_find_by_type (guint elements) +{ + struct MateThemeInfoHashData data; + data.user_data = GINT_TO_POINTER (elements); + data.list = NULL; + + g_hash_table_foreach (theme_hash_by_name, + (GHFunc) mate_theme_info_find_by_type_helper, + &data); + + return data.list; +} + +static void mate_theme_info_find_all_helper(const gchar* key, GList* list, GList** themes) +{ + /* only return visible themes */ + if (!((MateThemeCommonInfo*) list->data)->hidden) + { + *themes = g_list_prepend(*themes, list->data); + } +} + +gchar* gtk_theme_info_missing_engine(const gchar* gtk_theme, gboolean nameOnly) +{ + gchar* engine = NULL; + gchar* gtkrc; + + gtkrc = gtkrc_find_named(gtk_theme); + + if (gtkrc) + { + GSList* engines = NULL; + GSList* l; + + gtkrc_get_details(gtkrc, &engines, NULL); + + g_free(gtkrc); + + for (l = engines; l; l = l->next) + { + #if 1 // set to 0 if you can not compile with the follow code + GtkThemeEngine* a = gtk_theme_engine_get((const gchar*) l->data); + + if (!a) + { + if (nameOnly) + { + engine = g_strdup(l->data); + } + else + { + // esto necesita más trabajo, pero creo que debian no se + // salva ni con el anterior fix. + // GTK_ENGINE_DIR aún sigue conteniendo un path erroneo. + engine = g_module_build_path(GTK_ENGINE_DIR, l->data); + } + + break; + } + + #else + + /* This code do not work on distros with more of one gtk theme + * engine path. Like debian. But yes on others like Archlinux. + * Example, debian use: + * /usr/lib/i386-linux-gnu/2.10.0/engines/ + * and /usr/lib/2.10.0/engines/ + * + * some links + * http://forums.linuxmint.com/viewtopic.php?f=190&t=85015 + */ + gchar* full = g_module_build_path(GTK_ENGINE_DIR, l->data); + + gboolean found = g_file_test(full, G_FILE_TEST_EXISTS); + + if (!found) + { + if (nameOnly) + { + engine = g_strdup(l->data); + g_free(full); + } + else + { + engine = full; + } + + break; + } + + g_free(full); + #endif + } + + g_slist_foreach(engines, (GFunc) g_free, NULL); + g_slist_free(engines); + } + + return engine; +} + +/* Icon themes */ +MateThemeIconInfo * +mate_theme_icon_info_new (void) +{ + MateThemeIconInfo *icon_theme_info; + + icon_theme_info = g_new0 (MateThemeIconInfo, 1); + icon_theme_info->type = MATE_THEME_TYPE_ICON; + + return icon_theme_info; +} + +void +mate_theme_icon_info_free (MateThemeIconInfo *icon_theme_info) +{ + g_free (icon_theme_info->name); + g_free (icon_theme_info->readable_name); + g_free (icon_theme_info->path); + g_free (icon_theme_info); +} + +MateThemeIconInfo * +mate_theme_icon_info_find (const gchar *icon_theme_name) +{ + g_return_val_if_fail (icon_theme_name != NULL, NULL); + + return (MateThemeIconInfo *) + get_theme_from_hash_by_name (icon_theme_hash_by_name, icon_theme_name, -1); +} + +GList * +mate_theme_icon_info_find_all (void) +{ + GList *list = NULL; + + g_hash_table_foreach (icon_theme_hash_by_name, + (GHFunc) mate_theme_info_find_all_helper, + &list); + + return list; +} + +gint +mate_theme_icon_info_compare (MateThemeIconInfo *a, + MateThemeIconInfo *b) +{ + gint cmp; + + cmp = safe_strcmp (a->path, b->path); + if (cmp != 0) return cmp; + + return safe_strcmp (a->name, b->name); +} + +/* Cursor themes */ +MateThemeCursorInfo * +mate_theme_cursor_info_new (void) +{ + MateThemeCursorInfo *theme_info; + + theme_info = g_new0 (MateThemeCursorInfo, 1); + theme_info->type = MATE_THEME_TYPE_CURSOR; + + return theme_info; +} + +void +mate_theme_cursor_info_free (MateThemeCursorInfo *cursor_theme_info) +{ + g_free (cursor_theme_info->name); + g_free (cursor_theme_info->readable_name); + g_free (cursor_theme_info->path); + g_array_free (cursor_theme_info->sizes, TRUE); + if (cursor_theme_info->thumbnail != NULL) + g_object_unref (cursor_theme_info->thumbnail); + g_free (cursor_theme_info); +} + +MateThemeCursorInfo * +mate_theme_cursor_info_find (const gchar *cursor_theme_name) +{ + g_return_val_if_fail (cursor_theme_name != NULL, NULL); + + return (MateThemeCursorInfo *) + get_theme_from_hash_by_name (cursor_theme_hash_by_name, cursor_theme_name, -1); +} + +GList * +mate_theme_cursor_info_find_all (void) +{ + GList *list = NULL; + + g_hash_table_foreach (cursor_theme_hash_by_name, + (GHFunc) mate_theme_info_find_all_helper, + &list); + + return list; +} + +gint +mate_theme_cursor_info_compare (MateThemeCursorInfo *a, + MateThemeCursorInfo *b) +{ + gint cmp; + + cmp = safe_strcmp (a->path, b->path); + if (cmp != 0) return cmp; + + return safe_strcmp (a->name, b->name); +} + +/* Meta themes */ +MateThemeMetaInfo* mate_theme_meta_info_new(void) +{ + MateThemeMetaInfo* theme_info; + + theme_info = g_new0(MateThemeMetaInfo, 1); + theme_info->type = MATE_THEME_TYPE_METATHEME; + + return theme_info; +} + +void mate_theme_meta_info_free(MateThemeMetaInfo* meta_theme_info) +{ + g_free(meta_theme_info->path); + g_free(meta_theme_info->readable_name); + g_free(meta_theme_info->name); + g_free(meta_theme_info->comment); + g_free(meta_theme_info->application_font); + g_free(meta_theme_info->documents_font); + g_free(meta_theme_info->desktop_font); + g_free(meta_theme_info->windowtitle_font); + g_free(meta_theme_info->monospace_font); + g_free(meta_theme_info->background_image); + g_free(meta_theme_info->gtk_theme_name); + g_free(meta_theme_info->gtk_color_scheme); + g_free(meta_theme_info->icon_theme_name); + g_free(meta_theme_info->marco_theme_name); + g_free(meta_theme_info->notification_theme_name); + g_free(meta_theme_info); +} + +gboolean mate_theme_meta_info_validate(const MateThemeMetaInfo* info, GError** error) +{ + MateThemeInfo* theme; + gchar* engine; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + theme = mate_theme_info_find (info->gtk_theme_name); + + if (!theme || !theme->has_gtk) + { + g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_THEME_NOT_AVAILABLE, + _("This theme will not look as intended because the required GTK+ theme '%s' is not installed."), + info->gtk_theme_name); + return FALSE; + } + + theme = mate_theme_info_find (info->marco_theme_name); + + if (!theme || !theme->has_marco) + { + g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_WM_THEME_NOT_AVAILABLE, + _("This theme will not look as intended because the required window manager theme '%s' is not installed."), + info->marco_theme_name); + return FALSE; + } + + if (!mate_theme_icon_info_find (info->icon_theme_name)) + { + g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_ICON_THEME_NOT_AVAILABLE, + _("This theme will not look as intended because the required icon theme '%s' is not installed."), + info->icon_theme_name); + return FALSE; + } + + /* check for gtk theme engines */ + engine = gtk_theme_info_missing_engine(info->gtk_theme_name, TRUE); + + if (engine != NULL) + { + g_set_error (error, MATE_THEME_ERROR, MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE, + _("This theme will not look as intended because the required GTK+ theme engine '%s' is not installed."), + engine); + g_free (engine); + return FALSE; + } + + return TRUE; +} + +MateThemeMetaInfo* mate_theme_meta_info_find(const char* meta_theme_name) +{ + g_return_val_if_fail(meta_theme_name != NULL, NULL); + + return (MateThemeMetaInfo*) get_theme_from_hash_by_name (meta_theme_hash_by_name, meta_theme_name, -1); +} + +GList* mate_theme_meta_info_find_all(void) +{ + GList* list = NULL; + + g_hash_table_foreach (meta_theme_hash_by_name, (GHFunc) mate_theme_info_find_all_helper, &list); + + return list; +} + +gint +mate_theme_meta_info_compare (MateThemeMetaInfo *a, + MateThemeMetaInfo *b) +{ + gint cmp; + + cmp = safe_strcmp (a->path, b->path); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->readable_name, b->readable_name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->name, b->name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->comment, b->comment); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->icon_file, b->icon_file); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->gtk_theme_name, b->gtk_theme_name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->gtk_color_scheme, b->gtk_color_scheme); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->marco_theme_name, b->marco_theme_name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->icon_theme_name, b->icon_theme_name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->notification_theme_name, b->notification_theme_name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->sound_theme_name, b->sound_theme_name); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->application_font, b->application_font); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->documents_font, b->documents_font); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->desktop_font, b->desktop_font); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->windowtitle_font, b->windowtitle_font); + if (cmp != 0) return cmp; + + cmp = safe_strcmp (a->monospace_font, b->monospace_font); + if (cmp != 0) return cmp; + + return safe_strcmp (a->background_image, b->background_image); +} + +void +mate_theme_info_register_theme_change (ThemeChangedCallback func, + gpointer data) +{ + ThemeCallbackData *callback_data; + + g_return_if_fail (func != NULL); + + callback_data = g_new (ThemeCallbackData, 1); + callback_data->func = func; + callback_data->data = data; + + callbacks = g_list_prepend (callbacks, callback_data); +} + +gboolean +mate_theme_color_scheme_parse (const gchar *scheme, GdkColor *colors) +{ + gchar **color_scheme_strings, **color_scheme_pair, *current_string; + gint i; + + if (!scheme || !strcmp (scheme, "")) + return FALSE; + + /* initialise the array */ + for (i = 0; i < NUM_SYMBOLIC_COLORS; i++) + colors[i].red = colors[i].green = colors[i].blue = 0; + + /* The color scheme string consists of name:color pairs, separated by + * newlines, so first we split the string up by new line */ + + color_scheme_strings = g_strsplit (scheme, "\n", 0); + + /* loop through the name:color pairs, and save the color if we recognise the name */ + i = 0; + while ((current_string = color_scheme_strings[i++])) { + color_scheme_pair = g_strsplit (current_string, ":", 0); + + if (color_scheme_pair[0] != NULL && color_scheme_pair[1] != NULL) { + g_strstrip (color_scheme_pair[0]); + g_strstrip (color_scheme_pair[1]); + + if (!strcmp ("fg_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_FG]); + else if (!strcmp ("bg_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_BG]); + else if (!strcmp ("text_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_TEXT]); + else if (!strcmp ("base_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_BASE]); + else if (!strcmp ("selected_fg_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_SELECTED_FG]); + else if (!strcmp ("selected_bg_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_SELECTED_BG]); + else if (!strcmp ("tooltip_fg_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_TOOLTIP_FG]); + else if (!strcmp ("tooltip_bg_color", color_scheme_pair[0])) + gdk_color_parse (color_scheme_pair[1], &colors[COLOR_TOOLTIP_BG]); + } + + g_strfreev (color_scheme_pair); + } + + g_strfreev (color_scheme_strings); + + return TRUE; +} + +gboolean +mate_theme_color_scheme_equal (const gchar *s1, const gchar *s2) +{ + GdkColor c1[NUM_SYMBOLIC_COLORS], c2[NUM_SYMBOLIC_COLORS]; + int i; + + if (!mate_theme_color_scheme_parse (s1, c1) || + !mate_theme_color_scheme_parse (s2, c2)) + return FALSE; + + for (i = 0; i < NUM_SYMBOLIC_COLORS; ++i) { + if (!gdk_color_equal (&c1[i], &c2[i])) + return FALSE; + } + + return TRUE; +} + +void +mate_theme_init () +{ + GFile *top_theme_dir; + gchar *top_theme_dir_string; + static gboolean initted = FALSE; + gchar **search_path; + gint i, n; + + if (initted) + return; + + initting = TRUE; + + meta_theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + meta_theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + icon_theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + icon_theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + cursor_theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + cursor_theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + theme_hash_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + theme_hash_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + /* Add all the toplevel theme dirs. */ + /* $datadir/themes */ + top_theme_dir_string = gtk_rc_get_theme_dir (); + top_theme_dir = g_file_new_for_path (top_theme_dir_string); + g_free (top_theme_dir_string); + add_top_theme_dir_monitor (top_theme_dir, 1, NULL); + g_object_unref (top_theme_dir); + + /* ~/.themes */ + top_theme_dir_string = g_build_filename (g_get_home_dir (), ".themes", NULL); + top_theme_dir = g_file_new_for_path (top_theme_dir_string); + g_free (top_theme_dir_string); + if (!g_file_query_exists (top_theme_dir, NULL)) + g_file_make_directory (top_theme_dir, NULL, NULL); + add_top_theme_dir_monitor (top_theme_dir, 0, NULL); + g_object_unref (top_theme_dir); + + /* ~/.icons */ + top_theme_dir_string = g_build_filename (g_get_home_dir (), ".icons", NULL); + top_theme_dir = g_file_new_for_path (top_theme_dir_string); + g_free (top_theme_dir_string); + if (!g_file_query_exists (top_theme_dir, NULL)) + g_file_make_directory (top_theme_dir, NULL, NULL); + g_object_unref (top_theme_dir); + + /* icon theme search path */ + gtk_icon_theme_get_search_path (gtk_icon_theme_get_default (), &search_path, &n); + for (i = 0; i < n; ++i) { + top_theme_dir = g_file_new_for_path (search_path[i]); + add_top_icon_theme_dir_monitor (top_theme_dir, i, NULL); + g_object_unref (top_theme_dir); + } + g_strfreev (search_path); + +#ifdef XCURSOR_ICONDIR + /* if there's a separate xcursors dir, add that as well */ + if (strcmp (XCURSOR_ICONDIR, top_theme_dir_string) && + strcmp (XCURSOR_ICONDIR, "/usr/share/icons")) { + top_theme_dir = g_file_new_for_path (XCURSOR_ICONDIR); + add_top_icon_theme_dir_monitor (top_theme_dir, 1, NULL); + g_object_unref (top_theme_dir); + } +#endif + +#ifdef HAVE_XCURSOR + /* make sure we have the default theme */ + if (!mate_theme_cursor_info_find ("default")) + add_default_cursor_theme (); +#else + /* If we don't have Xcursor, use the built-in cursor fonts instead */ + read_cursor_fonts (); +#endif + + /* done */ + initted = TRUE; + initting = FALSE; +} diff --git a/capplets/common/mate-theme-info.h b/capplets/common/mate-theme-info.h new file mode 100644 index 00000000..095e98ae --- /dev/null +++ b/capplets/common/mate-theme-info.h @@ -0,0 +1,190 @@ +/* mate-theme-info.h - MATE Theme information + + Copyright (C) 2002 Jonathan Blandford + All rights reserved. + + This file is part of the Mate Library. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef MATE_THEME_INFO_H +#define MATE_THEME_INFO_H + +#include +#include +#include +#include + +typedef enum { + MATE_THEME_TYPE_METATHEME, + MATE_THEME_TYPE_ICON, + MATE_THEME_TYPE_CURSOR, + MATE_THEME_TYPE_REGULAR +} MateThemeType; + +typedef enum { + MATE_THEME_CHANGE_CREATED, + MATE_THEME_CHANGE_DELETED, + MATE_THEME_CHANGE_CHANGED +} MateThemeChangeType; + +typedef enum { + MATE_THEME_MARCO = 1 << 0, + MATE_THEME_GTK_2 = 1 << 1, + MATE_THEME_GTK_2_KEYBINDING = 1 << 2 +} MateThemeElement; + +typedef struct _MateThemeCommonInfo MateThemeCommonInfo; +typedef struct _MateThemeCommonInfo MateThemeIconInfo; +struct _MateThemeCommonInfo +{ + MateThemeType type; + gchar* path; + gchar* name; + gchar* readable_name; + gint priority; + gboolean hidden; +}; + +typedef struct _MateThemeInfo MateThemeInfo; +struct _MateThemeInfo +{ + MateThemeType type; + gchar* path; + gchar* name; + gchar* readable_name; + gint priority; + gboolean hidden; + + guint has_gtk : 1; + guint has_keybinding : 1; + guint has_marco : 1; +}; + +typedef struct _MateThemeCursorInfo MateThemeCursorInfo; +struct _MateThemeCursorInfo { + MateThemeType type; + gchar* path; + gchar* name; + gchar* readable_name; + gint priority; + gboolean hidden; + + GArray* sizes; + GdkPixbuf* thumbnail; +}; + +typedef struct _MateThemeMetaInfo MateThemeMetaInfo; +struct _MateThemeMetaInfo { + MateThemeType type; + gchar* path; + gchar* name; + gchar* readable_name; + gint priority; + gboolean hidden; + + gchar* comment; + gchar* icon_file; + + gchar* gtk_theme_name; + gchar* gtk_color_scheme; + gchar* marco_theme_name; + gchar* icon_theme_name; + gchar* notification_theme_name; + gchar* sound_theme_name; + gchar* cursor_theme_name; + guint cursor_size; + + gchar* application_font; + gchar* documents_font; + gchar* desktop_font; + gchar* windowtitle_font; + gchar* monospace_font; + gchar* background_image; +}; + +enum { + COLOR_FG, + COLOR_BG, + COLOR_TEXT, + COLOR_BASE, + COLOR_SELECTED_FG, + COLOR_SELECTED_BG, + COLOR_TOOLTIP_FG, + COLOR_TOOLTIP_BG, + NUM_SYMBOLIC_COLORS +}; + +typedef void (*ThemeChangedCallback) (MateThemeCommonInfo* theme, MateThemeChangeType change_type, MateThemeElement element_type, gpointer user_data); + +#define MATE_THEME_ERROR mate_theme_info_error_quark() + +enum { + MATE_THEME_ERROR_GTK_THEME_NOT_AVAILABLE = 1, + MATE_THEME_ERROR_WM_THEME_NOT_AVAILABLE, + MATE_THEME_ERROR_ICON_THEME_NOT_AVAILABLE, + MATE_THEME_ERROR_GTK_ENGINE_NOT_AVAILABLE, + MATE_THEME_ERROR_UNKNOWN +}; + + +/* GTK/Marco/keybinding Themes */ +MateThemeInfo *mate_theme_info_new (void); +void mate_theme_info_free (MateThemeInfo *theme_info); +MateThemeInfo *mate_theme_info_find (const gchar *theme_name); +GList *mate_theme_info_find_by_type (guint elements); +GQuark mate_theme_info_error_quark (void); +gchar *gtk_theme_info_missing_engine (const gchar *gtk_theme, + gboolean nameOnly); + +/* Icon Themes */ +MateThemeIconInfo *mate_theme_icon_info_new (void); +void mate_theme_icon_info_free (MateThemeIconInfo *icon_theme_info); +MateThemeIconInfo *mate_theme_icon_info_find (const gchar *icon_theme_name); +GList *mate_theme_icon_info_find_all (void); +gint mate_theme_icon_info_compare (MateThemeIconInfo *a, + MateThemeIconInfo *b); + +/* Cursor Themes */ +MateThemeCursorInfo *mate_theme_cursor_info_new (void); +void mate_theme_cursor_info_free (MateThemeCursorInfo *info); +MateThemeCursorInfo *mate_theme_cursor_info_find (const gchar *name); +GList *mate_theme_cursor_info_find_all (void); +gint mate_theme_cursor_info_compare (MateThemeCursorInfo *a, + MateThemeCursorInfo *b); + +/* Meta themes*/ +MateThemeMetaInfo *mate_theme_meta_info_new (void); +void mate_theme_meta_info_free (MateThemeMetaInfo *meta_theme_info); +MateThemeMetaInfo *mate_theme_meta_info_find (const gchar *meta_theme_name); +GList *mate_theme_meta_info_find_all (void); +gint mate_theme_meta_info_compare (MateThemeMetaInfo *a, + MateThemeMetaInfo *b); +gboolean mate_theme_meta_info_validate (const MateThemeMetaInfo *info, + GError **error); +MateThemeMetaInfo *mate_theme_read_meta_theme (GFile *meta_theme_uri); + +/* Other */ +void mate_theme_init (void); +void mate_theme_info_register_theme_change (ThemeChangedCallback func, + gpointer data); + +gboolean mate_theme_color_scheme_parse (const gchar *scheme, + GdkColor *colors); +gboolean mate_theme_color_scheme_equal (const gchar *s1, + const gchar *s2); + +#endif /* MATE_THEME_INFO_H */ diff --git a/capplets/common/mate-theme-test.c b/capplets/common/mate-theme-test.c new file mode 100644 index 00000000..0c3c51f9 --- /dev/null +++ b/capplets/common/mate-theme-test.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include "mate-theme-info.h" + +int +main (int argc, char *argv[]) +{ + GList *themes, *list; + + g_thread_init (NULL); + gtk_init (&argc, &argv); + mate_theme_init (); + + themes = mate_theme_meta_info_find_all (); + if (themes == NULL) + { + g_print ("No meta themes were found.\n"); + } + else + { + g_print ("%d meta themes were found:\n", g_list_length (themes)); + for (list = themes; list; list = list->next) + { + MateThemeMetaInfo *meta_theme_info; + + meta_theme_info = list->data; + g_print ("\t%s\n", meta_theme_info->readable_name); + } + } + g_list_free (themes); + + themes = mate_theme_icon_info_find_all (); + if (themes == NULL) + { + g_print ("No icon themes were found.\n"); + } + else + { + g_print ("%d icon themes were found:\n", g_list_length (themes)); + for (list = themes; list; list = list->next) + { + MateThemeIconInfo *icon_theme_info; + + icon_theme_info = list->data; + g_print ("\t%s\n", icon_theme_info->name); + } + } + g_list_free (themes); + + themes = mate_theme_info_find_by_type (MATE_THEME_MARCO); + if (themes == NULL) + { + g_print ("No marco themes were found.\n"); + } + else + { + g_print ("%d marco themes were found:\n", g_list_length (themes)); + for (list = themes; list; list = list->next) + { + MateThemeInfo *theme_info; + + theme_info = list->data; + g_print ("\t%s\n", theme_info->name); + } + } + g_list_free (themes); + + themes = mate_theme_info_find_by_type (MATE_THEME_GTK_2); + if (themes == NULL) + { + gchar *str; + + g_print ("No gtk-2 themes were found. The following directories were tested:\n"); + str = gtk_rc_get_theme_dir (); + g_print ("\t%s\n", str); + g_free (str); + str = g_build_filename (g_get_home_dir (), ".themes", NULL); + g_print ("\t%s\n", str); + g_free (str); + } + else + { + g_print ("%d gtk-2 themes were found:\n", g_list_length (themes)); + for (list = themes; list; list = list->next) + { + MateThemeInfo *theme_info; + + theme_info = list->data; + g_print ("\t%s\n", theme_info->name); + } + } + g_list_free (themes); + + themes = mate_theme_info_find_by_type (MATE_THEME_GTK_2_KEYBINDING); + if (themes == NULL) + { + g_print ("No keybinding themes were found.\n"); + } + else + { + g_print ("%d keybinding themes were found:\n", g_list_length (themes)); + for (list = themes; list; list = list->next) + { + MateThemeInfo *theme_info; + + theme_info = list->data; + g_print ("\t%s\n", theme_info->name); + } + } + g_list_free (themes); + + themes = mate_theme_cursor_info_find_all (); + if (themes == NULL) + { + g_print ("No cursor themes were found.\n"); + } + else + { + g_print ("%d cursor themes were found:\n", g_list_length (themes)); + for (list = themes; list; list = list->next) + { + MateThemeCursorInfo *cursor_theme_info; + + cursor_theme_info = list->data; + g_print ("\t%s\n", cursor_theme_info->name); + } + } + g_list_free (themes); + + return 0; +} + diff --git a/capplets/common/mateconf-property-editor-marshal.c b/capplets/common/mateconf-property-editor-marshal.c new file mode 100644 index 00000000..9bead349 --- /dev/null +++ b/capplets/common/mateconf-property-editor-marshal.c @@ -0,0 +1,41 @@ +#include +#include +#include "mateconf-property-editor-marshal.h" + +/* VOID:STRING,POINTER (peditor-marshal.list:25) */ +void +mateconf_property_editor_marshal_VOID__STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__STRING_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__STRING_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + (char*) g_value_get_string (param_values + 1), + g_value_get_pointer (param_values + 2), + data2); +} + diff --git a/capplets/common/mateconf-property-editor-marshal.h b/capplets/common/mateconf-property-editor-marshal.h new file mode 100644 index 00000000..e26505cf --- /dev/null +++ b/capplets/common/mateconf-property-editor-marshal.h @@ -0,0 +1,19 @@ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* VOID:STRING,POINTER (peditor-marshal.list:25) */ +extern void mateconf_property_editor_marshal_VOID__STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +#ifdef __cplusplus +} +#endif + diff --git a/capplets/common/mateconf-property-editor.c b/capplets/common/mateconf-property-editor.c new file mode 100644 index 00000000..f565a168 --- /dev/null +++ b/capplets/common/mateconf-property-editor.c @@ -0,0 +1,1801 @@ +/* -*- mode: c; style: linux -*- */ + +/* mateconf-property-editor.c + * Copyright (C) 2001 Ximian, Inc. + * + * Written by Bradford Hovinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "mateconf-property-editor.h" +#include "mateconf-property-editor-marshal.h" + +enum { + VALUE_CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_KEY, + PROP_CALLBACK, + PROP_CHANGESET, + PROP_CONV_TO_WIDGET_CB, + PROP_CONV_FROM_WIDGET_CB, + PROP_UI_CONTROL, + PROP_DATA, + PROP_DATA_FREE_CB +}; + +struct _MateConfPropertyEditorPrivate +{ + gchar *key; + guint handler_id; + MateConfChangeSet *changeset; + GObject *ui_control; + MateConfPEditorValueConvFn conv_to_widget_cb; + MateConfPEditorValueConvFn conv_from_widget_cb; + MateConfClientNotifyFunc callback; + gboolean inited; + + gpointer data; + GFreeFunc data_free_cb; +}; + +typedef struct +{ + GType enum_type; + MateConfPEditorGetValueFn enum_val_true_fn; + gpointer enum_val_true_fn_data; + guint enum_val_false; + gboolean use_nick; +} MateConfPropertyEditorEnumData; + +static guint peditor_signals[LAST_SIGNAL]; + +static void mateconf_property_editor_set_prop (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void mateconf_property_editor_get_prop (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void mateconf_property_editor_finalize (GObject *object); + +static GObject *mateconf_peditor_new (const gchar *key, + MateConfClientNotifyFunc cb, + MateConfChangeSet *changeset, + GObject *ui_control, + const gchar *first_prop_name, + va_list var_args, + const gchar *first_custom, + ...); + +G_DEFINE_TYPE (MateConfPropertyEditor, mateconf_property_editor, G_TYPE_OBJECT) + +#define MATECONF_PROPERTY_EDITOR_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), mateconf_property_editor_get_type (), MateConfPropertyEditorPrivate)) + + +static MateConfValue* +mateconf_property_editor_conv_default (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + return mateconf_value_copy (value); +} + +static void +mateconf_property_editor_init (MateConfPropertyEditor *mateconf_property_editor) +{ + mateconf_property_editor->p = MATECONF_PROPERTY_EDITOR_GET_PRIVATE (mateconf_property_editor); + mateconf_property_editor->p->conv_to_widget_cb = mateconf_property_editor_conv_default; + mateconf_property_editor->p->conv_from_widget_cb = mateconf_property_editor_conv_default; + mateconf_property_editor->p->inited = FALSE; +} + +static void +mateconf_property_editor_class_init (MateConfPropertyEditorClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = mateconf_property_editor_finalize; + object_class->set_property = mateconf_property_editor_set_prop; + object_class->get_property = mateconf_property_editor_get_prop; + + g_object_class_install_property + (object_class, PROP_KEY, + g_param_spec_string ("key", + _("Key"), + _("MateConf key to which this property editor is attached"), + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property + (object_class, PROP_CALLBACK, + g_param_spec_pointer ("callback", + _("Callback"), + _("Issue this callback when the value associated with key gets changed"), + G_PARAM_WRITABLE)); + g_object_class_install_property + (object_class, PROP_CHANGESET, + g_param_spec_pointer ("changeset", + _("Change set"), + _("MateConf change set containing data to be forwarded to the mateconf client on apply"), + G_PARAM_READWRITE)); + g_object_class_install_property + (object_class, PROP_CONV_TO_WIDGET_CB, + g_param_spec_pointer ("conv-to-widget-cb", + _("Conversion to widget callback"), + _("Callback to be issued when data are to be converted from MateConf to the widget"), + G_PARAM_WRITABLE)); + g_object_class_install_property + (object_class, PROP_CONV_FROM_WIDGET_CB, + g_param_spec_pointer ("conv-from-widget-cb", + _("Conversion from widget callback"), + _("Callback to be issued when data are to be converted to MateConf from the widget"), + G_PARAM_WRITABLE)); + g_object_class_install_property + (object_class, PROP_UI_CONTROL, + g_param_spec_object ("ui-control", + _("UI Control"), + _("Object that controls the property (normally a widget)"), + G_TYPE_OBJECT, + G_PARAM_WRITABLE)); + + peditor_signals[VALUE_CHANGED] = + g_signal_new ("value-changed", + G_TYPE_FROM_CLASS (object_class), 0, + G_STRUCT_OFFSET (MateConfPropertyEditorClass, value_changed), + NULL, NULL, + (GSignalCMarshaller) mateconf_property_editor_marshal_VOID__STRING_POINTER, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER); + + g_object_class_install_property + (object_class, PROP_DATA, + g_param_spec_pointer ("data", + _("Property editor object data"), + _("Custom data required by the specific property editor"), + G_PARAM_READWRITE)); + + g_object_class_install_property + (object_class, PROP_DATA_FREE_CB, + g_param_spec_pointer ("data-free-cb", + _("Property editor data freeing callback"), + _("Callback to be issued when property editor object data is to be freed"), + G_PARAM_WRITABLE)); + + g_type_class_add_private (class, sizeof (MateConfPropertyEditorPrivate)); +} + +static void +mateconf_property_editor_set_prop (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MateConfPropertyEditor *peditor; + MateConfClient *client; + MateConfNotifyFunc cb; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (object)); + + peditor = MATECONF_PROPERTY_EDITOR (object); + + switch (prop_id) { + case PROP_KEY: + peditor->p->key = g_value_dup_string (value); + break; + + case PROP_CALLBACK: + client = mateconf_client_get_default (); + cb = g_value_get_pointer (value); + peditor->p->callback = (MateConfClientNotifyFunc) cb; + if (peditor->p->handler_id != 0) { + mateconf_client_notify_remove (client, + peditor->p->handler_id); + } + peditor->p->handler_id = + mateconf_client_notify_add (client, peditor->p->key, + peditor->p->callback, + peditor, NULL, NULL); + g_object_unref (client); + break; + + case PROP_CHANGESET: + peditor->p->changeset = g_value_get_pointer (value); + break; + + case PROP_CONV_TO_WIDGET_CB: + peditor->p->conv_to_widget_cb = g_value_get_pointer (value); + break; + + case PROP_CONV_FROM_WIDGET_CB: + peditor->p->conv_from_widget_cb = g_value_get_pointer (value); + break; + + case PROP_UI_CONTROL: + peditor->p->ui_control = g_value_get_object (value); + g_object_weak_ref (peditor->p->ui_control, (GWeakNotify) g_object_unref, object); + break; + case PROP_DATA: + peditor->p->data = g_value_get_pointer (value); + break; + case PROP_DATA_FREE_CB: + peditor->p->data_free_cb = g_value_get_pointer (value); + break; + default: + g_warning ("Bad argument set"); + break; + } +} + +static void +mateconf_property_editor_get_prop (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MateConfPropertyEditor *peditor; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (object)); + + peditor = MATECONF_PROPERTY_EDITOR (object); + + switch (prop_id) { + case PROP_KEY: + g_value_set_string (value, peditor->p->key); + break; + + case PROP_CHANGESET: + g_value_set_pointer (value, peditor->p->changeset); + break; + + case PROP_DATA: + g_value_set_pointer (value, peditor->p->data); + break; + default: + g_warning ("Bad argument get"); + break; + } +} + +static void +mateconf_property_editor_finalize (GObject *object) +{ + MateConfPropertyEditor *mateconf_property_editor; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (object)); + + mateconf_property_editor = MATECONF_PROPERTY_EDITOR (object); + + g_free (mateconf_property_editor->p->key); + + if (mateconf_property_editor->p->data_free_cb) + mateconf_property_editor->p->data_free_cb (mateconf_property_editor->p->data); + + if (mateconf_property_editor->p->handler_id != 0) { + MateConfClient *client; + + client = mateconf_client_get_default (); + mateconf_client_notify_remove (client, + mateconf_property_editor->p->handler_id); + g_object_unref (client); + } + + G_OBJECT_CLASS (mateconf_property_editor_parent_class)->finalize (object); +} + +static GObject * +mateconf_peditor_new (const gchar *key, + MateConfClientNotifyFunc cb, + MateConfChangeSet *changeset, + GObject *ui_control, + const gchar *first_prop_name, + va_list var_args, + const gchar *first_custom, + ...) +{ + GObject *obj; + MateConfClient *client; + MateConfEntry *mateconf_entry; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (cb != NULL, NULL); + + obj = g_object_new (mateconf_property_editor_get_type (), + "key", key, + "callback", cb, + "changeset", changeset, + "ui-control", ui_control, + NULL); + + g_object_set_valist (obj, first_prop_name, var_args); + + if (first_custom) + { + va_list custom_args; + va_start (custom_args, first_custom); + g_object_set_valist (obj, first_custom, custom_args); + va_end (custom_args); + } + + client = mateconf_client_get_default (); + mateconf_entry = mateconf_client_get_entry (client, MATECONF_PROPERTY_EDITOR (obj)->p->key, NULL, TRUE, NULL); + MATECONF_PROPERTY_EDITOR (obj)->p->callback (client, 0, mateconf_entry, obj); + MATECONF_PROPERTY_EDITOR (obj)->p->inited = TRUE; + if (mateconf_entry) + mateconf_entry_free (mateconf_entry); + g_object_unref (client); + + return obj; +} + +const gchar * +mateconf_property_editor_get_key (MateConfPropertyEditor *peditor) +{ + return peditor->p->key; +} + +GObject * +mateconf_property_editor_get_ui_control (MateConfPropertyEditor *peditor) +{ + return peditor->p->ui_control; +} + +static void +peditor_set_mateconf_value (MateConfPropertyEditor *peditor, + const gchar *key, + MateConfValue *value) +{ + + if (peditor->p->changeset != NULL) { + if (value) + mateconf_change_set_set (peditor->p->changeset, peditor->p->key, value); + else + mateconf_change_set_unset (peditor->p->changeset, peditor->p->key); + } else { + MateConfClient *client = mateconf_client_get_default(); + + if (value) + mateconf_client_set (client, peditor->p->key, value, NULL); + else + mateconf_client_unset (client, peditor->p->key, NULL); + + g_object_unref (client); + } + +} + +static void +peditor_boolean_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (peditor->p->ui_control), mateconf_value_get_bool (value_wid)); + mateconf_value_free (value_wid); + } +} + +static void +peditor_boolean_widget_changed (MateConfPropertyEditor *peditor, + GtkToggleButton *tb) +{ + MateConfValue *value, *value_wid; + + if (!peditor->p->inited) return; + value_wid = mateconf_value_new (MATECONF_VALUE_BOOL); + mateconf_value_set_bool (value_wid, gtk_toggle_button_get_active (tb)); + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + mateconf_value_free (value_wid); + mateconf_value_free (value); +} + +GObject * +mateconf_peditor_new_boolean (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *checkbox, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (checkbox != NULL, NULL); + g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (checkbox), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_boolean_value_changed, + changeset, + G_OBJECT (checkbox), + first_property_name, + var_args, + NULL); + + va_end (var_args); + + g_signal_connect_swapped (checkbox, "toggled", + (GCallback) peditor_boolean_widget_changed, peditor); + + return peditor; +} + +static void +peditor_integer_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + const char *entry_current_text; + int entry_current_integer; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + entry_current_text = gtk_entry_get_text (GTK_ENTRY (peditor->p->ui_control)); + entry_current_integer = strtol (entry_current_text, NULL, 10); + if (entry_current_integer != mateconf_value_get_int (value)) { + char *buf = g_strdup_printf ("%d", mateconf_value_get_int (value_wid)); + gtk_entry_set_text (GTK_ENTRY (peditor->p->ui_control), buf); + g_free (buf); + } + mateconf_value_free (value_wid); + } +} + +static void +peditor_integer_widget_changed (MateConfPropertyEditor *peditor, + GtkEntry *entry) +{ + MateConfValue *value, *value_wid; + + if (!peditor->p->inited) return; + + value_wid = mateconf_value_new (MATECONF_VALUE_INT); + + mateconf_value_set_int (value_wid, strtol (gtk_entry_get_text (entry), NULL, 10)); + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + + peditor_set_mateconf_value (peditor, peditor->p->key, value); + + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + mateconf_value_free (value_wid); + mateconf_value_free (value); +} + +static GObject * +mateconf_peditor_new_integer_valist (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *entry, + const gchar *first_property_name, + va_list var_args) +{ + GObject *peditor; + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_integer_value_changed, + changeset, + G_OBJECT (entry), + first_property_name, + var_args, NULL); + + g_signal_connect_swapped (entry, "changed", + (GCallback) peditor_integer_widget_changed, peditor); + + return peditor; +} + +GObject * +mateconf_peditor_new_integer (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *entry, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (entry != NULL, NULL); + g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new_integer_valist + (changeset, key, entry, + first_property_name, var_args); + + va_end (var_args); + + return peditor; +} + +static void +peditor_string_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + const char *entry_current_text; + const char *mateconf_text; + + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + mateconf_text = mateconf_value_get_string (value_wid); + entry_current_text = gtk_entry_get_text (GTK_ENTRY (peditor->p->ui_control)); + + if (mateconf_text && strcmp (entry_current_text, mateconf_text) != 0) { + gtk_entry_set_text (GTK_ENTRY (peditor->p->ui_control), mateconf_value_get_string (value_wid)); + } + mateconf_value_free (value_wid); + } +} + +static void +peditor_string_widget_changed (MateConfPropertyEditor *peditor, + GtkEntry *entry) +{ + MateConfValue *value, *value_wid; + + if (!peditor->p->inited) return; + + value_wid = mateconf_value_new (MATECONF_VALUE_STRING); + + mateconf_value_set_string (value_wid, gtk_entry_get_text (entry)); + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + + peditor_set_mateconf_value (peditor, peditor->p->key, value); + + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + mateconf_value_free (value_wid); + mateconf_value_free (value); +} + +static GObject * +mateconf_peditor_new_string_valist (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *entry, + const gchar *first_property_name, + va_list var_args) +{ + GObject *peditor; + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_string_value_changed, + changeset, + G_OBJECT (entry), + first_property_name, + var_args, NULL); + + g_signal_connect_swapped (entry, "changed", + (GCallback) peditor_string_widget_changed, peditor); + + return peditor; +} + +GObject * +mateconf_peditor_new_string (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *entry, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (entry != NULL, NULL); + g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new_string_valist + (changeset, key, entry, + first_property_name, var_args); + + va_end (var_args); + + return peditor; +} + +static void +peditor_color_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + const gchar *spec; + + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + spec = mateconf_value_get_string (value_wid); + if (spec) { + GdkColor color; + gdk_color_parse (mateconf_value_get_string (value_wid), &color); + gtk_color_button_set_color ( + GTK_COLOR_BUTTON (peditor->p->ui_control), &color); + } + mateconf_value_free (value_wid); + } +} + +static void +peditor_color_widget_changed (MateConfPropertyEditor *peditor, + GtkColorButton *cb) +{ + gchar *str; + MateConfValue *value, *value_wid; + GdkColor color; + + if (!peditor->p->inited) return; + + value_wid = mateconf_value_new (MATECONF_VALUE_STRING); + gtk_color_button_get_color (cb, &color); + str = g_strdup_printf ("#%02x%02x%02x", color.red >> 8, + color.green >> 8, color.blue >> 8); + mateconf_value_set_string (value_wid, str); + g_free (str); + + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + + mateconf_value_free (value_wid); + mateconf_value_free (value); +} + +GObject * +mateconf_peditor_new_color (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *cb, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (cb != NULL, NULL); + g_return_val_if_fail (GTK_IS_COLOR_BUTTON (cb), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_color_value_changed, + changeset, + G_OBJECT (cb), + first_property_name, + var_args, NULL); + + va_end (var_args); + + g_signal_connect_swapped (cb, "color_set", + (GCallback) peditor_color_widget_changed, peditor); + + return peditor; +} + +static int +peditor_enum_int_from_string (GType type, const gchar *str, gboolean use_nick) +{ + GEnumClass *klass; + GEnumValue *val; + int ret = -1; + + klass = g_type_class_ref (type); + if (use_nick) + val = g_enum_get_value_by_nick (klass, str); + else + val = g_enum_get_value_by_name (klass, str); + + g_type_class_unref (klass); + + if (val) + ret = val->value; + + return ret; +} + +static gchar* +peditor_enum_string_from_int (GType type, const int index, gboolean use_nick) +{ + GEnumClass *klass; + GEnumValue *val; + gchar *ret = NULL; + + klass = g_type_class_ref (type); + val = g_enum_get_value (klass, index); + if (val) + { + if (val->value_nick && use_nick) + ret = g_strdup (val->value_nick); + else + ret = g_strdup (val->value_name); + } + + g_type_class_unref (klass); + + return ret; +} + +static MateConfValue* +peditor_enum_conv_to_widget (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *ret; + MateConfPropertyEditorEnumData *data = peditor->p->data; + int index; + + if (value->type == MATECONF_VALUE_INT) + return mateconf_value_copy (value); + + ret = mateconf_value_new (MATECONF_VALUE_INT); + + index = peditor_enum_int_from_string (data->enum_type, + mateconf_value_get_string (value), + data->use_nick); + + mateconf_value_set_int (ret, index); + + return ret; +} + +static MateConfValue* +peditor_enum_conv_from_widget (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *ret; + MateConfPropertyEditorEnumData *data = peditor->p->data; + gchar *str; + + if (value->type == MATECONF_VALUE_STRING) + return mateconf_value_copy (value); + + ret = mateconf_value_new (MATECONF_VALUE_STRING); + str = peditor_enum_string_from_int (data->enum_type, + mateconf_value_get_int (value), + data->use_nick); + mateconf_value_set_string (ret, str); + g_free (str); + + return ret; +} + +static void +peditor_combo_box_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + gtk_combo_box_set_active (GTK_COMBO_BOX (peditor->p->ui_control), mateconf_value_get_int (value_wid)); + mateconf_value_free (value_wid); + } +} + +static void +peditor_combo_box_widget_changed (MateConfPropertyEditor *peditor, + GtkComboBox *combo_box) +{ + MateConfValue *value, *value_wid; + + if (!peditor->p->inited) return; + value_wid = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (value_wid, gtk_combo_box_get_active (combo_box)); + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + mateconf_value_free (value_wid); + if (value) + mateconf_value_free (value); +} + +GObject * +mateconf_peditor_new_combo_box (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *combo_box, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (combo_box != NULL, NULL); + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_combo_box_value_changed, + changeset, + G_OBJECT (combo_box), + first_property_name, + var_args, NULL); + + va_end (var_args); + + g_signal_connect_swapped (combo_box, "changed", + (GCallback) peditor_combo_box_widget_changed, peditor); + + return peditor; +} + +GObject * +mateconf_peditor_new_combo_box_with_enum (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *combo_box, + GType enum_type, + gboolean use_nick, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + MateConfPropertyEditorEnumData *data; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (combo_box != NULL, NULL); + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + g_return_val_if_fail (enum_type != G_TYPE_NONE, NULL); + + data = g_new0 (MateConfPropertyEditorEnumData, 1); + data->enum_type = enum_type; + data->use_nick = use_nick; + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_combo_box_value_changed, + changeset, + G_OBJECT (combo_box), + first_property_name, + var_args, + "conv-to-widget-cb", + peditor_enum_conv_to_widget, + "conv-from-widget-cb", + peditor_enum_conv_from_widget, + "data", + data, + "data-free-cb", + g_free, + NULL + ); + + va_end (var_args); + + g_signal_connect_swapped (combo_box, "changed", + (GCallback) peditor_combo_box_widget_changed, peditor); + + return peditor; +} + +static void +peditor_select_radio_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + GSList *group, *link; + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + group = g_slist_copy (gtk_radio_button_get_group (GTK_RADIO_BUTTON (peditor->p->ui_control))); + group = g_slist_reverse (group); + link = g_slist_nth (group, mateconf_value_get_int (value_wid)); + if (link && link->data) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (link->data), TRUE); + mateconf_value_free (value_wid); + g_slist_free (group); + } +} + +static void +peditor_select_radio_widget_changed (MateConfPropertyEditor *peditor, + GtkToggleButton *tb) +{ + GSList *group; + MateConfValue *value, *value_wid; + + if (!peditor->p->inited) return; + if (!gtk_toggle_button_get_active (tb)) return; + + value_wid = mateconf_value_new (MATECONF_VALUE_INT); + group = g_slist_copy (gtk_radio_button_get_group (GTK_RADIO_BUTTON (peditor->p->ui_control))); + group = g_slist_reverse (group); + + mateconf_value_set_int (value_wid, g_slist_index (group, tb)); + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + + mateconf_value_free (value_wid); + mateconf_value_free (value); + g_slist_free (group); +} + +GObject * +mateconf_peditor_new_select_radio (MateConfChangeSet *changeset, + const gchar *key, + GSList *radio_group, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + GtkRadioButton *first_button; + GSList *item; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (radio_group != NULL, NULL); + g_return_val_if_fail (radio_group->data != NULL, NULL); + g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_group->data), NULL); + + first_button = GTK_RADIO_BUTTON (radio_group->data); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_select_radio_value_changed, + changeset, + G_OBJECT (first_button), + first_property_name, + var_args, NULL); + + va_end (var_args); + + for (item = radio_group; item != NULL; item = item->next) + g_signal_connect_swapped (item->data, "toggled", + (GCallback) peditor_select_radio_widget_changed, peditor); + + return peditor; +} + +static void +peditor_numeric_range_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + + switch (value_wid->type) { + case MATECONF_VALUE_FLOAT: + gtk_adjustment_set_value (GTK_ADJUSTMENT (peditor->p->ui_control), mateconf_value_get_float (value_wid)); + break; + case MATECONF_VALUE_INT: + gtk_adjustment_set_value (GTK_ADJUSTMENT (peditor->p->ui_control), mateconf_value_get_int (value_wid)); + break; + default: + g_warning ("Unknown type in range peditor: %d\n", value_wid->type); + } + mateconf_value_free (value_wid); + } +} + +static void +peditor_numeric_range_widget_changed (MateConfPropertyEditor *peditor, + GtkAdjustment *adjustment) +{ + MateConfValue *value, *value_wid, *default_value; + MateConfClient *client; + + if (!peditor->p->inited) return; + + /* We try to get the default type from the schemas. if not, we default + * to an int. + */ + client = mateconf_client_get_default(); + + default_value = mateconf_client_get_default_from_schema (client, + peditor->p->key, + NULL); + g_object_unref (client); + + if (default_value) { + value_wid = mateconf_value_new (default_value->type); + mateconf_value_free (default_value); + } else { + g_warning ("Unable to find a default value for key for %s.\n" + "I'll assume it is an integer, but that may break things.\n" + "Please be sure that the associated schema is installed", + peditor->p->key); + value_wid = mateconf_value_new (MATECONF_VALUE_INT); + } + + g_assert (value_wid); + + if (value_wid->type == MATECONF_VALUE_INT) + mateconf_value_set_int (value_wid, gtk_adjustment_get_value (adjustment)); + else if (value_wid->type == MATECONF_VALUE_FLOAT) + mateconf_value_set_float (value_wid, gtk_adjustment_get_value (adjustment)); + else { + g_warning ("unable to set a mateconf key for %s of type %d", + peditor->p->key, + value_wid->type); + mateconf_value_free (value_wid); + return; + } + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + mateconf_value_free (value_wid); + mateconf_value_free (value); +} + +GObject * +mateconf_peditor_new_numeric_range (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *range, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + GObject *adjustment = NULL; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (range != NULL, NULL); + g_return_val_if_fail (GTK_IS_RANGE (range)||GTK_IS_SPIN_BUTTON (range), NULL); + + if (GTK_IS_RANGE (range)) + adjustment = G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (range))); + else if (GTK_IS_SPIN_BUTTON (range)) + adjustment = G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (range))); + else + g_assert_not_reached (); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_numeric_range_value_changed, + changeset, + adjustment, + first_property_name, + var_args, NULL); + + va_end (var_args); + + g_signal_connect_swapped (adjustment, "value_changed", + (GCallback) peditor_numeric_range_widget_changed, peditor); + + return peditor; +} + +static gboolean +guard_get_bool (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + if (value->type == MATECONF_VALUE_BOOL) + return mateconf_value_get_bool (value); + else + { + MateConfPropertyEditorEnumData *data = peditor->p->data; + int index = peditor_enum_int_from_string (data->enum_type, mateconf_value_get_string (value), data->use_nick); + return (index != data->enum_val_false); + } +} + +static void +guard_value_changed (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + GtkWidget *widget) +{ + gtk_widget_set_sensitive (widget, guard_get_bool (peditor, value)); +} + +void +mateconf_peditor_widget_set_guard (MateConfPropertyEditor *peditor, + GtkWidget *widget) +{ + MateConfClient *client; + MateConfValue *value; + + g_return_if_fail (peditor != NULL); + g_return_if_fail (IS_MATECONF_PROPERTY_EDITOR (peditor)); + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + client = mateconf_client_get_default (); + value = mateconf_client_get (client, peditor->p->key, NULL); + g_object_unref (client); + + if (value) { + gtk_widget_set_sensitive (widget, guard_get_bool (peditor, value)); + mateconf_value_free (value); + } else { + g_warning ("NULL MateConf value: %s: possibly incomplete setup", peditor->p->key); + } + + g_signal_connect (peditor, "value-changed", (GCallback) guard_value_changed, widget); +} + +MateConfValue * +mateconf_value_int_to_float (MateConfPropertyEditor *ignored, const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_FLOAT); + mateconf_value_set_float (new_value, mateconf_value_get_int (value)); + return new_value; +} + +MateConfValue * +mateconf_value_float_to_int (MateConfPropertyEditor *ignored, const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (new_value, mateconf_value_get_float (value)); + return new_value; +} + +static void +peditor_font_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + const gchar *font; + + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + font = mateconf_value_get_string (value_wid); + gtk_font_button_set_font_name (GTK_FONT_BUTTON (peditor->p->ui_control), + font); + mateconf_value_free (value_wid); + } +} + +static void +peditor_font_widget_changed (MateConfPropertyEditor *peditor, + GtkFontButton *font_button) +{ + const gchar *font_name; + MateConfValue *value, *value_wid = NULL; + + if (!peditor->p->inited) + return; + + font_name = gtk_font_button_get_font_name (font_button); + + value_wid = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (value_wid, font_name); + + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + + mateconf_value_free (value_wid); + mateconf_value_free (value); +} + +GObject * +mateconf_peditor_new_font (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *font_button, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (GTK_IS_FONT_BUTTON (font_button), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new (key, + (MateConfClientNotifyFunc) peditor_font_value_changed, + changeset, + G_OBJECT (font_button), + first_property_name, + var_args, + NULL); + + va_end (var_args); + + g_signal_connect_swapped (font_button, "font_set", + (GCallback) peditor_font_widget_changed, peditor); + + return peditor; +} + +static MateConfValue* +peditor_enum_toggle_conv_to_widget (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *ret; + MateConfPropertyEditorEnumData *data = peditor->p->data; + int index; + + if (value->type == MATECONF_VALUE_BOOL) + return mateconf_value_copy (value); + + ret = mateconf_value_new (MATECONF_VALUE_BOOL); + + index = peditor_enum_int_from_string (data->enum_type, + mateconf_value_get_string (value), + data->use_nick); + mateconf_value_set_bool (ret, (index != data->enum_val_false)); + + return ret; +} + +static MateConfValue* +peditor_enum_toggle_conv_from_widget (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *ret; + MateConfPropertyEditorEnumData *data = peditor->p->data; + gchar *str; + int index; + + if (value->type == MATECONF_VALUE_STRING) + return mateconf_value_copy (value); + + ret = mateconf_value_new (MATECONF_VALUE_STRING); + if (mateconf_value_get_bool (value)) + index = data->enum_val_true_fn (peditor, data->enum_val_true_fn_data); + else + index = data->enum_val_false; + + str = peditor_enum_string_from_int (data->enum_type, index, data->use_nick); + mateconf_value_set_string (ret, str); + g_free (str); + + return ret; +} + +GObject * +mateconf_peditor_new_enum_toggle (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *checkbox, + GType enum_type, + MateConfPEditorGetValueFn val_true_fn, + guint val_false, + gboolean use_nick, + gpointer data, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + MateConfPropertyEditorEnumData *enum_data; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (checkbox != NULL, NULL); + g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (checkbox), NULL); + + enum_data = g_new0 (MateConfPropertyEditorEnumData, 1); + enum_data->enum_type = enum_type; + enum_data->enum_val_true_fn = val_true_fn; + enum_data->enum_val_true_fn_data = data; + enum_data->enum_val_false = val_false; + enum_data->use_nick = use_nick; + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_boolean_value_changed, + changeset, + G_OBJECT (checkbox), + first_property_name, + var_args, + "conv-to-widget-cb", + peditor_enum_toggle_conv_to_widget, + "conv-from-widget-cb", + peditor_enum_toggle_conv_from_widget, + "data", + enum_data, + "data-free-cb", + g_free, + NULL); + + va_end (var_args); + + g_signal_connect_swapped (checkbox, "toggled", + (GCallback) peditor_boolean_widget_changed, peditor); + + return peditor; +} + +static gboolean +peditor_image_set_filename (MateConfPropertyEditor *peditor, const gchar *filename) +{ + GdkPixbuf *pixbuf = NULL; + GtkImage *image = NULL; + const int scale = 100; + gchar *message = NULL; + GtkWidget *ui_control_child; + GList *l; + + /* NULL is not valid, however "" is, but not an error (it's + * the default) */ + g_return_val_if_fail (filename != NULL, FALSE); + + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + message = g_strdup_printf (_("Couldn't find the file '%s'.\n\nPlease make " + "sure it exists and try again, " + "or choose a different background picture."), + filename); + + } + else if (!(pixbuf = gdk_pixbuf_new_from_file_at_size (filename, scale, scale, NULL))) + { + message = g_strdup_printf (_("I don't know how to open the file '%s'.\n" + "Perhaps it's " + "a kind of picture that is not yet supported.\n\n" + "Please select a different picture instead."), + filename); + } + + ui_control_child = gtk_bin_get_child (GTK_BIN (peditor->p->ui_control)); + + if (GTK_IS_IMAGE (ui_control_child)) + image = GTK_IMAGE (ui_control_child); + else + { + for (l = gtk_container_get_children (GTK_CONTAINER (ui_control_child)); l != NULL; l = l->next) + { + if (GTK_IS_IMAGE (l->data)) + image = GTK_IMAGE (l->data); + else if (GTK_IS_LABEL (l->data) && message == NULL) + { + gchar *base = g_path_get_basename (filename); + gtk_label_set_text (GTK_LABEL (l->data), base); + g_free (base); + } + } + } + + if (message) + { + if (peditor->p->inited) + { + GtkWidget *box; + + box = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + message); + gtk_dialog_run (GTK_DIALOG (box)); + gtk_widget_destroy (box); + } else { + gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE, + GTK_ICON_SIZE_BUTTON); + } + g_free (message); + + return FALSE; + } + + gtk_image_set_from_pixbuf (image, pixbuf); + g_object_unref (pixbuf); + + return TRUE; +} + +static void +peditor_image_chooser_response_cb (GtkWidget *chooser, + gint response, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + gchar *filename; + + if (response == GTK_RESPONSE_CANCEL || + response == GTK_RESPONSE_DELETE_EVENT) + { + gtk_widget_destroy (chooser); + return; + } + + if (!peditor->p->inited) + return; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); + if (!(filename && peditor_image_set_filename (peditor, filename))) + { + g_free (filename); + return; + } + + value_wid = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (value_wid, filename); + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + + mateconf_value_free (value_wid); + mateconf_value_free (value); + g_free (filename); + gtk_widget_destroy (chooser); +} + +static void +peditor_image_chooser_update_preview_cb (GtkFileChooser *chooser, + GtkImage *preview) +{ + char *filename; + GdkPixbuf *pixbuf = NULL; + const int scale = 100; + + filename = gtk_file_chooser_get_preview_filename (chooser); + + if (filename != NULL && g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + pixbuf = gdk_pixbuf_new_from_file_at_size (filename, scale, scale, NULL); + + gtk_image_set_from_pixbuf (preview, pixbuf); + + g_free (filename); + + if (pixbuf != NULL) + g_object_unref (pixbuf); +} + +static void +peditor_image_clicked_cb (MateConfPropertyEditor *peditor, GtkButton *button) +{ + MateConfValue *value = NULL, *value_wid; + const gchar *filename; + GtkWidget *chooser, *toplevel, *preview, *preview_box; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + chooser = gtk_file_chooser_dialog_new (_("Please select an image."), + GTK_IS_WINDOW (toplevel) ? GTK_WINDOW (toplevel) + : NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("_Select"), GTK_RESPONSE_OK, + NULL); + + preview = gtk_image_new (); + + preview_box = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (preview_box), preview, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (preview_box), 6); + + gtk_widget_show_all (preview_box); + gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (chooser), + preview_box); + gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (chooser), + TRUE); + + gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK); + gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE); + gtk_window_set_modal (GTK_WINDOW (chooser), TRUE); + + /* need the current filename */ + if (peditor->p->changeset) + mateconf_change_set_check_value (peditor->p->changeset, peditor->p->key, &value); + + if (value) + { + /* the one we got is not a copy */ + value = mateconf_value_copy (value); + } + else + { + MateConfClient *client = mateconf_client_get_default (); + value = mateconf_client_get (client, peditor->p->key, NULL); + g_object_unref (client); + } + + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + filename = mateconf_value_get_string (value_wid); + + if (filename && strcmp (filename, "")) + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), filename); + + g_signal_connect (chooser, "update-preview", + G_CALLBACK (peditor_image_chooser_update_preview_cb), + preview); + g_signal_connect (chooser, "response", + G_CALLBACK (peditor_image_chooser_response_cb), + peditor); + + if (gtk_grab_get_current ()) + gtk_grab_add (chooser); + + gtk_widget_show (chooser); + + mateconf_value_free (value); + mateconf_value_free (value_wid); +} + +static void +peditor_image_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value, *value_wid; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + const gchar *filename; + + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + filename = mateconf_value_get_string (value_wid); + peditor_image_set_filename (peditor, filename); + mateconf_value_free (value_wid); + } +} + +GObject * +mateconf_peditor_new_image (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *button, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (button != NULL, NULL); + g_return_val_if_fail (GTK_IS_BUTTON (button), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_image_value_changed, + changeset, + G_OBJECT (button), + first_property_name, + var_args, NULL); + + va_end (var_args); + + g_signal_connect_swapped (button, "clicked", + (GCallback) peditor_image_clicked_cb, peditor); + + return peditor; +} + +GObject * +mateconf_peditor_new_select_radio_with_enum (MateConfChangeSet *changeset, + const gchar *key, + GSList *radio_group, + GType enum_type, + gboolean use_nick, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + MateConfPropertyEditorEnumData *enum_data; + GtkRadioButton *first_button; + GSList *item; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (radio_group != NULL, NULL); + g_return_val_if_fail (radio_group->data != NULL, NULL); + g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_group->data), NULL); + + enum_data = g_new0 (MateConfPropertyEditorEnumData, 1); + enum_data->enum_type = enum_type; + enum_data->use_nick = use_nick; + + first_button = GTK_RADIO_BUTTON (radio_group->data); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_select_radio_value_changed, + changeset, + G_OBJECT (first_button), + first_property_name, + var_args, + "conv-to-widget-cb", + peditor_enum_conv_to_widget, + "conv-from-widget-cb", + peditor_enum_conv_from_widget, + "data", + enum_data, + "data-free-cb", + g_free, + NULL); + + va_end (var_args); + + for (item = radio_group; item != NULL; item = item->next) + g_signal_connect_swapped (item->data, "toggled", + (GCallback) peditor_select_radio_widget_changed, peditor); + + return peditor; +} + +static void +peditor_tree_view_value_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MateConfPropertyEditor *peditor) +{ + MateConfValue *value; + + if (peditor->p->changeset != NULL) + mateconf_change_set_remove (peditor->p->changeset, peditor->p->key); + + if (entry && (value = mateconf_entry_get_value (entry))) { + GtkTreeView *treeview; + GtkTreeSelection *selection; + MateConfValue *value_wid; + + treeview = GTK_TREE_VIEW (peditor->p->ui_control); + selection = gtk_tree_view_get_selection (treeview); + + value_wid = peditor->p->conv_to_widget_cb (peditor, value); + + if (value_wid != NULL) { + GtkTreePath *path = gtk_tree_path_new_from_string ( + mateconf_value_get_string (value_wid)); + gtk_tree_selection_select_path (selection, path); + gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); + mateconf_value_free (value_wid); + } else { + gtk_tree_selection_unselect_all (selection); + } + } +} + +static void +peditor_tree_view_widget_changed (MateConfPropertyEditor *peditor, + GtkTreeSelection *selection) +{ + GtkTreeModel *model; + GtkTreeIter iter; + MateConfValue *value, *value_wid; + + if (!peditor->p->inited) return; + + /* we don't support GTK_SELECTION_MULTIPLE */ + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *path; + + path = gtk_tree_model_get_string_from_iter (model, &iter); + value_wid = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (value_wid, path); + g_free (path); + } else + value_wid = NULL; + + value = peditor->p->conv_from_widget_cb (peditor, value_wid); + peditor_set_mateconf_value (peditor, peditor->p->key, value); + g_signal_emit (peditor, peditor_signals[VALUE_CHANGED], 0, peditor->p->key, value); + + if (value_wid) + mateconf_value_free (value_wid); + if (value) + mateconf_value_free (value); +} + +GObject * +mateconf_peditor_new_tree_view (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *tree_view, + const gchar *first_property_name, + ...) +{ + GObject *peditor; + va_list var_args; + + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (tree_view != NULL, NULL); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + va_start (var_args, first_property_name); + + peditor = mateconf_peditor_new + (key, + (MateConfClientNotifyFunc) peditor_tree_view_value_changed, + changeset, + G_OBJECT (tree_view), + first_property_name, + var_args, NULL); + + va_end (var_args); + + g_signal_connect_swapped (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)), + "changed", + (GCallback) peditor_tree_view_widget_changed, peditor); + + return peditor; +} diff --git a/capplets/common/mateconf-property-editor.h b/capplets/common/mateconf-property-editor.h new file mode 100644 index 00000000..1f15b9fd --- /dev/null +++ b/capplets/common/mateconf-property-editor.h @@ -0,0 +1,162 @@ +/* -*- mode: c; style: linux -*- */ + +/* mateconf-property-editor.h + * Copyright (C) 2001 Ximian, Inc. + * + * Written by Bradford Hovinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __MATECONF_PROPERTY_EDITOR_H +#define __MATECONF_PROPERTY_EDITOR_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MATECONF_PROPERTY_EDITOR(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, mateconf_property_editor_get_type (), MateConfPropertyEditor) +#define MATECONF_PROPERTY_EDITOR_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, mateconf_property_editor_get_type (), MateConfPropertyEditorClass) +#define IS_MATECONF_PROPERTY_EDITOR(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, mateconf_property_editor_get_type ()) + +typedef struct _MateConfPropertyEditor MateConfPropertyEditor; +typedef struct _MateConfPropertyEditorClass MateConfPropertyEditorClass; +typedef struct _MateConfPropertyEditorPrivate MateConfPropertyEditorPrivate; + +typedef MateConfValue *(*MateConfPEditorValueConvFn) (MateConfPropertyEditor *peditor, const MateConfValue *); +typedef int (*MateConfPEditorGetValueFn) (MateConfPropertyEditor *peditor, gpointer data); + +struct _MateConfPropertyEditor +{ + GObject parent; + + MateConfPropertyEditorPrivate *p; +}; + +struct _MateConfPropertyEditorClass +{ + GObjectClass g_object_class; + + void (*value_changed) (MateConfPropertyEditor *peditor, gchar *key, const MateConfValue *value); +}; + +GType mateconf_property_editor_get_type (void); + +const gchar *mateconf_property_editor_get_key (MateConfPropertyEditor *peditor); +GObject *mateconf_property_editor_get_ui_control (MateConfPropertyEditor *peditor); + +GObject *mateconf_peditor_new_boolean (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *checkbox, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_enum_toggle (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *checkbox, + GType enum_type, + MateConfPEditorGetValueFn val_true_fn, + guint val_false, + gboolean use_nick, + gpointer data, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_integer (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *entry, + const gchar *first_property_name, + ...); +GObject *mateconf_peditor_new_string (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *entry, + const gchar *first_property_name, + ...); +GObject *mateconf_peditor_new_color (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *color_entry, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_combo_box (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *combo_box, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_combo_box_with_enum (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *combo_box, + GType enum_type, + gboolean use_nick, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_select_radio (MateConfChangeSet *changeset, + const gchar *key, + GSList *radio_group, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_select_radio_with_enum (MateConfChangeSet *changeset, + const gchar *key, + GSList *radio_group, + GType enum_type, + gboolean use_nick, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_numeric_range (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *range, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_font (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *font_button, + const gchar *first_property_name, + ...); + +GObject *mateconf_peditor_new_image (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *button, + const gchar *first_property, + ...); + +GObject *mateconf_peditor_new_tree_view (MateConfChangeSet *changeset, + const gchar *key, + GtkWidget *tree_view, + const gchar *first_property_name, + ...); + +void mateconf_peditor_widget_set_guard (MateConfPropertyEditor *peditor, + GtkWidget *widget); + +/* some convenience callbacks to map int <-> float */ +MateConfValue *mateconf_value_int_to_float (MateConfPropertyEditor *ignored, MateConfValue const *value); +MateConfValue *mateconf_value_float_to_int (MateConfPropertyEditor *ignored, MateConfValue const *value); + +#ifdef __cplusplus +} +#endif + +#endif /* __MATECONF_PROPERTY_EDITOR_H */ diff --git a/capplets/common/theme-thumbnail.c b/capplets/common/theme-thumbnail.c new file mode 100644 index 00000000..4afa4454 --- /dev/null +++ b/capplets/common/theme-thumbnail.c @@ -0,0 +1,1144 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We have to #undef this as marco #defines these. */ +#undef _ +#undef N_ + +#include + +#include "theme-thumbnail.h" +#include "gtkrc-utils.h" +#include "capplet-util.h" + +typedef struct { + gboolean set; + gint thumbnail_width; + gint thumbnail_height; + GByteArray* data; + gchar* theme_name; + ThemeThumbnailFunc func; + gpointer user_data; + GDestroyNotify destroy; + GIOChannel* channel; + guint watch_id; +} ThemeThumbnailAsyncData; + + +static ThemeThumbnailAsyncData async_data; + +/* Protocol */ + +/* Our protocol is pretty simple. The parent process will write several strings + * (separated by a '\000'). They are the widget theme, the wm theme, the icon + * theme, etc. Then, it will wait for the child to write back the data. The + * parent expects ICON_SIZE_WIDTH * ICON_SIZE_HEIGHT * 4 bytes of information. + * After that, the child is ready for the next theme to render. + */ + +enum { + READY_FOR_THEME, + READING_TYPE, + READING_CONTROL_THEME_NAME, + READING_GTK_COLOR_SCHEME, + READING_WM_THEME_NAME, + READING_ICON_THEME_NAME, + READING_APPLICATION_FONT, + WRITING_PIXBUF_DATA +}; + +typedef struct { + gint status; + GByteArray* type; + GByteArray* control_theme_name; + GByteArray* gtk_color_scheme; + GByteArray* wm_theme_name; + GByteArray* icon_theme_name; + GByteArray* application_font; +} ThemeThumbnailData; + +typedef struct { + gchar* thumbnail_type; + gpointer theme_info; + ThemeThumbnailFunc func; + gpointer user_data; + GDestroyNotify destroy; +} ThemeQueueItem; + +static GList* theme_queue = NULL; + +static int pipe_to_factory_fd[2]; +static int pipe_from_factory_fd[2]; + +#define THUMBNAIL_TYPE_META "meta" +#define THUMBNAIL_TYPE_GTK "gtk" +#define THUMBNAIL_TYPE_MARCO "marco" +#define THUMBNAIL_TYPE_ICON "icon" + +#define META_THUMBNAIL_SIZE 128 +#define GTK_THUMBNAIL_SIZE 96 +#define MARCO_THUMBNAIL_WIDTH 120 +#define MARCO_THUMBNAIL_HEIGHT 60 + +/* This draw the thumbnail of gtk + */ +static GdkPixmap* draw_window_on_pixbuf(GtkWidget* widget) +{ + GdkVisual* visual; + GdkPixmap* pixmap; + GtkStyle* style; + GdkScreen* screen = gdk_screen_get_default(); + GdkWindow* window; + gint width, height; + + gtk_widget_ensure_style(widget); + + style = gtk_widget_get_style(widget); + + g_assert(style); + g_assert(style->font_desc); + + gtk_window_get_size(GTK_WINDOW(widget), &width, &height); + + visual = gtk_widget_get_visual(widget); + pixmap = gdk_pixmap_new(NULL, width, height, visual->depth); + gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), gtk_widget_get_colormap(widget)); + + window = gtk_widget_get_window(widget); + + /* This is a hack for the default resize grip on Ubuntu. + * Once again, thank you Ubuntu. + * + * We need to add a --enable-ubuntu for this. + */ + #ifdef UBUNTU + gtk_window_set_has_resize_grip(GTK_WINDOW(widget), FALSE); + #endif + + gdk_window_redirect_to_drawable(window, pixmap, 0, 0, 0, 0, width, height); + gdk_window_set_override_redirect(window, TRUE); + gtk_window_move(GTK_WINDOW(widget), gdk_screen_get_width(screen), gdk_screen_get_height(screen)); + gtk_widget_show(widget); + + gdk_window_process_updates(window, TRUE); + gtk_widget_hide(widget); + + return pixmap; +} + +static void pixbuf_apply_mask_region(GdkPixbuf* pixbuf, GdkRegion* region) +{ + gint nchannels, rowstride, w, h; + guchar *pixels, *p; + + g_return_if_fail (pixbuf); + g_return_if_fail (region); + + nchannels = gdk_pixbuf_get_n_channels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + + /* we need an alpha channel ... */ + if (!gdk_pixbuf_get_has_alpha (pixbuf) || nchannels != 4) + return; + + for (w = 0; w < gdk_pixbuf_get_width (pixbuf); ++w) + for (h = 0; h < gdk_pixbuf_get_height (pixbuf); ++h) + { + if (!gdk_region_point_in (region, w, h)) + { + p = pixels + h * rowstride + w * nchannels; + if (G_BYTE_ORDER == G_BIG_ENDIAN) + p[0] = 0x0; + else + p[3] = 0x0; + } + } + +} + +static GdkPixbuf * +create_folder_icon (char *icon_theme_name) +{ + GtkIconTheme *icon_theme; + GdkPixbuf *folder_icon = NULL; + GtkIconInfo *folder_icon_info; + gchar *example_icon_name; + const gchar *icon_names[5]; + gint i; + + icon_theme = gtk_icon_theme_new (); + gtk_icon_theme_set_custom_theme (icon_theme, icon_theme_name); + + i = 0; + /* Get the Example icon name in the theme if specified */ + example_icon_name = gtk_icon_theme_get_example_icon_name (icon_theme); + if (example_icon_name != NULL) + icon_names[i++] = example_icon_name; + icon_names[i++] = "x-directory-normal"; + icon_names[i++] = "mate-fs-directory"; + icon_names[i++] = "folder"; + icon_names[i++] = NULL; + + folder_icon_info = gtk_icon_theme_choose_icon (icon_theme, icon_names, 48, GTK_ICON_LOOKUP_FORCE_SIZE); + if (folder_icon_info != NULL) + { + folder_icon = gtk_icon_info_load_icon (folder_icon_info, NULL); + gtk_icon_info_free (folder_icon_info); + } + + g_object_unref (icon_theme); + g_free (example_icon_name); + + /* render the icon to the thumbnail */ + if (folder_icon == NULL) + { + GtkWidget *dummy; + dummy = gtk_label_new (""); + + folder_icon = gtk_widget_render_icon (dummy, + GTK_STOCK_MISSING_IMAGE, + GTK_ICON_SIZE_DIALOG, + NULL); + + gtk_widget_destroy (dummy); + } + + return folder_icon; +} + +static GdkPixbuf * +create_meta_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data) +{ + GtkWidget *window; + GtkWidget *preview; + GtkWidget *vbox; + GtkWidget *align; + GtkWidget *box; + GtkWidget *stock_button; + GtkWidget *checkbox; + GtkWidget *radio; + + GtkRequisition requisition; + GtkAllocation allocation; + GtkAllocation vbox_allocation; + GdkPixmap *pixmap; + MetaFrameFlags flags; + MetaTheme *theme; + GdkPixbuf *pixbuf, *icon; + int icon_width, icon_height; + GdkRegion *region; + + g_object_set (gtk_settings_get_default (), + "gtk-theme-name", (char *) theme_thumbnail_data->control_theme_name->data, + "gtk-font-name", (char *) theme_thumbnail_data->application_font->data, + "gtk-icon-theme-name", (char *) theme_thumbnail_data->icon_theme_name->data, + "gtk-color-scheme", (char *) theme_thumbnail_data->gtk_color_scheme->data, + NULL); + + theme = meta_theme_load ((char *) theme_thumbnail_data->wm_theme_name->data, NULL); + if (theme == NULL) + return NULL; + + /* Represent the icon theme */ + icon = create_folder_icon ((char *) theme_thumbnail_data->icon_theme_name->data); + icon_width = gdk_pixbuf_get_width (icon); + icon_height = gdk_pixbuf_get_height (icon); + + /* Create a fake window */ + flags = META_FRAME_ALLOWS_DELETE | + META_FRAME_ALLOWS_MENU | + META_FRAME_ALLOWS_MINIMIZE | + META_FRAME_ALLOWS_MAXIMIZE | + META_FRAME_ALLOWS_VERTICAL_RESIZE | + META_FRAME_ALLOWS_HORIZONTAL_RESIZE | + META_FRAME_HAS_FOCUS | + META_FRAME_ALLOWS_SHADE | + META_FRAME_ALLOWS_MOVE; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + preview = meta_preview_new (); + gtk_container_add (GTK_CONTAINER (window), preview); + gtk_widget_realize (window); + gtk_widget_realize (preview); + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + gtk_container_add (GTK_CONTAINER (preview), vbox); + align = gtk_alignment_new (0, 0, 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); + stock_button = gtk_button_new_from_stock (GTK_STOCK_OPEN); + gtk_container_add (GTK_CONTAINER (align), stock_button); + box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0); + checkbox = gtk_check_button_new (); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), TRUE); + gtk_box_pack_start (GTK_BOX (box), checkbox, FALSE, FALSE, 0); + radio = gtk_radio_button_new (NULL); + gtk_box_pack_start (GTK_BOX (box), radio, FALSE, FALSE, 0); + + gtk_widget_show_all (preview); + + meta_preview_set_frame_flags (META_PREVIEW (preview), flags); + meta_preview_set_theme (META_PREVIEW (preview), theme); + meta_preview_set_title (META_PREVIEW (preview), ""); + + gtk_window_set_default_size (GTK_WINDOW (window), META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE); + + gtk_widget_size_request (window, &requisition); + allocation.x = 0; + allocation.y = 0; + allocation.width = META_THUMBNAIL_SIZE; + allocation.height = META_THUMBNAIL_SIZE; + gtk_widget_size_allocate (window, &allocation); + gtk_widget_size_request (window, &requisition); + + pixmap = draw_window_on_pixbuf (window); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE); + gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE); + + gtk_widget_get_allocation (vbox, &vbox_allocation); + + /* Add the icon theme to the pixbuf */ + gdk_pixbuf_composite (icon, pixbuf, + vbox_allocation.x + vbox_allocation.width - icon_width - 5, + vbox_allocation.y + vbox_allocation.height - icon_height - 5, + icon_width, icon_height, + vbox_allocation.x + vbox_allocation.width - icon_width - 5, + vbox_allocation.y + vbox_allocation.height - icon_height - 5, + 1.0, 1.0, GDK_INTERP_BILINEAR, 255); + region = meta_preview_get_clip_region (META_PREVIEW (preview), + META_THUMBNAIL_SIZE, META_THUMBNAIL_SIZE); + pixbuf_apply_mask_region (pixbuf, region); + gdk_region_destroy (region); + + g_object_unref (icon); + gtk_widget_destroy (window); + meta_theme_free (theme); + g_object_unref (pixmap); + + return pixbuf; +} + +static GdkPixbuf * +create_gtk_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data) +{ + GtkSettings *settings; + GtkWidget *window, *vbox, *box, *stock_button, *checkbox, *radio; + GtkRequisition requisition; + GtkAllocation allocation; + GdkPixmap *pixmap; + GdkPixbuf *pixbuf, *retval; + gint width, height; + + settings = gtk_settings_get_default (); + g_object_set (settings, "gtk-theme-name", (char *) theme_thumbnail_data->control_theme_name->data, + "gtk-color-scheme", (char *) theme_thumbnail_data->gtk_color_scheme->data, + NULL); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), vbox); + box = gtk_hbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (box), 6); + gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0); + stock_button = gtk_button_new_from_stock (GTK_STOCK_OPEN); + gtk_box_pack_start (GTK_BOX (box), stock_button, FALSE, FALSE, 0); + checkbox = gtk_check_button_new (); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), TRUE); + gtk_box_pack_start (GTK_BOX (box), checkbox, FALSE, FALSE, 0); + radio = gtk_radio_button_new_from_widget (NULL); + gtk_box_pack_start (GTK_BOX (box), radio, FALSE, FALSE, 0); + + gtk_widget_show_all (vbox); + gtk_widget_realize (stock_button); + gtk_widget_realize (gtk_bin_get_child (GTK_BIN (stock_button))); + gtk_widget_realize (checkbox); + gtk_widget_realize (radio); + gtk_widget_map (stock_button); + gtk_widget_map (gtk_bin_get_child (GTK_BIN (stock_button))); + gtk_widget_map (checkbox); + gtk_widget_map (radio); + + gtk_widget_size_request (window, &requisition); + allocation.x = 0; + allocation.y = 0; + allocation.width = requisition.width; + allocation.height = requisition.height; + gtk_widget_size_allocate (window, &allocation); + gtk_widget_size_request (window, &requisition); + + gtk_window_get_size (GTK_WINDOW (window), &width, &height); + + pixmap = draw_window_on_pixbuf (window); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, width, height); + + retval = gdk_pixbuf_scale_simple (pixbuf, + GTK_THUMBNAIL_SIZE, + (int) GTK_THUMBNAIL_SIZE * (((double) height) / ((double) width)), + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + gtk_widget_destroy (window); + g_object_unref (pixmap); + + return retval; +} + +static GdkPixbuf * +create_marco_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data) +{ + GtkWidget *window, *preview, *dummy; + MetaFrameFlags flags; + MetaTheme *theme; + GtkRequisition requisition; + GtkAllocation allocation; + GdkPixmap *pixmap; + GdkPixbuf *pixbuf, *retval; + GdkRegion *region; + + theme = meta_theme_load ((char *) theme_thumbnail_data->wm_theme_name->data, NULL); + if (theme == NULL) + return NULL; + + flags = META_FRAME_ALLOWS_DELETE | + META_FRAME_ALLOWS_MENU | + META_FRAME_ALLOWS_MINIMIZE | + META_FRAME_ALLOWS_MAXIMIZE | + META_FRAME_ALLOWS_VERTICAL_RESIZE | + META_FRAME_ALLOWS_HORIZONTAL_RESIZE | + META_FRAME_HAS_FOCUS | + META_FRAME_ALLOWS_SHADE | + META_FRAME_ALLOWS_MOVE; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size (GTK_WINDOW (window), (int) MARCO_THUMBNAIL_WIDTH * 1.2, (int) MARCO_THUMBNAIL_HEIGHT * 1.2); + + preview = meta_preview_new (); + meta_preview_set_frame_flags (META_PREVIEW (preview), flags); + meta_preview_set_theme (META_PREVIEW (preview), theme); + meta_preview_set_title (META_PREVIEW (preview), ""); + gtk_container_add (GTK_CONTAINER (window), preview); + + dummy = gtk_label_new (""); + gtk_container_add (GTK_CONTAINER (preview), dummy); + + gtk_widget_realize (window); + gtk_widget_realize (preview); + gtk_widget_realize (dummy); + gtk_widget_show_all (preview); + gtk_widget_map (dummy); + + gtk_widget_size_request (window, &requisition); + allocation.x = 0; + allocation.y = 0; + allocation.width = (int) MARCO_THUMBNAIL_WIDTH * 1.2; + allocation.height = (int) MARCO_THUMBNAIL_HEIGHT * 1.2; + gtk_widget_size_allocate (window, &allocation); + gtk_widget_size_request (window, &requisition); + + pixmap = draw_window_on_pixbuf (window); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, (int) MARCO_THUMBNAIL_WIDTH * 1.2, (int) MARCO_THUMBNAIL_HEIGHT * 1.2); + gdk_pixbuf_get_from_drawable (pixbuf, pixmap, NULL, 0, 0, 0, 0, (int) MARCO_THUMBNAIL_WIDTH * 1.2, (int) MARCO_THUMBNAIL_HEIGHT * 1.2); + + region = meta_preview_get_clip_region (META_PREVIEW (preview), + MARCO_THUMBNAIL_WIDTH * 1.2, MARCO_THUMBNAIL_HEIGHT * 1.2); + pixbuf_apply_mask_region (pixbuf, region); + gdk_region_destroy (region); + + + retval = gdk_pixbuf_scale_simple (pixbuf, + MARCO_THUMBNAIL_WIDTH, + MARCO_THUMBNAIL_HEIGHT, + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + + gtk_widget_destroy (window); + meta_theme_free (theme); + g_object_unref (pixmap); + + return retval; +} + +static GdkPixbuf * +create_icon_theme_pixbuf (ThemeThumbnailData *theme_thumbnail_data) +{ + return create_folder_icon ((char *) theme_thumbnail_data->icon_theme_name->data); +} + + +static void +handle_bytes (const gchar *buffer, + gint bytes_read, + ThemeThumbnailData *theme_thumbnail_data) +{ + const gchar *ptr; + ptr = buffer; + + while (bytes_read > 0) + { + char *nil; + + switch (theme_thumbnail_data->status) + { + case READY_FOR_THEME: + theme_thumbnail_data->status = READING_TYPE; + /* fall through */ + case READING_TYPE: + nil = memchr (ptr, '\000', bytes_read); + if (nil == NULL) + { + g_byte_array_append (theme_thumbnail_data->type, ptr, bytes_read); + bytes_read = 0; + } + else + { + g_byte_array_append (theme_thumbnail_data->type, ptr, nil - ptr + 1); + bytes_read -= (nil - ptr + 1); + ptr = nil + 1; + theme_thumbnail_data->status = READING_CONTROL_THEME_NAME; + } + break; + + case READING_CONTROL_THEME_NAME: + nil = memchr (ptr, '\000', bytes_read); + if (nil == NULL) + { + g_byte_array_append (theme_thumbnail_data->control_theme_name, ptr, bytes_read); + bytes_read = 0; + } + else + { + g_byte_array_append (theme_thumbnail_data->control_theme_name, ptr, nil - ptr + 1); + bytes_read -= (nil - ptr + 1); + ptr = nil + 1; + theme_thumbnail_data->status = READING_GTK_COLOR_SCHEME; + } + break; + + case READING_GTK_COLOR_SCHEME: + nil = memchr (ptr, '\000', bytes_read); + if (nil == NULL) + { + g_byte_array_append (theme_thumbnail_data->gtk_color_scheme, ptr, bytes_read); + bytes_read = 0; + } + else + { + g_byte_array_append (theme_thumbnail_data->gtk_color_scheme, ptr, nil - ptr + 1); + bytes_read -= (nil - ptr + 1); + ptr = nil + 1; + theme_thumbnail_data->status = READING_WM_THEME_NAME; + } + break; + + case READING_WM_THEME_NAME: + nil = memchr (ptr, '\000', bytes_read); + if (nil == NULL) + { + g_byte_array_append (theme_thumbnail_data->wm_theme_name, ptr, bytes_read); + bytes_read = 0; + } + else + { + g_byte_array_append (theme_thumbnail_data->wm_theme_name, ptr, nil - ptr + 1); + bytes_read -= (nil - ptr + 1); + ptr = nil + 1; + theme_thumbnail_data->status = READING_ICON_THEME_NAME; + } + break; + + case READING_ICON_THEME_NAME: + nil = memchr (ptr, '\000', bytes_read); + if (nil == NULL) + { + g_byte_array_append (theme_thumbnail_data->icon_theme_name, ptr, bytes_read); + bytes_read = 0; + } + else + { + g_byte_array_append (theme_thumbnail_data->icon_theme_name, ptr, nil - ptr + 1); + bytes_read -= (nil - ptr + 1); + ptr = nil + 1; + theme_thumbnail_data->status = READING_APPLICATION_FONT; + } + break; + + case READING_APPLICATION_FONT: + nil = memchr (ptr, '\000', bytes_read); + if (nil == NULL) + { + g_byte_array_append (theme_thumbnail_data->application_font, ptr, bytes_read); + bytes_read = 0; + } + else + { + g_byte_array_append (theme_thumbnail_data->application_font, ptr, nil - ptr + 1); + bytes_read -= (nil - ptr + 1); + ptr = nil + 1; + theme_thumbnail_data->status = WRITING_PIXBUF_DATA; + } + break; + + default: + g_assert_not_reached (); + } + } +} + +static gboolean +message_from_capplet (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + gchar buffer[1024]; + GIOStatus status; + gsize bytes_read; + ThemeThumbnailData *theme_thumbnail_data; + + theme_thumbnail_data = (ThemeThumbnailData *) data; + status = g_io_channel_read_chars (source, + buffer, + 1024, + &bytes_read, + NULL); + + switch (status) + { + case G_IO_STATUS_NORMAL: + handle_bytes (buffer, bytes_read, theme_thumbnail_data); + + if (theme_thumbnail_data->status == WRITING_PIXBUF_DATA) + { + GdkPixbuf *pixbuf = NULL; + gint i, rowstride; + guchar *pixels; + gint width, height; + const gchar *type = (const gchar *) theme_thumbnail_data->type->data; + + if (!strcmp (type, THUMBNAIL_TYPE_META)) + pixbuf = create_meta_theme_pixbuf (theme_thumbnail_data); + else if (!strcmp (type, THUMBNAIL_TYPE_GTK)) + pixbuf = create_gtk_theme_pixbuf (theme_thumbnail_data); + else if (!strcmp (type, THUMBNAIL_TYPE_MARCO)) + pixbuf = create_marco_theme_pixbuf (theme_thumbnail_data); + else if (!strcmp (type, THUMBNAIL_TYPE_ICON)) + pixbuf = create_icon_theme_pixbuf (theme_thumbnail_data); + else + g_assert_not_reached (); + + if (pixbuf == NULL) { + width = height = rowstride = 0; + pixels = NULL; + } else { + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + } + + /* Write the pixbuf's size */ + write (pipe_from_factory_fd[1], &width, sizeof (width)); + write (pipe_from_factory_fd[1], &height, sizeof (height)); + + for (i = 0; i < height; i++) + { + write (pipe_from_factory_fd[1], pixels + rowstride * i, width * gdk_pixbuf_get_n_channels (pixbuf)); + } + + if (pixbuf) + g_object_unref (pixbuf); + g_byte_array_set_size (theme_thumbnail_data->type, 0); + g_byte_array_set_size (theme_thumbnail_data->control_theme_name, 0); + g_byte_array_set_size (theme_thumbnail_data->gtk_color_scheme, 0); + g_byte_array_set_size (theme_thumbnail_data->wm_theme_name, 0); + g_byte_array_set_size (theme_thumbnail_data->icon_theme_name, 0); + g_byte_array_set_size (theme_thumbnail_data->application_font, 0); + theme_thumbnail_data->status = READY_FOR_THEME; + } + return TRUE; + + case G_IO_STATUS_AGAIN: + return TRUE; + + case G_IO_STATUS_EOF: + case G_IO_STATUS_ERROR: + _exit (0); + + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static void +generate_next_in_queue (void) +{ + ThemeQueueItem *item; + + if (theme_queue == NULL) + return; + + item = theme_queue->data; + theme_queue = g_list_delete_link (theme_queue, g_list_first (theme_queue)); + + if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_META)) + generate_meta_theme_thumbnail_async ((MateThemeMetaInfo *) item->theme_info, + item->func, + item->user_data, + item->destroy); + else if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_GTK)) + generate_gtk_theme_thumbnail_async ((MateThemeInfo *) item->theme_info, + item->func, + item->user_data, + item->destroy); + else if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_MARCO)) + generate_marco_theme_thumbnail_async ((MateThemeInfo *) item->theme_info, + item->func, + item->user_data, + item->destroy); + else if (!strcmp (item->thumbnail_type, THUMBNAIL_TYPE_ICON)) + generate_icon_theme_thumbnail_async ((MateThemeIconInfo *) item->theme_info, + item->func, + item->user_data, + item->destroy); + + g_free (item); +} + +static gboolean +message_from_child (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + gchar buffer[1024]; + GIOStatus status; + gsize bytes_read; + + if (async_data.set == FALSE) + return TRUE; + + if (condition == G_IO_HUP) + return FALSE; + + status = g_io_channel_read_chars (source, + buffer, + 1024, + &bytes_read, + NULL); + switch (status) + { + case G_IO_STATUS_NORMAL: + g_byte_array_append (async_data.data, (guchar *) buffer, bytes_read); + + if (async_data.thumbnail_width == -1 && async_data.data->len >= 2 * sizeof (gint)) + { + async_data.thumbnail_width = *((gint *) async_data.data->data); + async_data.thumbnail_height = *(((gint *) async_data.data->data) + 1); + g_byte_array_remove_range (async_data.data, 0, 2 * sizeof (gint)); + } + + if (async_data.thumbnail_width >= 0 && async_data.data->len == async_data.thumbnail_width * async_data.thumbnail_height * 4) + { + GdkPixbuf *pixbuf = NULL; + + if (async_data.thumbnail_width > 0) { + gchar *pixels; + gint i, rowstride; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, async_data.thumbnail_width, async_data.thumbnail_height); + pixels = (gchar *) gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + + for (i = 0; i < async_data.thumbnail_height; ++i) + memcpy (pixels + rowstride * i, async_data.data->data + 4 * async_data.thumbnail_width * i, async_data.thumbnail_width * 4); + } + + /* callback function needs to ref the pixbuf if it wants to keep it */ + (* async_data.func) (pixbuf, async_data.theme_name, async_data.user_data); + + if (async_data.destroy) + (* async_data.destroy) (async_data.user_data); + + if (pixbuf) + g_object_unref (pixbuf); + + /* Clean up async_data */ + g_free (async_data.theme_name); + g_source_remove (async_data.watch_id); + g_io_channel_unref (async_data.channel); + + /* reset async_data */ + async_data.thumbnail_width = -1; + async_data.thumbnail_height = -1; + async_data.theme_name = NULL; + async_data.channel = NULL; + async_data.func = NULL; + async_data.user_data = NULL; + async_data.destroy = NULL; + async_data.set = FALSE; + g_byte_array_set_size (async_data.data, 0); + + generate_next_in_queue (); + } + return TRUE; + + case G_IO_STATUS_AGAIN: + return TRUE; + + case G_IO_STATUS_EOF: + case G_IO_STATUS_ERROR: + return FALSE; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static void +send_thumbnail_request (gchar *thumbnail_type, + gchar *gtk_theme_name, + gchar *gtk_color_scheme, + gchar *marco_theme_name, + gchar *icon_theme_name, + gchar *application_font) +{ + write (pipe_to_factory_fd[1], thumbnail_type, strlen (thumbnail_type) + 1); + + if (gtk_theme_name) + write (pipe_to_factory_fd[1], gtk_theme_name, strlen (gtk_theme_name) + 1); + else + write (pipe_to_factory_fd[1], "", 1); + + if (gtk_color_scheme) + write (pipe_to_factory_fd[1], gtk_color_scheme, strlen (gtk_color_scheme) + 1); + else + write (pipe_to_factory_fd[1], "", 1); + + if (marco_theme_name) + write (pipe_to_factory_fd[1], marco_theme_name, strlen (marco_theme_name) + 1); + else + write (pipe_to_factory_fd[1], "", 1); + + if (icon_theme_name) + write (pipe_to_factory_fd[1], icon_theme_name, strlen (icon_theme_name) + 1); + else + write (pipe_to_factory_fd[1], "", 1); + + if (application_font) + write (pipe_to_factory_fd[1], application_font, strlen (application_font) + 1); + else + write (pipe_to_factory_fd[1], "Sans 10", strlen ("Sans 10") + 1); + +} + +static GdkPixbuf * +read_pixbuf (void) +{ + gint bytes_read, i, j = 0; + gint size[2]; + GdkPixbuf *pixbuf; + gint rowstride; + guchar *pixels; + + do + { + bytes_read = read (pipe_from_factory_fd[0], ((guint8*) size) + j, 2 * sizeof (gint)); + if (bytes_read == 0) + goto eof; + j += bytes_read; + } + while (j < 2 * sizeof (gint)); + + if (size[0] <= 0 || size[1] <= 0) + return NULL; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size[0], size[1]); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + for (i = 0; i < size[1]; i++) + { + j = 0; + + do + { + bytes_read = read (pipe_from_factory_fd[0], pixels + rowstride * i + j, size[0] * gdk_pixbuf_get_n_channels (pixbuf) - j); + + if (bytes_read > 0) + j += bytes_read; + else if (bytes_read == 0) + { + g_object_unref (pixbuf); + goto eof; + } + } + while (j < size[0] * gdk_pixbuf_get_n_channels (pixbuf)); + } + + return pixbuf; + +eof: + g_warning ("Received EOF while reading thumbnail"); + close (pipe_to_factory_fd[1]); + pipe_to_factory_fd[1] = 0; + close (pipe_from_factory_fd[0]); + pipe_from_factory_fd[0] = 0; + return NULL; +} + +static GdkPixbuf * +generate_theme_thumbnail (gchar *thumbnail_type, + gchar *gtk_theme_name, + gchar *gtk_color_scheme, + gchar *marco_theme_name, + gchar *icon_theme_name, + gchar *application_font) +{ + if (async_data.set || !pipe_to_factory_fd[1] || !pipe_from_factory_fd[0]) + return NULL; + + send_thumbnail_request (thumbnail_type, + gtk_theme_name, + gtk_color_scheme, + marco_theme_name, + icon_theme_name, + application_font); + + return read_pixbuf (); +} + +GdkPixbuf * +generate_meta_theme_thumbnail (MateThemeMetaInfo *theme_info) +{ + return generate_theme_thumbnail (THUMBNAIL_TYPE_META, + theme_info->gtk_theme_name, + theme_info->gtk_color_scheme, + theme_info->marco_theme_name, + theme_info->icon_theme_name, + theme_info->application_font); +} + +GdkPixbuf * +generate_gtk_theme_thumbnail (MateThemeInfo *theme_info) +{ + gchar *scheme; + + scheme = gtkrc_get_color_scheme_for_theme (theme_info->name); + + return generate_theme_thumbnail (THUMBNAIL_TYPE_GTK, + theme_info->name, + scheme, + NULL, + NULL, + NULL); + g_free (scheme); +} + +GdkPixbuf * +generate_marco_theme_thumbnail (MateThemeInfo *theme_info) +{ + return generate_theme_thumbnail (THUMBNAIL_TYPE_MARCO, + NULL, + NULL, + theme_info->name, + NULL, + NULL); +} + +GdkPixbuf * +generate_icon_theme_thumbnail (MateThemeIconInfo *theme_info) +{ + return generate_theme_thumbnail (THUMBNAIL_TYPE_ICON, + NULL, + NULL, + NULL, + theme_info->name, + NULL); +} + +static void generate_theme_thumbnail_async(gpointer theme_info, gchar* theme_name, gchar* thumbnail_type, gchar* gtk_theme_name, gchar* gtk_color_scheme, gchar* marco_theme_name, gchar* icon_theme_name, gchar* application_font, ThemeThumbnailFunc func, gpointer user_data, GDestroyNotify destroy) +{ + if (async_data.set) + { + ThemeQueueItem* item = g_new0 (ThemeQueueItem, 1); + + item->thumbnail_type = thumbnail_type; + item->theme_info = theme_info; + item->func = func; + item->user_data = user_data; + item->destroy = destroy; + + theme_queue = g_list_append(theme_queue, item); + + return; + } + + if (!pipe_to_factory_fd[1] || !pipe_from_factory_fd[0]) + { + (*func)(NULL, theme_name, user_data); + + if (destroy) + { + (*destroy)(user_data); + } + + return; + } + + if (async_data.channel == NULL) + { + async_data.channel = g_io_channel_unix_new(pipe_from_factory_fd[0]); + + g_io_channel_set_flags(async_data.channel, g_io_channel_get_flags (async_data.channel) | G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding(async_data.channel, NULL, NULL); + + async_data.watch_id = g_io_add_watch(async_data.channel, G_IO_IN | G_IO_HUP, message_from_child, NULL); + } + + async_data.set = TRUE; + async_data.thumbnail_width = -1; + async_data.thumbnail_height = -1; + async_data.theme_name = g_strdup(theme_name); + async_data.func = func; + async_data.user_data = user_data; + async_data.destroy = destroy; + + send_thumbnail_request(thumbnail_type, gtk_theme_name, gtk_color_scheme, marco_theme_name, icon_theme_name, application_font); +} + +void +generate_meta_theme_thumbnail_async (MateThemeMetaInfo *theme_info, + ThemeThumbnailFunc func, + gpointer user_data, + GDestroyNotify destroy) +{ + generate_theme_thumbnail_async (theme_info, + theme_info->name, + THUMBNAIL_TYPE_META, + theme_info->gtk_theme_name, + theme_info->gtk_color_scheme, + theme_info->marco_theme_name, + theme_info->icon_theme_name, + theme_info->application_font, + func, user_data, destroy); +} + +void generate_gtk_theme_thumbnail_async (MateThemeInfo* theme_info, ThemeThumbnailFunc func, gpointer user_data, GDestroyNotify destroy) +{ + gchar* scheme = gtkrc_get_color_scheme_for_theme(theme_info->name); + + generate_theme_thumbnail_async(theme_info, theme_info->name, THUMBNAIL_TYPE_GTK, theme_info->name, scheme, NULL, NULL, NULL, func, user_data, destroy); + + g_free(scheme); +} + +void +generate_marco_theme_thumbnail_async (MateThemeInfo *theme_info, + ThemeThumbnailFunc func, + gpointer user_data, + GDestroyNotify destroy) +{ + generate_theme_thumbnail_async (theme_info, + theme_info->name, + THUMBNAIL_TYPE_MARCO, + NULL, + NULL, + theme_info->name, + NULL, + NULL, + func, user_data, destroy); +} + +void +generate_icon_theme_thumbnail_async (MateThemeIconInfo *theme_info, + ThemeThumbnailFunc func, + gpointer user_data, + GDestroyNotify destroy) +{ + generate_theme_thumbnail_async (theme_info, + theme_info->name, + THUMBNAIL_TYPE_ICON, + NULL, + NULL, + NULL, + theme_info->name, + NULL, + func, user_data, destroy); +} + +void +theme_thumbnail_factory_init (int argc, char *argv[]) +{ +#ifndef __APPLE__ + gint child_pid; +#endif + + pipe (pipe_to_factory_fd); + pipe (pipe_from_factory_fd); + +/* Apple's CoreFoundation classes must not be used from forked + * processes. Since freetype (and thus GTK) uses them, we simply + * disable the thumbnailer on MacOS for now. That means no thumbs + * until the thumbnailing process is rewritten, but at least we won't + * make apps crash. */ +#ifndef __APPLE__ + child_pid = fork (); + if (child_pid == 0) + { + ThemeThumbnailData data; + GIOChannel *channel; + + /* Child */ + gtk_init (&argc, &argv); + + close (pipe_to_factory_fd[1]); + pipe_to_factory_fd[1] = 0; + close (pipe_from_factory_fd[0]); + pipe_from_factory_fd[0] = 0; + + data.status = READY_FOR_THEME; + data.type = g_byte_array_new (); + data.control_theme_name = g_byte_array_new (); + data.gtk_color_scheme = g_byte_array_new (); + data.wm_theme_name = g_byte_array_new (); + data.icon_theme_name = g_byte_array_new (); + data.application_font = g_byte_array_new (); + + channel = g_io_channel_unix_new (pipe_to_factory_fd[0]); + g_io_channel_set_flags (channel, g_io_channel_get_flags (channel) | + G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding (channel, NULL, NULL); + g_io_add_watch (channel, G_IO_IN | G_IO_HUP, message_from_capplet, &data); + g_io_channel_unref (channel); + + gtk_main (); + _exit (0); + } + + g_assert (child_pid > 0); + + /* Parent */ + close (pipe_to_factory_fd[0]); + close (pipe_from_factory_fd[1]); +#endif /* __APPLE__ */ + + async_data.set = FALSE; + async_data.theme_name = NULL; + async_data.data = g_byte_array_new (); +} diff --git a/capplets/common/theme-thumbnail.h b/capplets/common/theme-thumbnail.h new file mode 100644 index 00000000..fe25f706 --- /dev/null +++ b/capplets/common/theme-thumbnail.h @@ -0,0 +1,37 @@ +#ifndef __THEME_THUMBNAIL_H__ +#define __THEME_THUMBNAIL_H__ + + +#include +#include "mate-theme-info.h" + +typedef void (* ThemeThumbnailFunc) (GdkPixbuf *pixbuf, + gchar *theme_name, + gpointer data); + +GdkPixbuf *generate_meta_theme_thumbnail (MateThemeMetaInfo *theme_info); +GdkPixbuf *generate_gtk_theme_thumbnail (MateThemeInfo *theme_info); +GdkPixbuf *generate_marco_theme_thumbnail (MateThemeInfo *theme_info); +GdkPixbuf *generate_icon_theme_thumbnail (MateThemeIconInfo *theme_info); + +void generate_meta_theme_thumbnail_async (MateThemeMetaInfo *theme_info, + ThemeThumbnailFunc func, + gpointer data, + GDestroyNotify destroy); +void generate_gtk_theme_thumbnail_async (MateThemeInfo *theme_info, + ThemeThumbnailFunc func, + gpointer data, + GDestroyNotify destroy); +void generate_marco_theme_thumbnail_async (MateThemeInfo *theme_info, + ThemeThumbnailFunc func, + gpointer data, + GDestroyNotify destroy); +void generate_icon_theme_thumbnail_async (MateThemeIconInfo *theme_info, + ThemeThumbnailFunc func, + gpointer data, + GDestroyNotify destroy); + +void theme_thumbnail_factory_init (int argc, + char *argv[]); + +#endif /* __THEME_THUMBNAIL_H__ */ diff --git a/capplets/common/wm-common.c b/capplets/common/wm-common.c new file mode 100644 index 00000000..31cd503d --- /dev/null +++ b/capplets/common/wm-common.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include "wm-common.h" + +typedef struct _WMCallbackData +{ + GFunc func; + gpointer data; +} WMCallbackData; + +/* Our WM Window */ +static Window wm_window = None; + +static char * +wm_common_get_window_manager_property (Atom atom) +{ + Atom utf8_string, type; + int result; + char *retval; + int format; + gulong nitems; + gulong bytes_after; + gchar *val; + + if (wm_window == None) + return NULL; + + utf8_string = XInternAtom (GDK_DISPLAY (), "UTF8_STRING", False); + + gdk_error_trap_push (); + + val = NULL; + result = XGetWindowProperty (GDK_DISPLAY (), + wm_window, + atom, + 0, G_MAXLONG, + False, utf8_string, + &type, &format, &nitems, + &bytes_after, (guchar **) &val); + + if (gdk_error_trap_pop () || result != Success || + type != utf8_string || format != 8 || nitems == 0 || + !g_utf8_validate (val, nitems, NULL)) + { + retval = NULL; + } + else + { + retval = g_strndup (val, nitems); + } + + if (val) + XFree (val); + + return retval; +} + +char* +wm_common_get_current_window_manager (void) +{ + Atom atom = XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False); + char *result; + + result = wm_common_get_window_manager_property (atom); + if (result) + return result; + else + return g_strdup (WM_COMMON_UNKNOWN); +} + +char** +wm_common_get_current_keybindings (void) +{ + Atom keybindings_atom = XInternAtom (GDK_DISPLAY (), "_MATE_WM_KEYBINDINGS", False); + char *keybindings = wm_common_get_window_manager_property (keybindings_atom); + char **results; + + if (keybindings) + { + char **p; + results = g_strsplit(keybindings, ",", -1); + for (p = results; *p; p++) + g_strstrip (*p); + g_free (keybindings); + } + else + { + Atom wm_atom = XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False); + char *wm_name = wm_common_get_window_manager_property (wm_atom); + char *to_copy[] = { NULL, NULL }; + + to_copy[0] = wm_name ? wm_name : WM_COMMON_UNKNOWN; + + results = g_strdupv (to_copy); + g_free (wm_name); + } + + return results; +} + +static void +update_wm_window (void) +{ + Window *xwindow; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + + XGetWindowProperty (GDK_DISPLAY (), GDK_ROOT_WINDOW (), + XInternAtom (GDK_DISPLAY (), "_NET_SUPPORTING_WM_CHECK", False), + 0, G_MAXLONG, False, XA_WINDOW, &type, &format, + &nitems, &bytes_after, (guchar **) &xwindow); + + if (type != XA_WINDOW) + { + wm_window = None; + return; + } + + gdk_error_trap_push (); + XSelectInput (GDK_DISPLAY (), *xwindow, StructureNotifyMask | PropertyChangeMask); + XSync (GDK_DISPLAY (), False); + + if (gdk_error_trap_pop ()) + { + XFree (xwindow); + wm_window = None; + return; + } + + wm_window = *xwindow; + XFree (xwindow); +} + +static GdkFilterReturn +wm_window_event_filter (GdkXEvent *xev, + GdkEvent *event, + gpointer data) +{ + WMCallbackData *ncb_data = (WMCallbackData*) data; + XEvent *xevent = (XEvent *)xev; + + if ((xevent->type == DestroyNotify && + wm_window != None && xevent->xany.window == wm_window) || + (xevent->type == PropertyNotify && + xevent->xany.window == GDK_ROOT_WINDOW () && + xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY (), "_NET_SUPPORTING_WM_CHECK", False))) || + (xevent->type == PropertyNotify && + wm_window != None && xevent->xany.window == wm_window && + xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False)))) + { + update_wm_window (); + (* ncb_data->func) ((gpointer)wm_common_get_current_window_manager(), + ncb_data->data); + } + + return GDK_FILTER_CONTINUE; +} + +void +wm_common_register_window_manager_change (GFunc func, + gpointer data) +{ + WMCallbackData *ncb_data; + + ncb_data = g_new0 (WMCallbackData, 1); + + ncb_data->func = func; + ncb_data->data = data; + + gdk_window_add_filter (NULL, wm_window_event_filter, ncb_data); + + update_wm_window (); + + XSelectInput (GDK_DISPLAY (), GDK_ROOT_WINDOW (), PropertyChangeMask); + XSync (GDK_DISPLAY (), False); +} + + diff --git a/capplets/common/wm-common.h b/capplets/common/wm-common.h new file mode 100644 index 00000000..564c6b9e --- /dev/null +++ b/capplets/common/wm-common.h @@ -0,0 +1,17 @@ +#ifndef WM_COMMON_H +#define WM_COMMON_H + +#define WM_COMMON_MARCO "Marco" +#define WM_COMMON_SAWFISH "Sawfish" +#define WM_COMMON_UNKNOWN "Unknown" + +gchar *wm_common_get_current_window_manager (void); +/* Returns a strv of keybinding names for the window manager; + * using _MATE_WM_KEYBINDINGS if available, _NET_WM_NAME otherwise. */ +char **wm_common_get_current_keybindings (void); + +void wm_common_register_window_manager_change (GFunc func, + gpointer data); + +#endif /* WM_COMMON_H */ + diff --git a/capplets/default-applications/Makefile.am b/capplets/default-applications/Makefile.am new file mode 100644 index 00000000..b6487ec7 --- /dev/null +++ b/capplets/default-applications/Makefile.am @@ -0,0 +1,75 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = default-applications + +bin_PROGRAMS = mate-default-applications-properties + +bin_SCRIPTS = mate-at-visual mate-at-mobility + +mate_default_applications_properties_LDADD = $(MATECC_CAPPLETS_LIBS) +mate_default_applications_properties_SOURCES = \ + mate-da-capplet.c mate-da-capplet.h \ + mate-da-xml.c mate-da-xml.h \ + mate-da-item.c mate-da-item.h + +@INTLTOOL_DESKTOP_RULE@ + +uidir = $(pkgdatadir)/ui +dist_ui_DATA = mate-default-applications-properties.ui + +mate-at-visual: mate-at-commandline.in + cp $< $@ + +mate-at-mobility: mate-at-commandline.in + cp $< $@ + +desktopdir = $(datadir)/applications +Desktop_in_files = default-applications.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +pkgconfigdir = $(datadir)/pkgconfig +pkgconfig_DATA = mate-default-applications.pc + +autostartdir = $(sysconfdir)/xdg/autostart +autostart_in_files = mate-at-session.desktop.in +autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) + +xmldata_in_files = mate-default-applications.xml.in +xmldatadir = $(pkgdatadir)/default-apps +xmldata_DATA = $(xmldata_in_files:.xml.in=.xml) +@INTLTOOL_XML_RULE@ + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + $(DEFAULT_APPLICATIONS_CAPPLET_CFLAGS) \ + -DMATELOCALEDIR=\""$(datadir)/locale"\"\ + -DMATECC_UI_DIR=\""$(uidir)"\" \ + -DMATECC_APPS_DIR=\""$(xmldatadir)"\" + +icons16dir = $(datadir)/icons/mate/16x16/apps +dist_icons16_DATA = icons/16x16/preferences-desktop-default-applications.png +icons22dir = $(datadir)/icons/mate/22x22/apps +dist_icons22_DATA = icons/22x22/preferences-desktop-default-applications.png +icons24dir = $(datadir)/icons/mate/24x24/apps +dist_icons24_DATA = icons/24x24/preferences-desktop-default-applications.png +icons32dir = $(datadir)/icons/mate/32x32/apps +dist_icons32_DATA = icons/32x32/preferences-desktop-default-applications.png +icons48dir = $(datadir)/icons/mate/48x48/apps +dist_icons48_DATA = icons/48x48/preferences-desktop-default-applications.png + +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor +uninstall-hook: update-icon-cache +install-data-hook: update-icon-cache +update-icon-cache: + @-if test -z "$(DESTDIR)"; then \ + echo "Updating Gtk icon cache."; \ + $(gtk_update_icon_cache); \ + else \ + echo "*** Icon cache not updated. After install, run this:"; \ + echo "*** $(gtk_update_icon_cache)"; \ + fi + + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) $(xmldata_DATA) $(autostart_DATA) $(bin_SCRIPTS) +EXTRA_DIST = $(xmldata_in_files) mate-default-applications.pc.in + +-include $(top_srcdir)/git.mk diff --git a/capplets/default-applications/default-applications.desktop.in.in b/capplets/default-applications/default-applications.desktop.in.in new file mode 100644 index 00000000..9b25b759 --- /dev/null +++ b/capplets/default-applications/default-applications.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Preferred Applications +_Comment=Select your default applications +Exec=mate-default-applications-properties +Icon=preferences-desktop-default-applications +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;X-MATE-PersonalSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Preferred applications +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/default-applications/icons/16x16/preferences-desktop-default-applications.png b/capplets/default-applications/icons/16x16/preferences-desktop-default-applications.png new file mode 100644 index 00000000..41a765aa Binary files /dev/null and b/capplets/default-applications/icons/16x16/preferences-desktop-default-applications.png differ diff --git a/capplets/default-applications/icons/22x22/preferences-desktop-default-applications.png b/capplets/default-applications/icons/22x22/preferences-desktop-default-applications.png new file mode 100644 index 00000000..83b6826c Binary files /dev/null and b/capplets/default-applications/icons/22x22/preferences-desktop-default-applications.png differ diff --git a/capplets/default-applications/icons/24x24/preferences-desktop-default-applications.png b/capplets/default-applications/icons/24x24/preferences-desktop-default-applications.png new file mode 100644 index 00000000..630ea040 Binary files /dev/null and b/capplets/default-applications/icons/24x24/preferences-desktop-default-applications.png differ diff --git a/capplets/default-applications/icons/32x32/preferences-desktop-default-applications.png b/capplets/default-applications/icons/32x32/preferences-desktop-default-applications.png new file mode 100644 index 00000000..23718f78 Binary files /dev/null and b/capplets/default-applications/icons/32x32/preferences-desktop-default-applications.png differ diff --git a/capplets/default-applications/icons/48x48/preferences-desktop-default-applications.png b/capplets/default-applications/icons/48x48/preferences-desktop-default-applications.png new file mode 100644 index 00000000..ac25569d Binary files /dev/null and b/capplets/default-applications/icons/48x48/preferences-desktop-default-applications.png differ diff --git a/capplets/default-applications/mate-at-commandline.in.in b/capplets/default-applications/mate-at-commandline.in.in new file mode 100644 index 00000000..f9d93b16 --- /dev/null +++ b/capplets/default-applications/mate-at-commandline.in.in @@ -0,0 +1,101 @@ +#!/bin/sh +# +# Copyright 2006 IBM Corp. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License +# as published by the Free Software Foundation +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. +# +############################################################################### +# +# NOTE: This script is intended to be run from the command line, +# MATE menu, or from the desktop autostart. +# +# /usr/bin/mate-at-visual +# /usr/bin/mate-at-mobility +# +# If the "-s" flag is used then it is assumed to have been invoked +# from /usr/share/mate/autostart/, and the first AT flagged +# to "startup" from MATECONF_ALL will be executed. +# + +USAGE="$0 [-s]" +MATECONF_PATH=/desktop/mate/applications/at +MATECONF_VISUAL="visual" +MATECONF_MOBILITY="mobility" +MATECONF_ALL="$MATECONF_VISUAL $MATECONF_MOBILITY" + +run_at() { + CMDLINE=`mateconftool-2 --get $MATECONF_PATH/$1/exec` + if [ $? -ne 0 ]; then + exit $? + fi + + if [ -z "$CMDLINE" ]; then + exit 2 + fi + + STARTUP=`mateconftool-2 --get $MATECONF_PATH/$1/startup` + if [ $? -ne 0 ]; then + exit $? + fi + + if [ ! -z "$AUTOSTART" ]; then + # assuming ran from /usr/share/mate/autostart + if [ "x$STARTUP" = "xtrue" ]; then + # mateconf indicated requested autostart + ($CMDLINE &) + fi + else + # run from command line or desktop menu + ($CMDLINE &) + fi +} + +case `basename $0` in + mate-at-visual ) + AT=$MATECONF_VISUAL + ;; + mate-at-mobility ) + AT=$MATECONF_MOBILITY + ;; + mate-at-session | * ) + AUTOSTART="yes" + AT=$MATECONF_ALL + ;; +esac + +while getopts "s" options; do + case $options in + s ) AUTOSTART="yes" + AT=$MATECONF_ALL + shift + ;; + \? ) echo $USAGE + exit 1 + ;; + * ) echo $USAGE + exit 1 + ;; + esac +done + +if [ $# -ne 0 ]; then + echo $USAGE + exit 1 +fi + +for I in $AT ; do + run_at $I +done + +#EOF diff --git a/capplets/default-applications/mate-at-session.desktop.in.in b/capplets/default-applications/mate-at-session.desktop.in.in new file mode 100644 index 00000000..2e32f8ba --- /dev/null +++ b/capplets/default-applications/mate-at-session.desktop.in.in @@ -0,0 +1,15 @@ +[Desktop Entry] +_Name=Visual Assistance +_Comment=Start the preferred visual assistive technology +Exec=mate-at-visual -s +Icon=preferences-desktop-accessibility +Terminal=false +Type=Application +StartupNotify=false +OnlyShowIn=MATE; +AutostartCondition=MATE /desktop/mate/interface/accessibility +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=other capplets +X-MATE-Bugzilla-Version=@VERSION@ +X-MATE-Autostart-enabled=true diff --git a/capplets/default-applications/mate-da-capplet.c b/capplets/default-applications/mate-da-capplet.c new file mode 100644 index 00000000..37a01359 --- /dev/null +++ b/capplets/default-applications/mate-da-capplet.c @@ -0,0 +1,970 @@ +/* + * Authors: Luca Cavalli + * + * Copyright 2005-2006 Luca Cavalli + * Copyright 2008 Thomas Wood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "mateconf-property-editor.h" +#include "mate-da-capplet.h" +#include "mate-da-xml.h" +#include "mate-da-item.h" +#include "capplet-util.h" + +enum { + PIXBUF_COL, + TEXT_COL, + N_COLUMNS +}; + +static void close_cb(GtkWidget* window, gint response, gpointer user_data) +{ + if (response == GTK_RESPONSE_HELP) + { + capplet_help (GTK_WINDOW (window), "prefs-preferredapps"); + } + else + { + gtk_widget_destroy (window); + gtk_main_quit (); + } +} + +static void set_icon (GtkImage* image, GtkIconTheme* theme, const char* name) +{ + GdkPixbuf* pixbuf; + + if ((pixbuf = gtk_icon_theme_load_icon(theme, name, 48, 0, NULL))) + { + gtk_image_set_from_pixbuf(image, pixbuf); + g_object_unref(pixbuf); + } +} + +static void web_radiobutton_toggled_cb(GtkWidget* togglebutton, MateDACapplet* capplet) +{ + gint index; + MateDAWebItem *item; + const gchar *command; + GError *error = NULL; + + index = gtk_combo_box_get_active (GTK_COMBO_BOX (capplet->web_combo_box)); + + if (index == -1) + return; + + item = (MateDAWebItem *) g_list_nth_data (capplet->web_browsers, index); + if (item == NULL) + return; + + if (togglebutton == capplet->new_win_radiobutton) { + command = item->win_command; + } + else if (togglebutton == capplet->new_tab_radiobutton) { + command = item->tab_command; + } + else { + command = item->generic.command; + } + + mateconf_client_set_string (capplet->mateconf, DEFAULT_APPS_KEY_HTTP_EXEC, command, &error); + + gtk_entry_set_text (GTK_ENTRY (capplet->web_browser_command_entry), command); + + if (error != NULL) { + g_warning (_("Error saving configuration: %s"), error->message); + g_error_free (error); + } +} + +static void web_combo_changed_cb(GtkComboBox* combo, MateDACapplet* capplet) +{ + guint current_index; + gboolean is_custom_active; + gboolean has_net_remote; + MateDAWebItem *item; + GtkWidget *active = NULL; + + current_index = gtk_combo_box_get_active (combo); + + if (current_index < g_list_length (capplet->web_browsers)) { + + item = (MateDAWebItem*) g_list_nth_data (capplet->web_browsers, current_index); + has_net_remote = item->netscape_remote; + is_custom_active = FALSE; + + } + else { + has_net_remote = FALSE; + is_custom_active = TRUE; + } + gtk_widget_set_sensitive (capplet->default_radiobutton, has_net_remote); + gtk_widget_set_sensitive (capplet->new_win_radiobutton, has_net_remote); + gtk_widget_set_sensitive (capplet->new_tab_radiobutton, has_net_remote); + + gtk_widget_set_sensitive (capplet->web_browser_command_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->web_browser_command_label, is_custom_active); + gtk_widget_set_sensitive (capplet->web_browser_terminal_checkbutton, is_custom_active); + + if (has_net_remote) { + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (capplet->new_win_radiobutton))) + active = capplet->new_win_radiobutton; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (capplet->new_tab_radiobutton))) + active = capplet->new_tab_radiobutton; + else + active = capplet->default_radiobutton; + } + + web_radiobutton_toggled_cb (active, capplet); +} + +/* FIXME: Refactor these two functions below into one... */ +static void mail_combo_changed_cb(GtkComboBox* combo, MateDACapplet* capplet) +{ + guint current_index; + gboolean is_custom_active; + + current_index = gtk_combo_box_get_active (combo); + is_custom_active = (current_index >= g_list_length (capplet->mail_readers)); + + gtk_widget_set_sensitive (capplet->mail_reader_command_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->mail_reader_command_label, is_custom_active); + gtk_widget_set_sensitive (capplet->mail_reader_terminal_checkbutton, is_custom_active); +} + +static void media_combo_changed_cb(GtkComboBox* combo, MateDACapplet* capplet) +{ + guint current_index; + gboolean is_custom_active; + + current_index = gtk_combo_box_get_active (combo); + is_custom_active = (current_index >= g_list_length (capplet->media_players)); + + gtk_widget_set_sensitive (capplet->media_player_command_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->media_player_command_label, is_custom_active); + gtk_widget_set_sensitive (capplet->media_player_terminal_checkbutton, is_custom_active); +} + +static void terminal_combo_changed_cb(GtkComboBox* combo, MateDACapplet* capplet) +{ + guint current_index; + gboolean is_custom_active; + + current_index = gtk_combo_box_get_active (combo); + is_custom_active = (current_index >= g_list_length (capplet->terminals)); + + gtk_widget_set_sensitive (capplet->terminal_command_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->terminal_command_label, is_custom_active); + gtk_widget_set_sensitive (capplet->terminal_exec_flag_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->terminal_exec_flag_label, is_custom_active); +} + +static void visual_combo_changed_cb(GtkComboBox* combo, MateDACapplet* capplet) +{ + guint current_index; + gboolean is_custom_active; + + current_index = gtk_combo_box_get_active (combo); + is_custom_active = (current_index >= g_list_length (capplet->visual_ats)); + + gtk_widget_set_sensitive (capplet->visual_command_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->visual_command_label, is_custom_active); +} + +static void mobility_combo_changed_cb(GtkComboBox* combo, MateDACapplet* capplet) +{ + guint current_index; + gboolean is_custom_active; + + current_index = gtk_combo_box_get_active (combo); + is_custom_active = (current_index >= g_list_length (capplet->mobility_ats)); + + gtk_widget_set_sensitive (capplet->mobility_command_entry, is_custom_active); + gtk_widget_set_sensitive (capplet->mobility_command_label, is_custom_active); +} + +static void refresh_combo_box_icons(GtkIconTheme* theme, GtkComboBox* combo_box, GList* app_list) +{ + GList *entry; + MateDAItem *item; + GtkTreeModel *model; + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + for (entry = app_list; entry != NULL; entry = g_list_next (entry)) { + item = (MateDAItem *) entry->data; + + model = gtk_combo_box_get_model (combo_box); + + if (item->icon_path && gtk_tree_model_get_iter_from_string (model, &iter, item->icon_path)) { + pixbuf = gtk_icon_theme_load_icon (theme, item->icon_name, 22, 0, NULL); + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + PIXBUF_COL, pixbuf, + -1); + + if (pixbuf) + g_object_unref (pixbuf); + } + } +} + +static struct { + const gchar* name; + const gchar* icon; +} icons[] = { + {"web_browser_image", "web-browser"}, + {"mail_reader_image", "emblem-mail"}, + {"media_player_image", "applications-multimedia"}, + {"visual_image", "zoom-best-fit"}, + {"mobility_image", "preferences-desktop-accessibility"}, + /* + {"messenger_image", "im"}, + {"file_manager_image", "file-manager"}, + {"image_image", "image-viewer"}, + {"video_image", "mate-multimedia"}, + {"text_image", "text-editor"}, + */ + {"terminal_image", "mate-terminal"} +}; + +static void theme_changed_cb(GtkIconTheme* theme, MateDACapplet* capplet) +{ + GObject *icon; + gint i; + + for (i = 0; i < G_N_ELEMENTS (icons); i++) + { + icon = gtk_builder_get_object (capplet->builder, icons[i].name); + set_icon (GTK_IMAGE (icon), theme, icons[i].icon); + } + + refresh_combo_box_icons (theme, GTK_COMBO_BOX (capplet->web_combo_box), capplet->web_browsers); + refresh_combo_box_icons (theme, GTK_COMBO_BOX (capplet->mail_combo_box), capplet->mail_readers); + refresh_combo_box_icons (theme, GTK_COMBO_BOX (capplet->media_combo_box), capplet->media_players); + refresh_combo_box_icons (theme, GTK_COMBO_BOX (capplet->term_combo_box), capplet->terminals); + refresh_combo_box_icons (theme, GTK_COMBO_BOX (capplet->visual_combo_box), capplet->visual_ats); + refresh_combo_box_icons (theme, GTK_COMBO_BOX (capplet->mobility_combo_box), capplet->mobility_ats); +} + +static void screen_changed_cb(GtkWidget* widget, GdkScreen* screen, MateDACapplet* capplet) +{ + GtkIconTheme* theme; + + theme = gtk_icon_theme_get_for_screen (screen); + + if (capplet->icon_theme != NULL) + { + g_signal_handlers_disconnect_by_func (capplet->icon_theme, theme_changed_cb, capplet); + } + + g_signal_connect (theme, "changed", G_CALLBACK (theme_changed_cb), capplet); + theme_changed_cb (theme, capplet); + + capplet->icon_theme = theme; +} + +static gint generic_item_comp(gconstpointer list_item, gconstpointer command) +{ + return (strcmp (((MateDAItem *) list_item)->command, (gchar *) command)); +} + +static gint web_item_comp(gconstpointer item, gconstpointer command) +{ + MateDAWebItem *web_list_item; + + web_list_item = (MateDAWebItem *) item; + + if (strcmp (web_list_item->generic.command, (gchar *) command) == 0) + return 0; + + if (web_list_item->netscape_remote) { + if (strcmp (web_list_item->tab_command, (gchar *) command) == 0) + return 0; + + if (strcmp (web_list_item->win_command, (gchar *) command) == 0) + return 0; + } + + return (strcmp (web_list_item->generic.command, (gchar *) command)); +} + +static void web_mateconf_changed_cb(MateConfPropertyEditor* peditor, gchar* key, MateConfValue* value, MateDACapplet* capplet) +{ + MateConfChangeSet *cs; + GError *error = NULL; + GList *list_entry; + + /* This function is used to update HTTPS,ABOUT and UNKNOWN handlers, which + * should also use the same value as HTTP + */ + + if (strcmp (key, DEFAULT_APPS_KEY_HTTP_EXEC) == 0) { + gchar *short_browser, *pos; + const gchar *value_str = mateconf_value_get_string (value); + + cs = mateconf_change_set_new (); + + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_HTTPS_EXEC, value); + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_UNKNOWN_EXEC, value); + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_ABOUT_EXEC, value); + pos = strstr (value_str, " "); + if (pos == NULL) + short_browser = g_strdup (value_str); + else + short_browser = g_strndup (value_str, pos - value_str); + mateconf_change_set_set_string (cs, DEFAULT_APPS_KEY_BROWSER_EXEC, short_browser); + g_free (short_browser); + + list_entry = g_list_find_custom (capplet->web_browsers, + value_str, + (GCompareFunc) web_item_comp); + + if (list_entry) { + MateDAWebItem *item = (MateDAWebItem *) list_entry->data; + + mateconf_change_set_set_bool (cs, DEFAULT_APPS_KEY_BROWSER_NREMOTE, item->netscape_remote); + } + + mateconf_client_commit_change_set (capplet->mateconf, cs, TRUE, &error); + + if (error != NULL) { + g_warning (_("Error saving configuration: %s"), error->message); + g_error_free (error); + error = NULL; + } + + mateconf_change_set_unref (cs); + } + else if (strcmp (key, DEFAULT_APPS_KEY_HTTP_NEEDS_TERM) == 0) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (capplet->web_browser_terminal_checkbutton), + mateconf_value_get_bool (value)); + + cs = mateconf_change_set_new (); + + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_HTTPS_NEEDS_TERM, value); + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_UNKNOWN_NEEDS_TERM, value); + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_ABOUT_NEEDS_TERM, value); + mateconf_change_set_set (cs, DEFAULT_APPS_KEY_BROWSER_NEEDS_TERM, value); + + mateconf_client_commit_change_set (capplet->mateconf, cs, TRUE, &error); + + if (error != NULL) { + g_warning (_("Error saving configuration: %s"), error->message); + g_error_free (error); + error = NULL; + } + + mateconf_change_set_unref (cs); + } +} + +static void web_browser_update_radio_buttons(MateDACapplet* capplet, const gchar* command) +{ + GList *entry; + gboolean has_net_remote; + + entry = g_list_find_custom (capplet->web_browsers, command, (GCompareFunc) web_item_comp); + + if (entry) { + MateDAWebItem *item = (MateDAWebItem *) entry->data; + + has_net_remote = item->netscape_remote; + + if (has_net_remote) { + /* disable "toggle" signal emitting, thus preventing calling this function twice */ + g_signal_handlers_block_matched (capplet->default_radiobutton, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, G_CALLBACK (web_radiobutton_toggled_cb), NULL); + g_signal_handlers_block_matched (capplet->new_tab_radiobutton, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, G_CALLBACK (web_radiobutton_toggled_cb), NULL); + g_signal_handlers_block_matched (capplet->new_win_radiobutton,G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, G_CALLBACK (web_radiobutton_toggled_cb), NULL); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (capplet->default_radiobutton), + strcmp (item->generic.command, command) == 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (capplet->new_tab_radiobutton), + strcmp (item->tab_command, command) == 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (capplet->new_win_radiobutton), + strcmp (item->win_command, command) == 0); + + g_signal_handlers_unblock_matched (capplet->default_radiobutton, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, G_CALLBACK (web_radiobutton_toggled_cb), NULL); + g_signal_handlers_unblock_matched (capplet->new_tab_radiobutton, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, G_CALLBACK (web_radiobutton_toggled_cb), NULL); + g_signal_handlers_unblock_matched (capplet->new_win_radiobutton, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, G_CALLBACK (web_radiobutton_toggled_cb), NULL); + } + } + else { + has_net_remote = FALSE; + } + + gtk_widget_set_sensitive (capplet->default_radiobutton, has_net_remote); + gtk_widget_set_sensitive (capplet->new_win_radiobutton, has_net_remote); + gtk_widget_set_sensitive (capplet->new_tab_radiobutton, has_net_remote); +} + +static MateConfValue* web_combo_conv_to_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *ret; + GList *entry, *handlers; + const gchar *command; + gint index; + MateDACapplet *capplet; + + g_object_get (G_OBJECT (peditor), "data", &capplet, NULL); + + command = mateconf_value_get_string (value); + handlers = capplet->web_browsers; + + if (handlers) + { + entry = g_list_find_custom (handlers, command, (GCompareFunc) web_item_comp); + if (entry) + index = g_list_position (handlers, entry); + else + index = g_list_length (handlers) + 1; + } + else + { + /* if the item has no handlers lsit then select the Custom item */ + index = 1; + } + + web_browser_update_radio_buttons (capplet, command); + + ret = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (ret, index); + + return ret; +} + +static MateConfValue* web_combo_conv_from_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *ret; + GList *handlers; + gint index; + MateDAWebItem *item; + const gchar *command; + MateDACapplet *capplet; + + g_object_get (G_OBJECT (peditor), "data", &capplet, NULL); + + index = mateconf_value_get_int (value); + handlers = capplet->web_browsers; + + item = g_list_nth_data (handlers, index); + + ret = mateconf_value_new (MATECONF_VALUE_STRING); + if (!item) + { + /* if item was not found, this is probably the "Custom" item */ + /* XXX: returning "" as the value here is not ideal, but required to + * prevent the combo box from jumping back to the previous value if the + * user has selected Custom */ + mateconf_value_set_string (ret, ""); + return ret; + } + else + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (capplet->new_win_radiobutton)) && item->netscape_remote == TRUE) + command = item->win_command; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (capplet->new_tab_radiobutton)) && item->netscape_remote == TRUE) + command = item->tab_command; + else + command = item->generic.command; + + mateconf_value_set_string (ret, command); + return ret; + } +} + +static MateConfValue* combo_conv_to_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *ret; + GList *entry, *handlers; + const gchar *command; + gint index; + + g_object_get (G_OBJECT (peditor), "data", &handlers, NULL); + + command = mateconf_value_get_string (value); + + if (handlers) + { + entry = g_list_find_custom (handlers, command, (GCompareFunc) generic_item_comp); + if (entry) + index = g_list_position (handlers, entry); + else + index = g_list_length (handlers) + 1; + } + else + { + /* if the item has no handlers lsit then select the Custom item */ + index = 1; + } + + ret = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (ret, index); + return ret; +} + +static MateConfValue* combo_conv_from_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *ret; + GList *handlers; + gint index; + MateDAItem *item; + + g_object_get (G_OBJECT (peditor), "data", &handlers, NULL); + index = mateconf_value_get_int (value); + + item = g_list_nth_data (handlers, index); + ret = mateconf_value_new (MATECONF_VALUE_STRING); + + if (!item) + { + /* if item was not found, this is probably the "Custom" item */ + + /* XXX: returning "" as the value here is not ideal, but required to + * prevent the combo box from jumping back to the previous value if the + * user has selected Custom */ + mateconf_value_set_string (ret, ""); + return ret; + } + else + { + mateconf_value_set_string (ret, item->command); + return ret; + } +} + +static MateConfValue* combo_conv_from_widget_term_flag (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *ret; + GList *handlers; + gint index; + MateDATermItem *item; + + g_object_get (G_OBJECT (peditor), "data", &handlers, NULL); + index = mateconf_value_get_int (value); + + item = g_list_nth_data (handlers, index); + ret = mateconf_value_new (MATECONF_VALUE_STRING); + + if (!item) + { + /* if item was not found, this is probably the "Custom" item */ + + /* XXX: returning "" as the value here is not ideal, but required to + * prevent the combo box from jumping back to the previous value if the + * user has selected Custom */ + mateconf_value_set_string (ret, ""); + return ret; + } + else + { + mateconf_value_set_string (ret, item->exec_flag); + return ret; + } +} + +static MateConfValue* combo_conv_to_widget_term_flag (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *ret; + GtkComboBox *combo; + + combo = GTK_COMBO_BOX (mateconf_property_editor_get_ui_control (peditor)); + + ret = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (ret, gtk_combo_box_get_active (combo)); + return ret; +} + +static gboolean is_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer sep_index) +{ + GtkTreePath *path; + gboolean result; + + path = gtk_tree_model_get_path (model, iter); + result = gtk_tree_path_get_indices (path)[0] == GPOINTER_TO_INT (sep_index); + gtk_tree_path_free (path); + + return result; +} + +static void fill_combo_box (GtkIconTheme *theme, GtkComboBox *combo_box, GList *app_list) +{ + GList *entry; + GtkTreeModel *model; + GtkCellRenderer *renderer; + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + if (theme == NULL) { + theme = gtk_icon_theme_get_default (); + } + + gtk_combo_box_set_row_separator_func (combo_box, is_separator, + GINT_TO_POINTER (g_list_length (app_list)), NULL); + + model = GTK_TREE_MODEL (gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING)); + gtk_combo_box_set_model (combo_box, model); + + renderer = gtk_cell_renderer_pixbuf_new (); + + /* not all cells have a pixbuf, this prevents the combo box to shrink */ + gtk_cell_renderer_set_fixed_size (renderer, -1, 22); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, + "pixbuf", PIXBUF_COL, + NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, + "text", TEXT_COL, + NULL); + + for (entry = app_list; entry != NULL; entry = g_list_next (entry)) { + MateDAItem *item; + item = (MateDAItem *) entry->data; + + pixbuf = gtk_icon_theme_load_icon (theme, item->icon_name, 22, 0, NULL); + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + PIXBUF_COL, pixbuf, + TEXT_COL, item->name, + -1); + + item->icon_path = gtk_tree_model_get_string_from_iter (model, &iter); + + if (pixbuf) + g_object_unref (pixbuf); + } + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, -1); + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + PIXBUF_COL, NULL, + TEXT_COL, _("Custom"), + -1); +} + +static GtkWidget* _gtk_builder_get_widget (GtkBuilder *builder, const gchar *name) +{ + return GTK_WIDGET (gtk_builder_get_object (builder, name)); +} + + +static void show_dialog (MateDACapplet* capplet, const gchar* start_page) +{ + GObject *obj; + GtkBuilder *builder; + guint builder_result; + + capplet->builder = builder = gtk_builder_new (); + + if (g_file_test (MATECC_UI_DIR "/mate-default-applications-properties.ui", G_FILE_TEST_EXISTS) != FALSE) { + builder_result = gtk_builder_add_from_file (builder, MATECC_UI_DIR "/mate-default-applications-properties.ui", NULL); + } + else { + builder_result = gtk_builder_add_from_file (builder, "./mate-default-applications-properties.ui", NULL); + } + + if (builder_result == 0) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Could not load the main interface")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Please make sure that the applet " + "is properly installed")); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + exit (EXIT_FAILURE); + } + + capplet->window = _gtk_builder_get_widget (builder,"preferred_apps_dialog"); + g_signal_connect (capplet->window, "response", G_CALLBACK (close_cb), NULL); + + capplet->web_browser_command_entry = _gtk_builder_get_widget (builder, "web_browser_command_entry"); + capplet->web_browser_command_label = _gtk_builder_get_widget (builder, "web_browser_command_label"); + capplet->web_browser_terminal_checkbutton = _gtk_builder_get_widget(builder, "web_browser_terminal_checkbutton"); + capplet->default_radiobutton = _gtk_builder_get_widget (builder, "web_browser_default_radiobutton"); + capplet->new_win_radiobutton = _gtk_builder_get_widget (builder, "web_browser_new_win_radiobutton"); + capplet->new_tab_radiobutton = _gtk_builder_get_widget (builder, "web_browser_new_tab_radiobutton"); + + capplet->mail_reader_command_entry = _gtk_builder_get_widget (builder, "mail_reader_command_entry"); + capplet->mail_reader_command_label = _gtk_builder_get_widget (builder, "mail_reader_command_label"); + capplet->mail_reader_terminal_checkbutton = _gtk_builder_get_widget (builder, "mail_reader_terminal_checkbutton"); + + capplet->terminal_command_entry = _gtk_builder_get_widget (builder, "terminal_command_entry"); + capplet->terminal_command_label = _gtk_builder_get_widget (builder, "terminal_command_label"); + capplet->terminal_exec_flag_entry = _gtk_builder_get_widget (builder, "terminal_exec_flag_entry"); + capplet->terminal_exec_flag_label = _gtk_builder_get_widget (builder, "terminal_exec_flag_label"); + + capplet->media_player_command_entry = _gtk_builder_get_widget (builder, "media_player_command_entry"); + capplet->media_player_command_label = _gtk_builder_get_widget (builder, "media_player_command_label"); + capplet->media_player_terminal_checkbutton = _gtk_builder_get_widget (builder, "media_player_terminal_checkbutton"); + + capplet->visual_command_entry = _gtk_builder_get_widget (builder, "visual_command_entry"); + capplet->visual_command_label = _gtk_builder_get_widget (builder, "visual_command_label"); + capplet->visual_startup_checkbutton = _gtk_builder_get_widget (builder, "visual_start_checkbutton"); + + capplet->mobility_command_entry = _gtk_builder_get_widget (builder, "mobility_command_entry"); + capplet->mobility_command_label = _gtk_builder_get_widget (builder, "mobility_command_label"); + capplet->mobility_startup_checkbutton = _gtk_builder_get_widget (builder, "mobility_start_checkbutton"); + + capplet->web_combo_box = _gtk_builder_get_widget (builder, "web_browser_combobox"); + capplet->mail_combo_box = _gtk_builder_get_widget (builder, "mail_reader_combobox"); + capplet->term_combo_box = _gtk_builder_get_widget (builder, "terminal_combobox"); + capplet->media_combo_box = _gtk_builder_get_widget (builder, "media_player_combobox"); + capplet->visual_combo_box = _gtk_builder_get_widget (builder, "visual_combobox"); + capplet->mobility_combo_box = _gtk_builder_get_widget (builder, "mobility_combobox"); + + g_signal_connect (capplet->window, "screen-changed", G_CALLBACK (screen_changed_cb), capplet); + screen_changed_cb (capplet->window, gdk_screen_get_default (), capplet); + + fill_combo_box (capplet->icon_theme, GTK_COMBO_BOX (capplet->web_combo_box), capplet->web_browsers); + fill_combo_box (capplet->icon_theme, GTK_COMBO_BOX (capplet->mail_combo_box), capplet->mail_readers); + fill_combo_box (capplet->icon_theme, GTK_COMBO_BOX (capplet->term_combo_box), capplet->terminals); + fill_combo_box (capplet->icon_theme, GTK_COMBO_BOX (capplet->media_combo_box), capplet->media_players); + fill_combo_box (capplet->icon_theme, GTK_COMBO_BOX (capplet->visual_combo_box), capplet->visual_ats); + fill_combo_box (capplet->icon_theme, GTK_COMBO_BOX (capplet->mobility_combo_box), capplet->mobility_ats); + + g_signal_connect (capplet->web_combo_box, "changed", G_CALLBACK (web_combo_changed_cb), capplet); + g_signal_connect (capplet->mail_combo_box, "changed", G_CALLBACK (mail_combo_changed_cb), capplet); + g_signal_connect (capplet->term_combo_box, "changed", G_CALLBACK (terminal_combo_changed_cb), capplet); + g_signal_connect (capplet->media_combo_box, "changed", G_CALLBACK (media_combo_changed_cb), capplet); + g_signal_connect (capplet->visual_combo_box, "changed", G_CALLBACK (visual_combo_changed_cb), capplet); + g_signal_connect (capplet->mobility_combo_box, "changed", G_CALLBACK (mobility_combo_changed_cb), capplet); + + + g_signal_connect (capplet->default_radiobutton, "toggled", G_CALLBACK (web_radiobutton_toggled_cb), capplet); + g_signal_connect (capplet->new_win_radiobutton, "toggled", G_CALLBACK (web_radiobutton_toggled_cb), capplet); + g_signal_connect (capplet->new_tab_radiobutton, "toggled", G_CALLBACK (web_radiobutton_toggled_cb), capplet); + + /* Setup MateConfPropertyEditors */ + + /* Web Browser */ + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_HTTP_EXEC, + capplet->web_combo_box, + "conv-from-widget-cb", web_combo_conv_from_widget, + "conv-to-widget-cb", web_combo_conv_to_widget, + "data", capplet, + NULL); + + obj = mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_HTTP_EXEC, + capplet->web_browser_command_entry, + NULL); + g_signal_connect (obj, "value-changed", G_CALLBACK (web_mateconf_changed_cb), capplet); + + obj = mateconf_peditor_new_boolean (NULL, + DEFAULT_APPS_KEY_HTTP_NEEDS_TERM, + capplet->web_browser_terminal_checkbutton, + NULL); + g_signal_connect (obj, "value-changed", G_CALLBACK (web_mateconf_changed_cb), capplet); + + /* Mailer */ + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_MAILER_EXEC, + capplet->mail_combo_box, + "conv-from-widget-cb", combo_conv_from_widget, + "conv-to-widget-cb", combo_conv_to_widget, + "data", capplet->mail_readers, + NULL); + + mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_MAILER_EXEC, + capplet->mail_reader_command_entry, + NULL); + + mateconf_peditor_new_boolean (NULL, + DEFAULT_APPS_KEY_MAILER_NEEDS_TERM, + capplet->mail_reader_terminal_checkbutton, + NULL); + + /* Media player */ + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_MEDIA_EXEC, + capplet->media_combo_box, + "conv-from-widget-cb", combo_conv_from_widget, + "conv-to-widget-cb", combo_conv_to_widget, + "data", capplet->media_players, + NULL); + + mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_MEDIA_EXEC, + capplet->media_player_command_entry, + NULL); + + mateconf_peditor_new_boolean (NULL, + DEFAULT_APPS_KEY_MEDIA_NEEDS_TERM, + capplet->media_player_terminal_checkbutton, + NULL); + + /* Terminal */ + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_TERMINAL_EXEC, + capplet->term_combo_box, + "conv-from-widget-cb", combo_conv_from_widget, + "conv-to-widget-cb", combo_conv_to_widget, + "data", capplet->terminals, + NULL); + + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_TERMINAL_EXEC_ARG, + capplet->term_combo_box, + "conv-from-widget-cb", combo_conv_from_widget_term_flag, + "conv-to-widget-cb", combo_conv_to_widget_term_flag, + "data", capplet->terminals, + NULL); + + mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_TERMINAL_EXEC, + capplet->terminal_command_entry, + NULL); + mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_TERMINAL_EXEC_ARG, + capplet->terminal_exec_flag_entry, + NULL); + + + /* Visual */ + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_VISUAL_EXEC, + capplet->visual_combo_box, + "conv-from-widget-cb", combo_conv_from_widget, + "conv-to-widget-cb", combo_conv_to_widget, + "data", capplet->visual_ats, + NULL); + + mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_VISUAL_EXEC, + capplet->visual_command_entry, + NULL); + + mateconf_peditor_new_boolean (NULL, + DEFAULT_APPS_KEY_VISUAL_STARTUP, + capplet->visual_startup_checkbutton, + NULL); + + + /* Mobility */ + mateconf_peditor_new_combo_box (NULL, + DEFAULT_APPS_KEY_MOBILITY_EXEC, + capplet->mobility_combo_box, + "conv-from-widget-cb", combo_conv_from_widget, + "conv-to-widget-cb", combo_conv_to_widget, + "data", capplet->mobility_ats, + NULL); + + mateconf_peditor_new_string (NULL, + DEFAULT_APPS_KEY_MOBILITY_EXEC, + capplet->mobility_command_entry, + NULL); + + mateconf_peditor_new_boolean (NULL, + DEFAULT_APPS_KEY_MOBILITY_STARTUP, + capplet->mobility_startup_checkbutton, + NULL); + + gtk_window_set_icon_name (GTK_WINDOW (capplet->window), + "preferences-desktop-default-applications"); + + if (start_page != NULL) { + gchar *page_name; + GtkWidget *w; + + page_name = g_strconcat (start_page, "_vbox", NULL); + + w = _gtk_builder_get_widget (builder, page_name); + if (w != NULL) { + GtkNotebook *nb; + gint pindex; + + nb = GTK_NOTEBOOK (_gtk_builder_get_widget (builder, + "preferred_apps_notebook")); + pindex = gtk_notebook_page_num (nb, w); + if (pindex != -1) + gtk_notebook_set_current_page (nb, pindex); + } + g_free (page_name); + } + + gtk_widget_show (capplet->window); +} + +int main (int argc, char** argv) +{ + MateDACapplet* capplet; + + gchar* start_page = NULL; + + GOptionContext* context; + GOptionEntry option_entries[] = { + { + "show-page", + 'p', + G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_STRING, + &start_page, + /* TRANSLATORS: don't translate the terms in brackets */ + N_("Specify the name of the page to show (internet|multimedia|system|a11y)"), + N_("page") + }, + {NULL} + }; + + context = g_option_context_new(_("- MATE Default Applications")); + g_option_context_add_main_entries (context, option_entries, GETTEXT_PACKAGE); + + capplet_init (context, &argc, &argv); + + capplet = g_new0(MateDACapplet, 1); + capplet->mateconf = mateconf_client_get_default(); + mateconf_client_add_dir(capplet->mateconf, "/desktop/mate/url-handlers", MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + mateconf_client_add_dir(capplet->mateconf, "/desktop/mate/applications", MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + + mate_da_xml_load_list(capplet); + + show_dialog(capplet, start_page); + g_free(start_page); + + gtk_main(); + + g_object_unref(capplet->mateconf); + + mate_da_xml_free(capplet); + + return 0; +} diff --git a/capplets/default-applications/mate-da-capplet.h b/capplets/default-applications/mate-da-capplet.h new file mode 100644 index 00000000..3ce72955 --- /dev/null +++ b/capplets/default-applications/mate-da-capplet.h @@ -0,0 +1,139 @@ +/* + * Authors: Luca Cavalli + * + * Copyright 2005-2006 Luca Cavalli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_DA_CAPPLET_H_ +#define _MATE_DA_CAPPLET_H_ + +#include +#include + +// Set http, https, about, and unknown keys to the chosen web browser. +#define DEFAULT_APPS_KEY_HTTP_PATH "/desktop/mate/url-handlers/http" +#define DEFAULT_APPS_KEY_HTTP_NEEDS_TERM DEFAULT_APPS_KEY_HTTP_PATH"/needs_terminal" +#define DEFAULT_APPS_KEY_HTTP_EXEC DEFAULT_APPS_KEY_HTTP_PATH"/command" + +#define DEFAULT_APPS_KEY_HTTPS_PATH "/desktop/mate/url-handlers/https" +#define DEFAULT_APPS_KEY_HTTPS_NEEDS_TERM DEFAULT_APPS_KEY_HTTPS_PATH"/needs_terminal" +#define DEFAULT_APPS_KEY_HTTPS_EXEC DEFAULT_APPS_KEY_HTTPS_PATH"/command" + +// While mate-vfs2 does not use the "unknown" key, several widespread apps like htmlview +// have read it for the past few years. Setting it should not hurt. +#define DEFAULT_APPS_KEY_UNKNOWN_PATH "/desktop/mate/url-handlers/unknown" +#define DEFAULT_APPS_KEY_UNKNOWN_NEEDS_TERM DEFAULT_APPS_KEY_UNKNOWN_PATH"/needs_terminal" +#define DEFAULT_APPS_KEY_UNKNOWN_EXEC DEFAULT_APPS_KEY_UNKNOWN_PATH"/command" + +// about:blank and other about: URI's are commonly used by browsers too +#define DEFAULT_APPS_KEY_ABOUT_PATH "/desktop/mate/url-handlers/about" +#define DEFAULT_APPS_KEY_ABOUT_NEEDS_TERM DEFAULT_APPS_KEY_ABOUT_PATH"/needs_terminal" +#define DEFAULT_APPS_KEY_ABOUT_EXEC DEFAULT_APPS_KEY_ABOUT_PATH"/command" + +#define DEFAULT_APPS_KEY_MAILER_PATH "/desktop/mate/url-handlers/mailto" +#define DEFAULT_APPS_KEY_MAILER_NEEDS_TERM DEFAULT_APPS_KEY_MAILER_PATH"/needs_terminal" +#define DEFAULT_APPS_KEY_MAILER_EXEC DEFAULT_APPS_KEY_MAILER_PATH"/command" + +#define DEFAULT_APPS_KEY_BROWSER_PATH "/desktop/mate/applications/browser" +#define DEFAULT_APPS_KEY_BROWSER_EXEC DEFAULT_APPS_KEY_BROWSER_PATH"/exec" +#define DEFAULT_APPS_KEY_BROWSER_NEEDS_TERM DEFAULT_APPS_KEY_BROWSER_PATH"/needs_term" +#define DEFAULT_APPS_KEY_BROWSER_NREMOTE DEFAULT_APPS_KEY_BROWSER_PATH"/nremote" + +#define DEFAULT_APPS_KEY_TERMINAL_PATH "/desktop/mate/applications/terminal" +#define DEFAULT_APPS_KEY_TERMINAL_EXEC_ARG DEFAULT_APPS_KEY_TERMINAL_PATH"/exec_arg" +#define DEFAULT_APPS_KEY_TERMINAL_EXEC DEFAULT_APPS_KEY_TERMINAL_PATH"/exec" + +#define DEFAULT_APPS_KEY_MEDIA_PATH "/desktop/mate/applications/media" +#define DEFAULT_APPS_KEY_MEDIA_EXEC DEFAULT_APPS_KEY_MEDIA_PATH"/exec" +#define DEFAULT_APPS_KEY_MEDIA_NEEDS_TERM DEFAULT_APPS_KEY_MEDIA_PATH"/needs_term" + +#define DEFAULT_APPS_KEY_VISUAL_PATH "/desktop/mate/applications/at/visual" +#define DEFAULT_APPS_KEY_VISUAL_EXEC DEFAULT_APPS_KEY_VISUAL_PATH"/exec" +#define DEFAULT_APPS_KEY_VISUAL_STARTUP DEFAULT_APPS_KEY_VISUAL_PATH"/startup" + +#define DEFAULT_APPS_KEY_MOBILITY_PATH "/desktop/mate/applications/at/mobility" +#define DEFAULT_APPS_KEY_MOBILITY_EXEC DEFAULT_APPS_KEY_MOBILITY_PATH"/exec" +#define DEFAULT_APPS_KEY_MOBILITY_STARTUP DEFAULT_APPS_KEY_MOBILITY_PATH"/startup" + +typedef struct _MateDACapplet MateDACapplet; + +struct _MateDACapplet { + GtkBuilder* builder; + + GtkIconTheme* icon_theme; + + GtkWidget* window; + + GtkWidget* web_combo_box; + GtkWidget* mail_combo_box; + GtkWidget* term_combo_box; + GtkWidget* media_combo_box; + GtkWidget* visual_combo_box; + GtkWidget* mobility_combo_box; + /* Para el File Manager */ + /*GtkWidget* filemanager_combo_box;*/ + + GtkWidget* web_browser_command_entry; + GtkWidget* web_browser_command_label; + GtkWidget* web_browser_terminal_checkbutton; + GtkWidget* default_radiobutton; + GtkWidget* new_win_radiobutton; + GtkWidget* new_tab_radiobutton; + + /* Para el File Manager */ + /*GtkWidget* file_manager_command_entry; + GtkWidget* file_manager_command_label; + GtkWidget* file_manager_terminal_checkbutton; + GtkWidget* file_manager_default_radiobutton; + GtkWidget* file_manager_new_win_radiobutton; + GtkWidget* file_manager_new_tab_radiobutton;*/ + + + GtkWidget* mail_reader_command_entry; + GtkWidget* mail_reader_command_label; + GtkWidget* mail_reader_terminal_checkbutton; + + GtkWidget* terminal_command_entry; + GtkWidget* terminal_command_label; + GtkWidget* terminal_exec_flag_entry; + GtkWidget* terminal_exec_flag_label; + + GtkWidget* media_player_command_entry; + GtkWidget* media_player_command_label; + GtkWidget* media_player_terminal_checkbutton; + + GtkWidget* visual_command_entry; + GtkWidget* visual_command_label; + GtkWidget* visual_startup_checkbutton; + + GtkWidget* mobility_command_entry; + GtkWidget* mobility_command_label; + GtkWidget* mobility_startup_checkbutton; + + MateConfClient* mateconf; + + GList* web_browsers; + GList* mail_readers; + GList* terminals; + GList* media_players; + GList* visual_ats; + GList* mobility_ats; + /* Para el File Manager */ + /*GList* file_managers;*/ +}; + +#endif diff --git a/capplets/default-applications/mate-da-item.c b/capplets/default-applications/mate-da-item.c new file mode 100644 index 00000000..06bda549 --- /dev/null +++ b/capplets/default-applications/mate-da-item.c @@ -0,0 +1,148 @@ +/* + * Authors: Luca Cavalli + * + * Copyright 2005-2006 Luca Cavalli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include "mate-da-capplet.h" +#include "mate-da-item.h" + +MateDAWebItem* +mate_da_web_item_new (void) +{ + MateDAWebItem *item = NULL; + + item = g_new0 (MateDAWebItem, 1); + + return item; +} + +MateDASimpleItem* +mate_da_simple_item_new (void) +{ + MateDASimpleItem *item = NULL; + + item = g_new0 (MateDASimpleItem, 1); + + return item; +} + +MateDATermItem* +mate_da_term_item_new (void) +{ + MateDATermItem *item = NULL; + + item = g_new0 (MateDATermItem, 1); + + return item; +} + +MateDAVisualItem* +mate_da_visual_item_new (void) +{ + MateDAVisualItem *item = NULL; + + item = g_new0 (MateDAVisualItem, 1); + + return item; +} + +MateDAMobilityItem* +mate_da_mobility_item_new (void) +{ + MateDAMobilityItem *item = NULL; + + item = g_new0 (MateDAMobilityItem, 1); + + return item; +} + +void +mate_da_web_item_free (MateDAWebItem *item) +{ + g_return_if_fail (item != NULL); + + g_free (item->generic.name); + g_free (item->generic.executable); + g_free (item->generic.command); + g_free (item->generic.icon_name); + g_free (item->generic.icon_path); + + g_free (item->tab_command); + g_free (item->win_command); + + g_free (item); +} + +void +mate_da_simple_item_free (MateDASimpleItem *item) +{ + g_return_if_fail (item != NULL); + + g_free (item->generic.name); + g_free (item->generic.executable); + g_free (item->generic.command); + g_free (item->generic.icon_name); + g_free (item->generic.icon_path); + + g_free (item); +} + +void +mate_da_term_item_free (MateDATermItem *item) +{ + g_return_if_fail (item != NULL); + + g_free (item->generic.name); + g_free (item->generic.executable); + g_free (item->generic.command); + g_free (item->generic.icon_name); + g_free (item->generic.icon_path); + + g_free (item->exec_flag); + + g_free (item); +} + +void +mate_da_visual_item_free (MateDAVisualItem *item) +{ + g_return_if_fail (item != NULL); + + g_free (item->generic.name); + g_free (item->generic.executable); + g_free (item->generic.command); + g_free (item->generic.icon_name); + g_free (item->generic.icon_path); + + g_free (item); +} + +void +mate_da_mobility_item_free (MateDAMobilityItem *item) +{ + g_return_if_fail (item != NULL); + + g_free (item->generic.name); + g_free (item->generic.executable); + g_free (item->generic.command); + g_free (item->generic.icon_name); + g_free (item->generic.icon_path); + + g_free (item); +} + diff --git a/capplets/default-applications/mate-da-item.h b/capplets/default-applications/mate-da-item.h new file mode 100644 index 00000000..81223f4e --- /dev/null +++ b/capplets/default-applications/mate-da-item.h @@ -0,0 +1,81 @@ +/* + * Authors: Luca Cavalli + * + * Copyright 2005-2006 Luca Cavalli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_DA_ITEM_H_ +#define _MATE_DA_ITEM_H_ + +#include + +typedef struct _MateDAItem MateDAItem; + +typedef struct _MateDAWebItem MateDAWebItem; +typedef struct _MateDATermItem MateDATermItem; +typedef struct _MateDASimpleItem MateDASimpleItem; +typedef struct _MateDAVisualItem MateDAVisualItem; +typedef struct _MateDAMobilityItem MateDAMobilityItem; + +struct _MateDAItem { + gchar *name; + gchar *executable; + gchar *command; + gchar *icon_name; + gchar *icon_path; +}; + +struct _MateDAWebItem { + MateDAItem generic; + gboolean run_in_terminal; + gboolean netscape_remote; + gchar *tab_command; + gchar *win_command; +}; + +struct _MateDASimpleItem { + MateDAItem generic; + gboolean run_in_terminal; +}; + +struct _MateDATermItem { + MateDAItem generic; + gchar *exec_flag; +}; + +struct _MateDAVisualItem { + MateDAItem generic; + gboolean run_at_startup; +}; + +struct _MateDAMobilityItem { + MateDAItem generic; + gboolean run_at_startup; +}; + +MateDAWebItem* mate_da_web_item_new (void); +MateDATermItem* mate_da_term_item_new (void); +MateDASimpleItem* mate_da_simple_item_new (void); +MateDAVisualItem* mate_da_visual_item_new (void); +MateDAMobilityItem* mate_da_mobility_item_new (void); +void mate_da_web_item_free (MateDAWebItem *item); +void mate_da_term_item_free (MateDATermItem *item); +void mate_da_simple_item_free (MateDASimpleItem *item); +void mate_da_visual_item_free (MateDAVisualItem *item); +void mate_da_mobility_item_free (MateDAMobilityItem *item); + +#endif diff --git a/capplets/default-applications/mate-da-xml.c b/capplets/default-applications/mate-da-xml.c new file mode 100644 index 00000000..288495c7 --- /dev/null +++ b/capplets/default-applications/mate-da-xml.c @@ -0,0 +1,327 @@ +/* + * Authors: Luca Cavalli + * + * Copyright 2005-2006 Luca Cavalli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "mate-da-capplet.h" +#include "mate-da-xml.h" +#include "mate-da-item.h" + + +static gboolean +mate_da_xml_get_bool (const xmlNode *parent, const gchar *val_name) +{ + xmlNode *element; + gboolean ret_val = FALSE; + xmlChar *xml_val_name; + gint len; + + g_return_val_if_fail (parent != NULL, FALSE); + g_return_val_if_fail (parent->children != NULL, ret_val); + g_return_val_if_fail (val_name != NULL, FALSE); + + xml_val_name = xmlCharStrdup (val_name); + len = xmlStrlen (xml_val_name); + + for (element = parent->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, xml_val_name, len)) { + xmlChar *cont = xmlNodeGetContent (element); + + if (!xmlStrcasecmp (cont, "true") || !xmlStrcasecmp (cont, "1")) + ret_val = TRUE; + else + ret_val = FALSE; + + xmlFree (cont); + } + } + + xmlFree (xml_val_name); + return ret_val; +} + +static gchar* +mate_da_xml_get_string (const xmlNode *parent, const gchar *val_name) +{ + const gchar * const *sys_langs; + xmlChar *node_lang; + xmlNode *element; + gchar *ret_val = NULL; + xmlChar *xml_val_name; + gint len; + gint i; + + g_return_val_if_fail (parent != NULL, ret_val); + g_return_val_if_fail (parent->children != NULL, ret_val); + g_return_val_if_fail (val_name != NULL, ret_val); + +#if GLIB_CHECK_VERSION (2, 6, 0) + sys_langs = g_get_language_names (); +#endif + + xml_val_name = xmlCharStrdup (val_name); + len = xmlStrlen (xml_val_name); + + for (element = parent->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, xml_val_name, len)) { + node_lang = xmlNodeGetLang (element); + + if (node_lang == NULL) { + ret_val = (gchar *) xmlNodeGetContent (element); + } + else { + for (i = 0; sys_langs[i] != NULL; i++) { + if (!strcmp (sys_langs[i], node_lang)) { + ret_val = (gchar *) xmlNodeGetContent (element); + /* since sys_langs is sorted from most desirable to + * least desirable, exit at first match + */ + break; + } + } + } + xmlFree (node_lang); + } + } + + xmlFree (xml_val_name); + return ret_val; +} + +static gboolean +is_executable_valid (gchar *executable) +{ + gchar *path; + + path = g_find_program_in_path (executable); + + if (path) { + g_free (path); + return TRUE; + } + + return FALSE; +} + +static void +mate_da_xml_load_xml (MateDACapplet *capplet, const gchar * filename) +{ + xmlDoc *xml_doc; + xmlNode *root, *section, *element; + gchar *executable; + MateDAWebItem *web_item; + MateDASimpleItem *mail_item; + MateDASimpleItem *media_item; + MateDATermItem *term_item; + MateDAVisualItem *visual_item; + MateDAMobilityItem *mobility_item; + + xml_doc = xmlParseFile (filename); + + if (!xml_doc) + return; + + root = xmlDocGetRootElement (xml_doc); + + for (section = root->children; section != NULL; section = section->next) { + if (!xmlStrncmp (section->name, "web-browsers", 12)) { + for (element = section->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, "web-browser", 11)) { + executable = mate_da_xml_get_string (element, "executable"); + if (is_executable_valid (executable)) { + web_item = mate_da_web_item_new (); + + web_item->generic.name = mate_da_xml_get_string (element, "name"); + web_item->generic.executable = executable; + web_item->generic.command = mate_da_xml_get_string (element, "command"); + web_item->generic.icon_name = mate_da_xml_get_string (element, "icon-name"); + + web_item->run_in_terminal = mate_da_xml_get_bool (element, "run-in-terminal"); + web_item->netscape_remote = mate_da_xml_get_bool (element, "netscape-remote"); + if (web_item->netscape_remote) { + web_item->tab_command = mate_da_xml_get_string (element, "tab-command"); + web_item->win_command = mate_da_xml_get_string (element, "win-command"); + } + + capplet->web_browsers = g_list_append (capplet->web_browsers, web_item); + } + else + g_free (executable); + } + } + } + else if (!xmlStrncmp (section->name, "mail-readers", 12)) { + for (element = section->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, "mail-reader", 11)) { + executable = mate_da_xml_get_string (element, "executable"); + if (is_executable_valid (executable)) { + mail_item = mate_da_simple_item_new (); + + mail_item->generic.name = mate_da_xml_get_string (element, "name"); + mail_item->generic.executable = executable; + mail_item->generic.command = mate_da_xml_get_string (element, "command"); + mail_item->generic.icon_name = mate_da_xml_get_string (element, "icon-name"); + + mail_item->run_in_terminal = mate_da_xml_get_bool (element, "run-in-terminal"); + + capplet->mail_readers = g_list_append (capplet->mail_readers, mail_item); + } + else + g_free (executable); + } + } + } + else if (!xmlStrncmp (section->name, "terminals", 9)) { + for (element = section->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, "terminal", 8)) { + executable = mate_da_xml_get_string (element, "executable"); + if (is_executable_valid (executable)) { + term_item = mate_da_term_item_new (); + + term_item->generic.name = mate_da_xml_get_string (element, "name"); + term_item->generic.executable = executable; + term_item->generic.command = mate_da_xml_get_string (element, "command"); + term_item->generic.icon_name = mate_da_xml_get_string (element, "icon-name"); + + term_item->exec_flag = mate_da_xml_get_string (element, "exec-flag"); + + capplet->terminals = g_list_append (capplet->terminals, term_item); + } + else + g_free (executable); + } + } + } + else if (!xmlStrncmp (section->name, "media-players", 13)) { + for (element = section->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, "media-player", 12)) { + executable = mate_da_xml_get_string (element, "executable"); + if (is_executable_valid (executable)) { + media_item = mate_da_simple_item_new (); + + media_item->generic.name = mate_da_xml_get_string (element, "name"); + media_item->generic.executable = executable; + media_item->generic.command = mate_da_xml_get_string (element, "command"); + media_item->generic.icon_name = mate_da_xml_get_string (element, "icon-name"); + + media_item->run_in_terminal = mate_da_xml_get_bool (element, "run-in-terminal"); + + capplet->media_players = g_list_append (capplet->media_players, media_item); + } + else + g_free (executable); + } + } + } + else if (!xmlStrncmp (section->name, "a11y-visual", 11)) { + for (element = section->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, "visual", 6)) { + executable = mate_da_xml_get_string (element,"executable"); + if (is_executable_valid (executable)) { + visual_item = mate_da_visual_item_new (); + + visual_item->generic.name = mate_da_xml_get_string (element, "name"); + visual_item->generic.executable = executable; + visual_item->generic.command = mate_da_xml_get_string (element, "command"); + visual_item->generic.icon_name = mate_da_xml_get_string (element, "icon-name"); + + visual_item->run_at_startup = mate_da_xml_get_bool (element, "run-at-startup"); + + capplet->visual_ats = g_list_append (capplet->visual_ats, visual_item); + } + else + g_free (executable); + } + } + } + else if (!xmlStrncmp (section->name, "a11y-mobility", 13)) { + for (element = section->children; element != NULL; element = element->next) { + if (!xmlStrncmp (element->name, "mobility", 8)) { + executable = mate_da_xml_get_string (element,"executable"); + if (is_executable_valid (executable)) { + mobility_item = mate_da_mobility_item_new (); + + mobility_item->generic.name = mate_da_xml_get_string (element, "name"); + mobility_item->generic.executable = executable; + mobility_item->generic.command = mate_da_xml_get_string (element, "command"); + mobility_item->generic.icon_name = mate_da_xml_get_string (element, "icon-name"); + + mobility_item->run_at_startup = mate_da_xml_get_bool (element, "run-at-startup"); + + capplet->mobility_ats = g_list_append (capplet->mobility_ats, mobility_item); + } + else + g_free (executable); + } + } + } + } + + xmlFreeDoc (xml_doc); +} + +void mate_da_xml_load_list(MateDACapplet* capplet) +{ + GDir* app_dir = g_dir_open(MATECC_APPS_DIR, 0, NULL); + + if (app_dir != NULL) + { + const gchar* extra_file; + gchar* filename; + + while ((extra_file = g_dir_read_name(app_dir)) != NULL) + { + filename = g_build_filename(MATECC_APPS_DIR, extra_file, NULL); + + if (g_str_has_suffix(filename, ".xml")) + { + mate_da_xml_load_xml(capplet, filename); + } + + g_free(filename); + } + + g_dir_close(app_dir); + } +} + +void +mate_da_xml_free (MateDACapplet *capplet) +{ + g_list_foreach (capplet->web_browsers, (GFunc) mate_da_web_item_free, NULL); + g_list_foreach (capplet->mail_readers, (GFunc) mate_da_simple_item_free, NULL); + g_list_foreach (capplet->terminals, (GFunc) mate_da_term_item_free, NULL); + g_list_foreach (capplet->media_players, (GFunc) mate_da_simple_item_free, NULL); + g_list_foreach (capplet->visual_ats, (GFunc) mate_da_visual_item_free, NULL); + g_list_foreach (capplet->mobility_ats, (GFunc) mate_da_mobility_item_free, NULL); + + g_list_free (capplet->web_browsers); + g_list_free (capplet->mail_readers); + g_list_free (capplet->terminals); + g_list_free (capplet->media_players); + g_list_free (capplet->visual_ats); + g_list_free (capplet->mobility_ats); + + g_object_unref (capplet->builder); + g_free (capplet); +} diff --git a/capplets/default-applications/mate-da-xml.h b/capplets/default-applications/mate-da-xml.h new file mode 100644 index 00000000..b209355c --- /dev/null +++ b/capplets/default-applications/mate-da-xml.h @@ -0,0 +1,27 @@ +/* + * Authors: Luca Cavalli + * + * Copyright 2005-2006 Luca Cavalli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _MATE_DA_XML_H_ +#define _MATE_DA_XML_H_ + +void mate_da_xml_load_list (MateDACapplet *capplet); +void mate_da_xml_free (MateDACapplet *capplet); + +#endif diff --git a/capplets/default-applications/mate-default-applications-properties.ui b/capplets/default-applications/mate-default-applications-properties.ui new file mode 100644 index 00000000..4edc2968 --- /dev/null +++ b/capplets/default-applications/mate-default-applications-properties.ui @@ -0,0 +1,1518 @@ + + + + + + 5 + Preferred Applications + False + dialog + False + + + True + + + True + True + 5 + + + True + 12 + 18 + + + True + 6 + + + True + 0 + Web Browser + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 4 + 3 + 12 + 6 + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + 3 + 4 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + Open link with web browser _default + True + False + True + False + True + True + True + + + 3 + GTK_FILL + + + + + + Open link in new _tab + True + False + True + False + True + True + web_browser_default_radiobutton + + + 3 + 2 + 3 + GTK_FILL + + + + + + Open link in new _window + True + False + True + False + True + True + web_browser_default_radiobutton + + + 3 + 1 + 2 + GTK_FILL + + + + + + True + False + 0 + C_ommand: + True + web_browser_command_entry + + + 3 + 4 + GTK_FILL + + + + + + False + False + 1 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + 6 + + + True + 0 + Mail Reader + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 3 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + mail_reader_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + 6 + + + True + 0 + Instant Messenger + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 3 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + messenger_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 2 + + + + + + + True + Internet + + + False + + + + + True + 12 + 18 + + + 6 + + + True + 0 + Image Viewer + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 3 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + image_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + 6 + + + True + 0 + Multimedia Player + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 3 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + media_player_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + 6 + + + True + 0 + Video Player + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 3 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + video_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 2 + + + + + 1 + + + + + True + Multimedia + + + 1 + False + + + + + True + 12 + 18 + + + 6 + + + True + 0 + Text Editor + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 3 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + text_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + Run in t_erminal + True + False + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + 6 + + + True + 0 + Terminal Emulator + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + True + 2 + 2 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + terminal_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + True + False + 0 + E_xecute flag: + True + terminal_exec_flag_entry + + + 1 + 2 + GTK_FILL + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + + + + 2 + + + + + True + System + + + 2 + False + + + + + True + 12 + 18 + + + True + 6 + + + True + 0 + Visual + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + _Run at start + True + True + False + True + True + + + 1 + + + + + True + 2 + 12 + 6 + + + True + False + 0 + C_ommand: + True + image_command_entry + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + 2 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + 6 + + + True + 0 + Mobility + + + + + + False + False + 0 + + + + + True + 12 + + + True + 0 + + + False + False + 0 + + + + + True + 6 + + + True + + + False + 0 + + + + + Run at st_art + True + True + False + True + True + + + 1 + + + + + True + 2 + 12 + 6 + + + True + False + 0 + Co_mmand: + True + + + GTK_FILL + + + + + + True + True + All %s occurrences will be replaced with actual link + + + 1 + 2 + + + + + + 2 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + 3 + + + + + True + Accessibility + + + 3 + False + + + + + False + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + helpbutton1 + closebutton1 + + + diff --git a/capplets/default-applications/mate-default-applications.pc.in b/capplets/default-applications/mate-default-applications.pc.in new file mode 100644 index 00000000..1bdae900 --- /dev/null +++ b/capplets/default-applications/mate-default-applications.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +datarootdir=@datarootdir@ +datadir=@datadir@ +pkgdatadir=${datadir}/@PACKAGE@ +defappsdir=${pkgdatadir}/default-apps + +Name: mate-default-applications +Description: Default MATE applications configuration +Version: @VERSION@ + diff --git a/capplets/default-applications/mate-default-applications.xml.in b/capplets/default-applications/mate-default-applications.xml.in new file mode 100644 index 00000000..4bd2f8b9 --- /dev/null +++ b/capplets/default-applications/mate-default-applications.xml.in @@ -0,0 +1,483 @@ + + + + + + + + + <_name>Opera + opera + opera %s + opera + false + true + opera -newpage %s + opera -newwindow %s + + + <_name>Debian Sensible Browser + sensible-browser + sensible-browser %s + + false + false + + + <_name>Epiphany Web Browser + epiphany + epiphany %s + web-browser + false + true + epiphany --new-tab %s + epiphany --new-window %s + + + <_name>Galeon + galeon + galeon %s + galeon + false + true + galeon -n %s + galeon -w %s + + + <_name>Encompass + encompass + encompass %s + encompass + false + false + + + <_name>Firebird + mozilla-firebird + mozilla-firebird %s + + false + true + mozilla-firebird -remote "openurl(%s,new-tab)" + mozilla-firebird -remote "openurl(%s,new-window)" + + + <_name>Firefox + firefox + firefox %s + firefox + false + true + firefox -new-tab "%s" + firefox -new-window "%s" + + + <_name>Iceweasel + iceweasel + iceweasel %s + iceweasel + false + true + iceweasel -new-tab "%s" + iceweasel -new-window "%s" + + + <_name>Mozilla 1.6 + mozilla-1.6 + mozilla-1.6 %s + mozilla-icon + false + true + mozilla-1.6 -remote "openurl(%s,new-tab)" + mozilla-1.6 -remote "openurl(%s,new-window)" + + + <_name>Mozilla + mozilla + mozilla %s + mozilla-icon + false + true + mozilla -remote "openurl(%s,new-tab)" + mozilla -remote "openurl(%s,new-window)" + + + <_name>SeaMonkey + seamonkey + seamonkey %s + seamonkey + false + true + seamonkey -remote "openurl(%s,new-tab)" + seamonkey -remote "openurl(%s,new-window)" + + + <_name>Iceape + iceape + iceape %s + iceape + false + true + iceape -remote "openurl(%s,new-tab)" + iceape -remote "openurl(%s,new-window)" + + + <_name>Netscape Communicator + netscape + netscape %s + netscape + false + true + netscape -remote "openurl(%s,new-tab)" + netscape -remote "openurl(%s,new-window)" + + + <_name>Konqueror + konqueror + konqueror %s + konqueror + false + false + + + <_name>Midori + midori + midori %s + web-browser + false + false + + + + + + <_name>Evolution Mail Reader + evolution + evolution %s + evolution + false + + + <_name>Balsa + balsa + balsa -m %s + mate-balsa2 + false + + + <_name>KMail + kmail + kmail %s + kmail + false + + + <_name>Icedove + icedove + icedove %s + icedove + false + + + <_name>Thunderbird + thunderbird + thunderbird %s + thunderbird + false + + + <_name>Mozilla Thunderbird + mozilla-thunderbird + mozilla-thunderbird %s + thunderbird + false + + + <_name>Mozilla Mail + mozilla + mozilla -mail %s + mozilla-mail-icon + false + + + <_name>SeaMonkey Mail + seamonkey + seamonkey -mail %s + seamonkey + false + + + <_name>Iceape Mail + iceape + iceape -mail %s + iceape + false + + + <_name>Mutt + mutt + mutt %s + mate-mime-application-x-executable + true + + + <_name>Claws Mail + claws-mail + claws-mail --compose %s + claws-mail + false + + + <_name>Sylpheed-Claws + sylpheed-claws + sylpheed-claws --compose %s + sylpheed + false + + + <_name>Sylpheed + sylpheed + sylpheed --compose %s + sylpheed + false + + + + + + + <_name>Mate File Manager + caja + false + caja %s + file-manager + + + + <_name>Nautilus + nautilus + false + nautilus %s + nautilus + + + + <_name>Thunar + thunar + false + thunar %s + thunar + + + + + + + <_name>Debian Terminal Emulator + x-terminal-emulator + x-terminal-emulator + mate-mime-application-x-executable + -e + + + <_name>MATE Terminal + mate-terminal + mate-terminal + utilities-terminal + -x + + + <_name>GNOME Terminal + gnome-terminal + gnome-terminal + gnome-terminal + -x + + + <_name>Terminator + terminator + terminator + terminator + -x + + + <_name>Standard XTerminal + xterm + xterm + mate-mime-application-x-executable + -e + + + <_name>NXterm + nxterm + nxterm + mate-mime-application-x-executable + -e + + + <_name>RXVT + rxvt + rxvt + mate-mime-application-x-executable + -e + + + <_name>aterm + aterm + aterm + mate-mime-application-x-executable + -e + + + <_name>ETerm + ETerm + ETerm + mate-mime-application-x-executable + -e + + + <_name>Konsole + konsole + konsole + konsole + -e + + + <_name>Terminal + terminal + terminal + utilities-terminal + -e + + + <_name>Sakura + sakura + sakura + terminal-tango + -e + + + + + + <_name>Banshee Music Player + banshee + banshee + music-player-banshee + false + + + <_name>Muine Music Player + muine + muine + muine + false + + + <_name>Rhythmbox Music Player + rhythmbox + rhythmbox + rhythmbox + false + + + <_name>Totem Movie Player + totem + totem + totem + false + + + <_name>Listen + listen + listen + listen + false + + + + + + <_name>Orca + orca + orca + orca + false + + + <_name>Orca with Magnifier + orca + orca -e magnifier + orca + false + + + <_name>Linux Screen Reader + lsr + lsr + lsr + false + + + <_name>Linux Screen Reader with Magnifier + lsr + lsr -p mag + lsr + false + + + <_name>Gnopernicus + gnopernicus + gnopernicus + icon-accessibility + false + + + <_name>Gnopernicus with Magnifier + gnopernicus + gnopernicus -m + icon-accessibility + false + + + <_name>MATE Magnifier without Screen Reader + magnifier + magnifier -m + mate-searchtool + false + + + <_name>KDE Magnifier without Screen Reader + kmag + kmag + mate-searchtool + false + + + + + + + <_name>MATE OnScreen Keyboard + gok + gok + accessibility-keyboard-capplet + false + + + <_name>Dasher + dasher + dasher + mate-searchtool + false + + + onBoard + onboard + onboard + onboard + false + + + + diff --git a/capplets/display/Makefile.am b/capplets/display/Makefile.am new file mode 100644 index 00000000..70f417c7 --- /dev/null +++ b/capplets/display/Makefile.am @@ -0,0 +1,78 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = display + +uidir = $(pkgdatadir)/ui +dist_ui_DATA = display-capplet.ui + +bin_PROGRAMS = mate-display-properties + +sbin_PROGRAMS = mate-display-properties-install-systemwide + +mate_display_properties_SOURCES = \ + xrandr-capplet.c \ + scrollarea.c \ + foo-marshal.c \ + scrollarea.h \ + foo-marshal.h + +mate_display_properties_LDFLAGS = -export-dynamic +mate_display_properties_LDADD = \ + $(top_builddir)/capplets/common/libcommon.la \ + $(DISPLAY_CAPPLET_LIBS) + +mate_display_properties_install_systemwide_SOURCES = \ + mate-display-properties-install-systemwide.c + +mate_display_properties_install_systemwide_LDADD = \ + $(GLIB_LIBS) + +polkit_policydir = $(datadir)/polkit-1/actions +dist_polkit_policy_DATA = \ + org.mate.randr.policy + +# You will need a recent intltool or the patch from this bug +# http://bugzilla.gnome.org/show_bug.cgi?id=462312 +@INTLTOOL_POLICY_RULE@ + +@INTLTOOL_DESKTOP_RULE@ + +icons16dir = $(datadir)/icons/hicolor/16x16/apps +dist_icons16_DATA = icons/16x16/mate-preferences-desktop-display.png +icons22dir = $(datadir)/icons/hicolor/22x22/apps +dist_icons22_DATA = icons/22x22/mate-preferences-desktop-display.png +icons24dir = $(datadir)/icons/hicolor/24x24/apps +dist_icons24_DATA = icons/24x24/mate-preferences-desktop-display.png +icons32dir = $(datadir)/icons/hicolor/32x32/apps +dist_icons32_DATA = icons/32x32/mate-preferences-desktop-display.png +iconssvgdir = $(datadir)/icons/hicolor/scalable/apps +dist_iconssvg_DATA = icons/scalable/mate-preferences-desktop-display.svg + +desktopdir = $(datadir)/applications +Desktop_in_files = display-properties.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +INCLUDES = $(DISPLAY_CAPPLET_CFLAGS) \ + $(MATECC_CAPPLETS_CFLAGS) \ + -DSBINDIR="\"$(sbindir)\"" \ + -DUIDIR="\"$(uidir)\"" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) + +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor +install-data-hook: update-icon-cache +uninstall-hook: update-icon-cache +update-icon-cache: + @-if test -z "$(DESTDIR)"; then \ + echo "Updating Gtk icon cache."; \ + $(gtk_update_icon_cache); \ + else \ + echo "*** Icon cache not updated. After (un)install, run this:"; \ + echo "*** $(gtk_update_icon_cache)"; \ + fi + +EXTRA_DIST = org.mate.randr.policy.in +DISTCLEANFILES = org.mate.randr.policy + +-include $(top_srcdir)/git.mk diff --git a/capplets/display/TODO b/capplets/display/TODO new file mode 100644 index 00000000..08f9e21b --- /dev/null +++ b/capplets/display/TODO @@ -0,0 +1,837 @@ +Highlevel overview: + +Tablet rotation things +only when there is a tablet attached. + +Here is the OS X Display menu: + + Detect Displays + Turn on mirroring + -------------------------- + SyncMaster + - 1280 x 1024, 60 Hz, Millions + - 1344 x ... + -------------------------------- + Color LCD + - 1024 x 1024 ... + -------------------------- + Displays Preferences + + Color LCD means "laptop panel". + +- GTK+ work. + + Allow applications to be notified whenever monitors are added + or removed. Allow applications to get more detailed + information about the connected monitors. + + The main complication is that XRRGetScreenResources() is very + slow. We could call it only when the X server sends an event, + but it's not desirable to have every application freeze for + half a second. And certainly not desirable to have the X + server block for n * 0.5 seconds. + + With the X server work below we should be fine just calling + XRRGetScreenResources on startup and in response to events. + +- X server work: + + X server needs to poll for whether a monitor is plugged + in. Whenever it detects a change, it should do an EDID query, + and cache the resulting information. That way XRRGetScreenResources() + can be the speed of a normal roundtrip. It's desirable that + normal client requests can still be processed during the EDID + querying, but only a nice-to-have. + + Drivers need to work reliably. There could be substantial work + here. For F9, possibly only the Intel driver can be made to + work. + + Interrupts and events must be generated whenever something changes + about the outputs, if necessary by polling. + + Events must be emitted whenever something changes, including when + the reason for the change is a manual change. + + The maximum framebuffer must be dynamically changable. + +- Control panel work: + Capplet needs to be written. The main complications: + + - It needs to pay attention to events from the X server + and update itself, ie., add show new monitors if they become + available when the applet is shown. + + - It needs to store information under a key computed + from a monitor identifier. The complication here is that + it's not completely clear how to do this in MateConf. + + - Would probably be worthwhile to drop libmate/libmateui from + the craplets. + +- Marco work: + - Marco is already Xinerama aware, but it needs to update itself + when monitors come and go. + +- MATE panel work: + - Is already Xinerama aware, but needs to listen and update itself + when monitors change. + +- Evince work: + - Make sure it deals sensibly with multiple monitors + +- OpenOffice work: + - Make sure it deals sensibly with multiple monitors + +- An Xlib call to just return all the available information would be + useful. At the moment we have to do a bunch of roundtrips to + get the information. This is a would-be-nice though. + +- A dbus service could be written that pops up the applet whenever a + monitor. It should only pop up if the new monitor is unknown. This + is at best a nice-to-have, and low priority in my opinion. + + +******************* Marco + +Havoc: + +> I was just talking to bryan about this and "helping" him design it ;-) + +> But I wanted to be sure and lobby for a fix window managers +> need. Basically right now the WM can't tell "physical" from +> "logical" monitors. + +> A "logical" monitor is a desktop; it has its own panel, windows +> maximize to it, etc. + +> A "physical" monitor is a piece of hardware. + +> Sometimes people want to combine physical monitors into a video wall +> or just two monitors treated as one. Or at least a couple of noisy +> people in bugzilla want to do this. + +> When people talk about a "Xinerama aware" app or WM they usually +> mean that all physical monitors are treated as logical monitors, +> while lack of Xinerama-aware means treating the entire X screen (all +> physical monitors) as one logical monitor. + +> The problem is that the setting for "ignore Xinerama" or "don't be +> Xinerama aware" should be global to the desktop (GTK, all apps, WM) +> and should not be a window manager setting. + +> Bryan thought people who wanted non-Xinerama-aware should just use +> fvwm, which may be right, but what I'd say is that if there is any +> setting for this, it should be desktop-global and in this monitor +> config dialog. + +> It should not be a marco or Compiz option, but in some way an X +> option in short. The implementation could be either an X server +> feature or an EWMH hint or whatever, but it should be controlled by +> the monitor config dialog and used by apps, GTK, etc. in addition to +> used by the WM. + +> People tend to insist this should be a WM option, but that's just +> busted, since GTK and apps also have Xinerama-awareness features. + + +******************* EDID + +edid-decode enhancements: + +- Rejects years <= 0x0f for all versions, but this should only be done + for monitors claiming conformance to 1.4 (since 1.4 was released in + 2006). A monitor produced in 2005 should have 0x0f - it's the only + reasonable thing to do. + +- Uses 0x80 as the conformance mask for 1.4, should be 0 + +- Should read from stdin + +- Should parse xrandr -verbose output more robustly + +- Color depth computation is wrong. It uses the formula + + (edid[0x14] >> 3) + 2 + + The correct formula to use is + + (edid[0x14] & 0x70) >> 3 + 4 + +- + +-=-=-=- +Computing a display name from EDID information: + + vendor = lookup_vendor (code); + + if (dsc_product && !is_gobbledigook (dsc_product)) + { + if (vendor && !fuzzy_string_search (vendor, dsc_product)) + prepend (vendor); + } + else + { + if (vendor) + append (vendor); + else + append ("Unknown"); + } + + if (has size) + { + convert_to_inches() + + append (" %d\"", inches) + } + +(Does this internationalize at all)? + +We also need the ability to get laptop names. The laptop panel may report +a manufacturer that has nothing to do with the laptop manufacturer. + +Needed XRandr output properties: + +- Modes that the monitor supports, or enough information that the + client can go throught the list of modes for the relevant + CRTC/Outputs and filter those out that the monitor can't support. + +- The preferred mode, if any. Also useful if we could get a "strongly + preferred" indication if it's an LCD with a fixed resolution. + +- Sufficient information that a fairly specific identifier can be + computed. The algorithm the client should use is: + + 1 Have we seen exactly this monitor before? If yes, use + settings for that. + + 2 Have we seen a monitor with similar specs before? If yes, + use settings for that. (But don't save, unless the user + changes the settings). + + 3 Otherwise, use some reasonable default for the monitor and + save it. + + A setting should only be used if the CRTC/Output allows it. Ie,. if + a user has installed a new video card, then previously-used settings + may no longer apply, so this must be checked every time. + + (1) Implies that we really need a globally unique identifier for + monitors. (2) is useful in an enterprise setting, but not absolutely + critical, since (3) would still handle the majority of cases. + + There is a question here: Where are machine specific preferences + stored? Havoc mentions three possibilities here: + + http://mail.gnome.org/archives/matecc-list/2001-October/msg00023.html + + I'm not sure if any of them are implementable at this point. Also + (1) may mostly take care of the problem. + + + Usecases: + + 1. Fixed setup with some number of monitors. + - They should be set to the correct mode on login. + Note that this involves setting the right position in the + framebuffer too. + + What if someone swaps two monitors? Users are going to expect + that the images will switch position. + + 2. Laptop being moved between home and work + - Setups should be detected and the correct mode set, at least on + login, but ideally when you put the laptop into the docking + station. + + 3. Laptop gets projector plugged in. + + Note the same model monitor can be used in two different ways. Ie., + at home, it's being used at one resolution, at work the same type of + monitor is used at a different resolution. + + Simple solution: + + - The on-disk database is just a list of monitors. Each monitor has an + associated mode. This has these problems: + - If someone uses the same monitor model in two different ways. + - If someone swaps the monitors around + + Better solution + + - The on-disk database is a list of configurations, where a + configuration is a list of monitors and what outputs they are + connected to, and the position in the framebuffer. + + - Picking a default configuration is then a matter of selecting the + closest existing configuration from the database. + + - If the stored configuration is a subset of the existing, + then use that - then pick the best mode available for the + rest of the monitors + + - If the stored configuration is a superset of the existing, + then use the projection of the configuration onto the monitors. + + - Pick the configuration with the most overlap in monitors. + Although, if a configuration differs only in what outputs + they are connected to, then those outputs should probably + get their original modes set. + + - Or maybe simply: + + - If there is an exact match, use it, if not, pick a default. + + - Picking a new default must never change the mode of any existing + output. + +******************* Capplet + +Somehow the applet will find out that a new monitor is plugged in +(either through notification, or through a refresh button). When this +happens, this monitor is looked up in a database and if it is found, +some suitable mode is set. + +Restrictions on the modes: + +- Monitors that are already plugged in should not get their mode + changed just because a new monitor is plugged in. + +- If the exact configuration of monitors is known, and all the old + monitors have the same mode as the known configuration, then just use + the known configuration. Also do this, if the configuration is a + subset of something known. + +- Otherwise, if the configuration is a subset of a known configuration + where the only difference is that existing monitors have different + modes, then try and convert that mode to something we can know + about. Maybe configurations should be stored in terms of edges that + line up. + +- Otherwise, just pick some good default for the mode, probably based + on the EDID prferred mode if possible. By default cloning is + probably best. + +- How do virtual desktops interact with this? + + +g-s-d: + +- On startup + + - It reads the configuration file into memory + + capplet --configure + + - It gathers the existing configuration from randr + + - If the existing config is in the file, set that mode + +- On changes, including changes to the config file [this is crack] + + - Reread configuration file + + - Compare new configuration to database, if it is there, set the + mode as appropriate + + - If a monitor was added, pop up a bubble + + capplet --show-bubble + + capplet --set-mode + +capplet + +- On changes + + - Update GUI + +- When user changes something, + + - Write configuration to file + + - Signal gsd somehow + +Schemes: + - configuration file changes + - randr code will have to be shared between gcc and gsd + + - binary installed by gcc + - something will still have to listen for changes to pop + up the notification bubble. + +Structure of capplet: + +- There is a database on disk with monitors and their corresponding + settings. + +- On startup, this database is read into memory. When the user accepts + new settings, it is written back to disk. + +- When something changes about the settings + + - If new configuration is in the database, use that mode + + - Else, find all outputs that are now connected but weren't before, + and set a default mode for them. + + - If GUI is running, update graphics. + + + - Notification thing: + - if + + - if the new configuration is found in the database, use it + + and added if they are not already there. Initial settings are + 1 what the output is already doing, if anything + 2 based on an existing sufficiently similar monitor, if possible + 3 some reasonable default. + +- When the user changes settings in the GUI, the corresponding monitor + in the database is updated. + +- Whenever the GUI settings change, for all displayed monitors the + possible modes are recomputed. + +- Whenever a new monitor is selected in the GUI, it first gets all its + possible modes computed based on the selections on other + outputs. Then, if the possible modes include the existing choice of + resolution, that is selected. + + Actually, + + - initially, the settings are copied from the current settings + + - whenever a gui setting changes for a monitor, all the other + monitors get their list of choices set to whatever is possible + given the chocie for the current monitor. A 'desired mode' is + maintained, and the closest choice to that is displayed. Whenever + the user actively selects something, that becomes the desired mode + for that monitor. + +- Required + + - Generate all outputs that are newly connected + + foreach_newly_connected (Configuration *before, Configuration *after, + OutputFunc); + + - A way to generate the best mode for a connected output + + existing best_mode() can probably be used + + - Given a list of modes, pick the one closest to a given mode. + + (a possibility here is: pick an exact match, if that's + impossible, then pick the best one with the same + width/height, if that's impossible, then just pick the + best mode on the list). + + - For a configuation, fix the mode for a subset of the outputs, then + list the combinations for the rest of the outputs. + + An obvious possibility here is to simply list all possibilities, + then weed out those that don't work. Is this too expensive? + It might be. + +Structure of login time program: + +- The configuration database is read + +- The current hardware configuration is generated + +- If the current configuration is found in the database, that mode is set. + +- If it isn't found, then nothing changes. + + This could just be mate-screen-resolution-capplet --reset + +******************* Things that need to be done to the xrandr.patch: + +=== + +XRRGetScreenResources() is a roundtrip and very slow (~0.5 s). GTK+ +needs to keep information up-to-date by tracking events rather than +calling this function. In fact we probably can't call it at all unless +its performance improves significantly. + +If EDID processing really has to be this slow, and we can't get +interrupts when monitors are plugged in, then we have a problem, +because we can't do anything this expensive once per second. + + +Detailed notes (but most of the patch should be rewritten): + + +=== FIXME in gdkscreen-x11.c in get_width_mm() + +/* monitor pixel width / screen pixel width * screen_physical width */ + + + +=== Check for 1.2 library + +The patch should check that the 1.2 version of the XRandR library is +available before using the functions. A possibility is to not use any +RandR unless 1.2 is available, another is to conditionalize the code. + +The most sane thing is probably to just require 1.2. + +On the other hand, installing a newer gtk+ on a system with older X is +probably not that unusual, so maybe it's better to do the full 1.0, +vs. 1.1 vs 1.2 check. + +For now it just requires 1.2. + +Actually, this might be fine because the only place where we make use +of a 1.1 library is in the _gdk_x11_screen_size_changed() function, +but there we have a fallback that just updates the variables in the +Screen struct itself. + +So, only defining HAVE_RANDR if we detect 1.2 should be ok. + +=== Monitor information available + +- Subpixel information. This should be set automatically for the fonts and + store under the name of the monitor. If the user changes the font + configuration, that change should also be stored under the monitor name. + +- When a monitor we don't know about is plugged in, a configuration should + be generated: + + - Screen size, computed based on the location of the screens + + - RGBA information + + - Whether the screen has a panel on it + + - If there is a conflict between stored information and EDID, + the stored information wins + + + +New API so far: + +(* monitors_changed) signal +gdk_screen_get_monitor_width_mm() +gdk_screen_get_monitor_height_mm() +gdk_screen_get_monitor_name() => Note this is the output (eg. "DVI-0") + +We should probably also have +get_manufacturer() +get_serial() +get_resolutions() + +etc. + +Should there be a GdkMonitor object that would correspond to an +output? Or maybe GdkOutput? + +screen_list_monitors() + + +*************************** Issues XRandR/Xserver + +- We need polling in the X server, whenever something changes, X must + recompute the information and cache it, then send an event. Note the + situation where the user disconnects and reconnects a monitor within + the polling interval. The event could missed in that case since the polling + cannot do a full EDID query. Difficult to see a way around this. + + Actually, DDC allows random access, so it should be possible to just + read theq vendor id and manufacturer codes. This can be done once a + second without a problem. The polling should be turned off in power + saving mode anyway. + + - Driver work: + + - Intel driver: + + - EDID information is not reported for VGA when the output is not + turned on (i945 laptop). + + - Screen size must be dynamically changable. (No xorg.conf changes + should be required). + + - Make use of ACPI information when possible. + + Adam has code on his freedesktop page. + + - i830 laptop can be put in a state where XRandr reports that no + outputs are connected to a CRTC, but the panel is on. + + - Plug in VGA + - xrandr --auto + - xrandr --output VGA --off + - run chk + - xrandr --verbose will now not report any outputs as turned on + - run chk again - all screens will be turned off + + - Small Sun monitor - an 1152x921 mode is generated, but the + monitor doesn't handle that. The monitor itself only claims to + handle 1152x920. It doesn't look to me like there is anything + in the EDID information that would indicate that it could handle + 1152x921. + + This happens with a radeon as wellso it may be a bug in the + generic X server EDID parsing. The X server apparently + interpretes the standard timing 1152x920 as 1152x921. + + This happens because the X server uses + + hsize * 4 / 5 + + which gives 921 for 1152. By using + + (hsize / 5) * 4 + + you get 920. The 66 Hz version can bet set, the 76 Hz mode gets + sync out of range. (Would be interesting to find out whether the + 1152x920 ModeLine would allow the 76 Hz version to be set). + + This is for the ATI driver as shipped in F8: + + - XRRGetScreenResources() takes half a second. + + - Adam has now removed a workaround that caused some of the slowdown. + + - If a DVI monitor is disconnected, you get "Unknown" for connection + status. + + - If a VGA monitor is plugged in, then EDID information is not + available, even after running xrandr --verbose. The monitor has + to be plugged in at driver startup time, apparently. + + - Logging out and logging back in often results in some random mode being + set. We need mode selection to not be completely screwed up. + Currently it is. + + - The set up at server startup needs to be fixed. *If* randr actually works, + then we might be able to do something sensible. + + - We need to revisit the idea that many monitors have broken EDID data. + This may be less widespread than previously believed. + +- It may be useful to return the connector names as identifiers instead + of relying on UTF-8 strings. Ie., have an enum + + { UNKNOWN, OTHER, DVI, VGA, HDMI, ..., } + + in addition to the string. The difference between UNKNOWN and OTHER is that + UNKNOWN means the driver doesn't know, whereas OTHER means it is something + not listed in the enum (which could be listed in a later version). + +- Mouse cursor should be confined to the visible area. (It is already, I think) + +- It looks like EDID information is only available for one output + even though it is actually read according to the log file. + (nv, intel drivers) + + +********************************* + + + +DONE: + +Server work: + + - i830 laptop incorrectly reports BadMatch when you configure the + CRTC to drive both VGA and LVDS with the 1024x768 mode that both + outputs can handle. (It should return 'failed' if it can't do + that). Same for i945 laptop. It seems as if the same CRTC can't + drive more than one output at the same time on Intel. + + This was a client bug, but the documentation for SetCrtcConfig + should say that BadMatch will be returned if the outputs aren't + clones. + +GTK+ patch is in now. + +=== Add helper function + + ++ if (screen_x11->randr12) ++ { ++ XRRScreenResources *sr; ++ XRROutputInfo *output; ++ gchar *retval; ++ ++ sr = XRRGetScreenResources ( screen_x11->xdisplay, ++ screen_x11->xroot_window ); ++ ++ output = XRRGetOutputInfo ( screen_x11->xdisplay, ++ sr, ++ (RROutput)screen_x11->act_outputs[monitor_num] +); + + Might be worthwhile to factor this out into a + gdk_screen_get_output_info (screen, monitor_num) + helper function ? + +Instead of cutting and pasting all over creation + +* Calling XRRGetScreenResources all the time is not going to fly. It + takes hundreds of milliseconds ... Even if it didn't, it wouldn't + be acceptable to do all those roundtrips. + + +=== Some g_prints left + + +=== Version check + +Should be (maj > 1) || (maj == 1 && min >= 2) + + +=== Grep for TODO + + +=== Setup XRRSelectInput() + + You should call XRRSelectInput() at the same place where you are + calling XSelectInput() right now. The right place to handle the + XRandr events is the huge switch in gdkevents-x11.c:gdk_event_translate + Check out how other extension events are handled there, like + XKB, or XFixes. + + +=== Lots of variable naming issues, such as act_output and noutput + +=== Needs to select the input, and hook it up to the signal + +=== Add version markers to API + +=== API to turn monitors on and off? + +- DPMS not exposed through randr, maybe should be + + - DPMS is presumably a property of either an + output or a CRTC. Logically it's an output. + +- Need events when DPMS happens. Exposing the "screen saving on" on + dbus may not be good enough. + +=== Why does init_multihead_support() start by freeing monitors and +outputs? + +=== Do we disable Xinerama support entirely when 1.2 is in use? + +=== We should expose information about what parts of the screen monitors +are viewing. + +=== Make use of the EDID information? + + +-- details for X server -- + +In nv driver SorSetOutputProperty should return TRUE for unknown +properties. (Like the Intel driver does). + +Detecting plugged in + +- Periodically poll + - + + - One ddc probe takes 5 ms, according to a comment in the intel + driver. Running this twice a second would mean spending 1% of + overall time doing ddc polling, which is almost certainly not + acceptable. + + 1) Async I2C: + + void I2CProbeAsync(..., callback, data); + Bool I2CPending() + void I2CUpdate() + + In Dispatch, call I2CUpdate() + Before going idle, do + + while (I2CPending()) + I2CUpdate() + + Would need + RegisterDispatchFunction() (Is this called Wakeup?) + RegisterIdleFunction() + + Note the idle function should have the option of saying: + "check if something else happened; if not, call me again" and + "ok, I'm done - go idle". Otherwise, we would be blocking for + 5 ms whenever the X server went idle. So actually the idle + function should be + + if (I2CPending()) + { + I2CUpdate(); + return TRUE; /* call me again */ + } + else + { + return FALSE; /* I'm done */ + } + + What happens if another I2C requests come in while an async one + is pending? Most likely we simply finish whatever is going on, + then process the new request. + + What happens if an X request takes so long that we get timeouts on + the i2c bus? Good question. Need to read the VESA ddc spec. + + 2) Run the polling in a separate thread. + + Probably crack. + + 3) Run the polling less, maybe once every three seconds. + +-- details for control panel -- +Screen changes + - Currently it is polling via rw_screen_refresh(), which will always emit + a screen-changed event. In reponse to this event the capplet currently + checks whether anything changed physically about the setup. This means + the capplet can't react to external changes to modes. On the other hand + if it didn't +Disallow combinations that would exceed the screen ranges. + - Note rotations + +Give rw objects stable positions in memory so that they can be cached +across screen_changed events. + +Add Clone Mode + +Drag and drop for the monitors + - 2 dimensional layout + +Store make and model in monitors.xml, then if serial numbers don't +match, fall back to a make and model match. Users with an nfs mounted +home directory should not have to reconfigure for each new system they +log in to. + +Make sure text is scaled correctly + +Need to sanitize naming + RWOutput vs Output - should probably be OutputInfo + rate vs. freq - decide on one + +Should probably reconsider the use of null terminated arrays. +Maybe lists would be better. + +Pick a fixed scale, so that two 1024x768 don't look like two 6x4. + - An alternative would be to draw a checkerboard pattern + below the monitors. + + + +done: + +Add rotation + +Disable panel checkbox for now + +Patch into mate-desktop + +Find out how to share code between gcc and gsd + +Make it assign coordinates correctly + - including computing correct screen size + diff --git a/capplets/display/display-capplet.ui b/capplets/display/display-capplet.ui new file mode 100644 index 00000000..82ed92bd --- /dev/null +++ b/capplets/display/display-capplet.ui @@ -0,0 +1,437 @@ + + + + + + 5 + Monitor Preferences + dialog + False + + + True + vertical + 2 + + + True + 2 + 2 + 12 + 12 + + + True + vertical + 12 + + + True + + + + + + 0 + + + + + True + 12 + + + Sa_me image in all monitors + True + True + False + True + True + + + False + False + 0 + + + + + _Detect monitors + True + True + True + True + + + False + False + end + 1 + + + + + False + False + 1 + + + + + + + True + vertical + + + True + 0 + Panel icon + + + + + + False + False + 0 + + + + + True + 12 + + + _Show monitors in panel + True + True + False + True + True + + + + + False + False + 1 + + + + + 2 + 1 + 2 + + + + + True + 0 + 0 + + + True + 6 + 2 + 12 + 6 + + + True + 0 + _Resolution: + True + resolution_combo + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + Re_fresh rate: + True + refresh_combo + + + 3 + 4 + GTK_FILL + + + + + + True + + + 1 + 2 + 2 + 3 + + + + + + True + + + 1 + 2 + 3 + 4 + + + + + + True + 12 + + + On + True + True + False + True + True + True + + + False + False + 0 + + + + + Off + True + True + False + True + True + monitor_on_radio + + + False + False + 1 + + + + + 2 + 1 + 2 + + + + + + True + + + True + 0 + Monitor + + + + + + + + 2 + + + + + + True + 0 + R_otation: + True + rotation_combo + + + 4 + 5 + GTK_FILL + + + + + + True + liststore1 + + + + 0 + + + + + 1 + 2 + 4 + 5 + + + + + + Include _panel + True + False + True + True + + + 5 + 6 + GTK_FILL + + + + + + True + + + + + + 1 + 2 + 5 + 6 + + + + + + + + + 1 + 2 + + GTK_FILL + + + + + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + True + + + + + Make Default + True + True + True + True + + + False + False + 1 + + + + + gtk-apply + True + True + True + False + True + + + False + False + 2 + + + + + gtk-close + True + True + True + False + True + + + False + False + 3 + + + + + False + end + 0 + + + + + + helpbutton1 + make_default_button + apply_button + button2 + + + + + + + + + + Normal + + + Left + + + Right + + + Upside-down + + + + diff --git a/capplets/display/display-properties.desktop.in.in b/capplets/display/display-properties.desktop.in.in new file mode 100644 index 00000000..bcfb5935 --- /dev/null +++ b/capplets/display/display-properties.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Monitors +_Comment=Change resolution and position of monitors +Exec=mate-display-properties +Icon=mate-preferences-desktop-display +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;HardwareSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Screen resolution +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/display/foo-marshal.c b/capplets/display/foo-marshal.c new file mode 100644 index 00000000..a40b0863 --- /dev/null +++ b/capplets/display/foo-marshal.c @@ -0,0 +1,279 @@ + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:OBJECT,OBJECT (marshal.list:1) */ +void +foo_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + +/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */ +void +foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (gpointer data1, + guint arg_1, + guint arg_2, + guint arg_3, + guint arg_4, + gpointer data2); + register GMarshalFunc_VOID__UINT_UINT_UINT_UINT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_uint (param_values + 3), + g_marshal_value_peek_uint (param_values + 4), + data2); +} + +/* VOID:UINT,UINT (marshal.list:3) */ +void +foo_marshal_VOID__UINT_UINT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1, + guint arg_1, + guint arg_2, + gpointer data2); + register GMarshalFunc_VOID__UINT_UINT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + data2); +} + +/* VOID:BOXED (marshal.list:4) */ + +/* VOID:BOXED,BOXED (marshal.list:5) */ +void +foo_marshal_VOID__BOXED_BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__BOXED_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */ +void +foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_BOXED_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer arg_3, + gpointer data2); + register GMarshalFunc_VOID__POINTER_BOXED_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_BOXED_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + g_marshal_value_peek_pointer (param_values + 3), + data2); +} + +/* VOID:POINTER,POINTER (marshal.list:7) */ +void +foo_marshal_VOID__POINTER_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__POINTER_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); +} + diff --git a/capplets/display/foo-marshal.h b/capplets/display/foo-marshal.h new file mode 100644 index 00000000..4bef795d --- /dev/null +++ b/capplets/display/foo-marshal.h @@ -0,0 +1,67 @@ + +#ifndef __foo_marshal_MARSHAL_H__ +#define __foo_marshal_MARSHAL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* VOID:OBJECT,OBJECT (marshal.list:1) */ +extern void foo_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */ +extern void foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:UINT,UINT (marshal.list:3) */ +extern void foo_marshal_VOID__UINT_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOXED (marshal.list:4) */ +#define foo_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED + +/* VOID:BOXED,BOXED (marshal.list:5) */ +extern void foo_marshal_VOID__BOXED_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */ +extern void foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:POINTER,POINTER (marshal.list:7) */ +extern void foo_marshal_VOID__POINTER_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +#ifdef __cplusplus +} +#endif + +#endif /* __foo_marshal_MARSHAL_H__ */ + diff --git a/capplets/display/icons/16x16/mate-preferences-desktop-display.png b/capplets/display/icons/16x16/mate-preferences-desktop-display.png new file mode 100644 index 00000000..f996ddf5 Binary files /dev/null and b/capplets/display/icons/16x16/mate-preferences-desktop-display.png differ diff --git a/capplets/display/icons/22x22/mate-preferences-desktop-display.png b/capplets/display/icons/22x22/mate-preferences-desktop-display.png new file mode 100644 index 00000000..cc47eec6 Binary files /dev/null and b/capplets/display/icons/22x22/mate-preferences-desktop-display.png differ diff --git a/capplets/display/icons/24x24/mate-preferences-desktop-display.png b/capplets/display/icons/24x24/mate-preferences-desktop-display.png new file mode 100644 index 00000000..49b4e12c Binary files /dev/null and b/capplets/display/icons/24x24/mate-preferences-desktop-display.png differ diff --git a/capplets/display/icons/32x32/mate-preferences-desktop-display.png b/capplets/display/icons/32x32/mate-preferences-desktop-display.png new file mode 100644 index 00000000..95de3eaa Binary files /dev/null and b/capplets/display/icons/32x32/mate-preferences-desktop-display.png differ diff --git a/capplets/display/icons/scalable/mate-preferences-desktop-display.svg b/capplets/display/icons/scalable/mate-preferences-desktop-display.svg new file mode 100644 index 00000000..0679b6b3 --- /dev/null +++ b/capplets/display/icons/scalable/mate-preferences-desktop-display.svg @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Change Resolution + + + Jakub Steiner + + + + + + display + resolution + video + + + + + Andreas Nilsson +Luca Ferretti <elle.uca@libero.it> + + + + http://www.gnome.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/capplets/display/mate-display-properties-install-systemwide.c b/capplets/display/mate-display-properties-install-systemwide.c new file mode 100644 index 00000000..2d074eb8 --- /dev/null +++ b/capplets/display/mate-display-properties-install-systemwide.c @@ -0,0 +1,272 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * mate-display-properties-install-systemwide - Install a RANDR profile for the whole system + * + * Copyright (C) 2010 Novell, Inc. + * + * Authors: Federico Mena Quintero + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYSTEM_RANDR_PATH "/etc/mate-settings-daemon/xrandr" + +static void +usage (const char *program_name) +{ + g_print (_("Usage: %s SOURCE_FILE DEST_NAME\n" + "\n" + "This program installs a RANDR profile for multi-monitor setups into\n" + "a systemwide location. The resulting profile will get used when\n" + "the RANDR plug-in gets run in mate-settings-daemon.\n" + "\n" + "SOURCE_FILE - a full pathname, typically /home/username/.config/monitors.xml\n" + "\n" + "DEST_NAME - relative name for the installed file. This will get put in\n" + " the systemwide directory for RANDR configurations,\n" + " so the result will typically be %s/DEST_NAME\n"), + program_name, + SYSTEM_RANDR_PATH); +} + +static gboolean +is_basename (const char *filename) +{ + if (*filename == '\0') + return FALSE; /* no empty strings, please */ + + for (; *filename; filename++) + if (G_IS_DIR_SEPARATOR (*filename)) + return FALSE; + + return TRUE; +} + +static gboolean +copy_file (int source_fd, int dest_fd) +{ + char buf[1024]; + int num_read; + int num_written; + + while (TRUE) { + char *p; + + num_read = read (source_fd, buf, sizeof (buf)); + if (num_read == 0) + break; + + if (num_read == -1) { + if (errno == EINTR) + continue; + else + return FALSE; + } + + p = buf; + while (num_read > 0) { + num_written = write (dest_fd, p, num_read); + if (num_written == -1) { + if (errno == EINTR) + continue; + else + return FALSE; + } + + num_read -= num_written; + p += num_written; + } + } + + return TRUE; +} + +/* This is essentially a "please copy a file to a privileged location" program. + * We try to be paranoid in the following ways: + * + * - We copy only regular files, owned by the user who called pkexec(1), to + * avoid attacks like "copy a file that I'm not allowed to read into a + * world-readable location". + * + * - We copy only to a well-known directory. + * + * - We try to avoid race conditions. We only fstat() files that we have open + * to avoid files moving under our feet. We only create files in directories + * that we have open. + * + * - We replace the destination file atomically. + */ + +int +main (int argc, char **argv) +{ + uid_t uid, euid; + const char *source_filename; + const char *dest_name; + const char *pkexec_uid_str; + int pkexec_uid; + struct stat statbuf; + int err; + int source_fd; + int dest_fd; + char template[100]; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* We only run as root */ + uid = getuid (); + euid = geteuid (); + if (uid != 0 || euid != 0) { + /* Translators: only able to install RANDR profiles as root */ + g_print ("%s\n", _("This program can only be used by the root user")); + return EXIT_FAILURE; + } + + /* Usage: gsd-xrandr-install-systemwide SOURCE_FILE DEST_NAME */ + + if (argc != 3) { + usage (argv[0]); + return EXIT_FAILURE; + } + + source_filename = argv[1]; + dest_name = argv[2]; + + /* Absolute source filenames only, please */ + + if (!g_path_is_absolute (source_filename)) { + g_print ("%s\n", _("The source filename must be absolute")); + return EXIT_FAILURE; + } + + /* We only copy regular files */ + + source_fd = open (source_filename, O_RDONLY); + if (source_fd == -1) { + err = errno; + + /* Translators: first %s is a filename; second %s is an error message */ + g_print (_("Could not open %s: %s\n"), + source_filename, + g_strerror (err)); + return EXIT_FAILURE; + } + + if (fstat (source_fd, &statbuf) != 0) { + err = errno; + /* Translators: first %s is a filename; second %s is an error message */ + g_print (_("Could not get information for %s: %s\n"), + source_filename, + g_strerror (err)); + return EXIT_FAILURE; + } + + if (!S_ISREG (statbuf.st_mode)) { + g_print (_("%s must be a regular file\n"), + source_filename); + return EXIT_FAILURE; + } + + /* We only copy files that are really owned by the calling user */ + + pkexec_uid_str = g_getenv ("PKEXEC_UID"); + if (pkexec_uid_str == NULL) { + g_print ("%s\n", _("This program must only be run through pkexec(1)")); + return EXIT_FAILURE; + } + + if (sscanf (pkexec_uid_str, "%d", &pkexec_uid) != 1) { + g_print ("%s\n", _("PKEXEC_UID must be set to an integer value")); + return EXIT_FAILURE; + } + + if (statbuf.st_uid != pkexec_uid) { + /* Translators: we are complaining that a file must be really owned by the user who called this program */ + g_print (_("%s must be owned by you\n"), source_filename); + return EXIT_FAILURE; + } + + /* We only accept basenames for the destination */ + + if (!is_basename (dest_name)) { + /* Translators: here we are saying that a plain filename must look like "filename", not like "some_dir/filename" */ + g_print (_("%s must not have any directory components\n"), + dest_name); + return EXIT_FAILURE; + } + + /* Chdir to the destination directory to keep it open... */ + + if (chdir (SYSTEM_RANDR_PATH) != 0) { + g_print (_("%s must be a directory\n"), SYSTEM_RANDR_PATH); + return EXIT_FAILURE; + } + + /* ... and open our temporary destination file right there */ + + strcpy (template, "gsd-XXXXXX"); + dest_fd = g_mkstemp_full (template, O_WRONLY, 0644); + if (dest_fd == -1) { + err = errno; + /* Translators: the first %s/%s is a directory/filename; the last %s is an error message */ + g_print (_("Could not open %s/%s: %s\n"), + SYSTEM_RANDR_PATH, + template, + g_strerror (err)); + return EXIT_FAILURE; + } + + /* Do the copy */ + + if (!copy_file (source_fd, dest_fd)) { + /* If something went wrong, remove the destination file to avoid leaving trash around */ + unlink (template); + return EXIT_FAILURE; + } + + /* Rename to the final filename */ + + if (rename (template, dest_name) != 0) { + err = errno; + unlink (template); + g_print (_("Could not rename %s to %s: %s\n"), + template, + dest_name, + g_strerror (err)); + return EXIT_FAILURE; + } + + /* Whew! We'll leave the final closing of the files to the almighty kernel. */ + + return EXIT_SUCCESS; +} diff --git a/capplets/display/org.mate.randr.policy.in b/capplets/display/org.mate.randr.policy.in new file mode 100644 index 00000000..6e9f65d2 --- /dev/null +++ b/capplets/display/org.mate.randr.policy.in @@ -0,0 +1,29 @@ + + + + + + + MATE Monitor Preferences + http://live.gnome.org/RandR + mate-display-properties + + + <_description>Install multi-monitor settings for the whole system + <_message>Authentication is required to install multi-monitor settings for all users + mate-display-properties + + no + no + auth_admin_keep + + /usr/sbin/mate-display-properties-install-systemwide + + + + diff --git a/capplets/display/scrollarea.c b/capplets/display/scrollarea.c new file mode 100644 index 00000000..9da2fb66 --- /dev/null +++ b/capplets/display/scrollarea.c @@ -0,0 +1,1944 @@ +/* Copyright 2006, 2007, 2008, Soren Sandmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include /* For GDK_PARENT_RELATIVE_BG */ +#include "scrollarea.h" +#include "foo-marshal.h" + +G_DEFINE_TYPE (FooScrollArea, foo_scroll_area, GTK_TYPE_CONTAINER); + +static GtkWidgetClass *parent_class; + +typedef struct BackingStore BackingStore; + +typedef void (* ExposeFunc) (cairo_t *cr, GdkRegion *region, gpointer data); + +#if 0 +static void backing_store_draw (BackingStore *store, + GdkDrawable *dest, + GdkRegion *clip, + int dest_x, + int dest_y); +static void backing_store_scroll (BackingStore *store, + int dx, int dy); +static void backing_store_invalidate_rect (BackingStore *store, + GdkRectangle *rect); +static void backing_store_invalidate_region (BackingStore *store, + GdkRegion *region); +static void backing_store_invalidate_all (BackingStore *store); +static BackingStore *backing_store_new (GdkWindow *window, + int width, int height); +static void backing_store_resize (BackingStore *store, + int width, int height); +static void backing_store_process_updates (BackingStore *store, + ExposeFunc func, + gpointer data); +static void backing_store_free (BackingStore *store); +#endif + +typedef struct InputPath InputPath; +typedef struct InputRegion InputRegion; +typedef struct AutoScrollInfo AutoScrollInfo; + +struct InputPath +{ + gboolean is_stroke; + cairo_fill_rule_t fill_rule; + double line_width; + cairo_path_t *path; /* In canvas coordinates */ + + FooScrollAreaEventFunc func; + gpointer data; + + InputPath *next; +}; + +/* InputRegions are mutually disjoint */ +struct InputRegion +{ + GdkRegion *region; /* the boundary of this area in canvas coordinates */ + InputPath *paths; +}; + +struct AutoScrollInfo +{ + int dx; + int dy; + int timeout_id; + int begin_x; + int begin_y; + double res_x; + double res_y; + GTimer *timer; +}; + +struct FooScrollAreaPrivate +{ + GdkWindow *input_window; + + int width; + int height; + + GtkAdjustment *hadj; + GtkAdjustment *vadj; + int x_offset; + int y_offset; + + int min_width; + int min_height; + + GPtrArray *input_regions; + + AutoScrollInfo *auto_scroll_info; + + /* During expose, this region is set to the region + * being exposed. At other times, it is NULL + * + * It is used for clipping of input areas + */ + GdkRegion *expose_region; + InputRegion *current_input; + + gboolean grabbed; + FooScrollAreaEventFunc grab_func; + gpointer grab_data; + + GdkPixmap *pixmap; + GdkRegion *update_region; /* In canvas coordinates */ +}; + +enum +{ + VIEWPORT_CHANGED, + PAINT, + INPUT, + LAST_SIGNAL, +}; + +static guint signals [LAST_SIGNAL] = { 0 }; + +static void foo_scroll_area_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static gboolean foo_scroll_area_expose (GtkWidget *widget, + GdkEventExpose *expose); +static void foo_scroll_area_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); +static void foo_scroll_area_realize (GtkWidget *widget); +static void foo_scroll_area_unrealize (GtkWidget *widget); +static void foo_scroll_area_map (GtkWidget *widget); +static void foo_scroll_area_unmap (GtkWidget *widget); +static gboolean foo_scroll_area_button_press (GtkWidget *widget, + GdkEventButton *event); +static gboolean foo_scroll_area_button_release (GtkWidget *widget, + GdkEventButton *event); +static gboolean foo_scroll_area_motion (GtkWidget *widget, + GdkEventMotion *event); + +static void +foo_scroll_area_map (GtkWidget *widget) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + + GTK_WIDGET_CLASS (parent_class)->map (widget); + + if (area->priv->input_window) + gdk_window_show (area->priv->input_window); +} + +static void +foo_scroll_area_unmap (GtkWidget *widget) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + + if (area->priv->input_window) + gdk_window_hide (area->priv->input_window); + + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static void +foo_scroll_area_finalize (GObject *object) +{ + FooScrollArea *scroll_area = FOO_SCROLL_AREA (object); + + g_object_unref (scroll_area->priv->hadj); + g_object_unref (scroll_area->priv->vadj); + + g_ptr_array_free (scroll_area->priv->input_regions, TRUE); + + g_free (scroll_area->priv); + + G_OBJECT_CLASS (foo_scroll_area_parent_class)->finalize (object); +} + +static void +foo_scroll_area_class_init (FooScrollAreaClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->finalize = foo_scroll_area_finalize; + widget_class->size_request = foo_scroll_area_size_request; + widget_class->expose_event = foo_scroll_area_expose; + widget_class->size_allocate = foo_scroll_area_size_allocate; + widget_class->realize = foo_scroll_area_realize; + widget_class->unrealize = foo_scroll_area_unrealize; + widget_class->button_press_event = foo_scroll_area_button_press; + widget_class->button_release_event = foo_scroll_area_button_release; + widget_class->motion_notify_event = foo_scroll_area_motion; + widget_class->map = foo_scroll_area_map; + widget_class->unmap = foo_scroll_area_unmap; + + class->set_scroll_adjustments = foo_scroll_area_set_scroll_adjustments; + + parent_class = g_type_class_peek_parent (class); + + signals[VIEWPORT_CHANGED] = + g_signal_new ("viewport_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (FooScrollAreaClass, + viewport_changed), + NULL, NULL, + foo_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GDK_TYPE_RECTANGLE, + GDK_TYPE_RECTANGLE); + + signals[PAINT] = + g_signal_new ("paint", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (FooScrollAreaClass, + paint), + NULL, NULL, + foo_marshal_VOID__POINTER_BOXED_POINTER, + G_TYPE_NONE, 3, + G_TYPE_POINTER, + GDK_TYPE_RECTANGLE, + G_TYPE_POINTER); + + widget_class->set_scroll_adjustments_signal = + g_signal_new ("set_scroll_adjustments", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (FooScrollAreaClass, + set_scroll_adjustments), + NULL, NULL, + foo_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_ADJUSTMENT, + GTK_TYPE_ADJUSTMENT); +} + +static GtkAdjustment * +new_adjustment (void) +{ + return GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); +} + +static void +foo_scroll_area_init (FooScrollArea *scroll_area) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (scroll_area); + + gtk_widget_set_has_window (widget, FALSE); + gtk_widget_set_redraw_on_allocate (widget, FALSE); + + scroll_area->priv = g_new0 (FooScrollAreaPrivate, 1); + scroll_area->priv->width = 0; + scroll_area->priv->height = 0; + scroll_area->priv->hadj = g_object_ref_sink (new_adjustment()); + scroll_area->priv->vadj = g_object_ref_sink (new_adjustment()); + scroll_area->priv->x_offset = 0.0; + scroll_area->priv->y_offset = 0.0; + scroll_area->priv->min_width = -1; + scroll_area->priv->min_height = -1; + scroll_area->priv->auto_scroll_info = NULL; + scroll_area->priv->input_regions = g_ptr_array_new (); + scroll_area->priv->pixmap = NULL; + scroll_area->priv->update_region = gdk_region_new (); + + gtk_widget_set_double_buffered (widget, FALSE); +} + +static void +translate_cairo_device (cairo_t *cr, + int x_offset, + int y_offset) +{ + cairo_surface_t *surface = cairo_get_target (cr); + double dev_x; + double dev_y; + + cairo_surface_get_device_offset (surface, &dev_x, &dev_y); + dev_x += x_offset; + dev_y += y_offset; + cairo_surface_set_device_offset (surface, dev_x, dev_y); +} + +#if 0 +static void +print_region (const char *header, GdkRegion *region) +{ + GdkRectangle *rects; + int n_rects; + int i; + + g_print ("%s\n", header); + + gdk_region_get_rectangles (region, &rects, &n_rects); + for (i = 0; i < n_rects; ++i) + { + GdkRectangle *rect = &(rects[i]); + g_print (" %d %d %d %d\n", + rect->x, rect->y, rect->width, rect->height); + } +} +#endif + +typedef void (* PathForeachFunc) (double *x, + double *y, + gpointer data); + +static void +path_foreach_point (cairo_path_t *path, + PathForeachFunc func, + gpointer user_data) +{ + int i; + + for (i = 0; i < path->num_data; i += path->data[i].header.length) + { + cairo_path_data_t *data = &(path->data[i]); + + switch (data->header.type) + { + case CAIRO_PATH_MOVE_TO: + case CAIRO_PATH_LINE_TO: + func (&(data[1].point.x), &(data[1].point.y), user_data); + break; + + case CAIRO_PATH_CURVE_TO: + func (&(data[1].point.x), &(data[1].point.y), user_data); + func (&(data[2].point.x), &(data[2].point.y), user_data); + func (&(data[3].point.x), &(data[3].point.y), user_data); + break; + + case CAIRO_PATH_CLOSE_PATH: + break; + } + } +} + +typedef struct +{ + double x1, y1, x2, y2; +} Box; + +#if 0 +static void +update_box (double *x, double *y, gpointer data) +{ + Box *box = data; + + if (*x < box->x1) + box->x1 = *x; + + if (*y < box->y1) + box->y1 = *y; + + if (*y > box->y2) + box->y2 = *y; + + if (*x > box->x2) + box->x2 = *x; +} +#endif + +#if 0 +static void +path_compute_extents (cairo_path_t *path, + GdkRectangle *rect) +{ + if (rect) + { + Box box = { G_MAXDOUBLE, G_MAXDOUBLE, G_MINDOUBLE, G_MINDOUBLE }; + + path_foreach_point (path, update_box, &box); + + rect->x = box.x1; + rect->y = box.y1; + rect->width = box.x2 - box.x1; + rect->height = box.y2 - box.y1; + } +} +#endif + +static void +input_path_free_list (InputPath *paths) +{ + if (!paths) + return; + + input_path_free_list (paths->next); + cairo_path_destroy (paths->path); + g_free (paths); +} + +static void +input_region_free (InputRegion *region) +{ + input_path_free_list (region->paths); + gdk_region_destroy (region->region); + + g_free (region); +} + +static void +get_viewport (FooScrollArea *scroll_area, + GdkRectangle *viewport) +{ + GtkAllocation allocation; + GtkWidget *widget = GTK_WIDGET (scroll_area); + + gtk_widget_get_allocation (widget, &allocation); + + viewport->x = scroll_area->priv->x_offset; + viewport->y = scroll_area->priv->y_offset; + viewport->width = allocation.width; + viewport->height = allocation.height; +} + +static void +allocation_to_canvas (FooScrollArea *area, + int *x, + int *y) +{ + *x += area->priv->x_offset; + *y += area->priv->y_offset; +} + +static void +clear_exposed_input_region (FooScrollArea *area, + GdkRegion *exposed) /* in canvas coordinates */ +{ + int i; + GdkRegion *viewport; + GdkRectangle allocation; + + gtk_widget_get_allocation (GTK_WIDGET (area), &allocation); + allocation.x = 0; + allocation.y = 0; + allocation_to_canvas (area, &allocation.x, &allocation.y); + viewport = gdk_region_rectangle (&allocation); + gdk_region_subtract (viewport, exposed); + + for (i = 0; i < area->priv->input_regions->len; ++i) + { + InputRegion *region = area->priv->input_regions->pdata[i]; + + gdk_region_intersect (region->region, viewport); + + if (gdk_region_empty (region->region)) + { + input_region_free (region); + g_ptr_array_remove_index_fast (area->priv->input_regions, i--); + } + } + + gdk_region_destroy (viewport); + +#if 0 + path = region->paths; + while (path != NULL) + { + GdkRectangle rect; + + path_compute_extents (path->path, &rect); + + if (gdk_region_rect_in (area->priv->expose_region, &rect) == GDK_OVERLAP_RECTANGLE_IN) + g_print ("we would have deleted it\n"); +#if 0 + else + g_print ("nope (%d %d %d %d)\n", ); +#endif + + path = path->next; + } + + /* FIXME: we should also delete paths (and path segments) + * completely contained in the expose_region + */ + } +#endif +} + +static void +setup_background_cr (GdkWindow *window, + cairo_t *cr, + int x_offset, + int y_offset) +{ + GdkWindowObject *private = (GdkWindowObject *)window; + + if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent) + { + x_offset += private->x; + y_offset += private->y; + + setup_background_cr (GDK_WINDOW (private->parent), cr, x_offset, y_offset); + } + else if (private->bg_pixmap && + private->bg_pixmap != GDK_PARENT_RELATIVE_BG && + private->bg_pixmap != GDK_NO_BG) + { + gdk_cairo_set_source_pixmap (cr, private->bg_pixmap, -x_offset, -y_offset); + } + else + { + gdk_cairo_set_source_color (cr, &private->bg_color); + } +} + +static void +initialize_background (GtkWidget *widget, + cairo_t *cr) +{ + setup_background_cr (gtk_widget_get_window (widget), cr, 0, 0); + + cairo_paint (cr); +} + +static void +clip_to_region (cairo_t *cr, GdkRegion *region) +{ + int n_rects; + GdkRectangle *rects; + + gdk_region_get_rectangles (region, &rects, &n_rects); + + cairo_new_path (cr); + while (n_rects--) + { + GdkRectangle *rect = &(rects[n_rects]); + + cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height); + } + cairo_clip (cr); + + g_free (rects); +} + +static void +simple_draw_drawable (GdkDrawable *dst, + GdkDrawable *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + GdkGC *gc = gdk_gc_new (dst); + + gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height); + + g_object_unref (gc); +} + +static gboolean +foo_scroll_area_expose (GtkWidget *widget, + GdkEventExpose *expose) +{ + FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget); + cairo_t *cr; + GdkRectangle extents; + GdkRegion *region; + int x_offset, y_offset; + GdkGC *gc; + GtkAllocation widget_allocation; + GdkWindow *window = gtk_widget_get_window (widget); + + /* I don't think expose can ever recurse for the same area */ + g_assert (!scroll_area->priv->expose_region); + + /* Note that this function can be called at a time + * where the adj->value is different from x_offset. + * Ie., the GtkScrolledWindow changed the adj->value + * without emitting the value_changed signal. + * + * Hence we must always use the value we got + * the last time the signal was emitted, ie., + * priv->{x,y}_offset. + */ + + x_offset = scroll_area->priv->x_offset; + y_offset = scroll_area->priv->y_offset; + + scroll_area->priv->expose_region = expose->region; + + /* Setup input areas */ + clear_exposed_input_region (scroll_area, scroll_area->priv->update_region); + + scroll_area->priv->current_input = g_new0 (InputRegion, 1); + scroll_area->priv->current_input->region = gdk_region_copy (scroll_area->priv->update_region); + scroll_area->priv->current_input->paths = NULL; + g_ptr_array_add (scroll_area->priv->input_regions, + scroll_area->priv->current_input); + + region = scroll_area->priv->update_region; + scroll_area->priv->update_region = gdk_region_new (); + + /* Create cairo context */ + cr = gdk_cairo_create (scroll_area->priv->pixmap); + translate_cairo_device (cr, -x_offset, -y_offset); + clip_to_region (cr, region); + initialize_background (widget, cr); + + /* Create regions */ + gdk_region_get_clipbox (region, &extents); + + g_signal_emit (widget, signals[PAINT], 0, cr, &extents, region); + + /* Destroy stuff */ + cairo_destroy (cr); + + scroll_area->priv->expose_region = NULL; + scroll_area->priv->current_input = NULL; + + /* Finally draw the backing pixmap */ + gc = gdk_gc_new (window); + + gdk_gc_set_clip_region (gc, expose->region); + + gtk_widget_get_allocation (widget, &widget_allocation); + gdk_draw_drawable (window, gc, scroll_area->priv->pixmap, + 0, 0, widget_allocation.x, widget_allocation.y, + widget_allocation.width, widget_allocation.height); + + g_object_unref (gc); + gdk_region_destroy (region); + + return TRUE; +} + +void +foo_scroll_area_get_viewport (FooScrollArea *scroll_area, + GdkRectangle *viewport) +{ + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + + if (!viewport) + return; + + get_viewport (scroll_area, viewport); +} + +static void +process_event (FooScrollArea *scroll_area, + FooScrollAreaEventType input_type, + int x, + int y); + +static void +emit_viewport_changed (FooScrollArea *scroll_area, + GdkRectangle *new_viewport, + GdkRectangle *old_viewport) +{ + int px, py; + g_signal_emit (scroll_area, signals[VIEWPORT_CHANGED], 0, + new_viewport, old_viewport); + + gdk_window_get_pointer (scroll_area->priv->input_window, &px, &py, NULL); + +#if 0 + g_print ("procc\n"); +#endif + + process_event (scroll_area, FOO_MOTION, px, py); +} + +static void +clamp_adjustment (GtkAdjustment *adj) +{ + if (gtk_adjustment_get_upper (adj) >= gtk_adjustment_get_page_size (adj)) + gtk_adjustment_set_value (adj, CLAMP (gtk_adjustment_get_value (adj), 0.0, + gtk_adjustment_get_upper (adj) + - gtk_adjustment_get_page_size (adj))); + else + gtk_adjustment_set_value (adj, 0.0); + + gtk_adjustment_changed (adj); +} + +static gboolean +set_adjustment_values (FooScrollArea *scroll_area) +{ + GtkAllocation allocation; + + GtkAdjustment *hadj = scroll_area->priv->hadj; + GtkAdjustment *vadj = scroll_area->priv->vadj; + + /* Horizontal */ + gtk_widget_get_allocation (GTK_WIDGET (scroll_area), &allocation); + g_object_freeze_notify (G_OBJECT (hadj)); + gtk_adjustment_set_page_size (hadj, allocation.width); + gtk_adjustment_set_step_increment (hadj, 0.1 * allocation.width); + gtk_adjustment_set_page_increment (hadj, 0.9 * allocation.width); + gtk_adjustment_set_lower (hadj, 0.0); + gtk_adjustment_set_upper (hadj, scroll_area->priv->width); + g_object_thaw_notify (G_OBJECT (hadj)); + + /* Vertical */ + g_object_freeze_notify (G_OBJECT (vadj)); + gtk_adjustment_set_page_size (vadj, allocation.height); + gtk_adjustment_set_step_increment (vadj, 0.1 * allocation.height); + gtk_adjustment_set_page_increment (vadj, 0.9 * allocation.height); + gtk_adjustment_set_lower (vadj, 0.0); + gtk_adjustment_set_upper (vadj, scroll_area->priv->height); + g_object_thaw_notify (G_OBJECT (vadj)); + + clamp_adjustment (hadj); + clamp_adjustment (vadj); + + return TRUE; +} + +static void +foo_scroll_area_realize (GtkWidget *widget) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + GdkWindowAttr attributes; + GtkAllocation widget_allocation; + GdkWindow *window; + gint attributes_mask; + + gtk_widget_get_allocation (widget, &widget_allocation); + gtk_widget_set_realized (widget, TRUE); + + 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_ONLY; + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON2_MOTION_MASK | + GDK_BUTTON3_MOTION_MASK | + GDK_POINTER_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK); + + attributes_mask = GDK_WA_X | GDK_WA_Y; + + window = gtk_widget_get_parent_window (widget); + gtk_widget_set_window (widget, window); + g_object_ref (window); + + area->priv->input_window = gdk_window_new (window, + &attributes, attributes_mask); + area->priv->pixmap = gdk_pixmap_new (window, + widget_allocation.width, + widget_allocation.height, + -1); + gdk_window_set_user_data (area->priv->input_window, area); + + gtk_widget_style_attach (widget); +} + +static void +foo_scroll_area_unrealize (GtkWidget *widget) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + + if (area->priv->input_window) + { + gdk_window_set_user_data (area->priv->input_window, NULL); + gdk_window_destroy (area->priv->input_window); + area->priv->input_window = NULL; + } + + GTK_WIDGET_CLASS (parent_class)->unrealize (widget); +} + +static GdkPixmap * +create_new_pixmap (GtkWidget *widget, + GdkPixmap *old) +{ + GtkAllocation widget_allocation; + GdkPixmap *new; + + gtk_widget_get_allocation (widget, &widget_allocation); + new = gdk_pixmap_new (gtk_widget_get_window (widget), + widget_allocation.width, + widget_allocation.height, + -1); + + /* Unfortunately we don't know in which direction we were resized, + * so we just assume we were dragged from the south-east corner. + * + * Although, maybe we could get the root coordinates of the input-window? + * That might just work, actually. We need to make sure marco uses + * static gravity for the window before this will be useful. + */ + simple_draw_drawable (new, old, 0, 0, 0, 0, -1, -1); + + return new; +} + +static void +allocation_to_canvas_region (FooScrollArea *area, + GdkRegion *region) +{ + gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset); +} + + +static void +foo_scroll_area_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget); + GdkRectangle new_viewport; + GdkRectangle old_viewport; + GdkRegion *old_allocation; + GdkRegion *invalid; + GtkAllocation widget_allocation; + + get_viewport (scroll_area, &old_viewport); + + gtk_widget_get_allocation (widget, &widget_allocation); + old_allocation = gdk_region_rectangle (&widget_allocation); + gdk_region_offset (old_allocation, + -widget_allocation.x, -widget_allocation.y); + invalid = gdk_region_rectangle (allocation); + gdk_region_offset (invalid, -allocation->x, -allocation->y); + gdk_region_xor (invalid, old_allocation); + allocation_to_canvas_region (scroll_area, invalid); + foo_scroll_area_invalidate_region (scroll_area, invalid); + gdk_region_destroy (old_allocation); + gdk_region_destroy (invalid); + + gtk_widget_set_allocation (widget, allocation); + + if (scroll_area->priv->input_window) + { + GdkPixmap *new_pixmap; + + gdk_window_move_resize (scroll_area->priv->input_window, + allocation->x, allocation->y, + allocation->width, allocation->height); + + new_pixmap = create_new_pixmap (widget, scroll_area->priv->pixmap); + + g_object_unref (scroll_area->priv->pixmap); + + scroll_area->priv->pixmap = new_pixmap; + } + + get_viewport (scroll_area, &new_viewport); + + emit_viewport_changed (scroll_area, &new_viewport, &old_viewport); +} + +static void +emit_input (FooScrollArea *scroll_area, + FooScrollAreaEventType type, + int x, + int y, + FooScrollAreaEventFunc func, + gpointer data) +{ + FooScrollAreaEvent event; + + if (!func) + return; + + if (type != FOO_MOTION) + emit_input (scroll_area, FOO_MOTION, x, y, func, data); + +#if 0 + x += scroll_area->priv->x_offset; + y += scroll_area->priv->y_offset; +#endif + + event.type = type; + event.x = x; + event.y = y; + + func (scroll_area, &event, data); +} + +#if 0 +static void +print_path (const char *header, + cairo_path_t *path) +{ + int i; + + g_print ("%s\n", header); + + for (i=0; i < path->num_data; i += path->data[i].header.length) + { + cairo_path_data_t *data = &(path->data[i]); + + switch (data->header.type) + { + case CAIRO_PATH_MOVE_TO: + g_print ("move to: %f, %f\n", data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_LINE_TO: + g_print ("line to: %f, %f\n", data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_CURVE_TO: + g_print ("curve to: %f, %f\n", data[1].point.x, data[1].point.y); + g_print (" %f, %f\n", data[1].point.x, data[1].point.y); + g_print (" %f, %f\n", data[1].point.x, data[1].point.y); + break; + + case CAIRO_PATH_CLOSE_PATH: + break; + } + } +} +#endif + +static void +process_event (FooScrollArea *scroll_area, + FooScrollAreaEventType input_type, + int x, + int y) +{ + GtkWidget *widget = GTK_WIDGET (scroll_area); + int i; + + allocation_to_canvas (scroll_area, &x, &y); + + if (scroll_area->priv->grabbed) + { + emit_input (scroll_area, input_type, x, y, + scroll_area->priv->grab_func, + scroll_area->priv->grab_data); + return; + } + + +#if 0 + x += widget->allocation.x; + y += widget->allocation.y; +#endif + +#if 0 + g_print ("number of input regions: %d\n", scroll_area->priv->input_regions->len); +#endif + + for (i = 0; i < scroll_area->priv->input_regions->len; ++i) + { + InputRegion *region = scroll_area->priv->input_regions->pdata[i]; + +#if 0 + g_print ("%d ", i); + print_region ("region:", region->region); +#endif + + if (gdk_region_point_in (region->region, x, y)) + { + InputPath *path; + + path = region->paths; + while (path) + { + cairo_t *cr; + gboolean inside; + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + cairo_set_fill_rule (cr, path->fill_rule); + cairo_set_line_width (cr, path->line_width); + cairo_append_path (cr, path->path); + + if (path->is_stroke) + inside = cairo_in_stroke (cr, x, y); + else + inside = cairo_in_fill (cr, x, y); + + cairo_destroy (cr); + + if (inside) + { + emit_input (scroll_area, input_type, + x, y, + path->func, + path->data); + return; + } + + path = path->next; + } + + /* Since the regions are all disjoint, no other region + * can match. Of course we could be clever and try and + * sort the regions, but so far I have been unable to + * make this loop show up on a profile. + */ + return; + } + } +} + +static void +process_gdk_event (FooScrollArea *scroll_area, + int x, + int y, + GdkEvent *event) +{ + FooScrollAreaEventType input_type; + + if (event->type == GDK_BUTTON_PRESS) + input_type = FOO_BUTTON_PRESS; + else if (event->type == GDK_BUTTON_RELEASE) + input_type = FOO_BUTTON_RELEASE; + else if (event->type == GDK_MOTION_NOTIFY) + input_type = FOO_MOTION; + else + return; + + process_event (scroll_area, input_type, x, y); +} + +static gboolean +foo_scroll_area_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + + process_gdk_event (area, event->x, event->y, (GdkEvent *)event); + + return TRUE; +} + +static gboolean +foo_scroll_area_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + + process_gdk_event (area, event->x, event->y, (GdkEvent *)event); + + return FALSE; +} + +static gboolean +foo_scroll_area_motion (GtkWidget *widget, + GdkEventMotion *event) +{ + FooScrollArea *area = FOO_SCROLL_AREA (widget); + + process_gdk_event (area, event->x, event->y, (GdkEvent *)event); + return TRUE; +} + +void +foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area, + int width, + int height, + int old_y, + int new_y) +{ + scroll_area->priv->width = width; + scroll_area->priv->height = height; + +#if 0 + g_print ("diff: %d\n", new_y - old_y); +#endif + g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj)); + gtk_adjustment_set_value (scroll_area->priv->vadj, new_y); + + set_adjustment_values (scroll_area); + g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj)); +} + +void +foo_scroll_area_set_size (FooScrollArea *scroll_area, + int width, + int height) +{ + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + + /* FIXME: Default scroll algorithm should probably be to + * keep the same *area* outside the screen as before. + * + * For wrapper widgets that will do something roughly + * right. For widgets that don't change size, it + * will do the right thing. Except for idle-layouting + * widgets. + * + * Maybe there should be some generic support for those + * widgets. Can that even be done? + * + * Should we have a version of this function using + * fixed points? + */ + + scroll_area->priv->width = width; + scroll_area->priv->height = height; + + set_adjustment_values (scroll_area); +} + +static void +foo_scroll_area_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget); + + requisition->width = scroll_area->priv->min_width; + requisition->height = scroll_area->priv->min_height; + +#if 0 + g_print ("request %d %d\n", requisition->width, requisition->height); +#endif +} + +#if 0 +static void +translate_point (double *x, double *y, gpointer data) +{ + int *translation = data; + + *x += translation[0]; + *y += translation[1]; +} +#endif + +#if 0 +static void +path_translate (cairo_path_t *path, + int dx, + int dy) +{ + int translation[2] = {dx, dy}; + + path_foreach_point (path, translate_point, translation); +} +#endif + +static void +translate_input_regions (FooScrollArea *scroll_area, + int dx, + int dy) +{ +#if 0 + int i; + + for (i = 0; i < scroll_area->priv->input_regions->len; ++i) + { + InputRegion *region = scroll_area->priv->input_regions->pdata[i]; + InputPath *path; + + gdk_region_offset (region->region, dx, dy); + + path = region->paths; + while (path != NULL) + { + path_translate (path->path, dx, dy); + path = path->next; + } + } +#endif +} + +#if 0 +static void +paint_region (FooScrollArea *area, GdkRegion *region) +{ + int n_rects; + GdkRectangle *rects; + region = gdk_region_copy (region); + + gdk_region_get_rectangles (region, &rects, &n_rects); + + gdk_region_offset (region, + GTK_WIDGET (area)->allocation.x, + GTK_WIDGET (area)->allocation.y); + + GdkGC *gc = gdk_gc_new (GTK_WIDGET (area)->window); + gdk_gc_set_clip_region (gc, region); + gdk_draw_rectangle (GTK_WIDGET (area)->window, gc, TRUE, 0, 0, -1, -1); + g_object_unref (gc); + g_free (rects); +} +#endif + +static void +foo_scroll_area_scroll (FooScrollArea *area, + gint dx, + gint dy) +{ + GdkRectangle allocation; + GdkRectangle src_area; + GdkRectangle move_area; + GdkRegion *invalid_region; + + gtk_widget_get_allocation (GTK_WIDGET (area), &allocation); + allocation.x = 0; + allocation.y = 0; + + src_area = allocation; + src_area.x -= dx; + src_area.y -= dy; + + invalid_region = gdk_region_rectangle (&allocation); + + if (gdk_rectangle_intersect (&allocation, &src_area, &move_area)) + { + GdkRegion *move_region; + +#if 0 + g_print ("scrolling %d %d %d %d (%d %d)\n", + move_area.x, move_area.y, + move_area.width, move_area.height, + dx, dy); +#endif + + simple_draw_drawable (area->priv->pixmap, area->priv->pixmap, + move_area.x, move_area.y, + move_area.x + dx, move_area.y + dy, + move_area.width, move_area.height); + gtk_widget_queue_draw (GTK_WIDGET (area)); + + move_region = gdk_region_rectangle (&move_area); + gdk_region_offset (move_region, dx, dy); + gdk_region_subtract (invalid_region, move_region); + gdk_region_destroy (move_region); + } + +#if 0 + paint_region (area, invalid_region); +#endif + + allocation_to_canvas_region (area, invalid_region); + + foo_scroll_area_invalidate_region (area, invalid_region); + + gdk_region_destroy (invalid_region); +} + +static void +foo_scrollbar_adjustment_changed (GtkAdjustment *adj, + FooScrollArea *scroll_area) +{ + GtkWidget *widget = GTK_WIDGET (scroll_area); + gint dx = 0; + gint dy = 0; + GdkRectangle old_viewport, new_viewport; + + get_viewport (scroll_area, &old_viewport); + + if (adj == scroll_area->priv->hadj) + { + /* FIXME: do we treat the offset as int or double, and, + * if int, how do we round? + */ + dx = (int)gtk_adjustment_get_value (adj) - scroll_area->priv->x_offset; + scroll_area->priv->x_offset = gtk_adjustment_get_value (adj); + } + else if (adj == scroll_area->priv->vadj) + { + dy = (int)gtk_adjustment_get_value (adj) - scroll_area->priv->y_offset; + scroll_area->priv->y_offset = gtk_adjustment_get_value (adj); + } + else + { + g_assert_not_reached (); + } + + if (gtk_widget_get_realized (widget)) + { + foo_scroll_area_scroll (scroll_area, -dx, -dy); + +#if 0 + window_scroll_area (widget->window, &widget->allocation, -dx, -dy); +#endif + translate_input_regions (scroll_area, -dx, -dy); + +#if 0 + gdk_window_process_updates (widget->window, TRUE); +#endif + } + + get_viewport (scroll_area, &new_viewport); + + emit_viewport_changed (scroll_area, &new_viewport, &old_viewport); +} + +static void +set_one_adjustment (FooScrollArea *scroll_area, + GtkAdjustment *adjustment, + GtkAdjustment **location) +{ + g_return_if_fail (location != NULL); + + if (adjustment == *location) + return; + + if (!adjustment) + adjustment = new_adjustment (); + + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + + if (*location) + { + g_signal_handlers_disconnect_by_func ( + *location, foo_scrollbar_adjustment_changed, scroll_area); + + g_object_unref (*location); + } + + *location = adjustment; + + g_object_ref_sink (*location); + + g_signal_connect (*location, "value_changed", + G_CALLBACK (foo_scrollbar_adjustment_changed), + scroll_area); +} + +static void +foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + set_one_adjustment (scroll_area, hadjustment, &scroll_area->priv->hadj); + set_one_adjustment (scroll_area, vadjustment, &scroll_area->priv->vadj); + + set_adjustment_values (scroll_area); +} + +FooScrollArea * +foo_scroll_area_new (void) +{ + return g_object_new (FOO_TYPE_SCROLL_AREA, NULL); +} + +void +foo_scroll_area_set_min_size (FooScrollArea *scroll_area, + int min_width, + int min_height) +{ + scroll_area->priv->min_width = min_width; + scroll_area->priv->min_height = min_height; + + /* FIXME: think through invalidation. + * + * Goals: - no repainting everything on size_allocate(), + * - make sure input boxes are invalidated when + * needed + */ + gtk_widget_queue_resize (GTK_WIDGET (scroll_area)); +} + +#if 0 +static void +warn_about_adding_input_outside_expose (const char *func) +{ + static gboolean warned = FALSE; + + if (!warned) + { + g_warning ("%s() can only be called " + "from the paint handler for the FooScrollArea\n", func); + + warned = TRUE; + } +} +#endif + +static void +user_to_device (double *x, double *y, + gpointer data) +{ + cairo_t *cr = data; + + cairo_user_to_device (cr, x, y); +} + +static InputPath * +make_path (FooScrollArea *area, + cairo_t *cr, + gboolean is_stroke, + FooScrollAreaEventFunc func, + gpointer data) +{ + InputPath *path = g_new0 (InputPath, 1); + + path->is_stroke = is_stroke; + path->fill_rule = cairo_get_fill_rule (cr); + path->line_width = cairo_get_line_width (cr); + path->path = cairo_copy_path (cr); + path_foreach_point (path->path, user_to_device, cr); + path->func = func; + path->data = data; + path->next = area->priv->current_input->paths; + area->priv->current_input->paths = path; + return path; +} + +/* FIXME: we probably really want a + * + * foo_scroll_area_add_input_from_fill (area, cr, ...); + * and + * foo_scroll_area_add_input_from_stroke (area, cr, ...); + * as well. + */ +void +foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area, + cairo_t *cr, + FooScrollAreaEventFunc func, + gpointer data) +{ + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + g_return_if_fail (cr != NULL); + g_return_if_fail (scroll_area->priv->current_input); + + make_path (scroll_area, cr, FALSE, func, data); +} + +void +foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area, + cairo_t *cr, + FooScrollAreaEventFunc func, + gpointer data) +{ + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + g_return_if_fail (cr != NULL); + g_return_if_fail (scroll_area->priv->current_input); + + make_path (scroll_area, cr, TRUE, func, data); +} + +void +foo_scroll_area_invalidate (FooScrollArea *scroll_area) +{ + GtkAllocation allocation; + GtkWidget *widget = GTK_WIDGET (scroll_area); + + gtk_widget_get_allocation (widget, &allocation); + foo_scroll_area_invalidate_rect (scroll_area, + scroll_area->priv->x_offset, scroll_area->priv->y_offset, + allocation.width, + allocation.height); +} + +static void +canvas_to_window (FooScrollArea *area, + GdkRegion *region) +{ + GtkAllocation allocation; + GtkWidget *widget = GTK_WIDGET (area); + + gtk_widget_get_allocation (widget, &allocation); + gdk_region_offset (region, + -area->priv->x_offset + allocation.x, + -area->priv->y_offset + allocation.y); +} + +static void +window_to_canvas (FooScrollArea *area, + GdkRegion *region) +{ + GtkAllocation allocation; + GtkWidget *widget = GTK_WIDGET (area); + + gtk_widget_get_allocation (widget, &allocation); + gdk_region_offset (region, + area->priv->x_offset - allocation.x, + area->priv->y_offset - allocation.y); +} + +void +foo_scroll_area_invalidate_region (FooScrollArea *area, + GdkRegion *region) +{ + GtkWidget *widget; + + g_return_if_fail (FOO_IS_SCROLL_AREA (area)); + + widget = GTK_WIDGET (area); + + gdk_region_union (area->priv->update_region, region); + + if (gtk_widget_get_realized (widget)) + { + canvas_to_window (area, region); + + gdk_window_invalidate_region (gtk_widget_get_window (widget), + region, TRUE); + + window_to_canvas (area, region); + } +} + +void +foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area, + int x, + int y, + int width, + int height) +{ + GdkRectangle rect = { x, y, width, height }; + GdkRegion *region; + + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + + region = gdk_region_rectangle (&rect); + + foo_scroll_area_invalidate_region (scroll_area, region); + + gdk_region_destroy (region); +} + +void +foo_scroll_area_begin_grab (FooScrollArea *scroll_area, + FooScrollAreaEventFunc func, + gpointer input_data) +{ + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + g_return_if_fail (!scroll_area->priv->grabbed); + + scroll_area->priv->grabbed = TRUE; + scroll_area->priv->grab_func = func; + scroll_area->priv->grab_data = input_data; + + /* FIXME: we should probably take a server grab */ + /* Also, maybe there should be support for setting the grab cursor */ +} + +void +foo_scroll_area_end_grab (FooScrollArea *scroll_area) +{ + g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area)); + + scroll_area->priv->grabbed = FALSE; + scroll_area->priv->grab_func = NULL; + scroll_area->priv->grab_data = NULL; +} + +gboolean +foo_scroll_area_is_grabbed (FooScrollArea *scroll_area) +{ + return scroll_area->priv->grabbed; +} + +void +foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area, + int x, + int y) +{ + g_object_freeze_notify (G_OBJECT (scroll_area->priv->hadj)); + g_object_freeze_notify (G_OBJECT (scroll_area->priv->vadj)); + gtk_adjustment_set_value (scroll_area->priv->hadj, x); + gtk_adjustment_set_value (scroll_area->priv->vadj, y); + + set_adjustment_values (scroll_area); + g_object_thaw_notify (G_OBJECT (scroll_area->priv->hadj)); + g_object_thaw_notify (G_OBJECT (scroll_area->priv->vadj)); +} + +static gboolean +rect_contains (const GdkRectangle *rect, int x, int y) +{ + return (x >= rect->x && + y >= rect->y && + x < rect->x + rect->width && + y < rect->y + rect->height); +} + +static void +stop_scrolling (FooScrollArea *area) +{ +#if 0 + g_print ("stop scrolling\n"); +#endif + if (area->priv->auto_scroll_info) + { + g_source_remove (area->priv->auto_scroll_info->timeout_id); + g_timer_destroy (area->priv->auto_scroll_info->timer); + g_free (area->priv->auto_scroll_info); + + area->priv->auto_scroll_info = NULL; + } +} + +static gboolean +scroll_idle (gpointer data) +{ + GdkRectangle viewport, new_viewport; + FooScrollArea *area = data; + AutoScrollInfo *info = area->priv->auto_scroll_info; +#if 0 + int dx, dy; +#endif + int new_x, new_y; + double elapsed; + + get_viewport (area, &viewport); + +#if 0 + g_print ("old info: %d %d\n", info->dx, info->dy); + + g_print ("timeout (%d %d)\n", dx, dy); +#endif + +#if 0 + viewport.x += info->dx; + viewport.y += info->dy; +#endif + +#if 0 + g_print ("new info %d %d\n", info->dx, info->dy); +#endif + + elapsed = g_timer_elapsed (info->timer, NULL); + + info->res_x = elapsed * info->dx / 0.2; + info->res_y = elapsed * info->dy / 0.2; + +#if 0 + g_print ("%f %f\n", info->res_x, info->res_y); +#endif + + new_x = viewport.x + info->res_x; + new_y = viewport.y + info->res_y; + +#if 0 + g_print ("%f\n", elapsed * (info->dx / 0.2)); +#endif + +#if 0 + g_print ("new_x, new_y\n: %d %d\n", new_x, new_y); +#endif + + foo_scroll_area_set_viewport_pos (area, new_x, new_y); +#if 0 + viewport.x + info->dx, + viewport.y + info->dy); +#endif + + get_viewport (area, &new_viewport); + + if (viewport.x == new_viewport.x && + viewport.y == new_viewport.y && + (info->res_x > 1.0 || + info->res_y > 1.0 || + info->res_x < -1.0 || + info->res_y < -1.0)) + { + stop_scrolling (area); + + /* stop scrolling if it didn't have an effect */ + return FALSE; + } + + return TRUE; +} + +static void +ensure_scrolling (FooScrollArea *area, + int dx, + int dy) +{ + if (!area->priv->auto_scroll_info) + { +#if 0 + g_print ("start scrolling\n"); +#endif + area->priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1); + area->priv->auto_scroll_info->timeout_id = + g_idle_add (scroll_idle, area); + area->priv->auto_scroll_info->timer = g_timer_new (); + } + +#if 0 + g_print ("setting scrolling to %d %d\n", dx, dy); +#endif + +#if 0 + g_print ("dx, dy: %d %d\n", dx, dy); +#endif + + area->priv->auto_scroll_info->dx = dx; + area->priv->auto_scroll_info->dy = dy; +} + +void +foo_scroll_area_auto_scroll (FooScrollArea *scroll_area, + FooScrollAreaEvent *event) +{ + GdkRectangle viewport; + + get_viewport (scroll_area, &viewport); + + if (rect_contains (&viewport, event->x, event->y)) + { + stop_scrolling (scroll_area); + } + else + { + int dx, dy; + + dx = dy = 0; + + if (event->y < viewport.y) + { + dy = event->y - viewport.y; + dy = MIN (dy + 2, 0); + } + else if (event->y >= viewport.y + viewport.height) + { + dy = event->y - (viewport.y + viewport.height - 1); + dy = MAX (dy - 2, 0); + } + + if (event->x < viewport.x) + { + dx = event->x - viewport.x; + dx = MIN (dx + 2, 0); + } + else if (event->x >= viewport.x + viewport.width) + { + dx = event->x - (viewport.x + viewport.width - 1); + dx = MAX (dx - 2, 0); + } + +#if 0 + g_print ("dx, dy: %d %d\n", dx, dy); +#endif + + ensure_scrolling (scroll_area, dx, dy); + } +} + +void +foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area) +{ + /* noop for now */ +} + +void +foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area) +{ + stop_scrolling (scroll_area); +} + + + +#if 0 +/* + * Backing Store + */ +struct BackingStore +{ + GdkPixmap *pixmap; + GdkRegion *update_region; + int width; + int height; +}; + +static BackingStore * +backing_store_new (GdkWindow *window, + int width, int height) +{ + BackingStore *store = g_new0 (BackingStore, 1); + GdkRectangle rect = { 0, 0, width, height }; + + store->pixmap = gdk_pixmap_new (window, width, height, -1); + store->update_region = gdk_region_rectangle (&rect); + store->width = width; + store->height = height; + + return store; +} + +static void +backing_store_free (BackingStore *store) +{ + g_object_unref (store->pixmap); + gdk_region_destroy (store->update_region); + g_free (store); +} + +static void +backing_store_draw (BackingStore *store, + GdkDrawable *dest, + GdkRegion *clip, + int x, + int y) +{ + GdkGC *gc = gdk_gc_new (dest); + + gdk_gc_set_clip_region (gc, clip); + + gdk_draw_drawable (dest, gc, store->pixmap, + 0, 0, x, y, store->width, store->height); + + g_object_unref (gc); +} + +static void +backing_store_scroll (BackingStore *store, + int dx, + int dy) +{ + GdkGC *gc = gdk_gc_new (store->pixmap); + GdkRectangle rect; + + gdk_draw_drawable (store->pixmap, gc, store->pixmap, + 0, 0, dx, dy, + store->width, store->height); + + /* Invalidate vertically */ + rect.x = 0; + rect.width = store->width; + + if (dy > 0) + { + rect.y = 0; + rect.height = dy; + } + else + { + rect.y = store->height + dy; + rect.y = -dy; + } + + gdk_region_union_with_rect (store->update_region, &rect); + + /* Invalidate horizontally */ + rect.y = 0; + rect.height = store->height; + + if (dx > 0) + { + rect.x = 0; + rect.width = dx; + } + else + { + rect.x = store->width + dx; + rect.width = -dx; + } + + gdk_region_union_with_rect (store->update_region, &rect); +} + +static void +backing_store_invalidate_rect (BackingStore *store, + GdkRectangle *rect) +{ + gdk_region_union_with_rect (store->update_region, rect); +} + +static void +backing_store_invalidate_region (BackingStore *store, + GdkRegion *region) +{ + gdk_region_union (store->update_region, region); +} + +static void +backing_store_invalidate_all (BackingStore *store) +{ + GdkRectangle rect = { 0, 0, store->width, store->height }; + gdk_region_destroy (store->update_region); + store->update_region = gdk_region_rectangle (&rect); +} + +static void +backing_store_resize (BackingStore *store, + int width, + int height) +{ + GdkPixmap *pixmap = gdk_pixmap_new (store->pixmap, width, height, -1); + + /* Unfortunately we don't know in which direction we were resized, + * so we just assume we were dragged from the south-east corner. + * + * Although, maybe we could get the root coordinates of the input-window? + * That might just work, actually. We need to make sure marco uses + * static gravity for the window before this will be useful. + */ + simple_draw_drawable (pixmap, store->pixmap, 0, 0, 0, 0, -1, -1); + + g_object_unref (store->pixmap); + + store->pixmap = pixmap; + + /* FIXME: invalidate uncovered strip only */ + + backing_store_invalidate_all (store); +} + +static void +cclip_to_region (cairo_t *cr, GdkRegion *region) +{ + int n_rects; + GdkRectangle *rects; + + gdk_region_get_rectangles (region, &rects, &n_rects); + + cairo_new_path (cr); + while (n_rects--) + { + GdkRectangle *rect = &(rects[n_rects]); + + cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height); + } + cairo_clip (cr); + + g_free (rects); +} + +static void +backing_store_process_updates (BackingStore *store, + ExposeFunc func, + gpointer data) +{ + cairo_t *cr = gdk_cairo_create (store->pixmap); + GdkRegion *region = store->update_region; + store->update_region = gdk_region_new (); + + cclip_to_region (cr, store->update_region); + + func (cr, store->update_region, data); + + gdk_region_destroy (region); + cairo_destroy (cr); +} + +#endif diff --git a/capplets/display/scrollarea.h b/capplets/display/scrollarea.h new file mode 100644 index 00000000..d1695fad --- /dev/null +++ b/capplets/display/scrollarea.h @@ -0,0 +1,124 @@ +/* Copyright 2006, 2007, 2008, Soren Sandmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include + +#define FOO_TYPE_SCROLL_AREA (foo_scroll_area_get_type ()) +#define FOO_SCROLL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_SCROLL_AREA, FooScrollArea)) +#define FOO_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_SCROLL_AREA, FooScrollAreaClass)) +#define FOO_IS_SCROLL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_SCROLL_AREA)) +#define FOO_IS_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_SCROLL_AREA)) +#define FOO_SCROLL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_SCROLL_AREA, FooScrollAreaClass)) + +typedef struct FooScrollArea FooScrollArea; +typedef struct FooScrollAreaClass FooScrollAreaClass; +typedef struct FooScrollAreaPrivate FooScrollAreaPrivate; +typedef struct FooScrollAreaEvent FooScrollAreaEvent; + +typedef enum +{ + FOO_BUTTON_PRESS, + FOO_BUTTON_RELEASE, + FOO_MOTION +} FooScrollAreaEventType; + +struct FooScrollAreaEvent +{ + FooScrollAreaEventType type; + int x; + int y; +}; + +typedef void (* FooScrollAreaEventFunc) (FooScrollArea *area, + FooScrollAreaEvent *event, + gpointer data); + +struct FooScrollArea +{ + GtkContainer parent_instance; + + FooScrollAreaPrivate *priv; +}; + +struct FooScrollAreaClass +{ + GtkContainerClass parent_class; + + void (*set_scroll_adjustments) (FooScrollArea *scroll_area, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + void (*viewport_changed) (FooScrollArea *scroll_area, + GdkRectangle *old_viewport, + GdkRectangle *new_viewport); + + void (*paint) (FooScrollArea *scroll_area, + cairo_t *cr, + GdkRectangle *extents, + GdkRegion *region); +}; + +GType foo_scroll_area_get_type (void); + +FooScrollArea *foo_scroll_area_new (void); + +/* Set the requisition for the widget. */ +void foo_scroll_area_set_min_size (FooScrollArea *scroll_area, + int min_width, + int min_height); + +/* Set how much of the canvas can be scrolled into view */ +void foo_scroll_area_set_size (FooScrollArea *scroll_area, + int width, + int height); +void foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area, + int width, + int height, + int old_y, + int new_y); +void foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area, + int x, + int y); +void foo_scroll_area_get_viewport (FooScrollArea *scroll_area, + GdkRectangle *viewport); +void foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area, + cairo_t *cr, + FooScrollAreaEventFunc func, + gpointer data); +void foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area, + cairo_t *cr, + FooScrollAreaEventFunc func, + gpointer data); +void foo_scroll_area_invalidate_region (FooScrollArea *area, + GdkRegion *region); +void foo_scroll_area_invalidate (FooScrollArea *scroll_area); +void foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area, + int x, + int y, + int width, + int height); +void foo_scroll_area_begin_grab (FooScrollArea *scroll_area, + FooScrollAreaEventFunc func, + gpointer input_data); +void foo_scroll_area_end_grab (FooScrollArea *scroll_area); +gboolean foo_scroll_area_is_grabbed (FooScrollArea *scroll_area); + +void foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area); +void foo_scroll_area_auto_scroll (FooScrollArea *scroll_area, + FooScrollAreaEvent *event); +void foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area); diff --git a/capplets/display/xrandr-capplet.c b/capplets/display/xrandr-capplet.c new file mode 100644 index 00000000..94de7771 --- /dev/null +++ b/capplets/display/xrandr-capplet.c @@ -0,0 +1,2571 @@ +/* Monitor Settings. A preference panel for configuring monitors + * + * Copyright (C) 2007, 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Soren Sandmann + */ + +#include +#include +#include +#include + +#include +#include "scrollarea.h" +#define MATE_DESKTOP_USE_UNSTABLE_API +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct App App; +typedef struct GrabInfo GrabInfo; + +struct App +{ + MateRRScreen *screen; + MateRRConfig *current_configuration; + MateRRLabeler *labeler; + MateOutputInfo *current_output; + + GtkWidget *dialog; + GtkWidget *current_monitor_event_box; + GtkWidget *current_monitor_label; + GtkWidget *monitor_on_radio; + GtkWidget *monitor_off_radio; + GtkListStore *resolution_store; + GtkWidget *resolution_combo; + GtkWidget *refresh_combo; + GtkWidget *rotation_combo; + GtkWidget *panel_checkbox; + GtkWidget *clone_checkbox; + GtkWidget *show_icon_checkbox; + + /* We store the event timestamp when the Apply button is clicked */ + GtkWidget *apply_button; + guint32 apply_button_clicked_timestamp; + + GtkWidget *area; + gboolean ignore_gui_changes; + MateConfClient *client; + + /* These are used while we are waiting for the ApplyConfiguration method to be executed over D-bus */ + DBusGConnection *connection; + DBusGProxy *proxy; + DBusGProxyCall *proxy_call; + + enum { + APPLYING_VERSION_1, + APPLYING_VERSION_2 + } apply_configuration_state; +}; + +/* Response codes for custom buttons in the main dialog */ +enum { + RESPONSE_MAKE_DEFAULT = 1 +}; + +static void rebuild_gui (App *app); +static void on_clone_changed (GtkWidget *box, gpointer data); +static void on_rate_changed (GtkComboBox *box, gpointer data); +static gboolean output_overlaps (MateOutputInfo *output, MateRRConfig *config); +static void select_current_output_from_dialog_position (App *app); +static void monitor_on_off_toggled_cb (GtkToggleButton *toggle, gpointer data); +static void get_geometry (MateOutputInfo *output, int *w, int *h); +static void apply_configuration_returned_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, void *data); +static gboolean get_clone_size (MateRRScreen *screen, int *width, int *height); +static gboolean output_info_supports_mode (App *app, MateOutputInfo *info, int width, int height); + +static void +error_message (App *app, const char *primary_text, const char *secondary_text) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new ((app && app->dialog) ? GTK_WINDOW (app->dialog) : NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", primary_text); + + if (secondary_text) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary_text); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static gboolean +do_free (gpointer data) +{ + g_free (data); + return FALSE; +} + +static gchar * +idle_free (gchar *s) +{ + g_idle_add (do_free, s); + + return s; +} + +static void +on_screen_changed (MateRRScreen *scr, + gpointer data) +{ + MateRRConfig *current; + App *app = data; + + current = mate_rr_config_new_current (app->screen); + + if (app->current_configuration) + mate_rr_config_free (app->current_configuration); + + app->current_configuration = current; + app->current_output = NULL; + + if (app->labeler) { + mate_rr_labeler_hide (app->labeler); + g_object_unref (app->labeler); + } + + app->labeler = mate_rr_labeler_new (app->current_configuration); + + select_current_output_from_dialog_position (app); +} + +static void +on_viewport_changed (FooScrollArea *scroll_area, + GdkRectangle *old_viewport, + GdkRectangle *new_viewport) +{ + foo_scroll_area_set_size (scroll_area, + new_viewport->width, + new_viewport->height); + + foo_scroll_area_invalidate (scroll_area); +} + +static void +layout_set_font (PangoLayout *layout, const char *font) +{ + PangoFontDescription *desc = + pango_font_description_from_string (font); + + if (desc) + { + pango_layout_set_font_description (layout, desc); + + pango_font_description_free (desc); + } +} + +static void +clear_combo (GtkWidget *widget) +{ + GtkComboBox *box = GTK_COMBO_BOX (widget); + GtkTreeModel *model = gtk_combo_box_get_model (box); + GtkListStore *store = GTK_LIST_STORE (model); + + gtk_list_store_clear (store); +} + +typedef struct +{ + const char *text; + gboolean found; + GtkTreeIter iter; +} ForeachInfo; + +static gboolean +foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + ForeachInfo *info = data; + char *text = NULL; + + gtk_tree_model_get (model, iter, 0, &text, -1); + + g_assert (text != NULL); + + if (strcmp (info->text, text) == 0) + { + info->found = TRUE; + info->iter = *iter; + return TRUE; + } + + return FALSE; +} + +static void +add_key (GtkWidget *widget, + const char *text, + int width, int height, int rate, + MateRRRotation rotation) +{ + ForeachInfo info; + GtkComboBox *box = GTK_COMBO_BOX (widget); + GtkTreeModel *model = gtk_combo_box_get_model (box); + GtkListStore *store = GTK_LIST_STORE (model); + gboolean retval; + + info.text = text; + info.found = FALSE; + + gtk_tree_model_foreach (model, foreach, &info); + + if (!info.found) + { + GtkTreeIter iter; + gtk_list_store_insert_with_values (store, &iter, -1, + 0, text, + 1, width, + 2, height, + 3, rate, + 4, width * height, + 5, rotation, + -1); + + retval = TRUE; + } + else + { + retval = FALSE; + } +} + +static gboolean +combo_select (GtkWidget *widget, const char *text) +{ + GtkComboBox *box = GTK_COMBO_BOX (widget); + GtkTreeModel *model = gtk_combo_box_get_model (box); + ForeachInfo info; + + info.text = text; + info.found = FALSE; + + gtk_tree_model_foreach (model, foreach, &info); + + if (!info.found) + return FALSE; + + gtk_combo_box_set_active_iter (box, &info.iter); + return TRUE; +} + +static MateRRMode ** +get_current_modes (App *app) +{ + MateRROutput *output; + + if (app->current_configuration->clone) + { + return mate_rr_screen_list_clone_modes (app->screen); + } + else + { + if (!app->current_output) + return NULL; + + output = mate_rr_screen_get_output_by_name ( + app->screen, app->current_output->name); + + if (!output) + return NULL; + + return mate_rr_output_list_modes (output); + } +} + +static void +rebuild_rotation_combo (App *app) +{ + typedef struct + { + MateRRRotation rotation; + const char * name; + } RotationInfo; + static const RotationInfo rotations[] = { + { MATE_RR_ROTATION_0, N_("Normal") }, + { MATE_RR_ROTATION_90, N_("Left") }, + { MATE_RR_ROTATION_270, N_("Right") }, + { MATE_RR_ROTATION_180, N_("Upside Down") }, + }; + const char *selection; + MateRRRotation current; + int i; + + clear_combo (app->rotation_combo); + + gtk_widget_set_sensitive ( + app->rotation_combo, app->current_output && app->current_output->on); + + if (!app->current_output) + return; + + current = app->current_output->rotation; + + selection = NULL; + for (i = 0; i < G_N_ELEMENTS (rotations); ++i) + { + const RotationInfo *info = &(rotations[i]); + + app->current_output->rotation = info->rotation; + + /* NULL-GError --- FIXME: we should say why this rotation is not available! */ + if (mate_rr_config_applicable (app->current_configuration, app->screen, NULL)) + { + add_key (app->rotation_combo, _(info->name), 0, 0, 0, info->rotation); + + if (info->rotation == current) + selection = _(info->name); + } + } + + app->current_output->rotation = current; + + if (!(selection && combo_select (app->rotation_combo, selection))) + combo_select (app->rotation_combo, _("Normal")); +} + +static char * +make_rate_string (int hz) +{ + return g_strdup_printf (_("%d Hz"), hz); +} + +static void +rebuild_rate_combo (App *app) +{ + GHashTable *rates; + MateRRMode **modes; + int best; + int i; + + clear_combo (app->refresh_combo); + + gtk_widget_set_sensitive ( + app->refresh_combo, app->current_output && app->current_output->on); + + if (!app->current_output + || !(modes = get_current_modes (app))) + return; + + rates = g_hash_table_new_full ( + g_str_hash, g_str_equal, (GFreeFunc) g_free, NULL); + + best = -1; + for (i = 0; modes[i] != NULL; ++i) + { + MateRRMode *mode = modes[i]; + int width, height, rate; + + width = mate_rr_mode_get_width (mode); + height = mate_rr_mode_get_height (mode); + rate = mate_rr_mode_get_freq (mode); + + if (width == app->current_output->width && + height == app->current_output->height) + { + add_key (app->refresh_combo, + idle_free (make_rate_string (rate)), + 0, 0, rate, -1); + + if (rate > best) + best = rate; + } + } + + if (!combo_select (app->refresh_combo, idle_free (make_rate_string (app->current_output->rate)))) + combo_select (app->refresh_combo, idle_free (make_rate_string (best))); +} + +static int +count_active_outputs (App *app) +{ + int i, count = 0; + + for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) + { + MateOutputInfo *output = app->current_configuration->outputs[i]; + if (output->on) + count++; + } + + return count; +} + +#if 0 +static int +count_all_outputs (MateRRConfig *config) +{ + int i; + + for (i = 0; config->outputs[i] != NULL; i++) + ; + + return i; +} +#endif + +/* Computes whether "Mirror Screens" (clone mode) is supported based on these criteria: + * + * 1. There is an available size for cloning. + * + * 2. There are 2 or more connected outputs that support that size. + */ +static gboolean +mirror_screens_is_supported (App *app) +{ + int clone_width, clone_height; + gboolean have_clone_size; + gboolean mirror_is_supported; + + mirror_is_supported = FALSE; + + have_clone_size = get_clone_size (app->screen, &clone_width, &clone_height); + + if (have_clone_size) { + int i; + int num_outputs_with_clone_size; + + num_outputs_with_clone_size = 0; + + for (i = 0; app->current_configuration->outputs[i] != NULL; i++) + { + MateOutputInfo *output = app->current_configuration->outputs[i]; + + /* We count the connected outputs that support the clone size. It + * doesn't matter if those outputs aren't actually On currently; we + * will turn them on in on_clone_changed(). + */ + if (output->connected && output_info_supports_mode (app, output, clone_width, clone_height)) + num_outputs_with_clone_size++; + } + + if (num_outputs_with_clone_size >= 2) + mirror_is_supported = TRUE; + } + + return mirror_is_supported; +} + +static void +rebuild_mirror_screens (App *app) +{ + gboolean mirror_is_active; + gboolean mirror_is_supported; + + g_signal_handlers_block_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app); + + mirror_is_active = app->current_configuration && app->current_configuration->clone; + + /* If mirror_is_active, then it *must* be possible to turn mirroring off */ + mirror_is_supported = mirror_is_active || mirror_screens_is_supported (app); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), mirror_is_active); + gtk_widget_set_sensitive (app->clone_checkbox, mirror_is_supported); + + g_signal_handlers_unblock_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app); +} + +static void +rebuild_current_monitor_label (App *app) +{ + char *str, *tmp; + GdkColor color; + gboolean use_color; + + if (app->current_output) + { + if (app->current_configuration->clone) + tmp = g_strdup (_("Mirror Screens")); + else + tmp = g_strdup_printf (_("Monitor: %s"), app->current_output->display_name); + + str = g_strdup_printf ("%s", tmp); + mate_rr_labeler_get_color_for_output (app->labeler, app->current_output, &color); + use_color = TRUE; + g_free (tmp); + } + else + { + str = g_strdup_printf ("%s", _("Monitor")); + use_color = FALSE; + } + + gtk_label_set_markup (GTK_LABEL (app->current_monitor_label), str); + g_free (str); + + if (use_color) + { + GdkColor black = { 0, 0, 0, 0 }; + + gtk_widget_modify_bg (app->current_monitor_event_box, gtk_widget_get_state (app->current_monitor_event_box), &color); + + /* Make the label explicitly black. We don't want it to follow the + * theme's colors, since the label is always shown against a light + * pastel background. See bgo#556050 + */ + gtk_widget_modify_fg (app->current_monitor_label, gtk_widget_get_state (app->current_monitor_label), &black); + } + else + { + /* Remove any modifications we did on the label's color */ + GtkRcStyle *reset_rc_style; + + reset_rc_style = gtk_rc_style_new (); + gtk_widget_modify_style (app->current_monitor_label, reset_rc_style); /* takes ownership of, and destroys, the rc style */ + } + + gtk_event_box_set_visible_window (GTK_EVENT_BOX (app->current_monitor_event_box), use_color); +} + +static void +rebuild_on_off_radios (App *app) +{ + gboolean sensitive; + gboolean on_active; + gboolean off_active; + + g_signal_handlers_block_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app); + g_signal_handlers_block_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app); + + sensitive = FALSE; + on_active = FALSE; + off_active = FALSE; + + if (!app->current_configuration->clone && app->current_output) + { + if (count_active_outputs (app) > 1 || !app->current_output->on) + sensitive = TRUE; + else + sensitive = FALSE; + + on_active = app->current_output->on; + off_active = !on_active; + } + + gtk_widget_set_sensitive (app->monitor_on_radio, sensitive); + gtk_widget_set_sensitive (app->monitor_off_radio, sensitive); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->monitor_on_radio), on_active); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->monitor_off_radio), off_active); + + g_signal_handlers_unblock_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app); + g_signal_handlers_unblock_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app); +} + +static char * +make_resolution_string (int width, int height) +{ + return g_strdup_printf (_("%d x %d"), width, height); +} + +static void +find_best_mode (MateRRMode **modes, int *out_width, int *out_height) +{ + int i; + + *out_width = 0; + *out_height = 0; + + for (i = 0; modes[i] != NULL; i++) + { + int w, h; + + w = mate_rr_mode_get_width (modes[i]); + h = mate_rr_mode_get_height (modes[i]); + + if (w * h > *out_width * *out_height) + { + *out_width = w; + *out_height = h; + } + } +} + +static void +rebuild_resolution_combo (App *app) +{ + int i; + MateRRMode **modes; + const char *current; + + clear_combo (app->resolution_combo); + + if (!(modes = get_current_modes (app)) + || !app->current_output + || !app->current_output->on) + { + gtk_widget_set_sensitive (app->resolution_combo, FALSE); + return; + } + + g_assert (app->current_output != NULL); + g_assert (app->current_output->width != 0 && app->current_output->height != 0); + + gtk_widget_set_sensitive (app->resolution_combo, TRUE); + + for (i = 0; modes[i] != NULL; ++i) + { + int width, height; + + width = mate_rr_mode_get_width (modes[i]); + height = mate_rr_mode_get_height (modes[i]); + + add_key (app->resolution_combo, + idle_free (make_resolution_string (width, height)), + width, height, 0, -1); + } + + current = idle_free (make_resolution_string (app->current_output->width, app->current_output->height)); + + if (!combo_select (app->resolution_combo, current)) + { + int best_w, best_h; + + find_best_mode (modes, &best_w, &best_h); + combo_select (app->resolution_combo, idle_free (make_resolution_string (best_w, best_h))); + } +} + +static void +rebuild_gui (App *app) +{ + gboolean sensitive; + + /* We would break spectacularly if we recursed, so + * just assert if that happens + */ + g_assert (app->ignore_gui_changes == FALSE); + + app->ignore_gui_changes = TRUE; + + sensitive = app->current_output ? TRUE : FALSE; + +#if 0 + g_debug ("rebuild gui, is on: %d", app->current_output->on); +#endif + + rebuild_mirror_screens (app); + rebuild_current_monitor_label (app); + rebuild_on_off_radios (app); + rebuild_resolution_combo (app); + rebuild_rate_combo (app); + rebuild_rotation_combo (app); + +#if 0 + g_debug ("sensitive: %d, on: %d", sensitive, app->current_output->on); +#endif + gtk_widget_set_sensitive (app->panel_checkbox, sensitive); + + app->ignore_gui_changes = FALSE; +} + +static gboolean +get_mode (GtkWidget *widget, int *width, int *height, int *freq, MateRRRotation *rot) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkComboBox *box = GTK_COMBO_BOX (widget); + int dummy; + + if (!gtk_combo_box_get_active_iter (box, &iter)) + return FALSE; + + if (!width) + width = &dummy; + + if (!height) + height = &dummy; + + if (!freq) + freq = &dummy; + + if (!rot) + rot = (MateRRRotation *)&dummy; + + model = gtk_combo_box_get_model (box); + gtk_tree_model_get (model, &iter, + 1, width, + 2, height, + 3, freq, + 5, rot, + -1); + + return TRUE; + +} + +static void +on_rotation_changed (GtkComboBox *box, gpointer data) +{ + App *app = data; + MateRRRotation rotation; + + if (!app->current_output) + return; + + if (get_mode (app->rotation_combo, NULL, NULL, NULL, &rotation)) + app->current_output->rotation = rotation; + + foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); +} + +static void +on_rate_changed (GtkComboBox *box, gpointer data) +{ + App *app = data; + int rate; + + if (!app->current_output) + return; + + if (get_mode (app->refresh_combo, NULL, NULL, &rate, NULL)) + app->current_output->rate = rate; + + foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); +} + +static void +select_resolution_for_current_output (App *app) +{ + MateRRMode **modes; + int width, height; + + if (app->current_output->pref_width != 0 && app->current_output->pref_height != 0) + { + app->current_output->width = app->current_output->pref_width; + app->current_output->height = app->current_output->pref_height; + return; + } + + modes = get_current_modes (app); + if (!modes) + return; + + find_best_mode (modes, &width, &height); + + app->current_output->width = width; + app->current_output->height = height; +} + +static void +monitor_on_off_toggled_cb (GtkToggleButton *toggle, gpointer data) +{ + App *app = data; + gboolean is_on; + + if (!app->current_output) + return; + + if (!gtk_toggle_button_get_active (toggle)) + return; + + if (GTK_WIDGET (toggle) == app->monitor_on_radio) + is_on = TRUE; + else if (GTK_WIDGET (toggle) == app->monitor_off_radio) + is_on = FALSE; + else + { + g_assert_not_reached (); + return; + } + + app->current_output->on = is_on; + + if (is_on) + select_resolution_for_current_output (app); /* The refresh rate will be picked in rebuild_rate_combo() */ + + rebuild_gui (app); + foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); +} + +static void +realign_outputs_after_resolution_change (App *app, MateOutputInfo *output_that_changed, int old_width, int old_height) +{ + /* We find the outputs that were below or to the right of the output that + * changed, and realign them; we also do that for outputs that shared the + * right/bottom edges with the output that changed. The outputs that are + * above or to the left of that output don't need to change. + */ + + int i; + int old_right_edge, old_bottom_edge; + int dx, dy; + + g_assert (app->current_configuration != NULL); + + if (output_that_changed->width == old_width && output_that_changed->height == old_height) + return; + + old_right_edge = output_that_changed->x + old_width; + old_bottom_edge = output_that_changed->y + old_height; + + dx = output_that_changed->width - old_width; + dy = output_that_changed->height - old_height; + + for (i = 0; app->current_configuration->outputs[i] != NULL; i++) { + MateOutputInfo *output; + int output_width, output_height; + + output = app->current_configuration->outputs[i]; + + if (output == output_that_changed || !output->connected) + continue; + + get_geometry (output, &output_width, &output_height); + + if (output->x >= old_right_edge) + output->x += dx; + else if (output->x + output_width == old_right_edge) + output->x = output_that_changed->x + output_that_changed->width - output_width; + + if (output->y >= old_bottom_edge) + output->y += dy; + else if (output->y + output_height == old_bottom_edge) + output->y = output_that_changed->y + output_that_changed->height - output_height; + } +} + +static void +on_resolution_changed (GtkComboBox *box, gpointer data) +{ + App *app = data; + int old_width, old_height; + int width; + int height; + + if (!app->current_output) + return; + + old_width = app->current_output->width; + old_height = app->current_output->height; + + if (get_mode (app->resolution_combo, &width, &height, NULL, NULL)) + { + app->current_output->width = width; + app->current_output->height = height; + + if (width == 0 || height == 0) + app->current_output->on = FALSE; + else + app->current_output->on = TRUE; + } + + realign_outputs_after_resolution_change (app, app->current_output, old_width, old_height); + + rebuild_rate_combo (app); + rebuild_rotation_combo (app); + + foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); +} + +static void +lay_out_outputs_horizontally (App *app) +{ + int i; + int x; + + /* Lay out all the monitors horizontally when "mirror screens" is turned + * off, to avoid having all of them overlapped initially. We put the + * outputs turned off on the right-hand side. + */ + + x = 0; + + /* First pass, all "on" outputs */ + + for (i = 0; app->current_configuration->outputs[i]; ++i) + { + MateOutputInfo *output; + + output = app->current_configuration->outputs[i]; + if (output->connected && output->on) { + output->x = x; + output->y = 0; + x += output->width; + } + } + + /* Second pass, all the black screens */ + + for (i = 0; app->current_configuration->outputs[i]; ++i) + { + MateOutputInfo *output; + + output = app->current_configuration->outputs[i]; + if (!(output->connected && output->on)) { + output->x = x; + output->y = 0; + x += output->width; + } + } + +} + +/* FIXME: this function is copied from mate-settings-daemon/plugins/xrandr/gsd-xrandr-manager.c. + * Do we need to put this function in mate-desktop for public use? + */ +static gboolean +get_clone_size (MateRRScreen *screen, int *width, int *height) +{ + MateRRMode **modes = mate_rr_screen_list_clone_modes (screen); + int best_w, best_h; + int i; + + best_w = 0; + best_h = 0; + + for (i = 0; modes[i] != NULL; ++i) { + MateRRMode *mode = modes[i]; + int w, h; + + w = mate_rr_mode_get_width (mode); + h = mate_rr_mode_get_height (mode); + + if (w * h > best_w * best_h) { + best_w = w; + best_h = h; + } + } + + if (best_w > 0 && best_h > 0) { + if (width) + *width = best_w; + if (height) + *height = best_h; + + return TRUE; + } + + return FALSE; +} + +static gboolean +output_info_supports_mode (App *app, MateOutputInfo *info, int width, int height) +{ + MateRROutput *output; + MateRRMode **modes; + int i; + + if (!info->connected) + return FALSE; + + output = mate_rr_screen_get_output_by_name (app->screen, info->name); + if (!output) + return FALSE; + + modes = mate_rr_output_list_modes (output); + + for (i = 0; modes[i]; i++) { + if (mate_rr_mode_get_width (modes[i]) == width + && mate_rr_mode_get_height (modes[i]) == height) + return TRUE; + } + + return FALSE; +} + +static void +on_clone_changed (GtkWidget *box, gpointer data) +{ + App *app = data; + + app->current_configuration->clone = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (app->clone_checkbox)); + + if (app->current_configuration->clone) + { + int i; + int width, height; + + for (i = 0; app->current_configuration->outputs[i]; ++i) + { + if (app->current_configuration->outputs[i]->connected) + { + app->current_output = app->current_configuration->outputs[i]; + break; + } + } + + /* Turn on all the connected screens that support the best clone mode. + * The user may hit "Mirror Screens", but he shouldn't have to turn on + * all the required outputs as well. + */ + + get_clone_size (app->screen, &width, &height); + + for (i = 0; app->current_configuration->outputs[i]; i++) { + if (output_info_supports_mode (app, app->current_configuration->outputs[i], width, height)) { + app->current_configuration->outputs[i]->on = TRUE; + app->current_configuration->outputs[i]->width = width; + app->current_configuration->outputs[i]->height = height; + } + } + } + else + { + if (output_overlaps (app->current_output, app->current_configuration)) + lay_out_outputs_horizontally (app); + } + + rebuild_gui (app); +} + +static void +get_geometry (MateOutputInfo *output, int *w, int *h) +{ + if (output->on) + { + *h = output->height; + *w = output->width; + } + else + { + *h = output->pref_height; + *w = output->pref_width; + } + if ((output->rotation & MATE_RR_ROTATION_90) || (output->rotation & MATE_RR_ROTATION_270)) + { + int tmp; + tmp = *h; + *h = *w; + *w = tmp; + } +} + +#define SPACE 15 +#define MARGIN 15 + +static GList * +list_connected_outputs (App *app, int *total_w, int *total_h) +{ + int i, dummy; + GList *result = NULL; + + if (!total_w) + total_w = &dummy; + if (!total_h) + total_h = &dummy; + + *total_w = 0; + *total_h = 0; + for (i = 0; app->current_configuration->outputs[i] != NULL; ++i) + { + MateOutputInfo *output = app->current_configuration->outputs[i]; + + if (output->connected) + { + int w, h; + + result = g_list_prepend (result, output); + + get_geometry (output, &w, &h); + + *total_w += w; + *total_h += h; + } + } + + return g_list_reverse (result); +} + +static int +get_n_connected (App *app) +{ + GList *connected_outputs = list_connected_outputs (app, NULL, NULL); + int n = g_list_length (connected_outputs); + + g_list_free (connected_outputs); + + return n; +} + +static double +compute_scale (App *app) +{ + int available_w, available_h; + int total_w, total_h; + int n_monitors; + GdkRectangle viewport; + GList *connected_outputs; + + foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area), &viewport); + + connected_outputs = list_connected_outputs (app, &total_w, &total_h); + + n_monitors = g_list_length (connected_outputs); + + g_list_free (connected_outputs); + + available_w = viewport.width - 2 * MARGIN - (n_monitors - 1) * SPACE; + available_h = viewport.height - 2 * MARGIN - (n_monitors - 1) * SPACE; + + return MIN ((double)available_w / total_w, (double)available_h / total_h); +} + +typedef struct Edge +{ + MateOutputInfo *output; + int x1, y1; + int x2, y2; +} Edge; + +typedef struct Snap +{ + Edge *snapper; /* Edge that should be snapped */ + Edge *snappee; + int dy, dx; +} Snap; + +static void +add_edge (MateOutputInfo *output, int x1, int y1, int x2, int y2, GArray *edges) +{ + Edge e; + + e.x1 = x1; + e.x2 = x2; + e.y1 = y1; + e.y2 = y2; + e.output = output; + + g_array_append_val (edges, e); +} + +static void +list_edges_for_output (MateOutputInfo *output, GArray *edges) +{ + int x, y, w, h; + + x = output->x; + y = output->y; + get_geometry (output, &w, &h); + + /* Top, Bottom, Left, Right */ + add_edge (output, x, y, x + w, y, edges); + add_edge (output, x, y + h, x + w, y + h, edges); + add_edge (output, x, y, x, y + h, edges); + add_edge (output, x + w, y, x + w, y + h, edges); +} + +static void +list_edges (MateRRConfig *config, GArray *edges) +{ + int i; + + for (i = 0; config->outputs[i]; ++i) + { + MateOutputInfo *output = config->outputs[i]; + + if (output->connected) + list_edges_for_output (output, edges); + } +} + +static gboolean +overlap (int s1, int e1, int s2, int e2) +{ + return (!(e1 < s2 || s1 >= e2)); +} + +static gboolean +horizontal_overlap (Edge *snapper, Edge *snappee) +{ + if (snapper->y1 != snapper->y2 || snappee->y1 != snappee->y2) + return FALSE; + + return overlap (snapper->x1, snapper->x2, snappee->x1, snappee->x2); +} + +static gboolean +vertical_overlap (Edge *snapper, Edge *snappee) +{ + if (snapper->x1 != snapper->x2 || snappee->x1 != snappee->x2) + return FALSE; + + return overlap (snapper->y1, snapper->y2, snappee->y1, snappee->y2); +} + +static void +add_snap (GArray *snaps, Snap snap) +{ + if (ABS (snap.dx) <= 200 || ABS (snap.dy) <= 200) + g_array_append_val (snaps, snap); +} + +static void +add_edge_snaps (Edge *snapper, Edge *snappee, GArray *snaps) +{ + Snap snap; + + snap.snapper = snapper; + snap.snappee = snappee; + + if (horizontal_overlap (snapper, snappee)) + { + snap.dx = 0; + snap.dy = snappee->y1 - snapper->y1; + + add_snap (snaps, snap); + } + else if (vertical_overlap (snapper, snappee)) + { + snap.dy = 0; + snap.dx = snappee->x1 - snapper->x1; + + add_snap (snaps, snap); + } + + /* Corner snaps */ + /* 1->1 */ + snap.dx = snappee->x1 - snapper->x1; + snap.dy = snappee->y1 - snapper->y1; + + add_snap (snaps, snap); + + /* 1->2 */ + snap.dx = snappee->x2 - snapper->x1; + snap.dy = snappee->y2 - snapper->y1; + + add_snap (snaps, snap); + + /* 2->2 */ + snap.dx = snappee->x2 - snapper->x2; + snap.dy = snappee->y2 - snapper->y2; + + add_snap (snaps, snap); + + /* 2->1 */ + snap.dx = snappee->x1 - snapper->x2; + snap.dy = snappee->y1 - snapper->y2; + + add_snap (snaps, snap); +} + +static void +list_snaps (MateOutputInfo *output, GArray *edges, GArray *snaps) +{ + int i; + + for (i = 0; i < edges->len; ++i) + { + Edge *output_edge = &(g_array_index (edges, Edge, i)); + + if (output_edge->output == output) + { + int j; + + for (j = 0; j < edges->len; ++j) + { + Edge *edge = &(g_array_index (edges, Edge, j)); + + if (edge->output != output) + add_edge_snaps (output_edge, edge, snaps); + } + } + } +} + +#if 0 +static void +print_edge (Edge *edge) +{ + g_debug ("(%d %d %d %d)", edge->x1, edge->y1, edge->x2, edge->y2); +} +#endif + +static gboolean +corner_on_edge (int x, int y, Edge *e) +{ + if (x == e->x1 && x == e->x2 && y >= e->y1 && y <= e->y2) + return TRUE; + + if (y == e->y1 && y == e->y2 && x >= e->x1 && x <= e->x2) + return TRUE; + + return FALSE; +} + +static gboolean +edges_align (Edge *e1, Edge *e2) +{ + if (corner_on_edge (e1->x1, e1->y1, e2)) + return TRUE; + + if (corner_on_edge (e2->x1, e2->y1, e1)) + return TRUE; + + return FALSE; +} + +static gboolean +output_is_aligned (MateOutputInfo *output, GArray *edges) +{ + gboolean result = FALSE; + int i; + + for (i = 0; i < edges->len; ++i) + { + Edge *output_edge = &(g_array_index (edges, Edge, i)); + + if (output_edge->output == output) + { + int j; + + for (j = 0; j < edges->len; ++j) + { + Edge *edge = &(g_array_index (edges, Edge, j)); + + /* We are aligned if an output edge matches + * an edge of another output + */ + if (edge->output != output_edge->output) + { + if (edges_align (output_edge, edge)) + { + result = TRUE; + goto done; + } + } + } + } + } +done: + + return result; +} + +static void +get_output_rect (MateOutputInfo *output, GdkRectangle *rect) +{ + int w, h; + + get_geometry (output, &w, &h); + + rect->width = w; + rect->height = h; + rect->x = output->x; + rect->y = output->y; +} + +static gboolean +output_overlaps (MateOutputInfo *output, MateRRConfig *config) +{ + int i; + GdkRectangle output_rect; + + get_output_rect (output, &output_rect); + + for (i = 0; config->outputs[i]; ++i) + { + MateOutputInfo *other = config->outputs[i]; + + if (other != output && other->connected) + { + GdkRectangle other_rect; + + get_output_rect (other, &other_rect); + if (gdk_rectangle_intersect (&output_rect, &other_rect, NULL)) + return TRUE; + } + } + + return FALSE; +} + +static gboolean +mate_rr_config_is_aligned (MateRRConfig *config, GArray *edges) +{ + int i; + gboolean result = TRUE; + + for (i = 0; config->outputs[i]; ++i) + { + MateOutputInfo *output = config->outputs[i]; + + if (output->connected) + { + if (!output_is_aligned (output, edges)) + return FALSE; + + if (output_overlaps (output, config)) + return FALSE; + } + } + + return result; +} + +struct GrabInfo +{ + int grab_x; + int grab_y; + int output_x; + int output_y; +}; + +static gboolean +is_corner_snap (const Snap *s) +{ + return s->dx != 0 && s->dy != 0; +} + +static int +compare_snaps (gconstpointer v1, gconstpointer v2) +{ + const Snap *s1 = v1; + const Snap *s2 = v2; + int sv1 = MAX (ABS (s1->dx), ABS (s1->dy)); + int sv2 = MAX (ABS (s2->dx), ABS (s2->dy)); + int d; + + d = sv1 - sv2; + + /* This snapping algorithm is good enough for rock'n'roll, but + * this is probably a better: + * + * First do a horizontal/vertical snap, then + * with the new coordinates from that snap, + * do a corner snap. + * + * Right now, it's confusing that corner snapping + * depends on the distance in an axis that you can't actually see. + * + */ + if (d == 0) + { + if (is_corner_snap (s1) && !is_corner_snap (s2)) + return -1; + else if (is_corner_snap (s2) && !is_corner_snap (s1)) + return 1; + else + return 0; + } + else + { + return d; + } +} + +/* Sets a mouse cursor for a widget's window. As a hack, you can pass + * GDK_BLANK_CURSOR to mean "set the cursor to NULL" (i.e. reset the widget's + * window's cursor to its default). + */ +static void +set_cursor (GtkWidget *widget, GdkCursorType type) +{ + GdkCursor *cursor; + GdkWindow *window; + + if (type == GDK_BLANK_CURSOR) + cursor = NULL; + else + cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), type); + + window = gtk_widget_get_window (widget); + + if (window) + gdk_window_set_cursor (window, cursor); + + if (cursor) + gdk_cursor_unref (cursor); +} + +static void +set_monitors_tooltip (App *app, gboolean is_dragging) +{ + const char *text; + + if (is_dragging) + text = NULL; + else + text = _("Select a monitor to change its properties; drag it to rearrange its placement."); + + gtk_widget_set_tooltip_text (app->area, text); +} + +static void +on_output_event (FooScrollArea *area, + FooScrollAreaEvent *event, + gpointer data) +{ + MateOutputInfo *output = data; + App *app = g_object_get_data (G_OBJECT (area), "app"); + + /* If the mouse is inside the outputs, set the cursor to "you can move me". See + * on_canvas_event() for where we reset the cursor to the default if it + * exits the outputs' area. + */ + if (!app->current_configuration->clone && get_n_connected (app) > 1) + set_cursor (GTK_WIDGET (area), GDK_FLEUR); + + if (event->type == FOO_BUTTON_PRESS) + { + GrabInfo *info; + + app->current_output = output; + + rebuild_gui (app); + set_monitors_tooltip (app, TRUE); + + if (!app->current_configuration->clone && get_n_connected (app) > 1) + { + foo_scroll_area_begin_grab (area, on_output_event, data); + + info = g_new0 (GrabInfo, 1); + info->grab_x = event->x; + info->grab_y = event->y; + info->output_x = output->x; + info->output_y = output->y; + + output->user_data = info; + } + + foo_scroll_area_invalidate (area); + } + else + { + if (foo_scroll_area_is_grabbed (area)) + { + GrabInfo *info = output->user_data; + double scale = compute_scale (app); + int old_x, old_y; + int new_x, new_y; + int i; + GArray *edges, *snaps, *new_edges; + + old_x = output->x; + old_y = output->y; + new_x = info->output_x + (event->x - info->grab_x) / scale; + new_y = info->output_y + (event->y - info->grab_y) / scale; + + output->x = new_x; + output->y = new_y; + + edges = g_array_new (TRUE, TRUE, sizeof (Edge)); + snaps = g_array_new (TRUE, TRUE, sizeof (Snap)); + new_edges = g_array_new (TRUE, TRUE, sizeof (Edge)); + + list_edges (app->current_configuration, edges); + list_snaps (output, edges, snaps); + + g_array_sort (snaps, compare_snaps); + + output->x = info->output_x; + output->y = info->output_y; + + for (i = 0; i < snaps->len; ++i) + { + Snap *snap = &(g_array_index (snaps, Snap, i)); + GArray *new_edges = g_array_new (TRUE, TRUE, sizeof (Edge)); + + output->x = new_x + snap->dx; + output->y = new_y + snap->dy; + + g_array_set_size (new_edges, 0); + list_edges (app->current_configuration, new_edges); + + if (mate_rr_config_is_aligned (app->current_configuration, new_edges)) + { + g_array_free (new_edges, TRUE); + break; + } + else + { + output->x = info->output_x; + output->y = info->output_y; + } + } + + g_array_free (new_edges, TRUE); + g_array_free (snaps, TRUE); + g_array_free (edges, TRUE); + + if (event->type == FOO_BUTTON_RELEASE) + { + foo_scroll_area_end_grab (area); + set_monitors_tooltip (app, FALSE); + + g_free (output->user_data); + output->user_data = NULL; + +#if 0 + g_debug ("new position: %d %d %d %d", output->x, output->y, output->width, output->height); +#endif + } + + foo_scroll_area_invalidate (area); + } + } +} + +static void +on_canvas_event (FooScrollArea *area, + FooScrollAreaEvent *event, + gpointer data) +{ + /* If the mouse exits the outputs, reset the cursor to the default. See + * on_output_event() for where we set the cursor to the movement cursor if + * it is over one of the outputs. + */ + set_cursor (GTK_WIDGET (area), GDK_BLANK_CURSOR); +} + +static PangoLayout * +get_display_name (App *app, + MateOutputInfo *output) +{ + const char *text; + + if (app->current_configuration->clone) { + /* Translators: this is the feature where what you see on your laptop's + * screen is the same as your external monitor. Here, "Mirror" is being + * used as an adjective, not as a verb. For example, the Spanish + * translation could be "Pantallas en Espejo", *not* "Espejar Pantallas". + */ + text = _("Mirror Screens"); + } else + text = output->display_name; + + return gtk_widget_create_pango_layout ( + GTK_WIDGET (app->area), text); +} + +static void +paint_background (FooScrollArea *area, + cairo_t *cr) +{ + GdkRectangle viewport; + GtkWidget *widget; + GtkStyle *widget_style; + + widget = GTK_WIDGET (area); + + foo_scroll_area_get_viewport (area, &viewport); + widget_style = gtk_widget_get_style (widget); + + cairo_set_source_rgb (cr, + widget_style->base[GTK_STATE_SELECTED].red / 65535.0, + widget_style->base[GTK_STATE_SELECTED].green / 65535.0, + widget_style->base[GTK_STATE_SELECTED].blue / 65535.0); + + cairo_rectangle (cr, + viewport.x, viewport.y, + viewport.width, viewport.height); + + cairo_fill_preserve (cr); + + foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL); + + cairo_set_source_rgb (cr, + widget_style->dark[GTK_STATE_SELECTED].red / 65535.0, + widget_style->dark[GTK_STATE_SELECTED].green / 65535.0, + widget_style->dark[GTK_STATE_SELECTED].blue / 65535.0); + + cairo_stroke (cr); +} + +static void +paint_output (App *app, cairo_t *cr, int i) +{ + int w, h; + double scale = compute_scale (app); + double x, y; + int total_w, total_h; + GList *connected_outputs = list_connected_outputs (app, &total_w, &total_h); + MateOutputInfo *output = g_list_nth (connected_outputs, i)->data; + PangoLayout *layout = get_display_name (app, output); + PangoRectangle ink_extent, log_extent; + GdkRectangle viewport; + GdkColor output_color; + double r, g, b; + double available_w; + double factor; + + cairo_save (cr); + + foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area), &viewport); + + get_geometry (output, &w, &h); + +#if 0 + g_debug ("%s (%p) geometry %d %d %d", output->name, output, + w, h, output->rate); +#endif + + viewport.height -= 2 * MARGIN; + viewport.width -= 2 * MARGIN; + + x = output->x * scale + MARGIN + (viewport.width - total_w * scale) / 2.0; + y = output->y * scale + MARGIN + (viewport.height - total_h * scale) / 2.0; + +#if 0 + g_debug ("scaled: %f %f", x, y); + + g_debug ("scale: %f", scale); + + g_debug ("%f %f %f %f", x, y, w * scale + 0.5, h * scale + 0.5); +#endif + + cairo_save (cr); + + cairo_translate (cr, + x + (w * scale + 0.5) / 2, + y + (h * scale + 0.5) / 2); + + /* rotation is already applied in get_geometry */ + + if (output->rotation & MATE_RR_REFLECT_X) + cairo_scale (cr, -1, 1); + + if (output->rotation & MATE_RR_REFLECT_Y) + cairo_scale (cr, 1, -1); + + cairo_translate (cr, + - x - (w * scale + 0.5) / 2, + - y - (h * scale + 0.5) / 2); + + + cairo_rectangle (cr, x, y, w * scale + 0.5, h * scale + 0.5); + cairo_clip_preserve (cr); + + mate_rr_labeler_get_color_for_output (app->labeler, output, &output_color); + r = output_color.red / 65535.0; + g = output_color.green / 65535.0; + b = output_color.blue / 65535.0; + + if (!output->on) + { + /* If the output is turned off, just darken the selected color */ + r *= 0.2; + g *= 0.2; + b *= 0.2; + } + + cairo_set_source_rgba (cr, r, g, b, 1.0); + + foo_scroll_area_add_input_from_fill (FOO_SCROLL_AREA (app->area), + cr, on_output_event, output); + cairo_fill (cr); + + if (output == app->current_output) + { + cairo_rectangle (cr, x + 2, y + 2, w * scale + 0.5 - 4, h * scale + 0.5 - 4); + + cairo_set_line_width (cr, 4); + cairo_set_source_rgba (cr, 0.33, 0.43, 0.57, 1.0); + cairo_stroke (cr); + } + + cairo_rectangle (cr, x + 0.5, y + 0.5, w * scale + 0.5 - 1, h * scale + 0.5 - 1); + + cairo_set_line_width (cr, 1); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); + + cairo_stroke (cr); + cairo_set_line_width (cr, 2); + + layout_set_font (layout, "Sans Bold 12"); + pango_layout_get_pixel_extents (layout, &ink_extent, &log_extent); + + available_w = w * scale + 0.5 - 6; /* Same as the inner rectangle's width, minus 1 pixel of padding on each side */ + if (available_w < ink_extent.width) + factor = available_w / ink_extent.width; + else + factor = 1.0; + + cairo_move_to (cr, + x + ((w * scale + 0.5) - factor * log_extent.width) / 2, + y + ((h * scale + 0.5) - factor * log_extent.height) / 2); + + cairo_scale (cr, factor, factor); + + if (output->on) + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + else + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + + pango_cairo_show_layout (cr, layout); + + cairo_restore (cr); + + g_object_unref (layout); +} + +static void +on_area_paint (FooScrollArea *area, + cairo_t *cr, + GdkRectangle *extent, + GdkRegion *region, + gpointer data) +{ + App *app = data; + double scale; + GList *connected_outputs = NULL; + GList *list; + + paint_background (area, cr); + + if (!app->current_configuration) + return; + + scale = compute_scale (app); + connected_outputs = list_connected_outputs (app, NULL, NULL); + +#if 0 + g_debug ("scale: %f", scale); +#endif + + for (list = connected_outputs; list != NULL; list = list->next) + { + paint_output (app, cr, g_list_position (connected_outputs, list)); + + if (app->current_configuration->clone) + break; + } +} + +static void +make_text_combo (GtkWidget *widget, int sort_column) +{ + GtkComboBox *box = GTK_COMBO_BOX (widget); + GtkListStore *store = gtk_list_store_new ( + 6, + G_TYPE_STRING, /* Text */ + G_TYPE_INT, /* Width */ + G_TYPE_INT, /* Height */ + G_TYPE_INT, /* Frequency */ + G_TYPE_INT, /* Width * Height */ + G_TYPE_INT); /* Rotation */ + + GtkCellRenderer *cell; + + gtk_cell_layout_clear (GTK_CELL_LAYOUT (widget)); + + gtk_combo_box_set_model (box, GTK_TREE_MODEL (store)); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell, + "text", 0, + NULL); + + if (sort_column != -1) + { + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + sort_column, + GTK_SORT_DESCENDING); + } +} + +static void +compute_virtual_size_for_configuration (MateRRConfig *config, int *ret_width, int *ret_height) +{ + int i; + int width, height; + + width = height = 0; + + for (i = 0; config->outputs[i] != NULL; i++) + { + MateOutputInfo *output; + + output = config->outputs[i]; + + if (output->on) + { + width = MAX (width, output->x + output->width); + height = MAX (height, output->y + output->height); + } + } + + *ret_width = width; + *ret_height = height; +} + +static void +check_required_virtual_size (App *app) +{ + int req_width, req_height; + int min_width, max_width; + int min_height, max_height; + + compute_virtual_size_for_configuration (app->current_configuration, &req_width, &req_height); + + mate_rr_screen_get_ranges (app->screen, &min_width, &max_width, &min_height, &max_height); + +#if 0 + g_debug ("X Server supports:"); + g_debug ("min_width = %d, max_width = %d", min_width, max_width); + g_debug ("min_height = %d, max_height = %d", min_height, max_height); + + g_debug ("Requesting size of %dx%d", req_width, req_height); +#endif + + if (!(min_width <= req_width && req_width <= max_width + && min_height <= req_height && req_height <= max_height)) + { + /* FIXME: present a useful dialog, maybe even before the user tries to Apply */ +#if 0 + g_debug ("Your X server needs a larger Virtual size!"); +#endif + } +} + +static void +begin_version2_apply_configuration (App *app, GdkWindow *parent_window, guint32 timestamp) +{ + XID parent_window_xid; + + parent_window_xid = GDK_WINDOW_XID (parent_window); + + app->proxy = dbus_g_proxy_new_for_name (app->connection, + "org.mate.SettingsDaemon", + "/org/mate/SettingsDaemon/XRANDR", + "org.mate.SettingsDaemon.XRANDR_2"); + g_assert (app->proxy != NULL); /* that call does not fail unless we pass bogus names */ + + app->apply_configuration_state = APPLYING_VERSION_2; + app->proxy_call = dbus_g_proxy_begin_call (app->proxy, "ApplyConfiguration", + apply_configuration_returned_cb, app, + NULL, + G_TYPE_INT64, (gint64) parent_window_xid, + G_TYPE_INT64, (gint64) timestamp, + G_TYPE_INVALID, + G_TYPE_INVALID); + /* FIXME: we don't check for app->proxy_call == NULL, which could happen if + * the connection was disconnected. This is left as an exercise for the + * reader. + */ +} + +static void +begin_version1_apply_configuration (App *app) +{ + app->proxy = dbus_g_proxy_new_for_name (app->connection, + "org.mate.SettingsDaemon", + "/org/mate/SettingsDaemon/XRANDR", + "org.mate.SettingsDaemon.XRANDR"); + g_assert (app->proxy != NULL); /* that call does not fail unless we pass bogus names */ + + app->apply_configuration_state = APPLYING_VERSION_1; + app->proxy_call = dbus_g_proxy_begin_call (app->proxy, "ApplyConfiguration", + apply_configuration_returned_cb, app, + NULL, + G_TYPE_INVALID, + G_TYPE_INVALID); + /* FIXME: we don't check for app->proxy_call == NULL, which could happen if + * the connection was disconnected. This is left as an exercise for the + * reader. + */ +} + +static void +ensure_current_configuration_is_saved (void) +{ + MateRRScreen *rr_screen; + MateRRConfig *rr_config; + + /* Normally, mate_rr_config_save() creates a backup file based on the + * old monitors.xml. However, if *that* file didn't exist, there is + * nothing from which to create a backup. So, here we'll save the + * current/unchanged configuration and then let our caller call + * mate_rr_config_save() again with the new/changed configuration, so + * that there *will* be a backup file in the end. + */ + + rr_screen = mate_rr_screen_new (gdk_screen_get_default (), NULL, NULL, NULL); /* NULL-GError */ + if (!rr_screen) + return; + + rr_config = mate_rr_config_new_current (rr_screen); + mate_rr_config_save (rr_config, NULL); /* NULL-GError */ + + mate_rr_config_free (rr_config); + mate_rr_screen_destroy (rr_screen); +} + +/* Callback for dbus_g_proxy_begin_call() */ +static void +apply_configuration_returned_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + void *data) +{ + App *app = data; + gboolean success; + GError *error; + + g_assert (call_id == app->proxy_call); + + error = NULL; + success = dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID); + + if (!success) { + if (app->apply_configuration_state == APPLYING_VERSION_2 + && g_error_matches (error, DBUS_GERROR, DBUS_GERROR_UNKNOWN_METHOD)) { + g_error_free (error); + + g_object_unref (app->proxy); + app->proxy = NULL; + + begin_version1_apply_configuration (app); + return; + } else { + /* We don't pop up an error message; mate-settings-daemon already does that + * in case the selected RANDR configuration could not be applied. + */ + g_error_free (error); + } + } + + g_object_unref (app->proxy); + app->proxy = NULL; + + dbus_g_connection_unref (app->connection); + app->connection = NULL; + app->proxy_call = NULL; + + gtk_widget_set_sensitive (app->dialog, TRUE); +} + +static gboolean +sanitize_and_save_configuration (App *app) +{ + GError *error; + + mate_rr_config_sanitize (app->current_configuration); + + check_required_virtual_size (app); + + foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)); + + ensure_current_configuration_is_saved (); + + error = NULL; + if (!mate_rr_config_save (app->current_configuration, &error)) + { + error_message (app, _("Could not save the monitor configuration"), error->message); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +static void +apply (App *app) +{ + GError *error = NULL; + + if (!sanitize_and_save_configuration (app)) + return; + + g_assert (app->connection == NULL); + g_assert (app->proxy == NULL); + g_assert (app->proxy_call == NULL); + + app->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (app->connection == NULL) { + error_message (app, _("Could not get session bus while applying display configuration"), error->message); + g_error_free (error); + return; + } + + gtk_widget_set_sensitive (app->dialog, FALSE); + + begin_version2_apply_configuration (app, gtk_widget_get_window (app->dialog), app->apply_button_clicked_timestamp); +} + +#if 0 +/* Returns whether the graphics driver doesn't advertise RANDR 1.2 features, and just 1.0 */ +static gboolean +driver_is_randr_10 (MateRRConfig *config) +{ + /* In the Xorg code, see xserver/randr/rrinfo.c:RRScanOldConfig(). It gets + * called when the graphics driver doesn't support RANDR 1.2 yet, just 1.0. + * In that case, the X server's base code (which supports RANDR 1.2) will + * simulate having a single output called "default". For drivers that *do* + * support RANDR 1.2, the separate outputs will be named differently, we + * hope. + * + * This heuristic is courtesy of Dirk Mueller + * + * FIXME: however, we don't even check for XRRQueryVersion() returning 1.2, neither + * here nor in mate-desktop/libmatedesktop*.c. Do we need to check for that, + * or is mate_rr_screen_new()'s return value sufficient? + */ + + return (count_all_outputs (config) == 1 && strcmp (config->outputs[0]->name, "default") == 0); +} +#endif + +static void +on_detect_displays (GtkWidget *widget, gpointer data) +{ + App *app = data; + GError *error; + + error = NULL; + if (!mate_rr_screen_refresh (app->screen, &error)) { + if (error) { + error_message (app, _("Could not detect displays"), error->message); + g_error_free (error); + } + } +} + +#define SHOW_ICON_KEY "/apps/mate_settings_daemon/xrandr/show_notification_icon" + + +static void +on_show_icon_toggled (GtkWidget *widget, gpointer data) +{ + GtkToggleButton *tb = GTK_TOGGLE_BUTTON (widget); + App *app = data; + + mateconf_client_set_bool (app->client, SHOW_ICON_KEY, + gtk_toggle_button_get_active (tb), NULL); +} + +static MateOutputInfo * +get_nearest_output (MateRRConfig *configuration, int x, int y) +{ + int i; + int nearest_index; + int nearest_dist; + + nearest_index = -1; + nearest_dist = G_MAXINT; + + for (i = 0; configuration->outputs[i] != NULL; i++) + { + MateOutputInfo *output; + int dist_x, dist_y; + + output = configuration->outputs[i]; + + if (!(output->connected && output->on)) + continue; + + if (x < output->x) + dist_x = output->x - x; + else if (x >= output->x + output->width) + dist_x = x - (output->x + output->width) + 1; + else + dist_x = 0; + + if (y < output->y) + dist_y = output->y - y; + else if (y >= output->y + output->height) + dist_y = y - (output->y + output->height) + 1; + else + dist_y = 0; + + if (MIN (dist_x, dist_y) < nearest_dist) + { + nearest_dist = MIN (dist_x, dist_y); + nearest_index = i; + } + } + + if (nearest_index != -1) + return configuration->outputs[nearest_index]; + else + return NULL; + +} + +/* Gets the output that contains the largest intersection with the window. + * Logic stolen from gdk_screen_get_monitor_at_window(). + */ +static MateOutputInfo * +get_output_for_window (MateRRConfig *configuration, GdkWindow *window) +{ + GdkRectangle win_rect; + int i; + int largest_area; + int largest_index; + + gdk_window_get_geometry (window, &win_rect.x, &win_rect.y, &win_rect.width, &win_rect.height, NULL); + gdk_window_get_origin (window, &win_rect.x, &win_rect.y); + + largest_area = 0; + largest_index = -1; + + for (i = 0; configuration->outputs[i] != NULL; i++) + { + MateOutputInfo *output; + GdkRectangle output_rect, intersection; + + output = configuration->outputs[i]; + + output_rect.x = output->x; + output_rect.y = output->y; + output_rect.width = output->width; + output_rect.height = output->height; + + if (output->connected && gdk_rectangle_intersect (&win_rect, &output_rect, &intersection)) + { + int area; + + area = intersection.width * intersection.height; + if (area > largest_area) + { + largest_area = area; + largest_index = i; + } + } + } + + if (largest_index != -1) + return configuration->outputs[largest_index]; + else + return get_nearest_output (configuration, + win_rect.x + win_rect.width / 2, + win_rect.y + win_rect.height / 2); +} + +/* We select the current output, i.e. select the one being edited, based on + * which output is showing the configuration dialog. + */ +static void +select_current_output_from_dialog_position (App *app) +{ + if (gtk_widget_get_realized (app->dialog)) + app->current_output = get_output_for_window (app->current_configuration, gtk_widget_get_window (app->dialog)); + else + app->current_output = NULL; + + rebuild_gui (app); +} + +/* This is a GtkWidget::map-event handler. We wait for the display-properties + * dialog to be mapped, and then we select the output which corresponds to the + * monitor on which the dialog is being shown. + */ +static gboolean +dialog_map_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data) +{ + App *app = data; + + select_current_output_from_dialog_position (app); + return FALSE; +} + +static void +hide_help_button (App *app) +{ + GtkWidget *action_area; + GList *children; + GList *l; + + action_area = gtk_dialog_get_action_area (GTK_DIALOG (app->dialog)); + children = gtk_container_get_children (GTK_CONTAINER (action_area)); + + for (l = children; l; l = l->next) + { + GtkWidget *child; + int response; + + child = GTK_WIDGET (l->data); + + response = gtk_dialog_get_response_for_widget (GTK_DIALOG (app->dialog), child); + if (response == GTK_RESPONSE_HELP) + { + gtk_widget_hide (child); + return; + } + } +} + +static void +apply_button_clicked_cb (GtkButton *button, gpointer data) +{ + App *app = data; + + /* We simply store the timestamp at which the Apply button was clicked. + * We'll just wait for the dialog to return from gtk_dialog_run(), and + * *then* use the timestamp when applying the RANDR configuration. + */ + + app->apply_button_clicked_timestamp = gtk_get_current_event_time (); +} + +static void +success_dialog_for_make_default (App *app) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (app->dialog), + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("The monitor configuration has been saved")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("This configuration will be used the next time someone logs in.")); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static void +error_dialog_for_make_default (App *app, const char *error_text) +{ + error_message (app, _("Could not set the default configuration for monitors"), error_text); +} + +static void +make_default (App *app) +{ + char *command_line; + char *source_filename; + char *dest_filename; + char *dest_basename; + char *std_error; + gint exit_status; + GError *error; + + if (!sanitize_and_save_configuration (app)) + return; + + dest_filename = mateconf_client_get_string (app->client, "/apps/mate_settings_daemon/xrandr/default_configuration_file", NULL); + if (!dest_filename) + return; /* FIXME: present an error? */ + + dest_basename = g_path_get_basename (dest_filename); + + source_filename = mate_rr_config_get_intended_filename (); + + command_line = g_strdup_printf ("pkexec %s/mate-display-properties-install-systemwide %s %s", + SBINDIR, + source_filename, + dest_basename); + + error = NULL; + if (!g_spawn_command_line_sync (command_line, NULL, &std_error, &exit_status, &error)) + { + error_dialog_for_make_default (app, error->message); + g_error_free (error); + } + else if (!WIFEXITED (exit_status) || WEXITSTATUS (exit_status) != 0) + { + error_dialog_for_make_default (app, std_error); + } + else + { + success_dialog_for_make_default (app); + } + + g_free (std_error); + g_free (dest_filename); + g_free (dest_basename); + g_free (source_filename); + g_free (command_line); +} + +static GtkWidget* +_gtk_builder_get_widget (GtkBuilder *builder, const gchar *name) +{ + return GTK_WIDGET (gtk_builder_get_object (builder, name)); +} + +static void +run_application (App *app) +{ +#ifndef UIDIR +#define UIDIR "." +#endif +#define UI_FILE UIDIR "/display-capplet.ui" + GtkBuilder *builder; + GtkWidget *align; + GError *error; + + error = NULL; + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, UI_FILE, &error) == 0) + { + g_warning ("Could not parse UI definition: %s", error->message); + g_error_free (error); + g_object_unref (builder); + return; + } + + app->screen = mate_rr_screen_new (gdk_screen_get_default (), + on_screen_changed, app, &error); + if (!app->screen) + { + error_message (NULL, _("Could not get screen information"), error->message); + g_error_free (error); + g_object_unref (builder); + return; + } + + app->client = mateconf_client_get_default (); + + app->dialog = _gtk_builder_get_widget (builder, "dialog"); + g_signal_connect_after (app->dialog, "map-event", + G_CALLBACK (dialog_map_event_cb), app); + + gtk_window_set_default_icon_name ("mate-preferences-desktop-display"); + gtk_window_set_icon_name (GTK_WINDOW (app->dialog), + "mate-preferences-desktop-display"); + + app->current_monitor_event_box = _gtk_builder_get_widget (builder, + "current_monitor_event_box"); + app->current_monitor_label = _gtk_builder_get_widget (builder, + "current_monitor_label"); + + app->monitor_on_radio = _gtk_builder_get_widget (builder, + "monitor_on_radio"); + app->monitor_off_radio = _gtk_builder_get_widget (builder, + "monitor_off_radio"); + g_signal_connect (app->monitor_on_radio, "toggled", + G_CALLBACK (monitor_on_off_toggled_cb), app); + g_signal_connect (app->monitor_off_radio, "toggled", + G_CALLBACK (monitor_on_off_toggled_cb), app); + + app->resolution_combo = _gtk_builder_get_widget (builder, + "resolution_combo"); + g_signal_connect (app->resolution_combo, "changed", + G_CALLBACK (on_resolution_changed), app); + + app->refresh_combo = _gtk_builder_get_widget (builder, "refresh_combo"); + g_signal_connect (app->refresh_combo, "changed", + G_CALLBACK (on_rate_changed), app); + + app->rotation_combo = _gtk_builder_get_widget (builder, "rotation_combo"); + g_signal_connect (app->rotation_combo, "changed", + G_CALLBACK (on_rotation_changed), app); + + app->clone_checkbox = _gtk_builder_get_widget (builder, "clone_checkbox"); + g_signal_connect (app->clone_checkbox, "toggled", + G_CALLBACK (on_clone_changed), app); + + g_signal_connect (_gtk_builder_get_widget (builder, "detect_displays_button"), + "clicked", G_CALLBACK (on_detect_displays), app); + + app->show_icon_checkbox = _gtk_builder_get_widget (builder, + "show_notification_icon"); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->show_icon_checkbox), + mateconf_client_get_bool (app->client, SHOW_ICON_KEY, NULL)); + + g_signal_connect (app->show_icon_checkbox, "toggled", G_CALLBACK (on_show_icon_toggled), app); + + app->panel_checkbox = _gtk_builder_get_widget (builder, "panel_checkbox"); + + make_text_combo (app->resolution_combo, 4); + make_text_combo (app->refresh_combo, 3); + make_text_combo (app->rotation_combo, -1); + + g_assert (app->panel_checkbox); + + /* Scroll Area */ + app->area = (GtkWidget *)foo_scroll_area_new (); + + g_object_set_data (G_OBJECT (app->area), "app", app); + + set_monitors_tooltip (app, FALSE); + + /* FIXME: this should be computed dynamically */ + foo_scroll_area_set_min_size (FOO_SCROLL_AREA (app->area), -1, 200); + gtk_widget_show (app->area); + g_signal_connect (app->area, "paint", + G_CALLBACK (on_area_paint), app); + g_signal_connect (app->area, "viewport_changed", + G_CALLBACK (on_viewport_changed), app); + + align = _gtk_builder_get_widget (builder, "align"); + + gtk_container_add (GTK_CONTAINER (align), app->area); + + /* Until we have help to show, we'll just hide the Help button */ + hide_help_button (app); + + app->apply_button = _gtk_builder_get_widget (builder, "apply_button"); + g_signal_connect (app->apply_button, "clicked", + G_CALLBACK (apply_button_clicked_cb), app); + + on_screen_changed (app->screen, app); + + g_object_unref (builder); + +restart: + switch (gtk_dialog_run (GTK_DIALOG (app->dialog))) + { + default: + /* Fall Through */ + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CLOSE: +#if 0 + g_debug ("Close"); +#endif + break; + + case GTK_RESPONSE_HELP: +#if 0 + g_debug ("Help"); +#endif + goto restart; + break; + + case GTK_RESPONSE_APPLY: + apply (app); + goto restart; + break; + + case RESPONSE_MAKE_DEFAULT: + make_default (app); + goto restart; + break; + } + + gtk_widget_destroy (app->dialog); + mate_rr_screen_destroy (app->screen); + g_object_unref (app->client); +} + +int +main (int argc, char **argv) +{ + App *app; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init (&argc, &argv); + + app = g_new0 (App, 1); + + run_application (app); + + g_free (app); + + return 0; +} diff --git a/capplets/keybindings/00-multimedia-key.xml.in b/capplets/keybindings/00-multimedia-key.xml.in new file mode 100644 index 00000000..cd8263a1 --- /dev/null +++ b/capplets/keybindings/00-multimedia-key.xml.in @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/capplets/keybindings/01-desktop-key.xml.in b/capplets/keybindings/01-desktop-key.xml.in new file mode 100644 index 00000000..dc0234bb --- /dev/null +++ b/capplets/keybindings/01-desktop-key.xml.in @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/capplets/keybindings/Makefile.am b/capplets/keybindings/Makefile.am new file mode 100644 index 00000000..ed97253f --- /dev/null +++ b/capplets/keybindings/Makefile.am @@ -0,0 +1,46 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = keybinding + +bin_PROGRAMS = mate-keybinding-properties + +mate_keybinding_properties_LDADD = $(MATECC_CAPPLETS_LIBS) +mate_keybinding_properties_SOURCES = \ + mate-keybinding-properties.c \ + eggcellrendererkeys.c \ + eggcellrendererkeys.h \ + eggaccelerators.c \ + eggaccelerators.h + +@INTLTOOL_DESKTOP_RULE@ + +uidir = $(pkgdatadir)/ui +ui_DATA = mate-keybinding-properties.ui + +desktopdir = $(datadir)/applications +Desktop_in_files = mate-keybinding.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +@INTLTOOL_XML_NOMERGE_RULE@ + +xmldir = $(pkgdatadir)/keybindings +xml_in_files = 00-multimedia-key.xml.in 01-desktop-key.xml.in +xml_DATA = $(xml_in_files:.xml.in=.xml) + +pkgconfigdir = $(datadir)/pkgconfig +pkgconfig_DATA = mate-keybindings.pc + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_UI_DIR="\"$(uidir)\"" \ + -DMATECC_KEYBINDINGS_DIR="\"$(pkgdatadir)/keybindings\"" +CLEANFILES = \ + $(MATECC_CAPPLETS_CLEANFILES) \ + $(Desktop_in_files) \ + $(desktop_DATA) \ + $(xml_DATA) +EXTRA_DIST = $(ui_DATA) $(xml_in_files) mate-keybindings.pc.in + + +-include $(top_srcdir)/git.mk diff --git a/capplets/keybindings/eggaccelerators.c b/capplets/keybindings/eggaccelerators.c new file mode 100644 index 00000000..0728229d --- /dev/null +++ b/capplets/keybindings/eggaccelerators.c @@ -0,0 +1,632 @@ +/* eggaccelerators.c + * Copyright (C) 2002 Red Hat, Inc.; Copyright 1998, 2001 Tim Janik + * Developed by Havoc Pennington, Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "eggaccelerators.h" + +#include +#include +#include +#include +#include + +enum +{ + EGG_MODMAP_ENTRY_SHIFT = 0, + EGG_MODMAP_ENTRY_LOCK = 1, + EGG_MODMAP_ENTRY_CONTROL = 2, + EGG_MODMAP_ENTRY_MOD1 = 3, + EGG_MODMAP_ENTRY_MOD2 = 4, + EGG_MODMAP_ENTRY_MOD3 = 5, + EGG_MODMAP_ENTRY_MOD4 = 6, + EGG_MODMAP_ENTRY_MOD5 = 7, + EGG_MODMAP_ENTRY_LAST = 8 +}; + +#define MODMAP_ENTRY_TO_MODIFIER(x) (1 << (x)) + +typedef struct +{ + EggVirtualModifierType mapping[EGG_MODMAP_ENTRY_LAST]; + +} EggModmap; + +const EggModmap* egg_keymap_get_modmap (GdkKeymap *keymap); + +static inline gboolean +is_alt (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'a' || string[1] == 'A') && + (string[2] == 'l' || string[2] == 'L') && + (string[3] == 't' || string[3] == 'T') && + (string[4] == '>')); +} + +static inline gboolean +is_ctl (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 't' || string[2] == 'T') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == '>')); +} + +static inline gboolean +is_modx (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'm' || string[1] == 'M') && + (string[2] == 'o' || string[2] == 'O') && + (string[3] == 'd' || string[3] == 'D') && + (string[4] >= '1' && string[4] <= '5') && + (string[5] == '>')); +} + +static inline gboolean +is_ctrl (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 't' || string[2] == 'T') && + (string[3] == 'r' || string[3] == 'R') && + (string[4] == 'l' || string[4] == 'L') && + (string[5] == '>')); +} + +static inline gboolean +is_shft (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'h' || string[2] == 'H') && + (string[3] == 'f' || string[3] == 'F') && + (string[4] == 't' || string[4] == 'T') && + (string[5] == '>')); +} + +static inline gboolean +is_shift (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'h' || string[2] == 'H') && + (string[3] == 'i' || string[3] == 'I') && + (string[4] == 'f' || string[4] == 'F') && + (string[5] == 't' || string[5] == 'T') && + (string[6] == '>')); +} + +static inline gboolean +is_control (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 'o' || string[2] == 'O') && + (string[3] == 'n' || string[3] == 'N') && + (string[4] == 't' || string[4] == 'T') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == 'o' || string[6] == 'O') && + (string[7] == 'l' || string[7] == 'L') && + (string[8] == '>')); +} + +static inline gboolean +is_release (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'r' || string[1] == 'R') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'a' || string[5] == 'A') && + (string[6] == 's' || string[6] == 'S') && + (string[7] == 'e' || string[7] == 'E') && + (string[8] == '>')); +} + +static inline gboolean +is_meta (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'm' || string[1] == 'M') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 't' || string[3] == 'T') && + (string[4] == 'a' || string[4] == 'A') && + (string[5] == '>')); +} + +static inline gboolean +is_super (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'u' || string[2] == 'U') && + (string[3] == 'p' || string[3] == 'P') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == '>')); +} + +static inline gboolean +is_hyper (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'h' || string[1] == 'H') && + (string[2] == 'y' || string[2] == 'Y') && + (string[3] == 'p' || string[3] == 'P') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == '>')); +} + +static inline gboolean +is_keycode (const gchar *string) +{ + return ((string[0] == '0') && + (string[1] == 'x')); +} + +/** + * egg_accelerator_parse_virtual: + * @accelerator: string representing an accelerator + * @accelerator_key: return location for accelerator keyval + * @accelerator_mods: return location for accelerator modifier mask + * + * Parses a string representing a virtual accelerator. The format + * looks like "<Control>a" or "<Shift><Alt>F1" or + * "<Release>z" (the last one is for key release). The parser + * is fairly liberal and allows lower or upper case, and also + * abbreviations such as "<Ctl>" and "<Ctrl>". + * + * If the parse fails, @accelerator_key and @accelerator_mods will + * be set to 0 (zero) and %FALSE will be returned. If the string contains + * only modifiers, @accelerator_key will be set to 0 but %TRUE will be + * returned. + * + * The virtual vs. concrete accelerator distinction is a relic of + * how the X Window System works; there are modifiers Mod2-Mod5 that + * can represent various keyboard keys (numlock, meta, hyper, etc.), + * the virtual modifier represents the keyboard key, the concrete + * modifier the actual Mod2-Mod5 bits in the key press event. + * + * Returns: %TRUE on success. + */ +gboolean +egg_accelerator_parse_virtual (const gchar *accelerator, + guint *accelerator_key, + guint *keycode, + EggVirtualModifierType *accelerator_mods) +{ + guint keyval; + GdkModifierType mods; + gint len; + gboolean bad_keyval; + + if (accelerator_key) + *accelerator_key = 0; + if (accelerator_mods) + *accelerator_mods = 0; + if (keycode) + *keycode = 0; + + g_return_val_if_fail (accelerator != NULL, FALSE); + + bad_keyval = FALSE; + + keyval = 0; + mods = 0; + len = strlen (accelerator); + while (len) + { + if (*accelerator == '<') + { + if (len >= 9 && is_release (accelerator)) + { + accelerator += 9; + len -= 9; + mods |= EGG_VIRTUAL_RELEASE_MASK; + } + else if (len >= 9 && is_control (accelerator)) + { + accelerator += 9; + len -= 9; + mods |= EGG_VIRTUAL_CONTROL_MASK; + } + else if (len >= 7 && is_shift (accelerator)) + { + accelerator += 7; + len -= 7; + mods |= EGG_VIRTUAL_SHIFT_MASK; + } + else if (len >= 6 && is_shft (accelerator)) + { + accelerator += 6; + len -= 6; + mods |= EGG_VIRTUAL_SHIFT_MASK; + } + else if (len >= 6 && is_ctrl (accelerator)) + { + accelerator += 6; + len -= 6; + mods |= EGG_VIRTUAL_CONTROL_MASK; + } + else if (len >= 6 && is_modx (accelerator)) + { + static const guint mod_vals[] = { + EGG_VIRTUAL_ALT_MASK, EGG_VIRTUAL_MOD2_MASK, EGG_VIRTUAL_MOD3_MASK, + EGG_VIRTUAL_MOD4_MASK, EGG_VIRTUAL_MOD5_MASK + }; + + len -= 6; + accelerator += 4; + mods |= mod_vals[*accelerator - '1']; + accelerator += 2; + } + else if (len >= 5 && is_ctl (accelerator)) + { + accelerator += 5; + len -= 5; + mods |= EGG_VIRTUAL_CONTROL_MASK; + } + else if (len >= 5 && is_alt (accelerator)) + { + accelerator += 5; + len -= 5; + mods |= EGG_VIRTUAL_ALT_MASK; + } + else if (len >= 6 && is_meta (accelerator)) + { + accelerator += 6; + len -= 6; + mods |= EGG_VIRTUAL_META_MASK; + } + else if (len >= 7 && is_hyper (accelerator)) + { + accelerator += 7; + len -= 7; + mods |= EGG_VIRTUAL_HYPER_MASK; + } + else if (len >= 7 && is_super (accelerator)) + { + accelerator += 7; + len -= 7; + mods |= EGG_VIRTUAL_SUPER_MASK; + } + else + { + gchar last_ch; + + last_ch = *accelerator; + while (last_ch && last_ch != '>') + { + last_ch = *accelerator; + accelerator += 1; + len -= 1; + } + } + } + else + { + keyval = gdk_keyval_from_name (accelerator); + + if (keyval == 0) + { + /* If keyval is 0, then maybe it's a keycode. Check for 0x## */ + if (len >= 4 && is_keycode (accelerator)) + { + char keystring[5]; + gchar *endptr; + gint tmp_keycode; + + memcpy (keystring, accelerator, 4); + keystring [4] = '\000'; + + tmp_keycode = strtol (keystring, &endptr, 16); + + if (endptr == NULL || *endptr != '\000') + { + bad_keyval = TRUE; + } + else if (keycode != NULL) + { + *keycode = tmp_keycode; + /* 0x00 is an invalid keycode too. */ + if (*keycode == 0) + bad_keyval = TRUE; + } + } + } + else if (keycode != NULL) + { + *keycode = XKeysymToKeycode (GDK_DISPLAY(), keyval); + if (*keycode == 0) + bad_keyval = TRUE; + } + + accelerator += len; + len -= len; + } + } + + if (accelerator_key) + *accelerator_key = gdk_keyval_to_lower (keyval); + if (accelerator_mods) + *accelerator_mods = mods; + + return !bad_keyval; +} + +/** + * egg_virtual_accelerator_name: + * @accelerator_key: accelerator keyval + * @accelerator_mods: accelerator modifier mask + * @returns: a newly-allocated accelerator name + * + * Converts an accelerator keyval and modifier mask + * into a string parseable by egg_accelerator_parse_virtual(). + * For example, if you pass in #GDK_q and #EGG_VIRTUAL_CONTROL_MASK, + * this function returns "<Control>q". + * + * The caller of this function must free the returned string. + */ +gchar* +egg_virtual_accelerator_name (guint accelerator_key, + guint keycode, + EggVirtualModifierType accelerator_mods) +{ + gchar *gtk_name; + GdkModifierType gdkmods = 0; + + egg_keymap_resolve_virtual_modifiers (NULL, accelerator_mods, &gdkmods); + gtk_name = gtk_accelerator_name (accelerator_key, gdkmods); + + if (!accelerator_key) + { + gchar *name; + name = g_strdup_printf ("%s0x%02x", gtk_name, keycode); + g_free (gtk_name); + return name; + } + + return gtk_name; +} + +/** + * egg_virtual_accelerator_label: + * @accelerator_key: accelerator keyval + * @accelerator_mods: accelerator modifier mask + * @returns: a newly-allocated accelerator label + * + * Converts an accelerator keyval and modifier mask + * into a (possibly translated) string that can be displayed to + * a user. + * For example, if you pass in #GDK_q and #EGG_VIRTUAL_CONTROL_MASK, + * and you use a German locale, this function returns "Strg+Q". + * + * The caller of this function must free the returned string. + */ +gchar* +egg_virtual_accelerator_label (guint accelerator_key, + guint keycode, + EggVirtualModifierType accelerator_mods) +{ + gchar *gtk_label; + GdkModifierType gdkmods = 0; + + egg_keymap_resolve_virtual_modifiers (NULL, accelerator_mods, &gdkmods); + gtk_label = gtk_accelerator_get_label (accelerator_key, gdkmods); + + if (!accelerator_key) + { + gchar *label; + label = g_strdup_printf ("%s0x%02x", gtk_label, keycode); + g_free (gtk_label); + return label; + } + + return gtk_label; +} + +void +egg_keymap_resolve_virtual_modifiers (GdkKeymap *keymap, + EggVirtualModifierType virtual_mods, + GdkModifierType *concrete_mods) +{ + GdkModifierType concrete; + int i; + const EggModmap *modmap; + + g_return_if_fail (concrete_mods != NULL); + g_return_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap)); + + modmap = egg_keymap_get_modmap (keymap); + + /* Not so sure about this algorithm. */ + + concrete = 0; + for (i = 0; i < EGG_MODMAP_ENTRY_LAST; ++i) + { + if (modmap->mapping[i] & virtual_mods) + concrete |= MODMAP_ENTRY_TO_MODIFIER (i); + } + + *concrete_mods = concrete; +} + +void +egg_keymap_virtualize_modifiers (GdkKeymap *keymap, + GdkModifierType concrete_mods, + EggVirtualModifierType *virtual_mods) +{ + GdkModifierType virtual; + int i; + const EggModmap *modmap; + + g_return_if_fail (virtual_mods != NULL); + g_return_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap)); + + modmap = egg_keymap_get_modmap (keymap); + + /* Not so sure about this algorithm. */ + + virtual = 0; + for (i = 0; i < EGG_MODMAP_ENTRY_LAST; ++i) + { + if (MODMAP_ENTRY_TO_MODIFIER (i) & concrete_mods) + { + EggVirtualModifierType cleaned; + + cleaned = modmap->mapping[i] & ~(EGG_VIRTUAL_MOD2_MASK | + EGG_VIRTUAL_MOD3_MASK | + EGG_VIRTUAL_MOD4_MASK | + EGG_VIRTUAL_MOD5_MASK); + + if (cleaned != 0) + { + virtual |= cleaned; + } + else + { + /* Rather than dropping mod2->mod5 if not bound, + * go ahead and use the concrete names + */ + virtual |= modmap->mapping[i]; + } + } + } + + *virtual_mods = virtual; +} + +static void +reload_modmap (GdkKeymap *keymap, + EggModmap *modmap) +{ + XModifierKeymap *xmodmap; + int map_size; + int i; + + /* FIXME multihead */ + xmodmap = XGetModifierMapping (gdk_x11_get_default_xdisplay ()); + + memset (modmap->mapping, 0, sizeof (modmap->mapping)); + + /* there are 8 modifiers in the order shift, shift lock, + * control, mod1-5 with up to max_keypermod bindings each + */ + map_size = 8 * xmodmap->max_keypermod; + for (i = 3 * xmodmap->max_keypermod; i < map_size; ++i) + { + /* get the key code at this point in the map, + * see if its keysym is one we're interested in + */ + int keycode = xmodmap->modifiermap[i]; + GdkKeymapKey *keys; + guint *keyvals; + int n_entries; + int j; + EggVirtualModifierType mask; + + keys = NULL; + keyvals = NULL; + n_entries = 0; + + gdk_keymap_get_entries_for_keycode (keymap, + keycode, + &keys, &keyvals, &n_entries); + + mask = 0; + for (j = 0; j < n_entries; ++j) + { + if (keyvals[j] == GDK_Num_Lock) + mask |= EGG_VIRTUAL_NUM_LOCK_MASK; + else if (keyvals[j] == GDK_Scroll_Lock) + mask |= EGG_VIRTUAL_SCROLL_LOCK_MASK; + else if (keyvals[j] == GDK_Meta_L || + keyvals[j] == GDK_Meta_R) + mask |= EGG_VIRTUAL_META_MASK; + else if (keyvals[j] == GDK_Hyper_L || + keyvals[j] == GDK_Hyper_R) + mask |= EGG_VIRTUAL_HYPER_MASK; + else if (keyvals[j] == GDK_Super_L || + keyvals[j] == GDK_Super_R) + mask |= EGG_VIRTUAL_SUPER_MASK; + else if (keyvals[j] == GDK_Mode_switch) + mask |= EGG_VIRTUAL_MODE_SWITCH_MASK; + } + + /* Mod1Mask is 1 << 3 for example, i.e. the + * fourth modifier, i / keyspermod is the modifier + * index + */ + modmap->mapping[i/xmodmap->max_keypermod] |= mask; + + g_free (keyvals); + g_free (keys); + } + + /* Add in the not-really-virtual fixed entries */ + modmap->mapping[EGG_MODMAP_ENTRY_SHIFT] |= EGG_VIRTUAL_SHIFT_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_CONTROL] |= EGG_VIRTUAL_CONTROL_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_LOCK] |= EGG_VIRTUAL_LOCK_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD1] |= EGG_VIRTUAL_ALT_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD2] |= EGG_VIRTUAL_MOD2_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD3] |= EGG_VIRTUAL_MOD3_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD4] |= EGG_VIRTUAL_MOD4_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD5] |= EGG_VIRTUAL_MOD5_MASK; + + XFreeModifiermap (xmodmap); +} + +const EggModmap* +egg_keymap_get_modmap (GdkKeymap *keymap) +{ + EggModmap *modmap; + + if (keymap == NULL) + keymap = gdk_keymap_get_default (); + + /* This is all a hack, much simpler when we can just + * modify GDK directly. + */ + + modmap = g_object_get_data (G_OBJECT (keymap), "egg-modmap"); + + if (modmap == NULL) + { + modmap = g_new0 (EggModmap, 1); + + /* FIXME modify keymap change events with an event filter + * and force a reload if we get one + */ + + reload_modmap (keymap, modmap); + + g_object_set_data_full (G_OBJECT (keymap), + "egg-modmap", + modmap, + g_free); + } + + g_assert (modmap != NULL); + + return modmap; +} diff --git a/capplets/keybindings/eggaccelerators.h b/capplets/keybindings/eggaccelerators.h new file mode 100644 index 00000000..660f5670 --- /dev/null +++ b/capplets/keybindings/eggaccelerators.h @@ -0,0 +1,99 @@ +/* eggaccelerators.h + * Copyright (C) 2002 Red Hat, Inc. + * Developed by Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_ACCELERATORS_H__ +#define __EGG_ACCELERATORS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Where a value is also in GdkModifierType we coincide, + * otherwise we don't overlap. + */ +typedef enum +{ + EGG_VIRTUAL_SHIFT_MASK = 1 << 0, + EGG_VIRTUAL_LOCK_MASK = 1 << 1, + EGG_VIRTUAL_CONTROL_MASK = 1 << 2, + + EGG_VIRTUAL_ALT_MASK = 1 << 3, /* fixed as Mod1 */ + + EGG_VIRTUAL_MOD2_MASK = 1 << 4, + EGG_VIRTUAL_MOD3_MASK = 1 << 5, + EGG_VIRTUAL_MOD4_MASK = 1 << 6, + EGG_VIRTUAL_MOD5_MASK = 1 << 7, + +#if 0 + GDK_BUTTON1_MASK = 1 << 8, + GDK_BUTTON2_MASK = 1 << 9, + GDK_BUTTON3_MASK = 1 << 10, + GDK_BUTTON4_MASK = 1 << 11, + GDK_BUTTON5_MASK = 1 << 12, + /* 13, 14 are used by Xkb for the keyboard group */ +#endif + + EGG_VIRTUAL_MODE_SWITCH_MASK = 1 << 23, + EGG_VIRTUAL_NUM_LOCK_MASK = 1 << 24, + EGG_VIRTUAL_SCROLL_LOCK_MASK = 1 << 25, + + /* Also in GdkModifierType */ + EGG_VIRTUAL_SUPER_MASK = 1 << 26, + EGG_VIRTUAL_HYPER_MASK = 1 << 27, + EGG_VIRTUAL_META_MASK = 1 << 28, + + /* Also in GdkModifierType */ + EGG_VIRTUAL_RELEASE_MASK = 1 << 30, + + /* 28-31 24-27 20-23 16-19 12-15 8-11 4-7 0-3 + * 5 f 8 0 0 0 f f + */ + EGG_VIRTUAL_MODIFIER_MASK = 0x5f8000ff + +} EggVirtualModifierType; + +gboolean egg_accelerator_parse_virtual (const gchar *accelerator, + guint *accelerator_key, + guint *keycode, + EggVirtualModifierType *accelerator_mods); +void egg_keymap_resolve_virtual_modifiers (GdkKeymap *keymap, + EggVirtualModifierType virtual_mods, + GdkModifierType *concrete_mods); +void egg_keymap_virtualize_modifiers (GdkKeymap *keymap, + GdkModifierType concrete_mods, + EggVirtualModifierType *virtual_mods); + +gchar* egg_virtual_accelerator_name (guint accelerator_key, + guint keycode, + EggVirtualModifierType accelerator_mods); + +gchar* egg_virtual_accelerator_label (guint accelerator_key, + guint keycode, + EggVirtualModifierType accelerator_mods); + +#ifdef __cplusplus +} +#endif + + +#endif /* __EGG_ACCELERATORS_H__ */ diff --git a/capplets/keybindings/eggcellrendererkeys.c b/capplets/keybindings/eggcellrendererkeys.c new file mode 100644 index 00000000..776a5391 --- /dev/null +++ b/capplets/keybindings/eggcellrendererkeys.c @@ -0,0 +1,695 @@ +#include +#include +#include +#include +#include +#include "eggcellrendererkeys.h" +#include "eggaccelerators.h" + +#ifndef EGG_COMPILATION +#ifndef _ +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#endif +#else +#define _(x) x +#define N_(x) x +#endif + +#define EGG_CELL_RENDERER_TEXT_PATH "egg-cell-renderer-text" + +#define TOOLTIP_TEXT _("New shortcut...") + +static void egg_cell_renderer_keys_finalize (GObject *object); +static void egg_cell_renderer_keys_init (EggCellRendererKeys *cell_keys); +static void egg_cell_renderer_keys_class_init (EggCellRendererKeysClass *cell_keys_class); +static GtkCellEditable *egg_cell_renderer_keys_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags); + + +static void egg_cell_renderer_keys_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void egg_cell_renderer_keys_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void egg_cell_renderer_keys_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); + + +enum { + PROP_0, + + PROP_ACCEL_KEY, + PROP_ACCEL_MASK, + PROP_KEYCODE, + PROP_ACCEL_MODE +}; + +static GtkCellRendererTextClass *parent_class = NULL; + +GType +egg_cell_renderer_keys_get_type (void) +{ + static GType cell_keys_type = 0; + + if (!cell_keys_type) + { + static const GTypeInfo cell_keys_info = + { + sizeof (EggCellRendererKeysClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)egg_cell_renderer_keys_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggCellRendererKeys), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_cell_renderer_keys_init + }; + + cell_keys_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT, "EggCellRendererKeys", &cell_keys_info, 0); + } + + return cell_keys_type; +} + +static void +egg_cell_renderer_keys_init (EggCellRendererKeys *cell_keys) +{ + cell_keys->accel_mode = EGG_CELL_RENDERER_KEYS_MODE_GTK; +} + +/* FIXME setup stuff to generate this */ +/* VOID:STRING,UINT,FLAGS,UINT */ +static void +marshal_VOID__STRING_UINT_FLAGS_UINT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT) (gpointer data1, + const char *arg_1, + guint arg_2, + int arg_3, + guint arg_4, + gpointer data2); + register GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + + callback = (GMarshalFunc_VOID__STRING_UINT_FLAGS_UINT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_value_get_string (param_values + 1), + g_value_get_uint (param_values + 2), + g_value_get_flags (param_values + 3), + g_value_get_uint (param_values + 4), + data2); +} + +static void +egg_cell_renderer_keys_class_init (EggCellRendererKeysClass *cell_keys_class) +{ + GObjectClass *object_class; + GtkCellRendererClass *cell_renderer_class; + + object_class = G_OBJECT_CLASS (cell_keys_class); + cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_keys_class); + parent_class = g_type_class_peek_parent (object_class); + + GTK_CELL_RENDERER_CLASS (cell_keys_class)->start_editing = egg_cell_renderer_keys_start_editing; + + object_class->set_property = egg_cell_renderer_keys_set_property; + object_class->get_property = egg_cell_renderer_keys_get_property; + cell_renderer_class->get_size = egg_cell_renderer_keys_get_size; + + object_class->finalize = egg_cell_renderer_keys_finalize; + + /* FIXME if this gets moved to a real library, rename the properties + * to match whatever the GTK convention is + */ + + g_object_class_install_property (object_class, + PROP_ACCEL_KEY, + g_param_spec_uint ("accel_key", + _("Accelerator key"), + _("Accelerator key"), + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_ACCEL_MASK, + g_param_spec_flags ("accel_mask", + _("Accelerator modifiers"), + _("Accelerator modifiers"), + GDK_TYPE_MODIFIER_TYPE, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_KEYCODE, + g_param_spec_uint ("keycode", + _("Accelerator keycode"), + _("Accelerator keycode"), + 0, + G_MAXINT, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + /* FIXME: Register the enum when moving to GTK+ */ + g_object_class_install_property (object_class, + PROP_ACCEL_MODE, + g_param_spec_int ("accel_mode", + _("Accel Mode"), + _("The type of accelerator."), + 0, + 2, + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_signal_new ("accel_edited", + EGG_TYPE_CELL_RENDERER_KEYS, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggCellRendererKeysClass, accel_edited), + NULL, NULL, + marshal_VOID__STRING_UINT_FLAGS_UINT, + G_TYPE_NONE, 4, + G_TYPE_STRING, + G_TYPE_UINT, + GDK_TYPE_MODIFIER_TYPE, + G_TYPE_UINT); + + g_signal_new ("accel_cleared", + EGG_TYPE_CELL_RENDERER_KEYS, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggCellRendererKeysClass, accel_cleared), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} + + +GtkCellRenderer * +egg_cell_renderer_keys_new (void) +{ + return GTK_CELL_RENDERER (g_object_new (EGG_TYPE_CELL_RENDERER_KEYS, NULL)); +} + +static void +egg_cell_renderer_keys_finalize (GObject *object) +{ + + (* G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static gchar * +convert_keysym_state_to_string (guint keysym, + guint keycode, + EggVirtualModifierType mask) +{ + if (keysym == 0 && keycode == 0) + return g_strdup (_("Disabled")); + else + return egg_virtual_accelerator_label (keysym, keycode, mask); +} + +static void +egg_cell_renderer_keys_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EggCellRendererKeys *keys; + + g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (object)); + + keys = EGG_CELL_RENDERER_KEYS (object); + + switch (param_id) + { + case PROP_ACCEL_KEY: + g_value_set_uint (value, keys->accel_key); + break; + + case PROP_ACCEL_MASK: + g_value_set_flags (value, keys->accel_mask); + break; + + case PROP_ACCEL_MODE: + g_value_set_int (value, keys->accel_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +egg_cell_renderer_keys_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EggCellRendererKeys *keys; + + g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (object)); + + keys = EGG_CELL_RENDERER_KEYS (object); + + switch (param_id) + { + case PROP_ACCEL_KEY: + egg_cell_renderer_keys_set_accelerator (keys, + g_value_get_uint (value), + keys->keycode, + keys->accel_mask); + break; + + case PROP_ACCEL_MASK: + egg_cell_renderer_keys_set_accelerator (keys, + keys->accel_key, + keys->keycode, + g_value_get_flags (value)); + break; + case PROP_KEYCODE: + egg_cell_renderer_keys_set_accelerator (keys, + keys->accel_key, + g_value_get_uint (value), + keys->accel_mask); + break; + + case PROP_ACCEL_MODE: + egg_cell_renderer_keys_set_accel_mode (keys, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static gboolean +is_modifier (guint keycode) +{ + gint i; + gint map_size; + XModifierKeymap *mod_keymap; + gboolean retval = FALSE; + + mod_keymap = XGetModifierMapping (gdk_display); + + map_size = 8 * mod_keymap->max_keypermod; + i = 0; + while (i < map_size) + { + if (keycode == mod_keymap->modifiermap[i]) + { + retval = TRUE; + break; + } + ++i; + } + + XFreeModifiermap (mod_keymap); + + return retval; +} + +static void +egg_cell_renderer_keys_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) + +{ + EggCellRendererKeys *keys = (EggCellRendererKeys *) cell; + GtkRequisition requisition; + + if (keys->sizing_label == NULL) + keys->sizing_label = gtk_label_new (TOOLTIP_TEXT); + + gtk_widget_size_request (keys->sizing_label, &requisition); + (* GTK_CELL_RENDERER_CLASS (parent_class)->get_size) (cell, widget, cell_area, x_offset, y_offset, width, height); + /* FIXME: need to take the cell_area et al. into account */ + if (width) + *width = MAX (*width, requisition.width); + if (height) + *height = MAX (*height, requisition.height); +} + +/* FIXME: Currently we don't differentiate between a 'bogus' key (like tab in + * GTK mode) and a removed key. + */ + +static gboolean +grab_key_callback (GtkWidget *widget, + GdkEventKey *event, + void *data) +{ + GdkModifierType accel_mods = 0; + guint accel_keyval; + EggCellRendererKeys *keys; + char *path; + gboolean edited; + gboolean cleared; + GdkModifierType consumed_modifiers; + guint upper; + GdkModifierType ignored_modifiers; + + keys = EGG_CELL_RENDERER_KEYS (data); + + if (is_modifier (event->hardware_keycode)) + return TRUE; + + edited = FALSE; + cleared = FALSE; + + consumed_modifiers = 0; + gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), + event->hardware_keycode, + event->state, + event->group, + NULL, NULL, NULL, &consumed_modifiers); + + upper = event->keyval; + accel_keyval = gdk_keyval_to_lower (upper); + if (accel_keyval == GDK_ISO_Left_Tab) + accel_keyval = GDK_Tab; + + + + /* Put shift back if it changed the case of the key, not otherwise. + */ + if (upper != accel_keyval && + (consumed_modifiers & GDK_SHIFT_MASK)) + { + consumed_modifiers &= ~(GDK_SHIFT_MASK); + } + + egg_keymap_resolve_virtual_modifiers (gdk_keymap_get_default (), + EGG_VIRTUAL_NUM_LOCK_MASK | + EGG_VIRTUAL_SCROLL_LOCK_MASK | + EGG_VIRTUAL_LOCK_MASK, + &ignored_modifiers); + + /* http://bugzilla.gnome.org/show_bug.cgi?id=139605 + * mouse keys should effect keybindings */ + ignored_modifiers |= GDK_BUTTON1_MASK | + GDK_BUTTON2_MASK | + GDK_BUTTON3_MASK | + GDK_BUTTON4_MASK | + GDK_BUTTON5_MASK; + + /* filter consumed/ignored modifiers */ + + if (keys->accel_mode == EGG_CELL_RENDERER_KEYS_MODE_GTK) + accel_mods = event->state & GDK_MODIFIER_MASK & ~(consumed_modifiers | ignored_modifiers); + else if (keys->accel_mode == EGG_CELL_RENDERER_KEYS_MODE_X) + accel_mods = event->state & GDK_MODIFIER_MASK & ~(ignored_modifiers); + else + g_assert_not_reached (); + + if (accel_mods == 0 && accel_keyval == GDK_Escape) + goto out; /* cancel */ + + /* clear the accelerator on Backspace */ + if (accel_mods == 0 && accel_keyval == GDK_BackSpace) + { + cleared = TRUE; + goto out; + } + + if (keys->accel_mode == EGG_CELL_RENDERER_KEYS_MODE_GTK) + { + if (!gtk_accelerator_valid (accel_keyval, accel_mods)) + { + accel_keyval = 0; + accel_mods = 0; + } + } + + edited = TRUE; + out: + gdk_keyboard_ungrab (event->time); + gdk_pointer_ungrab (event->time); + + path = g_strdup (g_object_get_data (G_OBJECT (keys->edit_widget), EGG_CELL_RENDERER_TEXT_PATH)); + + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (keys->edit_widget)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (keys->edit_widget)); + keys->edit_widget = NULL; + keys->grab_widget = NULL; + + if (edited) + { + g_signal_emit_by_name (G_OBJECT (keys), "accel_edited", path, + accel_keyval, accel_mods, event->hardware_keycode); + } + else if (cleared) + { + g_signal_emit_by_name (G_OBJECT (keys), "accel_cleared", path); + } + + g_free (path); + return TRUE; +} + +static void +ungrab_stuff (GtkWidget *widget, gpointer data) +{ + EggCellRendererKeys *keys = EGG_CELL_RENDERER_KEYS (data); + + gdk_keyboard_ungrab (GDK_CURRENT_TIME); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + + g_signal_handlers_disconnect_by_func (G_OBJECT (keys->grab_widget), + G_CALLBACK (grab_key_callback), data); +} + +static void +pointless_eventbox_start_editing (GtkCellEditable *cell_editable, + GdkEvent *event) +{ + /* do nothing, because we are pointless */ +} + +static void +pointless_eventbox_cell_editable_init (GtkCellEditableIface *iface) +{ + iface->start_editing = pointless_eventbox_start_editing; +} + +static GType +pointless_eventbox_subclass_get_type (void) +{ + static GType eventbox_type = 0; + + if (!eventbox_type) + { + static const GTypeInfo eventbox_info = + { + sizeof (GtkEventBoxClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkEventBox), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + static const GInterfaceInfo cell_editable_info = { + (GInterfaceInitFunc) pointless_eventbox_cell_editable_init, + NULL, NULL }; + + eventbox_type = g_type_register_static (GTK_TYPE_EVENT_BOX, "EggCellEditableEventBox", &eventbox_info, 0); + + g_type_add_interface_static (eventbox_type, + GTK_TYPE_CELL_EDITABLE, + &cell_editable_info); + } + + return eventbox_type; +} + +static GtkCellEditable * +egg_cell_renderer_keys_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkCellRendererText *celltext; + EggCellRendererKeys *keys; + GtkWidget *label; + GtkWidget *eventbox; + GValue celltext_editable = {0}; + + celltext = GTK_CELL_RENDERER_TEXT (cell); + keys = EGG_CELL_RENDERER_KEYS (cell); + + /* If the cell isn't editable we return NULL. */ + g_value_init (&celltext_editable, G_TYPE_BOOLEAN); + g_object_get_property (G_OBJECT (celltext), "editable", &celltext_editable); + if (g_value_get_boolean (&celltext_editable) == FALSE) + return NULL; + g_return_val_if_fail (gtk_widget_get_window (widget) != NULL, NULL); + + if (gdk_keyboard_grab (gtk_widget_get_window (widget), FALSE, + gdk_event_get_time (event)) != GDK_GRAB_SUCCESS) + return NULL; + + if (gdk_pointer_grab (gtk_widget_get_window (widget), FALSE, + GDK_BUTTON_PRESS_MASK, + NULL, NULL, + gdk_event_get_time (event)) != GDK_GRAB_SUCCESS) + { + gdk_keyboard_ungrab (gdk_event_get_time (event)); + return NULL; + } + + keys->grab_widget = widget; + + g_signal_connect (G_OBJECT (widget), "key_press_event", + G_CALLBACK (grab_key_callback), + keys); + + eventbox = g_object_new (pointless_eventbox_subclass_get_type (), + NULL); + keys->edit_widget = eventbox; + g_object_add_weak_pointer (G_OBJECT (keys->edit_widget), + (void**) &keys->edit_widget); + + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + + gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL, + >k_widget_get_style (widget)->bg[GTK_STATE_SELECTED]); + + gtk_widget_modify_fg (label, GTK_STATE_NORMAL, + >k_widget_get_style (widget)->fg[GTK_STATE_SELECTED]); + + gtk_label_set_text (GTK_LABEL (label), + TOOLTIP_TEXT); + + gtk_container_add (GTK_CONTAINER (eventbox), label); + + g_object_set_data_full (G_OBJECT (keys->edit_widget), EGG_CELL_RENDERER_TEXT_PATH, + g_strdup (path), g_free); + + gtk_widget_show_all (keys->edit_widget); + + g_signal_connect (G_OBJECT (keys->edit_widget), "unrealize", + G_CALLBACK (ungrab_stuff), keys); + + keys->edit_key = keys->accel_key; + + return GTK_CELL_EDITABLE (keys->edit_widget); +} + +void +egg_cell_renderer_keys_set_accelerator (EggCellRendererKeys *keys, + guint keyval, + guint keycode, + EggVirtualModifierType mask) +{ + char *text; + gboolean changed; + + g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys)); + + g_object_freeze_notify (G_OBJECT (keys)); + + changed = FALSE; + + if (keyval != keys->accel_key) + { + keys->accel_key = keyval; + g_object_notify (G_OBJECT (keys), "accel_key"); + changed = TRUE; + } + + if (mask != keys->accel_mask) + { + keys->accel_mask = mask; + + g_object_notify (G_OBJECT (keys), "accel_mask"); + changed = TRUE; + } + + if (keycode != keys->keycode) + { + keys->keycode = keycode; + + g_object_notify (G_OBJECT (keys), "keycode"); + changed = TRUE; + } + g_object_thaw_notify (G_OBJECT (keys)); + + if (changed) + { + /* sync string to the key values */ + text = convert_keysym_state_to_string (keys->accel_key, keys->keycode, keys->accel_mask); + g_object_set (keys, "text", text, NULL); + g_free (text); + } +} + +void +egg_cell_renderer_keys_get_accelerator (EggCellRendererKeys *keys, + guint *keyval, + EggVirtualModifierType *mask) +{ + g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys)); + + if (keyval) + *keyval = keys->accel_key; + + if (mask) + *mask = keys->accel_mask; +} + +void +egg_cell_renderer_keys_set_accel_mode (EggCellRendererKeys *keys, + EggCellRendererKeysMode accel_mode) +{ + g_return_if_fail (EGG_IS_CELL_RENDERER_KEYS (keys)); + keys->accel_mode = accel_mode; + g_object_notify (G_OBJECT (keys), "accel_mode"); +} diff --git a/capplets/keybindings/eggcellrendererkeys.h b/capplets/keybindings/eggcellrendererkeys.h new file mode 100644 index 00000000..2fe515e1 --- /dev/null +++ b/capplets/keybindings/eggcellrendererkeys.h @@ -0,0 +1,93 @@ +/* gtkcellrendererkeybinding.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_CELL_RENDERER_KEYS_H__ +#define __EGG_CELL_RENDERER_KEYS_H__ + +#include +#include "eggaccelerators.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EGG_TYPE_CELL_RENDERER_KEYS (egg_cell_renderer_keys_get_type ()) +#define EGG_CELL_RENDERER_KEYS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_CELL_RENDERER_KEYS, EggCellRendererKeys)) +#define EGG_CELL_RENDERER_KEYS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_CELL_RENDERER_KEYS, EggCellRendererKeysClass)) +#define EGG_IS_CELL_RENDERER_KEYS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_CELL_RENDERER_KEYS)) +#define EGG_IS_CELL_RENDERER_KEYS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_CELL_RENDERER_KEYS)) +#define EGG_CELL_RENDERER_KEYS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_CELL_RENDERER_KEYS, EggCellRendererKeysClass)) + +typedef struct _EggCellRendererKeys EggCellRendererKeys; +typedef struct _EggCellRendererKeysClass EggCellRendererKeysClass; + + +typedef enum +{ + EGG_CELL_RENDERER_KEYS_MODE_GTK, + EGG_CELL_RENDERER_KEYS_MODE_X +} EggCellRendererKeysMode; + +struct _EggCellRendererKeys +{ + GtkCellRendererText parent; + guint accel_key; + guint keycode; + EggVirtualModifierType accel_mask; + GtkWidget *edit_widget; + GtkWidget *grab_widget; + guint edit_key; + GtkWidget *sizing_label; + EggCellRendererKeysMode accel_mode; +}; + +struct _EggCellRendererKeysClass +{ + GtkCellRendererTextClass parent_class; + + void (* accel_edited) (EggCellRendererKeys *keys, + const char *path_string, + guint keyval, + EggVirtualModifierType mask, + guint hardware_keycode); + + void (* accel_cleared) (EggCellRendererKeys *keys, + const char *path_string); +}; + +GType egg_cell_renderer_keys_get_type (void); +GtkCellRenderer *egg_cell_renderer_keys_new (void); + +void egg_cell_renderer_keys_set_accelerator (EggCellRendererKeys *keys, + guint keyval, + guint keycode, + EggVirtualModifierType mask); +void egg_cell_renderer_keys_get_accelerator (EggCellRendererKeys *keys, + guint *keyval, + EggVirtualModifierType *mask); +void egg_cell_renderer_keys_set_accel_mode (EggCellRendererKeys *keys, + EggCellRendererKeysMode accel_mode); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __GTK_CELL_RENDERER_KEYS_H__ */ diff --git a/capplets/keybindings/mate-keybinding-properties.c b/capplets/keybindings/mate-keybinding-properties.c new file mode 100644 index 00000000..fb6c8785 --- /dev/null +++ b/capplets/keybindings/mate-keybinding-properties.c @@ -0,0 +1,1948 @@ +/* This program was written with lots of love under the GPL by Jonathan + * Blandford + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm-common.h" +#include "capplet-util.h" +#include "eggcellrendererkeys.h" +#include "activate-settings-daemon.h" + +#define MATECONF_BINDING_DIR "/desktop/mate/keybindings" +#define MAX_ELEMENTS_BEFORE_SCROLLING 10 +#define MAX_CUSTOM_SHORTCUTS 1000 +#define RESPONSE_ADD 0 +#define RESPONSE_REMOVE 1 + +typedef struct { + char *name; + /* The gettext package to use to translate the section title */ + char *package; + /* Name of the window manager the keys would apply to */ + char *wm_name; + /* an array of KeyListEntry */ + GArray *entries; +} KeyList; + +typedef enum { + COMPARISON_NONE = 0, + COMPARISON_GT, + COMPARISON_LT, + COMPARISON_EQ +} Comparison; + +typedef struct +{ + char *name; + int value; + char *key; + char *description_name; + char *cmd_name; + Comparison comparison; +} KeyListEntry; + +enum +{ + DESCRIPTION_COLUMN, + KEYENTRY_COLUMN, + N_COLUMNS +}; + +typedef struct +{ + char *mateconf_key; + guint keyval; + guint keycode; + EggVirtualModifierType mask; + gboolean editable; + GtkTreeModel *model; + char *description; + char *desc_mateconf_key; + gboolean desc_editable; + char *command; + char *cmd_mateconf_key; + gboolean cmd_editable; + guint mateconf_cnxn; + guint mateconf_cnxn_desc; + guint mateconf_cnxn_cmd; +} KeyEntry; + +static gboolean block_accels = FALSE; +static GtkWidget *custom_shortcut_dialog = NULL; +static GtkWidget *custom_shortcut_name_entry = NULL; +static GtkWidget *custom_shortcut_command_entry = NULL; + +static GtkWidget* +_gtk_builder_get_widget (GtkBuilder *builder, const gchar *name) +{ + return GTK_WIDGET (gtk_builder_get_object (builder, name)); +} + +static GtkBuilder * +create_builder (void) +{ + GtkBuilder *builder = gtk_builder_new(); + GError *error = NULL; + static const gchar *uifile = MATECC_UI_DIR "/mate-keybinding-properties.ui"; + + if (gtk_builder_add_from_file (builder, uifile, &error) == 0) { + g_warning ("Could not load UI: %s", error->message); + g_error_free (error); + g_object_unref (builder); + builder = NULL; + } + + return builder; +} + +static char* +binding_name (guint keyval, + guint keycode, + EggVirtualModifierType mask, + gboolean translate) +{ + if (keyval != 0 || keycode != 0) + return translate ? + egg_virtual_accelerator_label (keyval, keycode, mask) : + egg_virtual_accelerator_name (keyval, keycode, mask); + else + return g_strdup (translate ? _("Disabled") : ""); +} + +static gboolean +binding_from_string (const char *str, + guint *accelerator_key, + guint *keycode, + EggVirtualModifierType *accelerator_mods) +{ + g_return_val_if_fail (accelerator_key != NULL, FALSE); + + if (str == NULL || strcmp (str, "disabled") == 0) + { + *accelerator_key = 0; + *keycode = 0; + *accelerator_mods = 0; + return TRUE; + } + + egg_accelerator_parse_virtual (str, accelerator_key, keycode, accelerator_mods); + + if (*accelerator_key == 0) + return FALSE; + else + return TRUE; +} + +static void +accel_set_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + KeyEntry *key_entry; + + gtk_tree_model_get (model, iter, + KEYENTRY_COLUMN, &key_entry, + -1); + + if (key_entry == NULL) + g_object_set (cell, + "visible", FALSE, + NULL); + else if (! key_entry->editable) + g_object_set (cell, + "visible", TRUE, + "editable", FALSE, + "accel_key", key_entry->keyval, + "accel_mask", key_entry->mask, + "keycode", key_entry->keycode, + "style", PANGO_STYLE_ITALIC, + NULL); + else + g_object_set (cell, + "visible", TRUE, + "editable", TRUE, + "accel_key", key_entry->keyval, + "accel_mask", key_entry->mask, + "keycode", key_entry->keycode, + "style", PANGO_STYLE_NORMAL, + NULL); +} + +static void +description_set_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + KeyEntry *key_entry; + + gtk_tree_model_get (model, iter, + KEYENTRY_COLUMN, &key_entry, + -1); + + if (key_entry != NULL) + g_object_set (cell, + "editable", FALSE, + "text", key_entry->description != NULL ? + key_entry->description : _(""), + NULL); + else + g_object_set (cell, + "editable", FALSE, NULL); +} + +static gboolean +keybinding_key_changed_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + KeyEntry *key_entry; + KeyEntry *tmp_key_entry; + + key_entry = (KeyEntry *)user_data; + gtk_tree_model_get (key_entry->model, iter, + KEYENTRY_COLUMN, &tmp_key_entry, + -1); + + if (key_entry == tmp_key_entry) + { + gtk_tree_model_row_changed (key_entry->model, path, iter); + return TRUE; + } + return FALSE; +} + +static void +keybinding_key_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + KeyEntry *key_entry; + const gchar *key_value; + + key_entry = (KeyEntry *) user_data; + key_value = entry->value ? mateconf_value_get_string (entry->value) : NULL; + + binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask); + key_entry->editable = mateconf_entry_get_is_writable (entry); + + /* update the model */ + gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry); +} + +static void +keybinding_description_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + KeyEntry *key_entry; + const gchar *key_value; + + key_entry = (KeyEntry *) user_data; + key_value = entry->value ? mateconf_value_get_string (entry->value) : NULL; + + g_free (key_entry->description); + key_entry->description = key_value ? g_strdup (key_value) : NULL; + key_entry->desc_editable = mateconf_entry_get_is_writable (entry); + + /* update the model */ + gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry); +} + +static void +keybinding_command_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + KeyEntry *key_entry; + const gchar *key_value; + + key_entry = (KeyEntry *) user_data; + key_value = entry->value ? mateconf_value_get_string (entry->value) : NULL; + + g_free (key_entry->command); + key_entry->command = key_value ? g_strdup (key_value) : NULL; + key_entry->cmd_editable = mateconf_entry_get_is_writable (entry); + + /* update the model */ + gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry); +} + +static int +keyentry_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + KeyEntry *key_entry_a; + KeyEntry *key_entry_b; + int retval; + + key_entry_a = NULL; + gtk_tree_model_get (model, a, + KEYENTRY_COLUMN, &key_entry_a, + -1); + + key_entry_b = NULL; + gtk_tree_model_get (model, b, + KEYENTRY_COLUMN, &key_entry_b, + -1); + + if (key_entry_a && key_entry_b) + { + if ((key_entry_a->keyval || key_entry_a->keycode) && + (key_entry_b->keyval || key_entry_b->keycode)) + { + gchar *name_a, *name_b; + + name_a = binding_name (key_entry_a->keyval, + key_entry_a->keycode, + key_entry_a->mask, + TRUE); + + name_b = binding_name (key_entry_b->keyval, + key_entry_b->keycode, + key_entry_b->mask, + TRUE); + + retval = g_utf8_collate (name_a, name_b); + + g_free (name_a); + g_free (name_b); + } + else if (key_entry_a->keyval || key_entry_a->keycode) + retval = -1; + else if (key_entry_b->keyval || key_entry_b->keycode) + retval = 1; + else + retval = 0; + } + else if (key_entry_a) + retval = -1; + else if (key_entry_b) + retval = 1; + else + retval = 0; + + return retval; +} + +static void +clear_old_model (GtkBuilder *builder) +{ + GtkWidget *tree_view; + GtkWidget *actions_swindow; + GtkTreeModel *model; + + tree_view = _gtk_builder_get_widget (builder, "shortcut_treeview"); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + + if (model == NULL) + { + /* create a new model */ + model = (GtkTreeModel *) gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model), + KEYENTRY_COLUMN, + keyentry_sort_func, + NULL, NULL); + + gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), model); + + g_object_unref (model); + } + else + { + /* clear the existing model */ + MateConfClient *client; + gboolean valid; + GtkTreeIter iter; + KeyEntry *key_entry; + + client = mateconf_client_get_default (); + /* we need the schema name below; + * cached values do not have that set, though */ + mateconf_client_clear_cache (client); + + for (valid = gtk_tree_model_get_iter_first (model, &iter); + valid; + valid = gtk_tree_model_iter_next (model, &iter)) + { + gtk_tree_model_get (model, &iter, + KEYENTRY_COLUMN, &key_entry, + -1); + + if (key_entry != NULL) + { + mateconf_client_remove_dir (client, key_entry->mateconf_key, NULL); + mateconf_client_notify_remove (client, key_entry->mateconf_cnxn); + if (key_entry->mateconf_cnxn_desc != 0) + mateconf_client_notify_remove (client, key_entry->mateconf_cnxn_desc); + if (key_entry->mateconf_cnxn_cmd != 0) + mateconf_client_notify_remove (client, key_entry->mateconf_cnxn_cmd); + g_free (key_entry->mateconf_key); + g_free (key_entry->description); + g_free (key_entry->desc_mateconf_key); + g_free (key_entry->command); + g_free (key_entry->cmd_mateconf_key); + g_free (key_entry); + } + } + + gtk_tree_store_clear (GTK_TREE_STORE (model)); + g_object_unref (client); + } + + actions_swindow = _gtk_builder_get_widget (builder, "actions_swindow"); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (actions_swindow), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_widget_set_size_request (actions_swindow, -1, -1); +} + +typedef struct { + const char *key; + gboolean found; +} KeyMatchData; + +static gboolean +key_match (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + KeyMatchData *match_data = data; + KeyEntry *element; + + gtk_tree_model_get (model, iter, + KEYENTRY_COLUMN, &element, + -1); + + if (element && g_strcmp0 (element->mateconf_key, match_data->key) == 0) + { + match_data->found = TRUE; + return TRUE; + } + + return FALSE; +} + +static gboolean +key_is_already_shown (GtkTreeModel *model, const KeyListEntry *entry) +{ + KeyMatchData data; + + data.key = entry->name; + data.found = FALSE; + gtk_tree_model_foreach (model, key_match, &data); + + return data.found; +} + +static gboolean +should_show_key (const KeyListEntry *entry) +{ + int value; + MateConfClient *client; + + if (entry->comparison == COMPARISON_NONE) + return TRUE; + + g_return_val_if_fail (entry->key != NULL, FALSE); + + client = mateconf_client_get_default(); + value = mateconf_client_get_int (client, entry->key, NULL); + g_object_unref (client); + + switch (entry->comparison) { + case COMPARISON_NONE: + /* For compiler warnings */ + g_assert_not_reached (); + return FALSE; + case COMPARISON_GT: + if (value > entry->value) + return TRUE; + break; + case COMPARISON_LT: + if (value < entry->value) + return TRUE; + break; + case COMPARISON_EQ: + if (value == entry->value) + return TRUE; + break; + } + + return FALSE; +} + +static gboolean +count_rows_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + gint *rows = data; + + (*rows)++; + + return FALSE; +} + +static void +ensure_scrollbar (GtkBuilder *builder, int i) +{ + if (i == MAX_ELEMENTS_BEFORE_SCROLLING) + { + GtkRequisition rectangle; + GObject *actions_swindow = gtk_builder_get_object (builder, + "actions_swindow"); + GtkWidget *treeview = _gtk_builder_get_widget (builder, + "shortcut_treeview"); + gtk_widget_ensure_style (treeview); + gtk_widget_size_request (treeview, &rectangle); + gtk_widget_set_size_request (treeview, -1, rectangle.height); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (actions_swindow), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + } +} + +static void +find_section (GtkTreeModel *model, + GtkTreeIter *iter, + const char *title) +{ + gboolean success; + + success = gtk_tree_model_get_iter_first (model, iter); + while (success) + { + char *description = NULL; + + gtk_tree_model_get (model, iter, + DESCRIPTION_COLUMN, &description, + -1); + + if (g_strcmp0 (description, title) == 0) + return; + success = gtk_tree_model_iter_next (model, iter); + } + + gtk_tree_store_append (GTK_TREE_STORE (model), iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (model), iter, + DESCRIPTION_COLUMN, title, + -1); +} + +static void +append_keys_to_tree (GtkBuilder *builder, + const gchar *title, + const KeyListEntry *keys_list) +{ + MateConfClient *client; + GtkTreeIter parent_iter, iter; + GtkTreeModel *model; + gint i, j; + + client = mateconf_client_get_default (); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview"))); + + /* Try to find a section parent iter, if it already exists */ + find_section (model, &iter, title); + parent_iter = iter; + + i = 0; + gtk_tree_model_foreach (model, count_rows_foreach, &i); + + /* If the header we just added is the MAX_ELEMENTS_BEFORE_SCROLLING th, + * then we need to scroll now */ + ensure_scrollbar (builder, i - 1); + + for (j = 0; keys_list[j].name != NULL; j++) + { + MateConfEntry *entry; + KeyEntry *key_entry; + const gchar *key_string; + gchar *key_value; + gchar *description; + gchar *command; + + if (!should_show_key (&keys_list[j])) + continue; + + if (key_is_already_shown (model, &keys_list[j])) + continue; + + key_string = keys_list[j].name; + + entry = mateconf_client_get_entry (client, + key_string, + NULL, + TRUE, + NULL); + if (entry == NULL) + { + /* We don't actually want to popup a dialog - just skip this one */ + continue; + } + + if (keys_list[j].description_name != NULL) + { + description = mateconf_client_get_string (client, keys_list[j].description_name, NULL); + } + else + { + description = NULL; + + if (mateconf_entry_get_schema_name (entry)) + { + MateConfSchema *schema; + + schema = mateconf_client_get_schema (client, + mateconf_entry_get_schema_name (entry), + NULL); + if (schema != NULL) + { + description = g_strdup (mateconf_schema_get_short_desc (schema)); + mateconf_schema_free (schema); + } + } + } + + if (description == NULL) + { + /* Only print a warning for keys that should have a schema */ + if (keys_list[j].description_name == NULL) + g_warning ("No description for key '%s'", key_string); + } + + if (keys_list[j].cmd_name != NULL) + { + command = mateconf_client_get_string (client, keys_list[j].cmd_name, NULL); + } + else + { + command = NULL; + } + + key_entry = g_new0 (KeyEntry, 1); + key_entry->mateconf_key = g_strdup (key_string); + key_entry->editable = mateconf_entry_get_is_writable (entry); + key_entry->model = model; + key_entry->description = description; + key_entry->command = command; + if (keys_list[j].description_name != NULL) + { + key_entry->desc_mateconf_key = g_strdup (keys_list[j].description_name); + key_entry->desc_editable = mateconf_client_key_is_writable (client, key_entry->desc_mateconf_key, NULL); + key_entry->mateconf_cnxn_desc = mateconf_client_notify_add (client, + key_entry->desc_mateconf_key, + (MateConfClientNotifyFunc) &keybinding_description_changed, + key_entry, NULL, NULL); + } + if (keys_list[j].cmd_name != NULL) + { + key_entry->cmd_mateconf_key = g_strdup (keys_list[j].cmd_name); + key_entry->cmd_editable = mateconf_client_key_is_writable (client, key_entry->cmd_mateconf_key, NULL); + key_entry->mateconf_cnxn_cmd = mateconf_client_notify_add (client, + key_entry->cmd_mateconf_key, + (MateConfClientNotifyFunc) &keybinding_command_changed, + key_entry, NULL, NULL); + } + + mateconf_client_add_dir (client, key_string, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + key_entry->mateconf_cnxn = mateconf_client_notify_add (client, + key_string, + (MateConfClientNotifyFunc) &keybinding_key_changed, + key_entry, NULL, NULL); + + key_value = mateconf_client_get_string (client, key_string, NULL); + binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask); + g_free (key_value); + + mateconf_entry_free (entry); + ensure_scrollbar (builder, i); + + ++i; + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent_iter); + /* we use the DESCRIPTION_COLUMN only for the section headers */ + gtk_tree_store_set (GTK_TREE_STORE (model), &iter, + KEYENTRY_COLUMN, key_entry, + -1); + gtk_tree_view_expand_all (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview"))); + } + + g_object_unref (client); + + /* Don't show an empty section */ + if (gtk_tree_model_iter_n_children (model, &parent_iter) == 0) + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent_iter); + + if (i == 0) + gtk_widget_hide (_gtk_builder_get_widget (builder, "shortcuts_vbox")); + else + gtk_widget_show (_gtk_builder_get_widget (builder, "shortcuts_vbox")); +} + +static void +parse_start_tag (GMarkupParseContext *ctx, + const gchar *element_name, + const gchar **attr_names, + const gchar **attr_values, + gpointer user_data, + GError **error) +{ + KeyList *keylist = (KeyList *) user_data; + KeyListEntry key; + const char *name, *mateconf_key; + int value; + Comparison comparison; + + name = NULL; + + /* The top-level element, names the section in the tree */ + if (g_str_equal (element_name, "KeyListEntries")) + { + const char *wm_name = NULL; + const char *package = NULL; + + while (*attr_names && *attr_values) + { + if (g_str_equal (*attr_names, "name")) + { + if (**attr_values) + name = *attr_values; + } else if (g_str_equal (*attr_names, "wm_name")) { + if (**attr_values) + wm_name = *attr_values; + } else if (g_str_equal (*attr_names, "package")) { + if (**attr_values) + package = *attr_values; + } + ++attr_names; + ++attr_values; + } + + if (name) + { + if (keylist->name) + g_warning ("Duplicate section name"); + g_free (keylist->name); + keylist->name = g_strdup (name); + } + if (wm_name) + { + if (keylist->wm_name) + g_warning ("Duplicate window manager name"); + g_free (keylist->wm_name); + keylist->wm_name = g_strdup (wm_name); + } + if (package) + { + if (keylist->package) + g_warning ("Duplicate gettext package name"); + g_free (keylist->package); + keylist->package = g_strdup (package); + } + return; + } + + if (!g_str_equal (element_name, "KeyListEntry") + || attr_names == NULL + || attr_values == NULL) + return; + + value = 0; + comparison = COMPARISON_NONE; + mateconf_key = NULL; + + while (*attr_names && *attr_values) + { + if (g_str_equal (*attr_names, "name")) + { + /* skip if empty */ + if (**attr_values) + name = *attr_values; + } else if (g_str_equal (*attr_names, "value")) { + if (**attr_values) { + value = (int) g_ascii_strtoull (*attr_values, NULL, 0); + } + } else if (g_str_equal (*attr_names, "key")) { + if (**attr_values) { + mateconf_key = *attr_values; + } + } else if (g_str_equal (*attr_names, "comparison")) { + if (**attr_values) { + if (g_str_equal (*attr_values, "gt")) { + comparison = COMPARISON_GT; + } else if (g_str_equal (*attr_values, "lt")) { + comparison = COMPARISON_LT; + } else if (g_str_equal (*attr_values, "eq")) { + comparison = COMPARISON_EQ; + } + } + } + + ++attr_names; + ++attr_values; + } + + if (name == NULL) + return; + + key.name = g_strdup (name); + key.description_name = NULL; + key.value = value; + if (mateconf_key) + key.key = g_strdup (mateconf_key); + else + key.key = NULL; + key.comparison = comparison; + key.cmd_name = NULL; + g_array_append_val (keylist->entries, key); +} + +static gboolean +strv_contains (char **strv, + char *str) +{ + char **p = strv; + for (p = strv; *p; p++) + if (strcmp (*p, str) == 0) + return TRUE; + + return FALSE; +} + +static void +append_keys_to_tree_from_file (GtkBuilder *builder, + const char *filename, + char **wm_keybindings) +{ + GMarkupParseContext *ctx; + GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL }; + KeyList *keylist; + KeyListEntry key, *keys; + GError *err = NULL; + char *buf; + const char *title; + gsize buf_len; + guint i; + + if (!g_file_get_contents (filename, &buf, &buf_len, &err)) + return; + + keylist = g_new0 (KeyList, 1); + keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry)); + ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL); + + if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) + { + g_warning ("Failed to parse '%s': '%s'", filename, err->message); + g_error_free (err); + g_free (keylist->name); + g_free (keylist->package); + g_free (keylist->wm_name); + for (i = 0; i < keylist->entries->len; i++) + g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name); + g_array_free (keylist->entries, TRUE); + g_free (keylist); + keylist = NULL; + } + g_markup_parse_context_free (ctx); + g_free (buf); + + if (keylist == NULL) + return; + + /* If there's no keys to add, or the settings apply to a window manager + * that's not the one we're running */ + if (keylist->entries->len == 0 + || (keylist->wm_name != NULL && !strv_contains (wm_keybindings, keylist->wm_name)) + || keylist->name == NULL) + { + g_free (keylist->name); + g_free (keylist->package); + g_free (keylist->wm_name); + g_array_free (keylist->entries, TRUE); + g_free (keylist); + return; + } + + /* Empty KeyListEntry to end the array */ + key.name = NULL; + key.description_name = NULL; + key.key = NULL; + key.value = 0; + key.comparison = COMPARISON_NONE; + g_array_append_val (keylist->entries, key); + + keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE); + if (keylist->package) + { + bind_textdomain_codeset (keylist->package, "UTF-8"); + title = dgettext (keylist->package, keylist->name); + } else { + title = _(keylist->name); + } + + append_keys_to_tree (builder, title, keys); + + g_free (keylist->name); + g_free (keylist->package); + for (i = 0; keys[i].name != NULL; i++) + g_free (keys[i].name); + g_free (keylist); +} + +static void +append_keys_to_tree_from_mateconf (GtkBuilder *builder, const gchar *mateconf_path) +{ + MateConfClient *client; + GSList *custom_list, *l; + GArray *entries; + KeyListEntry key; + + /* load custom shortcuts from MateConf */ + entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry)); + + key.key = NULL; + key.value = 0; + key.comparison = COMPARISON_NONE; + + client = mateconf_client_get_default (); + custom_list = mateconf_client_all_dirs (client, mateconf_path, NULL); + + for (l = custom_list; l != NULL; l = l->next) + { + key.name = g_strconcat (l->data, "/binding", NULL); + key.cmd_name = g_strconcat (l->data, "/action", NULL); + key.description_name = g_strconcat (l->data, "/name", NULL); + g_array_append_val (entries, key); + + g_free (l->data); + } + + g_slist_free (custom_list); + g_object_unref (client); + + if (entries->len > 0) + { + KeyListEntry *keys; + int i; + + /* Empty KeyListEntry to end the array */ + key.name = NULL; + key.description_name = NULL; + g_array_append_val (entries, key); + + keys = (KeyListEntry *) entries->data; + append_keys_to_tree (builder, _("Custom Shortcuts"), keys); + for (i = 0; i < entries->len; ++i) + { + g_free (keys[i].name); + g_free (keys[i].description_name); + } + } + + g_array_free (entries, TRUE); +} + +static void +reload_key_entries (GtkBuilder *builder) +{ + gchar **wm_keybindings; + GDir *dir; + const char *name; + GList *list, *l; + + wm_keybindings = wm_common_get_current_keybindings(); + + clear_old_model (builder); + + dir = g_dir_open (MATECC_KEYBINDINGS_DIR, 0, NULL); + if (!dir) + return; + + list = NULL; + for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir)) + { + if (g_str_has_suffix (name, ".xml")) + { + list = g_list_insert_sorted (list, g_strdup (name), + (GCompareFunc) g_ascii_strcasecmp); + } + } + g_dir_close (dir); + + for (l = list; l != NULL; l = l->next) + { + gchar *path; + + path = g_build_filename (MATECC_KEYBINDINGS_DIR, l->data, NULL); + append_keys_to_tree_from_file (builder, path, wm_keybindings); + g_free (l->data); + g_free (path); + } + g_list_free (list); + + /* Load custom shortcuts _after_ system-provided ones, + * since some of the custom shortcuts may also be listed + * in a file. Loading the custom shortcuts last makes + * such keys not show up in the custom section. + */ + append_keys_to_tree_from_mateconf (builder, MATECONF_BINDING_DIR); + + g_strfreev (wm_keybindings); +} + +static void +key_entry_controlling_key_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + reload_key_entries (user_data); +} + +static gboolean +cb_check_for_uniqueness (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + KeyEntry *new_key) +{ + KeyEntry *element; + + gtk_tree_model_get (new_key->model, iter, + KEYENTRY_COLUMN, &element, + -1); + + /* no conflict for : blanks, different modifiers, or ourselves */ + if (element == NULL || new_key->mask != element->mask || + !strcmp (new_key->mateconf_key, element->mateconf_key)) + return FALSE; + + if (new_key->keyval != 0) { + if (new_key->keyval != element->keyval) + return FALSE; + } else if (element->keyval != 0 || new_key->keycode != element->keycode) + return FALSE; + + new_key->editable = FALSE; + new_key->mateconf_key = element->mateconf_key; + new_key->description = element->description; + new_key->desc_mateconf_key = element->desc_mateconf_key; + new_key->desc_editable = element->desc_editable; + return TRUE; +} + +static const guint forbidden_keyvals[] = { + /* Navigation keys */ + GDK_Home, + GDK_Left, + GDK_Up, + GDK_Right, + GDK_Down, + GDK_Page_Up, + GDK_Page_Down, + GDK_End, + GDK_Tab, + + /* Return */ + GDK_KP_Enter, + GDK_Return, + + GDK_space, + GDK_Mode_switch +}; + +static gboolean +keyval_is_forbidden (guint keyval) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS(forbidden_keyvals); i++) { + if (keyval == forbidden_keyvals[i]) + return TRUE; + } + + return FALSE; +} + +static void +show_error (GtkWindow *parent, + GError *err) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + _("Error saving the new shortcut")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", err->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static void +accel_edited_callback (GtkCellRendererText *cell, + const char *path_string, + guint keyval, + EggVirtualModifierType mask, + guint keycode, + gpointer data) +{ + MateConfClient *client; + GtkTreeView *view = (GtkTreeView *)data; + GtkTreeModel *model; + GtkTreePath *path = gtk_tree_path_new_from_string (path_string); + GtkTreeIter iter; + KeyEntry *key_entry, tmp_key; + GError *err = NULL; + char *str; + + block_accels = FALSE; + + model = gtk_tree_view_get_model (view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + gtk_tree_model_get (model, &iter, + KEYENTRY_COLUMN, &key_entry, + -1); + + /* sanity check */ + if (key_entry == NULL) + return; + + /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */ + mask &= ~EGG_VIRTUAL_LOCK_MASK; + + tmp_key.model = model; + tmp_key.keyval = keyval; + tmp_key.keycode = keycode; + tmp_key.mask = mask; + tmp_key.mateconf_key = key_entry->mateconf_key; + tmp_key.description = NULL; + tmp_key.editable = TRUE; /* kludge to stuff in a return flag */ + + if (keyval != 0 || keycode != 0) /* any number of keys can be disabled */ + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) cb_check_for_uniqueness, + &tmp_key); + + /* Check for unmodified keys */ + if (tmp_key.mask == 0 && tmp_key.keycode != 0) + { + if ((tmp_key.keyval >= GDK_a && tmp_key.keyval <= GDK_z) + || (tmp_key.keyval >= GDK_A && tmp_key.keyval <= GDK_Z) + || (tmp_key.keyval >= GDK_0 && tmp_key.keyval <= GDK_9) + || (tmp_key.keyval >= GDK_kana_fullstop && tmp_key.keyval <= GDK_semivoicedsound) + || (tmp_key.keyval >= GDK_Arabic_comma && tmp_key.keyval <= GDK_Arabic_sukun) + || (tmp_key.keyval >= GDK_Serbian_dje && tmp_key.keyval <= GDK_Cyrillic_HARDSIGN) + || (tmp_key.keyval >= GDK_Greek_ALPHAaccent && tmp_key.keyval <= GDK_Greek_omega) + || (tmp_key.keyval >= GDK_hebrew_doublelowline && tmp_key.keyval <= GDK_hebrew_taf) + || (tmp_key.keyval >= GDK_Thai_kokai && tmp_key.keyval <= GDK_Thai_lekkao) + || (tmp_key.keyval >= GDK_Hangul && tmp_key.keyval <= GDK_Hangul_Special) + || (tmp_key.keyval >= GDK_Hangul_Kiyeog && tmp_key.keyval <= GDK_Hangul_J_YeorinHieuh) + || keyval_is_forbidden (tmp_key.keyval)) { + GtkWidget *dialog; + char *name; + + name = binding_name (keyval, keycode, mask, TRUE); + + dialog = + gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + _("The shortcut \"%s\" cannot be used because it will become impossible to type using this key.\n" + "Please try with a key such as Control, Alt or Shift at the same time."), + name); + + g_free (name); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + /* set it back to its previous value. */ + egg_cell_renderer_keys_set_accelerator + (EGG_CELL_RENDERER_KEYS (cell), + key_entry->keyval, key_entry->keycode, key_entry->mask); + return; + } + } + + /* flag to see if the new accelerator was in use by something */ + if (!tmp_key.editable) + { + GtkWidget *dialog; + char *name; + int response; + + name = binding_name (keyval, keycode, mask, TRUE); + + dialog = + gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + _("The shortcut \"%s\" is already used for\n\"%s\""), + name, tmp_key.description ? + tmp_key.description : tmp_key.mateconf_key); + g_free (name); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut " + "will be disabled."), + key_entry->description ? + key_entry->description : key_entry->mateconf_key, + tmp_key.description ? + tmp_key.description : tmp_key.mateconf_key); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Reassign"), + GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + if (response == GTK_RESPONSE_ACCEPT) + { + MateConfClient *client; + + client = mateconf_client_get_default (); + + mateconf_client_set_string (client, + tmp_key.mateconf_key, + "", &err); + + if (err != NULL) + { + show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), + err); + g_error_free (err); + g_object_unref (client); + return; + } + + str = binding_name (keyval, keycode, mask, FALSE); + mateconf_client_set_string (client, + key_entry->mateconf_key, + str, &err); + + if (err != NULL) + { + show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), + err); + g_error_free (err); + + /* reset the previous shortcut */ + mateconf_client_set_string (client, + tmp_key.mateconf_key, + str, NULL); + } + + g_free (str); + g_object_unref (client); + } + else + { + /* set it back to its previous value. */ + egg_cell_renderer_keys_set_accelerator (EGG_CELL_RENDERER_KEYS (cell), + key_entry->keyval, + key_entry->keycode, + key_entry->mask); + } + + return; + } + + str = binding_name (keyval, keycode, mask, FALSE); + + client = mateconf_client_get_default (); + mateconf_client_set_string (client, + key_entry->mateconf_key, + str, + &err); + g_free (str); + g_object_unref (client); + + if (err != NULL) + { + show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), err); + g_error_free (err); + key_entry->editable = FALSE; + } +} + +static void +accel_cleared_callback (GtkCellRendererText *cell, + const char *path_string, + gpointer data) +{ + MateConfClient *client; + GtkTreeView *view = (GtkTreeView *) data; + GtkTreePath *path = gtk_tree_path_new_from_string (path_string); + KeyEntry *key_entry; + GtkTreeIter iter; + GError *err = NULL; + GtkTreeModel *model; + + block_accels = FALSE; + + model = gtk_tree_view_get_model (view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + gtk_tree_model_get (model, &iter, + KEYENTRY_COLUMN, &key_entry, + -1); + + /* sanity check */ + if (key_entry == NULL) + return; + + /* Unset the key */ + client = mateconf_client_get_default(); + mateconf_client_set_string (client, + key_entry->mateconf_key, + "", + &err); + g_object_unref (client); + + if (err != NULL) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + _("Error unsetting accelerator in configuration database: %s"), + err->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); + g_error_free (err); + key_entry->editable = FALSE; + } +} + +static void +description_edited_callback (GtkCellRendererText *renderer, + gchar *path_string, + gchar *new_text, + gpointer user_data) +{ + MateConfClient *client; + GtkTreeView *view = GTK_TREE_VIEW (user_data); + GtkTreeModel *model; + GtkTreePath *path = gtk_tree_path_new_from_string (path_string); + GtkTreeIter iter; + KeyEntry *key_entry; + + model = gtk_tree_view_get_model (view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + + gtk_tree_model_get (model, &iter, + KEYENTRY_COLUMN, &key_entry, + -1); + + /* sanity check */ + if (key_entry == NULL || key_entry->desc_mateconf_key == NULL) + return; + + client = mateconf_client_get_default (); + if (!mateconf_client_set_string (client, key_entry->desc_mateconf_key, new_text, NULL)) + key_entry->desc_editable = FALSE; + + g_object_unref (client); +} + + +typedef struct +{ + GtkTreeView *tree_view; + GtkTreePath *path; + GtkTreeViewColumn *column; +} IdleData; + +static gboolean +real_start_editing_cb (IdleData *idle_data) +{ + gtk_widget_grab_focus (GTK_WIDGET (idle_data->tree_view)); + gtk_tree_view_set_cursor (idle_data->tree_view, + idle_data->path, + idle_data->column, + TRUE); + gtk_tree_path_free (idle_data->path); + g_free (idle_data); + return FALSE; +} + +static gboolean +edit_custom_shortcut (KeyEntry *key) +{ + gint result; + const gchar *text; + gboolean ret; + + gtk_entry_set_text (GTK_ENTRY (custom_shortcut_name_entry), key->description ? key->description : ""); + gtk_widget_set_sensitive (custom_shortcut_name_entry, key->desc_editable); + gtk_widget_grab_focus (custom_shortcut_name_entry); + gtk_entry_set_text (GTK_ENTRY (custom_shortcut_command_entry), key->command ? key->command : ""); + gtk_widget_set_sensitive (custom_shortcut_command_entry, key->cmd_editable); + + gtk_window_present (GTK_WINDOW (custom_shortcut_dialog)); + result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog)); + switch (result) + { + case GTK_RESPONSE_OK: + text = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_name_entry)); + g_free (key->description); + key->description = g_strdup (text); + text = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_command_entry)); + g_free (key->command); + key->command = g_strdup (text); + ret = TRUE; + break; + default: + ret = FALSE; + break; + } + + gtk_widget_hide (custom_shortcut_dialog); + + return ret; +} + +static gboolean +remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter parent; + MateConfClient *client; + gchar *base; + KeyEntry *key; + + gtk_tree_model_get (model, iter, + KEYENTRY_COLUMN, &key, + -1); + + /* not a custom shortcut */ + if (key->command == NULL) + return FALSE; + + client = mateconf_client_get_default (); + + mateconf_client_notify_remove (client, key->mateconf_cnxn); + if (key->mateconf_cnxn_desc != 0) + mateconf_client_notify_remove (client, key->mateconf_cnxn_desc); + if (key->mateconf_cnxn_cmd != 0) + mateconf_client_notify_remove (client, key->mateconf_cnxn_cmd); + + base = g_path_get_dirname (key->mateconf_key); + mateconf_client_recursive_unset (client, base, 0, NULL); + g_free (base); + /* suggest sync now so the unset directory actually gets dropped; + * if we don't do this we may end up with 'zombie' shortcuts when + * restarting the app */ + mateconf_client_suggest_sync (client, NULL); + g_object_unref (client); + + g_free (key->mateconf_key); + g_free (key->description); + g_free (key->desc_mateconf_key); + g_free (key->command); + g_free (key->cmd_mateconf_key); + g_free (key); + + gtk_tree_model_iter_parent (model, &parent, iter); + gtk_tree_store_remove (GTK_TREE_STORE (model), iter); + if (!gtk_tree_model_iter_has_child (model, &parent)) + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent); + + return TRUE; +} + +static void +update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter) +{ + KeyEntry *key; + + gtk_tree_model_get (model, iter, + KEYENTRY_COLUMN, &key, + -1); + + edit_custom_shortcut (key); + if (key->command == NULL || key->command[0] == '\0') + { + remove_custom_shortcut (model, iter); + } + else + { + MateConfClient *client; + + gtk_tree_store_set (GTK_TREE_STORE (model), iter, + KEYENTRY_COLUMN, key, -1); + client = mateconf_client_get_default (); + if (key->description != NULL) + mateconf_client_set_string (client, key->desc_mateconf_key, key->description, NULL); + else + mateconf_client_unset (client, key->desc_mateconf_key, NULL); + mateconf_client_set_string (client, key->cmd_mateconf_key, key->command, NULL); + g_object_unref (client); + } +} + +static gchar * +find_free_mateconf_key (GError **error) +{ + MateConfClient *client; + + gchar *dir; + int i; + + client = mateconf_client_get_default (); + + for (i = 0; i < MAX_CUSTOM_SHORTCUTS; i++) + { + dir = g_strdup_printf ("%s/custom%d", MATECONF_BINDING_DIR, i); + if (!mateconf_client_dir_exists (client, dir, NULL)) + break; + g_free (dir); + } + + if (i == MAX_CUSTOM_SHORTCUTS) + { + dir = NULL; + g_set_error_literal (error, + g_quark_from_string ("Keyboard Shortcuts"), + 0, + _("Too many custom shortcuts")); + } + + g_object_unref (client); + + return dir; +} + +static void +add_custom_shortcut (GtkTreeView *tree_view, + GtkTreeModel *model) +{ + KeyEntry *key_entry; + GtkTreeIter iter; + GtkTreeIter parent_iter; + GtkTreePath *path; + gchar *dir; + MateConfClient *client; + GError *error; + + error = NULL; + dir = find_free_mateconf_key (&error); + if (dir == NULL) + { + show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree_view))), error); + + g_error_free (error); + return; + } + + key_entry = g_new0 (KeyEntry, 1); + key_entry->mateconf_key = g_strconcat (dir, "/binding", NULL); + key_entry->editable = TRUE; + key_entry->model = model; + key_entry->desc_mateconf_key = g_strconcat (dir, "/name", NULL); + key_entry->description = g_strdup (""); + key_entry->desc_editable = TRUE; + key_entry->cmd_mateconf_key = g_strconcat (dir, "/action", NULL); + key_entry->command = g_strdup (""); + key_entry->cmd_editable = TRUE; + g_free (dir); + + if (edit_custom_shortcut (key_entry) && + key_entry->command && key_entry->command[0]) + { + find_section (model, &iter, _("Custom Shortcuts")); + parent_iter = iter; + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent_iter); + gtk_tree_store_set (GTK_TREE_STORE (model), &iter, KEYENTRY_COLUMN, key_entry, -1); + + /* store in mateconf */ + client = mateconf_client_get_default (); + mateconf_client_set_string (client, key_entry->mateconf_key, "", NULL); + mateconf_client_set_string (client, key_entry->desc_mateconf_key, key_entry->description, NULL); + mateconf_client_set_string (client, key_entry->cmd_mateconf_key, key_entry->command, NULL); + + /* add mateconf watches */ + key_entry->mateconf_cnxn_desc = mateconf_client_notify_add (client, + key_entry->desc_mateconf_key, + (MateConfClientNotifyFunc) &keybinding_description_changed, + key_entry, NULL, NULL); + key_entry->mateconf_cnxn_cmd = mateconf_client_notify_add (client, + key_entry->cmd_mateconf_key, + (MateConfClientNotifyFunc) &keybinding_command_changed, + key_entry, NULL, NULL); + key_entry->mateconf_cnxn = mateconf_client_notify_add (client, + key_entry->mateconf_key, + (MateConfClientNotifyFunc) &keybinding_key_changed, + key_entry, NULL, NULL); + + + g_object_unref (client); + + /* make the new shortcut visible */ + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_view_expand_to_path (tree_view, path); + gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); + } + else + { + g_free (key_entry->mateconf_key); + g_free (key_entry->description); + g_free (key_entry->desc_mateconf_key); + g_free (key_entry->command); + g_free (key_entry->cmd_mateconf_key); + g_free (key_entry); + } +} + +static void +start_editing_kb_cb (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + KeyEntry *key; + + model = gtk_tree_view_get_model (treeview); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + KEYENTRY_COLUMN, &key, + -1); + + if (key == NULL) + { + /* This is a section heading - expand or collapse */ + if (gtk_tree_view_row_expanded (treeview, path)) + gtk_tree_view_collapse_row (treeview, path); + else + gtk_tree_view_expand_row (treeview, path, FALSE); + return; + } + + /* if only the accel can be edited on the selected row + * always select the accel column */ + if (key->desc_editable && + column == gtk_tree_view_get_column (treeview, 0)) + { + gtk_widget_grab_focus (GTK_WIDGET (treeview)); + gtk_tree_view_set_cursor (treeview, path, + gtk_tree_view_get_column (treeview, 0), + FALSE); + update_custom_shortcut (model, &iter); + } + else + { + gtk_widget_grab_focus (GTK_WIDGET (treeview)); + gtk_tree_view_set_cursor (treeview, + path, + gtk_tree_view_get_column (treeview, 1), + TRUE); + } +} + +static gboolean +start_editing_cb (GtkTreeView *tree_view, + GdkEventButton *event, + gpointer user_data) +{ + GtkTreePath *path; + GtkTreeViewColumn *column; + + if (event->window != gtk_tree_view_get_bin_window (tree_view)) + return FALSE; + + if (gtk_tree_view_get_path_at_pos (tree_view, + (gint) event->x, + (gint) event->y, + &path, &column, + NULL, NULL)) + { + IdleData *idle_data; + GtkTreeModel *model; + GtkTreeIter iter; + KeyEntry *key; + + if (gtk_tree_path_get_depth (path) == 1) + { + gtk_tree_path_free (path); + return FALSE; + } + + model = gtk_tree_view_get_model (tree_view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + KEYENTRY_COLUMN, &key, + -1); + + /* if only the accel can be edited on the selected row + * always select the accel column */ + if (key->desc_editable && + column == gtk_tree_view_get_column (tree_view, 0)) + { + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + gtk_tree_view_set_cursor (tree_view, path, + gtk_tree_view_get_column (tree_view, 0), + FALSE); + update_custom_shortcut (model, &iter); + } + else + { + idle_data = g_new (IdleData, 1); + idle_data->tree_view = tree_view; + idle_data->path = path; + idle_data->column = key->desc_editable ? column : + gtk_tree_view_get_column (tree_view, 1); + g_idle_add ((GSourceFunc) real_start_editing_cb, idle_data); + block_accels = TRUE; + } + g_signal_stop_emission_by_name (tree_view, "button_press_event"); + } + return TRUE; +} + +/* this handler is used to keep accels from activating while the user + * is assigning a new shortcut so that he won't accidentally trigger one + * of the widgets */ +static gboolean +maybe_block_accels (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + if (block_accels) + { + return gtk_window_propagate_key_event (GTK_WINDOW (widget), event); + } + return FALSE; +} + +static void +cb_dialog_response (GtkWidget *widget, gint response_id, gpointer data) +{ + GtkBuilder *builder = data; + GtkTreeView *treeview; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, + "shortcut_treeview")); + model = gtk_tree_view_get_model (treeview); + + if (response_id == GTK_RESPONSE_HELP) + { + capplet_help (GTK_WINDOW (widget), + "goscustdesk-39"); + } + else if (response_id == RESPONSE_ADD) + { + add_custom_shortcut (treeview, model); + } + else if (response_id == RESPONSE_REMOVE) + { + selection = gtk_tree_view_get_selection (treeview); + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + remove_custom_shortcut (model, &iter); + } + } + else + { + clear_old_model (builder); + gtk_main_quit (); + } +} + +static void +selection_changed (GtkTreeSelection *selection, gpointer data) +{ + GtkWidget *button = data; + GtkTreeModel *model; + GtkTreeIter iter; + KeyEntry *key; + gboolean can_remove; + + can_remove = FALSE; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, KEYENTRY_COLUMN, &key, -1); + if (key && key->command != NULL && key->editable) + can_remove = TRUE; + } + + gtk_widget_set_sensitive (button, can_remove); +} + +static void +setup_dialog (GtkBuilder *builder) +{ + MateConfClient *client; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *widget; + GtkTreeView *treeview; + GtkTreeSelection *selection; + GSList *allowed_keys; + + treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, + "shortcut_treeview")); + + client = mateconf_client_get_default (); + + g_signal_connect (treeview, "button_press_event", + G_CALLBACK (start_editing_cb), builder); + g_signal_connect (treeview, "row-activated", + G_CALLBACK (start_editing_kb_cb), NULL); + + renderer = gtk_cell_renderer_text_new (); + + g_signal_connect (renderer, "edited", + G_CALLBACK (description_edited_callback), + treeview); + + column = gtk_tree_view_column_new_with_attributes (_("Action"), + renderer, + "text", DESCRIPTION_COLUMN, + NULL); + gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL); + gtk_tree_view_column_set_resizable (column, FALSE); + + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_sort_column_id (column, DESCRIPTION_COLUMN); + + renderer = (GtkCellRenderer *) g_object_new (EGG_TYPE_CELL_RENDERER_KEYS, + "accel_mode", EGG_CELL_RENDERER_KEYS_MODE_X, + NULL); + + g_signal_connect (renderer, "accel_edited", + G_CALLBACK (accel_edited_callback), + treeview); + + g_signal_connect (renderer, "accel_cleared", + G_CALLBACK (accel_cleared_callback), + treeview); + + column = gtk_tree_view_column_new_with_attributes (_("Shortcut"), renderer, NULL); + gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL); + gtk_tree_view_column_set_resizable (column, FALSE); + + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_sort_column_id (column, KEYENTRY_COLUMN); + + mateconf_client_add_dir (client, MATECONF_BINDING_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir (client, "/apps/marco/general", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_notify_add (client, + "/apps/marco/general/num_workspaces", + (MateConfClientNotifyFunc) key_entry_controlling_key_changed, + builder, NULL, NULL); + + /* set up the dialog */ + reload_key_entries (builder); + + widget = _gtk_builder_get_widget (builder, "mate-keybinding-dialog"); + capplet_set_icon (widget, "preferences-desktop-keyboard-shortcuts"); + gtk_widget_show (widget); + + g_signal_connect (widget, "key_press_event", G_CALLBACK (maybe_block_accels), NULL); + g_signal_connect (widget, "response", G_CALLBACK (cb_dialog_response), builder); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + g_signal_connect (selection, "changed", + G_CALLBACK (selection_changed), + _gtk_builder_get_widget (builder, "remove-button")); + + allowed_keys = mateconf_client_get_list (client, + MATECONF_BINDING_DIR "/allowed_keys", + MATECONF_VALUE_STRING, + NULL); + if (allowed_keys != NULL) + { + g_slist_foreach (allowed_keys, (GFunc)g_free, NULL); + g_slist_free (allowed_keys); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "add-button"), + FALSE); + } + + g_object_unref (client); + + /* setup the custom shortcut dialog */ + custom_shortcut_dialog = _gtk_builder_get_widget (builder, + "custom-shortcut-dialog"); + custom_shortcut_name_entry = _gtk_builder_get_widget (builder, + "custom-shortcut-name-entry"); + custom_shortcut_command_entry = _gtk_builder_get_widget (builder, + "custom-shortcut-command-entry"); + gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog), + GTK_RESPONSE_OK); + gtk_window_set_transient_for (GTK_WINDOW (custom_shortcut_dialog), + GTK_WINDOW (widget)); +} + +static void +on_window_manager_change (const char *wm_name, GtkBuilder *builder) +{ + reload_key_entries (builder); +} + +int +main (int argc, char *argv[]) +{ + GtkBuilder *builder; + + g_thread_init (NULL); + gtk_init (&argc, &argv); + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init (&argc, &argv); + + activate_settings_daemon (); + + builder = create_builder (); + + if (!builder) /* Warning was already printed to console */ + exit (EXIT_FAILURE); + + wm_common_register_window_manager_change ((GFunc) on_window_manager_change, builder); + setup_dialog (builder); + + gtk_main (); + + g_object_unref (builder); + return 0; +} + +/* + * vim: sw=2 ts=8 cindent noai bs=2 + */ diff --git a/capplets/keybindings/mate-keybinding-properties.ui b/capplets/keybindings/mate-keybinding-properties.ui new file mode 100644 index 00000000..ecd0ae26 --- /dev/null +++ b/capplets/keybindings/mate-keybinding-properties.ui @@ -0,0 +1,301 @@ + + + + + + 5 + Keyboard Shortcuts + dialog + False + + + True + 2 + + + True + 5 + 12 + + + True + 6 + + + True + True + never + never + in + + + True + True + True + + + + + 0 + + + + + True + 6 + 12 + + + True + 0 + gtk-dialog-info + 6 + + + False + False + 0 + + + + + True + 0 + To edit a shortcut key, click on the corresponding row and type a new key combination, or press backspace to clear. + fill + True + + + 1 + + + + + False + 1 + + + + + 0 + + + + + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + gtk-add + True + True + True + False + True + + + False + False + 1 + + + + + gtk-remove + True + False + True + True + False + True + + + False + False + 2 + + + + + gtk-close + True + True + True + False + True + + + False + False + 3 + + + + + False + end + 0 + + + + + + helpbutton1 + add-button + remove-button + button1 + + + + Custom Shortcut + dialog + False + + + True + + + True + 5 + 6 + + + True + 2 + 2 + 6 + 6 + + + True + 0 + _Name: + True + custom-shortcut-name-entry + + + GTK_FILL + + + + + + True + 0 + C_ommand: + True + custom-shortcut-command-entry + + + 1 + 2 + GTK_FILL + + + + + + True + True + + True + + + 1 + 2 + + + + + + True + True + + True + + + 1 + 2 + 1 + 2 + + + + + + 0 + + + + + 1 + + + + + True + end + + + gtk-cancel + True + True + False + True + + + False + False + 0 + + + + + gtk-apply + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + cancelbutton1 + okbutton1 + + + diff --git a/capplets/keybindings/mate-keybinding.desktop.in.in b/capplets/keybindings/mate-keybinding.desktop.in.in new file mode 100644 index 00000000..b05bb1ef --- /dev/null +++ b/capplets/keybindings/mate-keybinding.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Keyboard Shortcuts +_Comment=Assign shortcut keys to commands +Exec=mate-keybinding-properties +Icon=preferences-desktop-keyboard-shortcuts +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;X-MATE-PersonalSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Keybinding +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/keybindings/mate-keybindings.pc.in b/capplets/keybindings/mate-keybindings.pc.in new file mode 100644 index 00000000..e9b95105 --- /dev/null +++ b/capplets/keybindings/mate-keybindings.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +datarootdir=@datarootdir@ +datadir=@datadir@ +pkgdatadir=${datadir}/@PACKAGE@ +keysdir=${pkgdatadir}/keybindings + +Name: mate-keybindings +Description: Keybindings configuration for MATE applications +Version: @VERSION@ + diff --git a/capplets/keyboard/Makefile.am b/capplets/keyboard/Makefile.am new file mode 100644 index 00000000..5705b01f --- /dev/null +++ b/capplets/keyboard/Makefile.am @@ -0,0 +1,42 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = keyboard + +bin_PROGRAMS = mate-keyboard-properties + +mate_keyboard_properties_SOURCES = \ + mate-keyboard-properties.c \ + mate-keyboard-properties-a11y.c \ + mate-keyboard-properties-a11y.h \ + mate-keyboard-properties-xkb.c \ + mate-keyboard-properties-xkbmc.c \ + mate-keyboard-properties-xkblt.c \ + mate-keyboard-properties-xkbltadd.c \ + mate-keyboard-properties-xkbot.c \ + mate-keyboard-properties-xkbpv.c \ + mate-keyboard-properties-xkb.h + +mate_keyboard_properties_LDADD = $(MATECC_CAPPLETS_LIBS) $(LIBMATEKBDUI_LIBS) + +@INTLTOOL_DESKTOP_RULE@ + +uidir = $(pkgdatadir)/ui +dist_ui_DATA = mate-keyboard-properties-a11y-notifications.ui \ + mate-keyboard-properties-dialog.ui \ + mate-keyboard-properties-layout-chooser.ui \ + mate-keyboard-properties-model-chooser.ui \ + mate-keyboard-properties-options-dialog.ui + +desktopdir = $(datadir)/applications +Desktop_in_files = keyboard.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + $(LIBMATEKBDUI_CFLAGS) \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_UI_DIR="\"$(uidir)\"" +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) +EXTRA_DIST = $(ui_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/keyboard/keyboard.desktop.in.in b/capplets/keyboard/keyboard.desktop.in.in new file mode 100644 index 00000000..fdfacfa6 --- /dev/null +++ b/capplets/keyboard/keyboard.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Keyboard +_Comment=Set your keyboard preferences +Exec=mate-keyboard-properties +Icon=preferences-desktop-keyboard +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;HardwareSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=keyboard +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/keyboard/mate-keyboard-properties-a11y-notifications.ui b/capplets/keyboard/mate-keyboard-properties-a11y-notifications.ui new file mode 100644 index 00000000..3f33d991 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-a11y-notifications.ui @@ -0,0 +1,526 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Keyboard Accessibility Audio Feedback + center-on-parent + dialog + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + vertical + 18 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + General + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Beep when _accessibility features are turned on or off + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + Beep when a _toggle key is pressed + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + vertical + 6 + + + True + 0 + Visual cues for sounds + + + + + + 0 + + + + + True + + + True + + + + False + 0 + + + + + True + vertical + 6 + + + Show _visual feedback for the alert sound + True + True + False + True + True + + + 0 + + + + + True + + + True + + + + False + 0 + + + + + True + vertical + 6 + + + Flash _window titlebar + True + True + False + True + True + True + + + 0 + + + + + Flash entire _screen + True + True + False + True + True + True + visual_bell_titlebar + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Slow Keys + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Beep when a key is pr_essed + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + Beep when key is _accepted + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 1 + + + + + Beep when key is _rejected + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 2 + + + + + 1 + + + + + 1 + + + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Bounce Keys + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Beep when a key is reje_cted + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + 1 + + + + + 1 + + + + + False + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Sticky Keys + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Beep when a _modifier key is pressed + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + gtk-help + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + gtk-close + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + button11 + button12 + + + diff --git a/capplets/keyboard/mate-keyboard-properties-a11y.c b/capplets/keyboard/mate-keyboard-properties-a11y.c new file mode 100644 index 00000000..3405ffa7 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-a11y.c @@ -0,0 +1,325 @@ +/* -*- mode: c; style: linux -*- */ + +/* + * Copyright (C) 2007 The MATE Foundation + * Written by Denis Washington + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "mate-keyboard-properties-a11y.h" +#include +#include "mateconf-property-editor.h" +#include "capplet-util.h" + +#define CONFIG_ROOT "/desktop/mate/accessibility/keyboard" +#define NWID(s) GTK_WIDGET (gtk_builder_get_object (notifications_dialog, s)) + +static GtkBuilder *notifications_dialog = NULL; + +static void +stickykeys_enable_toggled_cb (GtkWidget *w, GtkBuilder *dialog) +{ + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)); + + gtk_widget_set_sensitive (WID ("stickykeys_two_key_off"), active); + if (notifications_dialog) + gtk_widget_set_sensitive (NWID ("stickykeys_notifications_box"), active); +} + +static void +slowkeys_enable_toggled_cb (GtkWidget *w, GtkBuilder *dialog) +{ + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)); + + gtk_widget_set_sensitive (WID ("slowkeys_delay_box"), active); + if (notifications_dialog) + gtk_widget_set_sensitive (NWID ("slowkeys_notifications_box"), active); +} + +static void +bouncekeys_enable_toggled_cb (GtkWidget *w, GtkBuilder *dialog) +{ + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)); + + gtk_widget_set_sensitive (WID ("bouncekeys_delay_box"), active); + if (notifications_dialog) + gtk_widget_set_sensitive (NWID ("bouncekeys_notifications_box"), active); +} + +static void +visual_bell_enable_toggled_cb (GtkWidget *w, GtkBuilder *dialog) +{ + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)); + + if (notifications_dialog) { + gtk_widget_set_sensitive (NWID ("visual_bell_titlebar"), active); + gtk_widget_set_sensitive (NWID ("visual_bell_fullscreen"), active); + } +} + +static MateConfEnumStringPair bell_flash_enums[] = { + { 0, "frame_flash" }, + { 1, "fullscreen" }, + { -1, NULL } +}; + +static MateConfValue * +bell_flash_from_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (new_value, + mateconf_enum_to_string (bell_flash_enums, mateconf_value_get_int (value))); + + return new_value; +} + +static MateConfValue * +bell_flash_to_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *new_value; + const gchar *str; + gint val = 2; + + str = (value && (value->type == MATECONF_VALUE_STRING)) ? mateconf_value_get_string (value) : NULL; + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + if (value->type == MATECONF_VALUE_STRING) { + mateconf_string_to_enum (bell_flash_enums, + str, + &val); + } + mateconf_value_set_int (new_value, val); + + return new_value; +} + +static void +a11y_notifications_dialog_response_cb (GtkWidget *w, gint response) +{ + if (response == GTK_RESPONSE_HELP) { + + } + else { + gtk_widget_destroy (w); + } +} +static void +notifications_button_clicked_cb (GtkWidget *button, GtkBuilder *dialog) +{ + GtkWidget *w; + + notifications_dialog = gtk_builder_new (); + gtk_builder_add_from_file (notifications_dialog, MATECC_UI_DIR + "/mate-keyboard-properties-a11y-notifications.ui", + NULL); + + stickykeys_enable_toggled_cb (WID ("stickykeys_enable"), dialog); + slowkeys_enable_toggled_cb (WID ("slowkeys_enable"), dialog); + bouncekeys_enable_toggled_cb (WID ("bouncekeys_enable"), dialog); + + w = NWID ("feature_state_change_beep"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/feature_state_change_beep", + w, NULL); + + w = NWID ("togglekeys_enable"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/togglekeys_enable", + w, NULL); + + w = NWID ("stickykeys_modifier_beep"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/stickykeys_modifier_beep", + w, NULL); + + w = NWID ("slowkeys_beep_press"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/slowkeys_beep_press", + w, NULL); + + w = NWID ("slowkeys_beep_accept"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/slowkeys_beep_accept", + w, NULL); + + w = NWID ("slowkeys_beep_reject"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/slowkeys_beep_reject", + w, NULL); + + w = NWID ("bouncekeys_beep_reject"); + mateconf_peditor_new_boolean (NULL, + CONFIG_ROOT "/bouncekeys_beep_reject", + w, NULL); + + w = NWID ("visual_bell_enable"); + mateconf_peditor_new_boolean (NULL, + "/apps/marco/general/visual_bell", + w, NULL); + g_signal_connect (w, "toggled", + G_CALLBACK (visual_bell_enable_toggled_cb), dialog); + visual_bell_enable_toggled_cb (w, dialog); + + mateconf_peditor_new_select_radio (NULL, + "/apps/marco/general/visual_bell_type", + gtk_radio_button_get_group (GTK_RADIO_BUTTON (NWID ("visual_bell_titlebar"))), + "conv-to-widget-cb", bell_flash_to_widget, + "conv-from-widget-cb", bell_flash_from_widget, + NULL); + + w = NWID ("a11y_notifications_dialog"); + gtk_window_set_transient_for (GTK_WINDOW (w), + GTK_WINDOW (WID ("keyboard_dialog"))); + g_signal_connect (w, "response", + G_CALLBACK (a11y_notifications_dialog_response_cb), NULL); + + gtk_dialog_run (GTK_DIALOG (w)); + + g_object_unref (notifications_dialog); + notifications_dialog = NULL; +} + +static void +mousekeys_enable_toggled_cb (GtkWidget *w, GtkBuilder *dialog) +{ + gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)); + gtk_widget_set_sensitive (WID ("mousekeys_table"), active); +} + +static MateConfValue * +mousekeys_accel_time_to_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + GtkAdjustment *adjustment; + gdouble range_upper; + MateConfValue *new_value; + + adjustment = GTK_ADJUSTMENT (mateconf_property_editor_get_ui_control (peditor)); + g_object_get (adjustment, + "upper", &range_upper, + NULL); + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (new_value, MAX (0, ((int) range_upper) - mateconf_value_get_int (value))); + + return new_value; +} + +static MateConfValue * +mousekeys_accel_time_from_widget (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + GtkAdjustment *adjustment; + gdouble range_value, range_upper; + MateConfValue *new_value; + + adjustment = GTK_ADJUSTMENT (mateconf_property_editor_get_ui_control (peditor)); + g_object_get (adjustment, + "value", &range_value, + "upper", &range_upper, + NULL); + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (new_value, (int) range_upper - range_value); + + return new_value; +} + +void +setup_a11y_tabs (GtkBuilder *dialog, MateConfChangeSet *changeset) +{ + MateConfClient *client; + GtkWidget *w; + + client = mateconf_client_get_default (); + mateconf_client_add_dir (client, CONFIG_ROOT, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + g_object_unref (client); + + /* Accessibility tab */ + + w = WID ("master_enable"); + mateconf_peditor_new_boolean (changeset, + CONFIG_ROOT "/enable", + w, NULL); + + w = WID ("stickykeys_enable"); + mateconf_peditor_new_boolean (changeset, + CONFIG_ROOT "/stickykeys_enable", + w, NULL); + g_signal_connect (w, "toggled", + G_CALLBACK (stickykeys_enable_toggled_cb), dialog); + stickykeys_enable_toggled_cb (w, dialog); + + w = WID ("stickykeys_two_key_off"); + mateconf_peditor_new_boolean (changeset, + CONFIG_ROOT "/stickykeys_two_key_off", + w, NULL); + + w = WID ("slowkeys_enable"); + mateconf_peditor_new_boolean (changeset, + CONFIG_ROOT "/slowkeys_enable", + w, NULL); + g_signal_connect (w, "toggled", + G_CALLBACK (slowkeys_enable_toggled_cb), dialog); + slowkeys_enable_toggled_cb (w, dialog); + + w = WID ("bouncekeys_enable"); + mateconf_peditor_new_boolean (changeset, + CONFIG_ROOT "/bouncekeys_enable", + w, NULL); + g_signal_connect (w, "toggled", + G_CALLBACK (bouncekeys_enable_toggled_cb), dialog); + bouncekeys_enable_toggled_cb (w, dialog); + + mateconf_peditor_new_numeric_range (changeset, + CONFIG_ROOT "/slowkeys_delay", + WID ("slowkeys_delay_slide"), NULL); + mateconf_peditor_new_numeric_range (changeset, + CONFIG_ROOT "/bouncekeys_delay", + WID ("bouncekeys_delay_slide"), NULL); + + w = WID ("notifications_button"); + g_signal_connect (w, "clicked", + G_CALLBACK (notifications_button_clicked_cb), dialog); + + /* Mouse Keys tab */ + + w = WID ("mousekeys_enable"); + mateconf_peditor_new_boolean (changeset, + CONFIG_ROOT "/mousekeys_enable", + w, NULL); + g_signal_connect (w, "toggled", + G_CALLBACK (mousekeys_enable_toggled_cb), dialog); + mousekeys_enable_toggled_cb (w, dialog); + + mateconf_peditor_new_numeric_range (changeset, + CONFIG_ROOT "/mousekeys_accel_time", + WID ("mousekeys_accel_time_slide"), + "conv-to-widget-cb", mousekeys_accel_time_to_widget, + "conv-from-widget-cb", mousekeys_accel_time_from_widget, + NULL); + mateconf_peditor_new_numeric_range (changeset, + CONFIG_ROOT "/mousekeys_max_speed", + WID ("mousekeys_max_speed_slide"), NULL); + mateconf_peditor_new_numeric_range (changeset, + CONFIG_ROOT "/mousekeys_init_delay", + WID ("mousekeys_init_delay_slide"), NULL); +} diff --git a/capplets/keyboard/mate-keyboard-properties-a11y.h b/capplets/keyboard/mate-keyboard-properties-a11y.h new file mode 100644 index 00000000..35e5678d --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-a11y.h @@ -0,0 +1,32 @@ +/* -*- mode: c; style: linux -*- */ + +/* accessibility-keyboard.c + * Copyright (C) 2002 Ximian, Inc. + * + * Written by: Jody Goldberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __MATE_KEYBOARD_PROPERTY_A11Y_H +#define __MATE_KEYBOARD_PROPERTY_A11Y_H + +#include +#include + +extern void setup_a11y_tabs (GtkBuilder * dialog, MateConfChangeSet * changeset); + +#endif /* __MATE_KEYBOARD_PROPERTY_A11Y_H */ diff --git a/capplets/keyboard/mate-keyboard-properties-dialog.ui b/capplets/keyboard/mate-keyboard-properties-dialog.ui new file mode 100644 index 00000000..eb9e92f0 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-dialog.ui @@ -0,0 +1,1857 @@ + + + + + + 500 + 100 + 2000 + 10 + 10 + + + 1 + 1 + 100000 + 1 + 10 + + + 30 + 10 + 110 + 10 + 10 + + + 1000 + 100 + 2500 + 200 + 200 + + + 0.5 + 500 + 10 + 10 + + + 0.5 + 900 + 10 + 10 + + + 1800 + 3000 + 10 + 10 + + + 300 + 10 + 1000 + 10 + 10 + + + 300 + 10 + 2000 + 10 + 10 + + + 1 + 1 + 100000 + 1 + 10 + + + 5 + Keyboard Preferences + 450 + 430 + dialog + False + + + True + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + 12 + + + True + True + + + True + 12 + 18 + + + True + 6 + + + True + 0 + Repeat Keys + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 6 + + + Key presses _repeat when key is held down + True + True + False + True + True + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + True + 0 + _Delay: + True + center + repeat_delay_scale + + + 0 + + + + + True + 0 + _Speed: + True + center + repeat_speed_scale + + + 1 + + + + + False + False + 0 + + + + + True + 6 + + + True + 1 + 10 + Short + + + + + + + 0 + + + + + True + 1 + 10 + Slow + + + + + + + 1 + + + + + False + False + 1 + + + + + True + 6 + + + True + True + adjustment1 + False + + + 0 + + + + + True + True + adjustment2 + False + + + Repeat keys speed + + + + + 1 + + + + + 2 + + + + + True + 6 + + + True + 0 + Long + + + + + + + 0 + + + + + True + 0 + Fast + + + + + + + 1 + + + + + False + False + 3 + + + + + False + 1 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + 6 + + + True + 0 + Cursor Blinking + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 6 + + + Cursor _blinks in text fields + True + True + False + True + True + + + False + False + 0 + + + + + True + 12 + + + True + 0 + S_peed: + True + center + cursor_blink_time_scale + + + False + False + 0 + + + + + True + 6 + + + True + 1 + 10 + Slow + + + + + + + False + False + 0 + + + + + True + True + discontinuous + adjustment3 + False + + + Cursor blinks speed + + + + + 1 + + + + + True + 0 + Fast + + + + + + + False + False + 2 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + + + True + General + center + + + False + + + + + True + 12 + 12 + + + True + 6 + + + True + True + automatic + automatic + in + + + True + True + List of keyboard layouts selected for usage + False + + + + + 0 + + + + + True + 6 + True + + + True + 6 + True + + + _Add... + True + True + True + Select a keyboard layout to be added to the list + True + + + 0 + + + + + gtk-remove + True + True + True + Remove the selected keyboard layout from the list + True + + + 1 + + + + + 0 + + + + + True + 6 + True + + + Move _Up + True + True + True + Move the selected keyboard layout up in the list + True + + + 0 + + + + + Move _Down + True + True + True + Move the selected keyboard layout down in the list + True + + + 1 + + + + + 1 + + + + + True + 6 + True + + + _Show... + True + True + True + Print a diagram of the selected keyboard layout + True + + + 0 + + + + + True + + + 1 + + + + + 2 + + + + + False + False + 1 + + + + + _Separate layout for each window + True + True + False + True + True + + + False + False + 2 + + + + + New windows u_se active window's layout + True + True + False + True + True + + + False + False + 3 + + + + + 0 + + + + + True + 12 + + + True + 0 + Keyboard _model: + True + xkb_model_pick + + + False + False + 0 + + + + + True + True + False + True + + + 1 + + + + + False + False + 1 + + + + + True + 6 + end + + + _Options... + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + View and edit keyboard layout options + True + + + False + False + 0 + + + + + Reset to De_faults + True + True + True + Replace the current keyboard layout settings with the +default settings + True + + + False + False + end + 1 + + + + + False + False + 2 + + + + + 1 + + + + + True + Layouts + + + 1 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + 18 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + _Accessibility features can be toggled with keyboard shortcuts + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + False + 0 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Sticky Keys + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + _Simulate simultaneous keypresses + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + Disa_ble sticky keys if two keys are pressed together + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Slow Keys + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + _Only accept long keypresses + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + False + 0 + + + + + True + 12 + + + True + 0 + _Delay: + True + center + slowkeys_delay_slide + + + False + False + 0 + + + + + True + 6 + + + True + 1 + 10 + Short + + + + + + + False + False + 0 + + + + + True + True + discontinuous + adjustment4 + False + + + Cursor blinks speed + + + + + 1 + + + + + True + 0 + Long + + + + + + + False + False + 2 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Bounce Keys + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + _Ignore fast duplicate keypresses + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + True + 12 + + + True + 0 + D_elay: + True + center + bouncekeys_delay_slide + + + False + False + 0 + + + + + True + 6 + + + True + 1 + 10 + Short + + + + + + + False + False + 0 + + + + + True + True + discontinuous + adjustment5 + False + + + Cursor blinks speed + + + + + 1 + + + + + True + 0 + Long + + + + + + + False + False + 2 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + False + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + Audio _Feedback... + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + False + 4 + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Accessibility + + + 2 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + _Pointer can be controlled using the keypad + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + 3 + 4 + 12 + 6 + + + True + 1 + Slow + center + + + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + 1 + Slow + center + + + + + + + 1 + 2 + GTK_FILL + + + + + + True + True + discontinuous + adjustment6 + False + right + + + 2 + 3 + GTK_FILL + + + + + True + True + discontinuous + adjustment7 + 0 + False + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + True + 0 + Fast + center + + + + + + + 3 + 4 + 1 + 2 + GTK_FILL + + + + + + True + 0 + Fast + center + + + + + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Speed: + True + center + mousekeys_max_speed_slide + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + _Acceleration: + True + center + mousekeys_accel_time_slide + + + GTK_FILL + GTK_FILL + + + + + True + 0 + _Delay: + True + center + mousekeys_init_delay_slide + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + 1 + Short + center + + + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + True + discontinuous + adjustment8 + 0 + False + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + True + 0 + Long + center + + + + + + + 3 + 4 + 2 + 3 + GTK_FILL + + + + + + 0 + + + + + 1 + + + + + 1 + + + + + False + 0 + + + + + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Mouse Keys + + + 3 + False + + + + + True + 12 + + + True + 12 + 18 + + + True + 6 + + + _Lock screen to enforce typing break + True + True + False + Lock screen after a certain duration to help prevent repetitive keyboard use injuries + True + True + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 6 + + + True + 12 + + + True + 6 + + + True + 0 + _Work interval lasts: + True + break_enabled_spin + + + 0 + + + + + True + 0 + _Break interval lasts: + True + break_interval_spin + + + 1 + + + + + False + False + 0 + + + + + True + 6 + + + True + 6 + + + True + True + Duration of work before forcing a break + adjustment9 + 1 + + + 0 + + + + + True + True + Duration of the break when typing is disallowed + adjustment10 + 1 + + + 1 + + + + + 0 + + + + + True + 6 + + + True + 0 + minutes + + + 0 + + + + + True + 0 + minutes + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 0 + + + + + All_ow postponing of breaks + True + True + False + Check if breaks are allowed to be postponed + True + True + + + False + False + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 0 + + + + + 0 + + + + + 4 + + + + + True + Typing Break + + + 4 + False + + + + + 0 + + + + + True + 12 + + + True + _Type to test settings: + True + test_entry + + + False + False + 0 + + + + + True + True + 256 + + + 1 + + + + + False + 1 + + + + + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + helpbutton1 + button4 + + + diff --git a/capplets/keyboard/mate-keyboard-properties-layout-chooser.ui b/capplets/keyboard/mate-keyboard-properties-layout-chooser.ui new file mode 100644 index 00000000..2beb56d0 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-layout-chooser.ui @@ -0,0 +1,315 @@ + + + + + + True + 5 + Choose a Layout + 670 + 350 + dialog + False + + + True + vertical + 2 + + + True + 5 + vertical + 6 + + + True + True + + + True + 3 + 3 + 3 + 6 + + + True + 2 + 2 + 12 + 6 + + + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Variants: + True + xkb_country_variants_available + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Country: + True + xkb_countries_available + + + GTK_FILL + + + + + + + + + + True + By _country + True + + + False + + + + + True + 3 + 3 + 3 + 3 + + + True + 2 + 2 + 12 + 6 + + + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Variants: + True + xkb_country_variants_available + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Language: + True + xkb_countries_available + + + GTK_FILL + + + + + + + + 1 + + + + + True + By _language + True + + + 1 + False + + + + + False + False + 0 + + + + + True + vertical + 6 + + + True + 6 + + + True + 0 + Preview: + + + False + False + 0 + + + + + False + False + 0 + + + + + True + 0 + in + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + True + end + + + gtk-print + True + True + True + False + True + + + False + False + 0 + True + + + + + gtk-cancel + True + True + True + False + True + + + False + False + 1 + + + + + gtk-add + True + True + True + False + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + btnPrint + cancelbutton2 + btnOk1 + + + diff --git a/capplets/keyboard/mate-keyboard-properties-model-chooser.ui b/capplets/keyboard/mate-keyboard-properties-model-chooser.ui new file mode 100644 index 00000000..3fe7d4e1 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-model-chooser.ui @@ -0,0 +1,142 @@ + + + + + + True + 5 + Choose a Keyboard Model + True + 450 + 400 + dialog + False + + + True + vertical + 2 + + + True + 5 + vertical + 6 + + + True + 0 + _Vendors: + True + + + False + False + 0 + + + + + True + True + automatic + automatic + in + + + True + True + False + + + + + 1 + + + + + True + 0 + _Models: + True + + + False + False + 2 + + + + + True + True + automatic + automatic + in + + + True + True + False + + + + + 3 + + + + + 1 + + + + + True + end + + + gtk-cancel + True + True + True + False + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + cancelbutton1 + btnOk + + + diff --git a/capplets/keyboard/mate-keyboard-properties-options-dialog.ui b/capplets/keyboard/mate-keyboard-properties-options-dialog.ui new file mode 100644 index 00000000..f92c4cd2 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-options-dialog.ui @@ -0,0 +1,94 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Keyboard Layout Options + center-on-parent + 550 + 400 + dialog + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 2 + + + True + True + 5 + automatic + automatic + out + + + True + none + + + True + vertical + + + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + + + gtk-help + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 0 + + + + + gtk-close + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + button3 + button2 + + + diff --git a/capplets/keyboard/mate-keyboard-properties-xkb.c b/capplets/keyboard/mate-keyboard-properties-xkb.c new file mode 100644 index 00000000..3316b4d4 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkb.c @@ -0,0 +1,254 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkb.c + * Copyright (C) 2003-2007 Sergey V. Udaltsov + * + * Written by: Sergey V. Udaltsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" + +#include "mate-keyboard-properties-xkb.h" + +#include + +XklEngine *engine; +XklConfigRegistry *config_registry; + +MatekbdKeyboardConfig initial_config; +MatekbdDesktopConfig desktop_config; + +MateConfClient *xkb_mateconf_client; + +char * +xci_desc_to_utf8 (XklConfigItem * ci) +{ + char *sd = g_strstrip (ci->description); + return sd[0] == 0 ? g_strdup (ci->name) : g_strdup (sd); +} + +static void +set_model_text (GtkWidget * picker, MateConfValue * value) +{ + XklConfigItem *ci = xkl_config_item_new (); + const char *model = NULL; + + if (value != NULL && value->type == MATECONF_VALUE_STRING) { + model = mateconf_value_get_string (value); + if (model != NULL && model[0] == '\0') + model = NULL; + } + + if (model == NULL) { + model = initial_config.model; + if (model == NULL) + model = ""; + } + + g_snprintf (ci->name, sizeof (ci->name), "%s", model); + + if (xkl_config_registry_find_model (config_registry, ci)) { + char *d; + + d = xci_desc_to_utf8 (ci); + gtk_button_set_label (GTK_BUTTON (picker), d); + g_free (d); + } else { + gtk_button_set_label (GTK_BUTTON (picker), _("Unknown")); + } + g_object_unref (G_OBJECT (ci)); +} + +static void +model_key_changed (MateConfClient * client, + guint cnxn_id, MateConfEntry * entry, GtkBuilder * dialog) +{ + set_model_text (WID ("xkb_model_pick"), + mateconf_entry_get_value (entry)); + + enable_disable_restoring (dialog); +} + +static void +setup_model_entry (GtkBuilder * dialog) +{ + MateConfValue *value; + + value = mateconf_client_get (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_MODEL, NULL); + set_model_text (WID ("xkb_model_pick"), value); + if (value != NULL) + mateconf_value_free (value); + + mateconf_client_notify_add (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_MODEL, + (MateConfClientNotifyFunc) model_key_changed, + dialog, NULL, NULL); +} + +static void +cleanup_xkb_tabs (GtkBuilder * dialog) +{ + matekbd_desktop_config_term (&desktop_config); + matekbd_keyboard_config_term (&initial_config); + g_object_unref (G_OBJECT (config_registry)); + config_registry = NULL; + g_object_unref (G_OBJECT (engine)); + engine = NULL; + g_object_unref (G_OBJECT (xkb_mateconf_client)); + xkb_mateconf_client = NULL; +} + +static void +reset_to_defaults (GtkWidget * button, GtkBuilder * dialog) +{ + MatekbdKeyboardConfig empty_kbd_config; + + matekbd_keyboard_config_init (&empty_kbd_config, xkb_mateconf_client, + engine); + matekbd_keyboard_config_save_to_mateconf (&empty_kbd_config); + matekbd_keyboard_config_term (&empty_kbd_config); + + mateconf_client_unset (xkb_mateconf_client, + MATEKBD_DESKTOP_CONFIG_KEY_DEFAULT_GROUP, NULL); + + /* all the rest is g-s-d's business */ +} + +static void +chk_separate_group_per_window_toggled (MateConfPropertyEditor * peditor, + const gchar * key, + const MateConfValue * value, + GtkBuilder * dialog) +{ + gtk_widget_set_sensitive (WID ("chk_new_windows_inherit_layout"), + mateconf_value_get_bool (value)); +} + +static void +chk_new_windows_inherit_layout_toggled (GtkWidget * + chk_new_windows_inherit_layout, + GtkBuilder * dialog) +{ + xkb_save_default_group (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON + (chk_new_windows_inherit_layout)) ? -1 : + 0); +} + +void +setup_xkb_tabs (GtkBuilder * dialog, MateConfChangeSet * changeset) +{ + GObject *peditor; + GtkWidget *chk_new_windows_inherit_layout = + WID ("chk_new_windows_inherit_layout"); + + xkb_mateconf_client = mateconf_client_get_default (); + + engine = xkl_engine_get_instance (GDK_DISPLAY ()); + config_registry = xkl_config_registry_get_instance (engine); + + matekbd_desktop_config_init (&desktop_config, xkb_mateconf_client, + engine); + matekbd_desktop_config_load_from_mateconf (&desktop_config); + + xkl_config_registry_load (config_registry, + desktop_config.load_extra_items); + + matekbd_keyboard_config_init (&initial_config, xkb_mateconf_client, + engine); + matekbd_keyboard_config_load_from_x_initial (&initial_config, NULL); + + setup_model_entry (dialog); + + peditor = mateconf_peditor_new_boolean + (changeset, (gchar *) MATEKBD_DESKTOP_CONFIG_KEY_GROUP_PER_WINDOW, + WID ("chk_separate_group_per_window"), NULL); + + g_signal_connect (peditor, "value-changed", (GCallback) + chk_separate_group_per_window_toggled, dialog); + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + if (strcmp (xkl_engine_get_backend_name (engine), "XKB")) +#endif + gtk_widget_hide (WID ("xkb_layouts_print")); + + xkb_layouts_prepare_selected_tree (dialog, changeset); + xkb_layouts_fill_selected_tree (dialog); + + gtk_widget_set_sensitive (chk_new_windows_inherit_layout, + gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON + (WID + ("chk_separate_group_per_window")))); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON + (chk_new_windows_inherit_layout), + xkb_get_default_group () < 0); + + xkb_layouts_register_buttons_handlers (dialog); + g_signal_connect (G_OBJECT (WID ("xkb_reset_to_defaults")), + "clicked", G_CALLBACK (reset_to_defaults), + dialog); + + g_signal_connect (G_OBJECT (chk_new_windows_inherit_layout), + "toggled", (GCallback) + chk_new_windows_inherit_layout_toggled, dialog); + + g_signal_connect_swapped (G_OBJECT (WID ("xkb_layout_options")), + "clicked", + G_CALLBACK (xkb_options_popup_dialog), + dialog); + + g_signal_connect_swapped (G_OBJECT (WID ("xkb_model_pick")), + "clicked", G_CALLBACK (choose_model), + dialog); + + xkb_layouts_register_mateconf_listener (dialog); + xkb_options_register_mateconf_listener (dialog); + + g_signal_connect (G_OBJECT (WID ("keyboard_dialog")), + "destroy", G_CALLBACK (cleanup_xkb_tabs), + dialog); + + enable_disable_restoring (dialog); +} + +void +enable_disable_restoring (GtkBuilder * dialog) +{ + MatekbdKeyboardConfig gswic; + gboolean enable; + + matekbd_keyboard_config_init (&gswic, xkb_mateconf_client, engine); + matekbd_keyboard_config_load_from_mateconf (&gswic, NULL); + + enable = !matekbd_keyboard_config_equals (&gswic, &initial_config); + + matekbd_keyboard_config_term (&gswic); + gtk_widget_set_sensitive (WID ("xkb_reset_to_defaults"), enable); +} diff --git a/capplets/keyboard/mate-keyboard-properties-xkb.h b/capplets/keyboard/mate-keyboard-properties-xkb.h new file mode 100644 index 00000000..19b673ba --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkb.h @@ -0,0 +1,104 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkb.h + * Copyright (C) 2003-2007 Sergey V Udaltsov + * + * Written by Sergey V. Udaltsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __MATE_KEYBOARD_PROPERTY_XKB_H +#define __MATE_KEYBOARD_PROPERTY_XKB_H + +#include + +#include "libmatekbd/matekbd-keyboard-config.h" + +#ifdef __cplusplus +extern "C" { +#endif +#define CWID(s) GTK_WIDGET (gtk_builder_get_object (chooser_dialog, s)) +extern XklEngine *engine; +extern XklConfigRegistry *config_registry; +extern MateConfClient *xkb_mateconf_client; +extern MatekbdKeyboardConfig initial_config; + +extern void setup_xkb_tabs (GtkBuilder * dialog, + MateConfChangeSet * changeset); + +extern void xkb_layouts_fill_selected_tree (GtkBuilder * dialog); + +extern void xkb_layouts_register_buttons_handlers (GtkBuilder * dialog); + +extern void xkb_layouts_register_mateconf_listener (GtkBuilder * dialog); + +extern void xkb_options_register_mateconf_listener (GtkBuilder * dialog); + +extern void xkb_layouts_prepare_selected_tree (GtkBuilder * dialog, + MateConfChangeSet * changeset); + +extern void xkb_options_load_options (GtkBuilder * dialog); + +extern void xkb_options_popup_dialog (GtkBuilder * dialog); + +extern void clear_xkb_elements_list (GSList * list); + +extern char *xci_desc_to_utf8 (XklConfigItem * ci); + +extern gchar *xkb_layout_description_utf8 (const gchar * visible); + +extern void enable_disable_restoring (GtkBuilder * dialog); + +extern void preview_toggled (GtkBuilder * dialog, GtkWidget * button); + +extern void choose_model (GtkBuilder * dialog); + +extern void xkb_layout_choose (GtkBuilder * dialog); + +extern GSList *xkb_layouts_get_selected_list (void); + +extern GSList *xkb_options_get_selected_list (void); + +#define xkb_layouts_set_selected_list(list) \ + mateconf_client_set_list (mateconf_client_get_default (), \ + MATEKBD_KEYBOARD_CONFIG_KEY_LAYOUTS, \ + MATECONF_VALUE_STRING, (list), NULL) + +#define xkb_options_set_selected_list(list) \ + mateconf_client_set_list (mateconf_client_get_default (), \ + MATEKBD_KEYBOARD_CONFIG_KEY_OPTIONS, \ + MATECONF_VALUE_STRING, (list), NULL) + +extern GtkWidget *xkb_layout_preview_create_widget (GtkBuilder * + chooser_dialog); + +extern void xkb_layout_preview_update (GtkBuilder * chooser_dialog); + +extern void xkb_layout_preview_set_drawing_layout (GtkWidget * kbdraw, + const gchar * id); + +extern gchar *xkb_layout_chooser_get_selected_id (GtkBuilder * + chooser_dialog); + +extern void xkb_save_default_group (gint group_no); + +extern gint xkb_get_default_group (void); + +#ifdef __cplusplus +} +#endif +#endif /* __MATE_KEYBOARD_PROPERTY_XKB_H */ diff --git a/capplets/keyboard/mate-keyboard-properties-xkblt.c b/capplets/keyboard/mate-keyboard-properties-xkblt.c new file mode 100644 index 00000000..41132647 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkblt.c @@ -0,0 +1,475 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkblt.c + * Copyright (C) 2003-2007 Sergey V. Udaltsov + * + * Written by: Sergey V. Udaltsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#include + +#include "capplet-util.h" +#include "mate-keyboard-properties-xkb.h" + + +#define SEL_LAYOUT_TREE_COL_DESCRIPTION 0 +#define SEL_LAYOUT_TREE_COL_ID 1 +#define SEL_LAYOUT_TREE_COL_ENABLED 2 + +static int idx2select = -1; +static int max_selected_layouts = -1; +static int default_group = -1; + +static GtkCellRenderer *text_renderer; + +static gboolean disable_buttons_sensibility_update = FALSE; + +void +clear_xkb_elements_list (GSList * list) +{ + while (list != NULL) { + GSList *p = list; + list = list->next; + g_free (p->data); + g_slist_free_1 (p); + } +} + +static gint +find_selected_layout_idx (GtkBuilder * dialog) +{ + GtkTreeSelection *selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW + (WID ("xkb_layouts_selected"))); + GtkTreeIter selected_iter; + GtkTreeModel *model; + GtkTreePath *path; + gint *indices; + gint rv; + + if (!gtk_tree_selection_get_selected + (selection, &model, &selected_iter)) + return -1; + + path = gtk_tree_model_get_path (model, &selected_iter); + if (path == NULL) + return -1; + + indices = gtk_tree_path_get_indices (path); + rv = indices[0]; + gtk_tree_path_free (path); + return rv; +} + +GSList * +xkb_layouts_get_selected_list (void) +{ + GSList *retval; + + retval = mateconf_client_get_list (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_LAYOUTS, + MATECONF_VALUE_STRING, NULL); + if (retval == NULL) { + GSList *cur_layout; + + for (cur_layout = initial_config.layouts_variants; + cur_layout != NULL; cur_layout = cur_layout->next) + retval = + g_slist_prepend (retval, + g_strdup (cur_layout->data)); + + retval = g_slist_reverse (retval); + } + + return retval; +} + +gint +xkb_get_default_group () +{ + return mateconf_client_get_int (xkb_mateconf_client, + MATEKBD_DESKTOP_CONFIG_KEY_DEFAULT_GROUP, + NULL); +} + +void +xkb_save_default_group (gint default_group) +{ + if (default_group != xkb_get_default_group ()) + mateconf_client_set_int (xkb_mateconf_client, + MATEKBD_DESKTOP_CONFIG_KEY_DEFAULT_GROUP, + default_group, NULL); +} + +static void +xkb_layouts_enable_disable_buttons (GtkBuilder * dialog) +{ + GtkWidget *add_layout_btn = WID ("xkb_layouts_add"); + GtkWidget *show_layout_btn = WID ("xkb_layouts_show"); + GtkWidget *del_layout_btn = WID ("xkb_layouts_remove"); + GtkWidget *selected_layouts_tree = WID ("xkb_layouts_selected"); + GtkWidget *move_up_layout_btn = WID ("xkb_layouts_move_up"); + GtkWidget *move_down_layout_btn = WID ("xkb_layouts_move_down"); + + GtkTreeSelection *s_selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW + (selected_layouts_tree)); + const int n_selected_selected_layouts = + gtk_tree_selection_count_selected_rows (s_selection); + GtkTreeModel *selected_layouts_model = gtk_tree_view_get_model + (GTK_TREE_VIEW (selected_layouts_tree)); + const int n_selected_layouts = + gtk_tree_model_iter_n_children (selected_layouts_model, + NULL); + gint sidx = find_selected_layout_idx (dialog); + + if (disable_buttons_sensibility_update) + return; + + gtk_widget_set_sensitive (add_layout_btn, + (n_selected_layouts < + max_selected_layouts + || max_selected_layouts == 0)); + gtk_widget_set_sensitive (del_layout_btn, (n_selected_layouts > 1) + && (n_selected_selected_layouts > 0)); + gtk_widget_set_sensitive (show_layout_btn, + (n_selected_selected_layouts > 0)); + gtk_widget_set_sensitive (move_up_layout_btn, sidx > 0); + gtk_widget_set_sensitive (move_down_layout_btn, sidx >= 0 + && sidx < (n_selected_layouts - 1)); +} + +static void +xkb_layouts_dnd_data_get (GtkWidget * widget, GdkDragContext * dc, + GtkSelectionData * selection_data, guint info, + guint t, GtkBuilder * dialog) +{ + /* Storing the value into selection - + * while it is actually not used + */ + gint idx = find_selected_layout_idx (dialog); + gtk_selection_data_set (selection_data, + GDK_SELECTION_TYPE_INTEGER, 32, + (guchar *) & idx, sizeof (idx)); +} + +static void +xkb_layouts_dnd_data_received (GtkWidget * widget, GdkDragContext * dc, + gint x, gint y, + GtkSelectionData * selection_data, + guint info, guint t, GtkBuilder * dialog) +{ + gint sidx = find_selected_layout_idx (dialog); + GtkWidget *tree_view = WID ("xkb_layouts_selected"); + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + gint didx; + gchar *id; + GSList *layouts_list; + GSList *node2Remove; + + if (sidx == -1) + return; + + layouts_list = xkb_layouts_get_selected_list (); + node2Remove = g_slist_nth (layouts_list, sidx); + + id = (gchar *) node2Remove->data; + layouts_list = g_slist_delete_link (layouts_list, node2Remove); + + if (!gtk_tree_view_get_dest_row_at_pos + (GTK_TREE_VIEW (tree_view), x, y, &path, &pos)) { + /* Move to the very end */ + layouts_list = + g_slist_append (layouts_list, g_strdup (id)); + xkb_layouts_set_selected_list (layouts_list); + } else if (path != NULL) { + gint *indices = gtk_tree_path_get_indices (path); + didx = indices[0]; + gtk_tree_path_free (path); + /* Move to the new position */ + if (sidx != didx) { + layouts_list = + g_slist_insert (layouts_list, g_strdup (id), + didx); + xkb_layouts_set_selected_list (layouts_list); + } + } + g_free (id); + clear_xkb_elements_list (layouts_list); +} + +void +xkb_layouts_prepare_selected_tree (GtkBuilder * dialog, + MateConfChangeSet * changeset) +{ + GtkListStore *list_store = + gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_BOOLEAN); + GtkWidget *tree_view = WID ("xkb_layouts_selected"); + GtkTreeSelection *selection; + GtkTargetEntry self_drag_target = + { "xkb_layouts_selected", GTK_TARGET_SAME_WIDGET, 0 }; + GtkTreeViewColumn *desc_column; + + text_renderer = GTK_CELL_RENDERER (gtk_cell_renderer_text_new ()); + + desc_column = + gtk_tree_view_column_new_with_attributes (_("Layout"), + text_renderer, + "text", + SEL_LAYOUT_TREE_COL_DESCRIPTION, + "sensitive", + SEL_LAYOUT_TREE_COL_ENABLED, + NULL); + selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + + gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), + GTK_TREE_MODEL (list_store)); + + gtk_tree_view_column_set_sizing (desc_column, + GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable (desc_column, TRUE); + gtk_tree_view_column_set_expand (desc_column, TRUE); + + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), + desc_column); + + g_signal_connect_swapped (G_OBJECT (selection), "changed", + G_CALLBACK + (xkb_layouts_enable_disable_buttons), + dialog); + max_selected_layouts = xkl_engine_get_max_num_groups (engine); + + /* Setting up DnD */ + gtk_drag_source_set (tree_view, GDK_BUTTON1_MASK, + &self_drag_target, 1, GDK_ACTION_MOVE); + gtk_drag_source_set_icon_name (tree_view, "input-keyboard"); + gtk_drag_dest_set (tree_view, GTK_DEST_DEFAULT_ALL, + &self_drag_target, 1, GDK_ACTION_MOVE); + + g_signal_connect (G_OBJECT (tree_view), "drag_data_get", + G_CALLBACK (xkb_layouts_dnd_data_get), dialog); + g_signal_connect (G_OBJECT (tree_view), "drag_data_received", + G_CALLBACK (xkb_layouts_dnd_data_received), + dialog); +} + +gchar * +xkb_layout_description_utf8 (const gchar * visible) +{ + char *l, *sl, *v, *sv; + if (matekbd_keyboard_config_get_descriptions + (config_registry, visible, &sl, &l, &sv, &v)) + visible = matekbd_keyboard_config_format_full_layout (l, v); + return g_strstrip (g_strdup (visible)); +} + +void +xkb_layouts_fill_selected_tree (GtkBuilder * dialog) +{ + GSList *layouts = xkb_layouts_get_selected_list (); + GSList *cur_layout; + GtkListStore *list_store = + GTK_LIST_STORE (gtk_tree_view_get_model + (GTK_TREE_VIEW + (WID ("xkb_layouts_selected")))); + int counter = 0; + + /* temporarily disable the buttons' status update */ + disable_buttons_sensibility_update = TRUE; + + gtk_list_store_clear (list_store); + + for (cur_layout = layouts; cur_layout != NULL; + cur_layout = cur_layout->next, counter++) { + GtkTreeIter iter; + const char *visible = (char *) cur_layout->data; + gchar *utf_visible = xkb_layout_description_utf8 (visible); + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, + SEL_LAYOUT_TREE_COL_DESCRIPTION, + utf_visible, + SEL_LAYOUT_TREE_COL_ID, + cur_layout->data, + SEL_LAYOUT_TREE_COL_ENABLED, + counter < max_selected_layouts, -1); + g_free (utf_visible); + } + + clear_xkb_elements_list (layouts); + + /* enable the buttons' status update */ + disable_buttons_sensibility_update = FALSE; + + if (idx2select != -1) { + GtkTreeSelection *selection = + gtk_tree_view_get_selection ((GTK_TREE_VIEW + (WID + ("xkb_layouts_selected")))); + GtkTreePath *path = + gtk_tree_path_new_from_indices (idx2select, -1); + gtk_tree_selection_select_path (selection, path); + gtk_tree_path_free (path); + idx2select = -1; + } else { + /* if there is nothing to select - just enable/disable the buttons, + otherwise it would be done by the selection change */ + xkb_layouts_enable_disable_buttons (dialog); + } +} + +static void +add_selected_layout (GtkWidget * button, GtkBuilder * dialog) +{ + xkb_layout_choose (dialog); +} + +static void +show_selected_layout (GtkWidget * button, GtkBuilder * dialog) +{ + gint idx = find_selected_layout_idx (dialog); + + if (idx != -1) { + GSList *layouts_list = xkb_layouts_get_selected_list (); + const gchar *id = g_slist_nth_data (layouts_list, idx); + char *descr = xkb_layout_description_utf8 (id); + GtkWidget *parent = WID ("keyboard_dialog"); + GtkWidget *popup = matekbd_keyboard_drawing_new_dialog (idx, descr); + gtk_widget_set_parent (popup, parent); + clear_xkb_elements_list (layouts_list); + g_free (descr); + } +} + +static void +remove_selected_layout (GtkWidget * button, GtkBuilder * dialog) +{ + gint idx = find_selected_layout_idx (dialog); + + if (idx != -1) { + GSList *layouts_list = xkb_layouts_get_selected_list (); + char *id = NULL; + GSList *node2Remove = g_slist_nth (layouts_list, idx); + + layouts_list = + g_slist_remove_link (layouts_list, node2Remove); + + id = (char *) node2Remove->data; + g_slist_free_1 (node2Remove); + g_free (id); + + if (default_group > idx) + xkb_save_default_group (default_group - 1); + else if (default_group == idx) + xkb_save_default_group (-1); + + xkb_layouts_set_selected_list (layouts_list); + clear_xkb_elements_list (layouts_list); + } +} + +static void +move_up_selected_layout (GtkWidget * button, GtkBuilder * dialog) +{ + gint idx = find_selected_layout_idx (dialog); + + if (idx != -1) { + GSList *layouts_list = xkb_layouts_get_selected_list (); + GSList *node2Remove = g_slist_nth (layouts_list, idx); + + layouts_list = + g_slist_remove_link (layouts_list, node2Remove); + layouts_list = + g_slist_insert (layouts_list, node2Remove->data, + idx - 1); + g_slist_free_1 (node2Remove); + + idx2select = idx - 1; + xkb_layouts_set_selected_list (layouts_list); + clear_xkb_elements_list (layouts_list); + } +} + +static void +move_down_selected_layout (GtkWidget * button, GtkBuilder * dialog) +{ + gint idx = find_selected_layout_idx (dialog); + + if (idx != -1) { + GSList *layouts_list = xkb_layouts_get_selected_list (); + GSList *node2Remove = g_slist_nth (layouts_list, idx); + + layouts_list = + g_slist_remove_link (layouts_list, node2Remove); + layouts_list = + g_slist_insert (layouts_list, node2Remove->data, + idx + 1); + g_slist_free_1 (node2Remove); + + idx2select = idx + 1; + xkb_layouts_set_selected_list (layouts_list); + clear_xkb_elements_list (layouts_list); + } +} + +void +xkb_layouts_register_buttons_handlers (GtkBuilder * dialog) +{ + g_signal_connect (G_OBJECT (WID ("xkb_layouts_add")), "clicked", + G_CALLBACK (add_selected_layout), dialog); + g_signal_connect (G_OBJECT (WID ("xkb_layouts_show")), "clicked", + G_CALLBACK (show_selected_layout), dialog); + g_signal_connect (G_OBJECT (WID ("xkb_layouts_remove")), "clicked", + G_CALLBACK (remove_selected_layout), dialog); + g_signal_connect (G_OBJECT (WID ("xkb_layouts_move_up")), + "clicked", G_CALLBACK (move_up_selected_layout), + dialog); + g_signal_connect (G_OBJECT (WID ("xkb_layouts_move_down")), + "clicked", + G_CALLBACK (move_down_selected_layout), dialog); +} + +static void +xkb_layouts_update_list (MateConfClient * client, + guint cnxn_id, MateConfEntry * entry, + GtkBuilder * dialog) +{ + xkb_layouts_fill_selected_tree (dialog); + enable_disable_restoring (dialog); +} + +void +xkb_layouts_register_mateconf_listener (GtkBuilder * dialog) +{ + mateconf_client_notify_add (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_LAYOUTS, + (MateConfClientNotifyFunc) + xkb_layouts_update_list, dialog, NULL, + NULL); +} diff --git a/capplets/keyboard/mate-keyboard-properties-xkbltadd.c b/capplets/keyboard/mate-keyboard-properties-xkbltadd.c new file mode 100644 index 00000000..e73e59cc --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkbltadd.c @@ -0,0 +1,561 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkbltadd.c + * Copyright (C) 2007 Sergey V. Udaltsov + * + * Written by: Sergey V. Udaltsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include + +#include "capplet-util.h" +#include "mate-keyboard-properties-xkb.h" + +enum { + COMBO_BOX_MODEL_COL_SORT, + COMBO_BOX_MODEL_COL_VISIBLE, + COMBO_BOX_MODEL_COL_XKB_ID, + COMBO_BOX_MODEL_COL_REAL_ID +}; + +typedef void (*LayoutIterFunc) (XklConfigRegistry * config, + ConfigItemProcessFunc func, gpointer data); + +typedef struct { + GtkListStore *list_store; + const gchar *lang_id; +} AddVariantData; + +static void + + + + + + + + + +xkb_layout_chooser_available_layouts_fill (GtkBuilder * chooser_dialog, + const gchar cblid[], + const gchar cbvid[], + LayoutIterFunc layout_iterator, + ConfigItemProcessFunc + layout_handler, + GCallback combo_changed_notify); + +static void + + + + + + + + + +xkb_layout_chooser_available_language_variants_fill (GtkBuilder * + chooser_dialog); + +static void + + + + + + + + + +xkb_layout_chooser_available_country_variants_fill (GtkBuilder * + chooser_dialog); + +static void + xkb_layout_chooser_add_variant_to_available_country_variants + (XklConfigRegistry * config_registry, + XklConfigItem * parent_config_item, XklConfigItem * config_item, + AddVariantData * data) { + gchar *utf_variant_name = config_item ? + xkb_layout_description_utf8 (matekbd_keyboard_config_merge_items + (parent_config_item->name, + config_item->name)) : + xci_desc_to_utf8 (parent_config_item); + GtkTreeIter iter; + const gchar *xkb_id = + config_item ? + matekbd_keyboard_config_merge_items (parent_config_item->name, + config_item->name) : + parent_config_item->name; + + if (config_item && g_object_get_data + (G_OBJECT (config_item), XCI_PROP_EXTRA_ITEM)) { + gchar *buf = + g_strdup_printf ("%s", utf_variant_name); + gtk_list_store_insert_with_values (data->list_store, &iter, + -1, + COMBO_BOX_MODEL_COL_SORT, + utf_variant_name, + COMBO_BOX_MODEL_COL_VISIBLE, + buf, + COMBO_BOX_MODEL_COL_XKB_ID, + xkb_id, -1); + g_free (buf); + } else + gtk_list_store_insert_with_values (data->list_store, &iter, + -1, + COMBO_BOX_MODEL_COL_SORT, + utf_variant_name, + COMBO_BOX_MODEL_COL_VISIBLE, + utf_variant_name, + COMBO_BOX_MODEL_COL_XKB_ID, + xkb_id, -1); + g_free (utf_variant_name); +} + +static void + xkb_layout_chooser_add_variant_to_available_language_variants + (XklConfigRegistry * config_registry, + XklConfigItem * parent_config_item, XklConfigItem * config_item, + AddVariantData * data) { + xkb_layout_chooser_add_variant_to_available_country_variants + (config_registry, parent_config_item, config_item, data); +} + +static void +xkb_layout_chooser_add_language_to_available_languages (XklConfigRegistry * + config_registry, + XklConfigItem * + config_item, + GtkListStore * + list_store) +{ + gtk_list_store_insert_with_values (list_store, NULL, -1, + COMBO_BOX_MODEL_COL_SORT, + config_item->description, + COMBO_BOX_MODEL_COL_VISIBLE, + config_item->description, + COMBO_BOX_MODEL_COL_REAL_ID, + config_item->name, -1); +} + +static void +xkb_layout_chooser_add_country_to_available_countries (XklConfigRegistry * + config_registry, + XklConfigItem * + config_item, + GtkListStore * + list_store) +{ + gtk_list_store_insert_with_values (list_store, NULL, -1, + COMBO_BOX_MODEL_COL_SORT, + config_item->description, + COMBO_BOX_MODEL_COL_VISIBLE, + config_item->description, + COMBO_BOX_MODEL_COL_REAL_ID, + config_item->name, -1); +} + +static void +xkb_layout_chooser_enable_disable_buttons (GtkBuilder * chooser_dialog) +{ + GtkWidget *cbv = + CWID (gtk_notebook_get_current_page + (GTK_NOTEBOOK (CWID ("choosers_nb"))) ? + "xkb_language_variants_available" : + "xkb_country_variants_available"); + GtkTreeIter viter; + gboolean enable_ok = + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (cbv), + &viter); + + gtk_dialog_set_response_sensitive (GTK_DIALOG + (CWID + ("xkb_layout_chooser")), + GTK_RESPONSE_OK, enable_ok); + gtk_widget_set_sensitive (CWID ("btnPrint"), enable_ok); +} + +static void +xkb_layout_chooser_available_variant_changed (GtkBuilder * chooser_dialog) +{ + xkb_layout_preview_update (chooser_dialog); + xkb_layout_chooser_enable_disable_buttons (chooser_dialog); +} + +static void +xkb_layout_chooser_available_language_changed (GtkBuilder * chooser_dialog) +{ + xkb_layout_chooser_available_language_variants_fill + (chooser_dialog); + xkb_layout_chooser_available_variant_changed (chooser_dialog); +} + +static void +xkb_layout_chooser_available_country_changed (GtkBuilder * chooser_dialog) +{ + xkb_layout_chooser_available_country_variants_fill + (chooser_dialog); + xkb_layout_chooser_available_variant_changed (chooser_dialog); +} + +static void +xkb_layout_chooser_page_changed (GtkWidget * notebook, GtkWidget * page, + gint page_num, + GtkBuilder * chooser_dialog) +{ + xkb_layout_chooser_available_variant_changed (chooser_dialog); +} + +static void +xkb_layout_chooser_available_language_variants_fill (GtkBuilder * + chooser_dialog) +{ + GtkWidget *cbl = CWID ("xkb_languages_available"); + GtkWidget *cbv = CWID ("xkb_language_variants_available"); + GtkListStore *list_store; + GtkTreeIter liter; + + list_store = gtk_list_store_new + (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING); + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (cbl), &liter)) { + GtkTreeModel *lm = + gtk_combo_box_get_model (GTK_COMBO_BOX (cbl)); + gchar *lang_id; + AddVariantData data = { list_store, 0 }; + + /* Now the variants of the selected layout */ + gtk_tree_model_get (lm, &liter, + COMBO_BOX_MODEL_COL_REAL_ID, + &lang_id, -1); + data.lang_id = lang_id; + + xkl_config_registry_foreach_language_variant + (config_registry, lang_id, (TwoConfigItemsProcessFunc) + xkb_layout_chooser_add_variant_to_available_language_variants, + &data); + g_free (lang_id); + } + + /* Turn on sorting after filling the store, since that's faster */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE + (list_store), + COMBO_BOX_MODEL_COL_SORT, + GTK_SORT_ASCENDING); + + gtk_combo_box_set_model (GTK_COMBO_BOX (cbv), + GTK_TREE_MODEL (list_store)); + gtk_combo_box_set_active (GTK_COMBO_BOX (cbv), 0); +} + +static void +xkb_layout_chooser_available_country_variants_fill (GtkBuilder * + chooser_dialog) +{ + GtkWidget *cbl = CWID ("xkb_countries_available"); + GtkWidget *cbv = CWID ("xkb_country_variants_available"); + GtkListStore *list_store; + GtkTreeIter liter; + + list_store = gtk_list_store_new + (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING); + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (cbl), &liter)) { + GtkTreeModel *lm = + gtk_combo_box_get_model (GTK_COMBO_BOX (cbl)); + gchar *country_id; + AddVariantData data = { list_store, 0 }; + + /* Now the variants of the selected layout */ + gtk_tree_model_get (lm, &liter, + COMBO_BOX_MODEL_COL_REAL_ID, + &country_id, -1); + xkl_config_registry_foreach_country_variant + (config_registry, country_id, + (TwoConfigItemsProcessFunc) + xkb_layout_chooser_add_variant_to_available_country_variants, + &data); + g_free (country_id); + } + + /* Turn on sorting after filling the store, since that's faster */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE + (list_store), + COMBO_BOX_MODEL_COL_SORT, + GTK_SORT_ASCENDING); + + gtk_combo_box_set_model (GTK_COMBO_BOX (cbv), + GTK_TREE_MODEL (list_store)); + gtk_combo_box_set_active (GTK_COMBO_BOX (cbv), 0); +} + +static void +xkb_layout_chooser_available_layouts_fill (GtkBuilder * + chooser_dialog, + const gchar cblid[], + const gchar cbvid[], + LayoutIterFunc layout_iterator, + ConfigItemProcessFunc + layout_handler, + GCallback combo_changed_notify) +{ + GtkWidget *cbl = CWID (cblid); + GtkWidget *cbev = CWID (cbvid); + GtkCellRenderer *renderer; + GtkListStore *list_store; + + list_store = gtk_list_store_new + (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING); + + gtk_combo_box_set_model (GTK_COMBO_BOX (cbl), + GTK_TREE_MODEL (list_store)); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbl), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbl), + renderer, "markup", + COMBO_BOX_MODEL_COL_VISIBLE, NULL); + + layout_iterator (config_registry, layout_handler, list_store); + + /* Turn on sorting after filling the model since that's faster */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE + (list_store), + COMBO_BOX_MODEL_COL_SORT, + GTK_SORT_ASCENDING); + + g_signal_connect_swapped (G_OBJECT (cbl), "changed", + combo_changed_notify, chooser_dialog); + + /* Setup the variants combo */ + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbev), + renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbev), + renderer, "markup", + COMBO_BOX_MODEL_COL_VISIBLE, NULL); + + g_signal_connect_swapped (G_OBJECT (cbev), "changed", + G_CALLBACK + (xkb_layout_chooser_available_variant_changed), + chooser_dialog); +} + +void +xkl_layout_chooser_add_default_switcher_if_necessary (GSList * + layouts_list) +{ + GSList *options_list = xkb_options_get_selected_list (); + gboolean was_appended; + + options_list = + matekbd_keyboard_config_add_default_switch_option_if_necessary + (layouts_list, options_list, &was_appended); + if (was_appended) + xkb_options_set_selected_list (options_list); + clear_xkb_elements_list (options_list); +} + +static void +xkb_layout_chooser_print (GtkBuilder * chooser_dialog) +{ + GtkWidget *chooser = CWID ("xkb_layout_chooser"); + GtkWidget *kbdraw = + GTK_WIDGET (g_object_get_data (G_OBJECT (chooser), "kbdraw")); + const char *id = + xkb_layout_chooser_get_selected_id (chooser_dialog); + char *descr = xkb_layout_description_utf8 (id); + matekbd_keyboard_drawing_print (MATEKBD_KEYBOARD_DRAWING + (kbdraw), + GTK_WINDOW (CWID + ("xkb_layout_chooser")), + descr); + g_free (descr); +} + +static void +xkb_layout_chooser_response (GtkDialog * dialog, + gint response, GtkBuilder * chooser_dialog) +{ + GdkRectangle rect; + + if (response == GTK_RESPONSE_OK) { + gchar *selected_id = (gchar *) + xkb_layout_chooser_get_selected_id (chooser_dialog); + + if (selected_id != NULL) { + GSList *layouts_list = + xkb_layouts_get_selected_list (); + + selected_id = g_strdup (selected_id); + + layouts_list = + g_slist_append (layouts_list, selected_id); + xkb_layouts_set_selected_list (layouts_list); + + xkl_layout_chooser_add_default_switcher_if_necessary + (layouts_list); + + clear_xkb_elements_list (layouts_list); + } + } else if (response == gtk_dialog_get_response_for_widget + (dialog, CWID ("btnPrint"))) { + xkb_layout_chooser_print (chooser_dialog); + g_signal_stop_emission_by_name (dialog, "response"); + return; + } + + gtk_window_get_position (GTK_WINDOW (dialog), &rect.x, &rect.y); + gtk_window_get_size (GTK_WINDOW (dialog), &rect.width, + &rect.height); + matekbd_preview_save_position (&rect); +} + +void +xkb_layout_choose (GtkBuilder * dialog) +{ + GtkBuilder *chooser_dialog; + + chooser_dialog = gtk_builder_new (); + gtk_builder_add_from_file (chooser_dialog, MATECC_UI_DIR + "/mate-keyboard-properties-layout-chooser.ui", + NULL); + GtkWidget *chooser = CWID ("xkb_layout_chooser"); + GtkWidget *lang_chooser = CWID ("xkb_languages_available"); + GtkWidget *notebook = CWID ("choosers_nb"); + GtkWidget *kbdraw = NULL; + GtkWidget *toplevel = NULL; + + gtk_window_set_transient_for (GTK_WINDOW (chooser), + GTK_WINDOW (WID + ("keyboard_dialog"))); + + xkb_layout_chooser_available_layouts_fill (chooser_dialog, + "xkb_countries_available", + "xkb_country_variants_available", + xkl_config_registry_foreach_country, + (ConfigItemProcessFunc) + xkb_layout_chooser_add_country_to_available_countries, + G_CALLBACK + (xkb_layout_chooser_available_country_changed)); + xkb_layout_chooser_available_layouts_fill (chooser_dialog, + "xkb_languages_available", + "xkb_language_variants_available", + xkl_config_registry_foreach_language, + (ConfigItemProcessFunc) + xkb_layout_chooser_add_language_to_available_languages, + G_CALLBACK + (xkb_layout_chooser_available_language_changed)); + + g_signal_connect_after (G_OBJECT (notebook), "switch_page", + G_CALLBACK + (xkb_layout_chooser_page_changed), + chooser_dialog); + + gtk_combo_box_set_active (GTK_COMBO_BOX + (CWID ("xkb_countries_available")), + FALSE); + + if (gtk_tree_model_iter_n_children + (gtk_combo_box_get_model (GTK_COMBO_BOX (lang_chooser)), + NULL)) { + gtk_combo_box_set_active (GTK_COMBO_BOX + (CWID + ("xkb_languages_available")), + FALSE); + } else { + /* If language info is not available - remove the corresponding tab, + pretend there is no notebook at all */ + gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), 1); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), + FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), + FALSE); + } + +#ifdef HAVE_X11_EXTENSIONS_XKB_H + if (!strcmp (xkl_engine_get_backend_name (engine), "XKB")) { + kbdraw = xkb_layout_preview_create_widget (chooser_dialog); + g_object_set_data (G_OBJECT (chooser), "kbdraw", kbdraw); + gtk_container_add (GTK_CONTAINER + (CWID ("previewFrame")), kbdraw); + gtk_widget_show_all (kbdraw); + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX + (CWID + ("hbtnBox")), + CWID + ("btnPrint"), TRUE); + } else +#endif + { + gtk_widget_hide_all (CWID ("vboxPreview")); + gtk_widget_hide (CWID ("btnPrint")); + } + + g_signal_connect (G_OBJECT (chooser), + "response", + G_CALLBACK (xkb_layout_chooser_response), + chooser_dialog); + + toplevel = gtk_widget_get_toplevel (chooser); + if (gtk_widget_is_toplevel (toplevel)) { + GdkRectangle *rect = matekbd_preview_load_position (); + if (rect != NULL) { + gtk_window_move (GTK_WINDOW (toplevel), + rect->x, rect->y); + gtk_window_resize (GTK_WINDOW (toplevel), + rect->width, rect->height); + g_free (rect); + } + } + + xkb_layout_preview_update (chooser_dialog); + gtk_dialog_run (GTK_DIALOG (chooser)); + gtk_widget_destroy (chooser); +} + +gchar * +xkb_layout_chooser_get_selected_id (GtkBuilder * chooser_dialog) +{ + GtkWidget *cbv = + CWID (gtk_notebook_get_current_page + (GTK_NOTEBOOK (CWID ("choosers_nb"))) ? + "xkb_language_variants_available" : + "xkb_country_variants_available"); + GtkTreeModel *vm = gtk_combo_box_get_model (GTK_COMBO_BOX (cbv)); + GtkTreeIter viter; + gchar *v_id; + + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (cbv), &viter)) + return NULL; + + gtk_tree_model_get (vm, &viter, + COMBO_BOX_MODEL_COL_XKB_ID, &v_id, -1); + + return v_id; +} diff --git a/capplets/keyboard/mate-keyboard-properties-xkbmc.c b/capplets/keyboard/mate-keyboard-properties-xkbmc.c new file mode 100644 index 00000000..b09068d7 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkbmc.c @@ -0,0 +1,347 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkbmc.c + * Copyright (C) 2003-2007 Sergey V. Udaltsov + * + * Written by: Sergey V. Udaltsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "capplet-util.h" + +#include "mate-keyboard-properties-xkb.h" + +static gchar *current_model_name = NULL; +static gchar *current_vendor_name = NULL; + +static void fill_models_list (GtkBuilder * chooser_dialog); + +static gboolean fill_vendors_list (GtkBuilder * chooser_dialog); + +static GtkTreePath * +gtk_list_store_find_entry (GtkListStore * list_store, + GtkTreeIter * iter, gchar * name, int column_id) +{ + GtkTreePath *path; + char *current_name = NULL; + if (gtk_tree_model_get_iter_first + (GTK_TREE_MODEL (list_store), iter)) { + do { + gtk_tree_model_get (GTK_TREE_MODEL + (list_store), iter, column_id, + ¤t_name, -1); + if (!g_ascii_strcasecmp (name, current_name)) { + path = + gtk_tree_model_get_path + (GTK_TREE_MODEL (list_store), iter); + return path; + } + g_free (current_name); + } while (gtk_tree_model_iter_next + (GTK_TREE_MODEL (list_store), iter)); + } + return NULL; +} + +static void +add_vendor_to_list (XklConfigRegistry * config_registry, + XklConfigItem * config_item, + GtkTreeView * vendors_list) +{ + GtkTreeIter iter; + GtkTreePath *found_existing; + GtkListStore *list_store; + + gchar *vendor_name = + (gchar *) g_object_get_data (G_OBJECT (config_item), + XCI_PROP_VENDOR); + + if (vendor_name == NULL) + return; + + list_store = + GTK_LIST_STORE (gtk_tree_view_get_model (vendors_list)); + + if (!g_ascii_strcasecmp (config_item->name, current_model_name)) { + current_vendor_name = g_strdup (vendor_name); + } + + found_existing = + gtk_list_store_find_entry (list_store, &iter, vendor_name, 0); + /* This vendor is already there */ + if (found_existing != NULL) { + gtk_tree_path_free (found_existing); + return; + } + + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, 0, vendor_name, -1); +} + +static void +add_model_to_list (XklConfigRegistry * config_registry, + XklConfigItem * config_item, GtkTreeView * models_list) +{ + GtkTreeIter iter; + GtkListStore *list_store = + GTK_LIST_STORE (gtk_tree_view_get_model (models_list)); + char *utf_model_name; + if (current_vendor_name != NULL) { + gchar *vendor_name = + (gchar *) g_object_get_data (G_OBJECT (config_item), + XCI_PROP_VENDOR); + if (vendor_name == NULL) + return; + + if (g_ascii_strcasecmp (vendor_name, current_vendor_name)) + return; + } + utf_model_name = xci_desc_to_utf8 (config_item); + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, + 0, utf_model_name, 1, config_item->name, -1); + + g_free (utf_model_name); +} + +static void +xkb_model_chooser_change_vendor_sel (GtkTreeSelection * selection, + GtkBuilder * chooser_dialog) +{ + GtkTreeIter iter; + GtkTreeModel *list_store = NULL; + if (gtk_tree_selection_get_selected + (selection, &list_store, &iter)) { + gchar *vendor_name = NULL; + gtk_tree_model_get (list_store, &iter, + 0, &vendor_name, -1); + + current_vendor_name = vendor_name; + fill_models_list (chooser_dialog); + g_free (vendor_name); + } else { + current_vendor_name = NULL; + fill_models_list (chooser_dialog); + } + +} + +static void +xkb_model_chooser_change_model_sel (GtkTreeSelection * selection, + GtkBuilder * chooser_dialog) +{ + gboolean anysel = + gtk_tree_selection_get_selected (selection, NULL, NULL); + gtk_dialog_set_response_sensitive (GTK_DIALOG + (CWID ("xkb_model_chooser")), + GTK_RESPONSE_OK, anysel); +} + +static void +prepare_vendors_list (GtkBuilder * chooser_dialog) +{ + GtkWidget *vendors_list = CWID ("vendors_list"); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *vendor_col = + gtk_tree_view_column_new_with_attributes (_("Vendors"), + renderer, + "text", 0, + NULL); + gtk_tree_view_column_set_visible (vendor_col, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (vendors_list), + vendor_col); +} + +static gboolean +fill_vendors_list (GtkBuilder * chooser_dialog) +{ + GtkWidget *vendors_list = CWID ("vendors_list"); + GtkListStore *list_store = gtk_list_store_new (1, G_TYPE_STRING); + GtkTreeIter iter; + GtkTreePath *path; + + gtk_tree_view_set_model (GTK_TREE_VIEW (vendors_list), + GTK_TREE_MODEL (list_store)); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE + (list_store), 0, + GTK_SORT_ASCENDING); + + current_vendor_name = NULL; + + xkl_config_registry_foreach_model (config_registry, + (ConfigItemProcessFunc) + add_vendor_to_list, + vendors_list); + + if (current_vendor_name != NULL) { + path = gtk_list_store_find_entry (list_store, + &iter, + current_vendor_name, 0); + if (path != NULL) { + gtk_tree_selection_select_iter + (gtk_tree_view_get_selection + (GTK_TREE_VIEW (vendors_list)), &iter); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW (vendors_list), + path, NULL, TRUE, 0.5, 0); + gtk_tree_path_free (path); + } + fill_models_list (chooser_dialog); + g_free (current_vendor_name); + } else { + fill_models_list (chooser_dialog); + } + + g_signal_connect (G_OBJECT + (gtk_tree_view_get_selection + (GTK_TREE_VIEW (vendors_list))), "changed", + G_CALLBACK (xkb_model_chooser_change_vendor_sel), + chooser_dialog); + + return gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), + &iter); +} + +static void +prepare_models_list (GtkBuilder * chooser_dialog) +{ + GtkWidget *models_list = CWID ("models_list"); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *description_col = + gtk_tree_view_column_new_with_attributes (_("Models"), + renderer, + "text", 0, + NULL); + gtk_tree_view_column_set_visible (description_col, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (models_list), + description_col); +} + +static void +fill_models_list (GtkBuilder * chooser_dialog) +{ + GtkWidget *models_list = CWID ("models_list"); + GtkTreeIter iter; + GtkTreePath *path; + + GtkListStore *list_store = + gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE + (list_store), 0, + GTK_SORT_ASCENDING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (models_list), + GTK_TREE_MODEL (list_store)); + + xkl_config_registry_foreach_model (config_registry, + (ConfigItemProcessFunc) + add_model_to_list, models_list); + + if (current_model_name != NULL) { + path = gtk_list_store_find_entry (list_store, + &iter, + current_model_name, 1); + if (path != NULL) { + gtk_tree_selection_select_iter + (gtk_tree_view_get_selection + (GTK_TREE_VIEW (models_list)), &iter); + gtk_tree_view_scroll_to_cell + (GTK_TREE_VIEW (models_list), + path, NULL, TRUE, 0.5, 0); + gtk_tree_path_free (path); + } + } + + g_signal_connect (G_OBJECT + (gtk_tree_view_get_selection + (GTK_TREE_VIEW (models_list))), "changed", + G_CALLBACK (xkb_model_chooser_change_model_sel), + chooser_dialog); +} + +static void +xkb_model_chooser_response (GtkDialog * dialog, + gint response, GtkBuilder * chooser_dialog) +{ + if (response == GTK_RESPONSE_OK) { + GtkWidget *models_list = CWID ("models_list"); + GtkTreeSelection *selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW + (models_list)); + GtkTreeIter iter; + GtkTreeModel *list_store = NULL; + if (gtk_tree_selection_get_selected + (selection, &list_store, &iter)) { + gchar *model_name = NULL; + gtk_tree_model_get (list_store, &iter, + 1, &model_name, -1); + + mateconf_client_set_string (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_MODEL, + model_name, NULL); + g_free (model_name); + } + } +} + +void +choose_model (GtkBuilder * dialog) +{ + GtkBuilder *chooser_dialog; + GtkWidget *chooser; + + chooser_dialog = gtk_builder_new (); + gtk_builder_add_from_file (chooser_dialog, MATECC_UI_DIR + "/mate-keyboard-properties-model-chooser.ui", + NULL); + chooser = CWID ("xkb_model_chooser"); + gtk_window_set_transient_for (GTK_WINDOW (chooser), + GTK_WINDOW (WID + ("keyboard_dialog"))); + current_model_name = + mateconf_client_get_string (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_MODEL, NULL); + + + prepare_vendors_list (chooser_dialog); + prepare_models_list (chooser_dialog); + + if (!fill_vendors_list (chooser_dialog)) { + gtk_widget_hide_all (CWID ("vendors_label")); + gtk_widget_hide_all (CWID ("vendors_scrolledwindow")); + current_vendor_name = NULL; + fill_models_list (chooser_dialog); + } + + g_signal_connect (G_OBJECT (chooser), + "response", + G_CALLBACK (xkb_model_chooser_response), + chooser_dialog); + gtk_dialog_run (GTK_DIALOG (chooser)); + gtk_widget_destroy (chooser); + g_free (current_model_name); +} diff --git a/capplets/keyboard/mate-keyboard-properties-xkbot.c b/capplets/keyboard/mate-keyboard-properties-xkbot.c new file mode 100644 index 00000000..1436d86a --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkbot.c @@ -0,0 +1,516 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkbot.c + * Copyright (C) 2003-2007 Sergey V. Udaltsov + * + * Written by: Sergey V. Udaltsov + * John Spray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "capplet-util.h" + +#include "mate-keyboard-properties-xkb.h" + +static GtkBuilder *chooser_dialog = NULL; +static const char *current1st_level_id = NULL; +static GSList *option_checks_list = NULL; +static GtkWidget *current_none_radio = NULL; +static GtkWidget *current_expander = NULL; +static gboolean current_multi_select = FALSE; +static GSList *current_radio_group = NULL; + +#define OPTION_ID_PROP "optionID" +#define SELCOUNTER_PROP "selectionCounter" +#define MATECONFSTATE_PROP "mateconfState" +#define EXPANDERS_PROP "expandersList" + +GSList * +xkb_options_get_selected_list (void) +{ + GSList *retval; + + retval = mateconf_client_get_list (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_OPTIONS, + MATECONF_VALUE_STRING, NULL); + if (retval == NULL) { + GSList *cur_option; + + for (cur_option = initial_config.options; + cur_option != NULL; cur_option = cur_option->next) + retval = + g_slist_prepend (retval, + g_strdup (cur_option->data)); + + retval = g_slist_reverse (retval); + } + + return retval; +} + +/* Returns the selection counter of the expander (static current_expander) */ +static int +xkb_options_expander_selcounter_get (void) +{ + return + GPOINTER_TO_INT (g_object_get_data + (G_OBJECT (current_expander), + SELCOUNTER_PROP)); +} + +/* Increments the selection counter in the expander (static current_expander) + using the value (can be 0)*/ +static void +xkb_options_expander_selcounter_add (int value) +{ + g_object_set_data (G_OBJECT (current_expander), SELCOUNTER_PROP, + GINT_TO_POINTER + (xkb_options_expander_selcounter_get () + + value)); +} + +/* Resets the seletion counter in the expander (static current_expander) */ +static void +xkb_options_expander_selcounter_reset (void) +{ + g_object_set_data (G_OBJECT (current_expander), SELCOUNTER_PROP, + GINT_TO_POINTER (0)); +} + +/* Formats the expander (static current_expander), based on the selection counter */ +static void +xkb_options_expander_highlight (void) +{ + char *utf_group_name = + g_object_get_data (G_OBJECT (current_expander), + "utfGroupName"); + int counter = xkb_options_expander_selcounter_get (); + if (utf_group_name != NULL) { + gchar *titlemarkup = + g_strconcat (counter > + 0 ? "" : "", + utf_group_name, "", NULL); + gtk_expander_set_label (GTK_EXPANDER (current_expander), + titlemarkup); + g_free (titlemarkup); + } +} + +/* Add optionname from the backend's selection list if it's not + already in there. */ +static void +xkb_options_select (gchar * optionname) +{ + gboolean already_selected = FALSE; + GSList *options_list = xkb_options_get_selected_list (); + GSList *option; + for (option = options_list; option != NULL; option = option->next) + if (!strcmp ((gchar *) option->data, optionname)) + already_selected = TRUE; + + if (!already_selected) + options_list = + g_slist_append (options_list, g_strdup (optionname)); + xkb_options_set_selected_list (options_list); + + clear_xkb_elements_list (options_list); +} + +/* Remove all occurences of optionname from the backend's selection list */ +static void +xkb_options_deselect (gchar * optionname) +{ + GSList *options_list = xkb_options_get_selected_list (); + GSList *nodetmp; + GSList *option = options_list; + while (option != NULL) { + gchar *id = (char *) option->data; + if (!strcmp (id, optionname)) { + nodetmp = option->next; + g_free (id); + options_list = + g_slist_remove_link (options_list, option); + g_slist_free_1 (option); + option = nodetmp; + } else + option = option->next; + } + xkb_options_set_selected_list (options_list); + clear_xkb_elements_list (options_list); +} + +/* Return true if optionname describes a string already in the backend's + list of selected options */ +static gboolean +xkb_options_is_selected (gchar * optionname) +{ + gboolean retval = FALSE; + GSList *options_list = xkb_options_get_selected_list (); + GSList *option; + for (option = options_list; option != NULL; option = option->next) { + if (!strcmp ((gchar *) option->data, optionname)) + retval = TRUE; + } + clear_xkb_elements_list (options_list); + return retval; +} + +/* Make sure selected options stay visible when navigating with the keyboard */ +static gboolean +option_focused_cb (GtkWidget * widget, GdkEventFocus * event, + gpointer data) +{ + GtkScrolledWindow *win = GTK_SCROLLED_WINDOW (data); + GtkAllocation alloc; + GtkAdjustment *adj; + + gtk_widget_get_allocation (widget, &alloc); + adj = gtk_scrolled_window_get_vadjustment (win); + gtk_adjustment_clamp_page (adj, alloc.y, + alloc.y + alloc.height); + + return FALSE; +} + +/* Update xkb backend to reflect the new UI state */ +static void +option_toggled_cb (GtkWidget * checkbutton, gpointer data) +{ + gpointer optionID = + g_object_get_data (G_OBJECT (checkbutton), OPTION_ID_PROP); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton))) + xkb_options_select (optionID); + else + xkb_options_deselect (optionID); +} + +/* Add a check_button or radio_button to control a particular option + This function makes particular use of the current... variables at + the top of this file. */ +static void +xkb_options_add_option (XklConfigRegistry * config_registry, + XklConfigItem * config_item, GtkBuilder * dialog) +{ + GtkWidget *option_check; + gchar *utf_option_name = xci_desc_to_utf8 (config_item); + /* Copy this out because we'll load it into the widget with set_data */ + gchar *full_option_name = + g_strdup (matekbd_keyboard_config_merge_items + (current1st_level_id, config_item->name)); + gboolean initial_state; + + if (current_multi_select) + option_check = + gtk_check_button_new_with_label (utf_option_name); + else { + if (current_radio_group == NULL) { + /* The first radio in a group is to be "Default", meaning none of + the below options are to be included in the selected list. + This is a HIG-compliant alternative to allowing no + selection in the group. */ + option_check = + gtk_radio_button_new_with_label + (current_radio_group, _("Default")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON + (option_check), + TRUE); + /* Make option name underscore - + to enforce its first position in the list */ + g_object_set_data_full (G_OBJECT (option_check), + "utfOptionName", + g_strdup (" "), g_free); + option_checks_list = + g_slist_append (option_checks_list, + option_check); + current_radio_group = + gtk_radio_button_get_group (GTK_RADIO_BUTTON + (option_check)); + current_none_radio = option_check; + + g_signal_connect (option_check, "focus-in-event", + G_CALLBACK (option_focused_cb), + WID ("options_scroll")); + } + option_check = + gtk_radio_button_new_with_label (current_radio_group, + utf_option_name); + current_radio_group = + gtk_radio_button_get_group (GTK_RADIO_BUTTON + (option_check)); + g_object_set_data (G_OBJECT (option_check), "NoneRadio", + current_none_radio); + } + + initial_state = xkb_options_is_selected (full_option_name); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (option_check), + initial_state); + + g_object_set_data_full (G_OBJECT (option_check), OPTION_ID_PROP, + full_option_name, g_free); + g_object_set_data_full (G_OBJECT (option_check), "utfOptionName", + utf_option_name, g_free); + + g_signal_connect (option_check, "toggled", + G_CALLBACK (option_toggled_cb), NULL); + + option_checks_list = + g_slist_append (option_checks_list, option_check); + + g_signal_connect (option_check, "focus-in-event", + G_CALLBACK (option_focused_cb), + WID ("options_scroll")); + + xkb_options_expander_selcounter_add (initial_state); + g_object_set_data (G_OBJECT (option_check), MATECONFSTATE_PROP, + GINT_TO_POINTER (initial_state)); +} + +static gint +xkb_option_checks_compare (GtkWidget * chk1, GtkWidget * chk2) +{ + const gchar *t1 = + g_object_get_data (G_OBJECT (chk1), "utfOptionName"); + const gchar *t2 = + g_object_get_data (G_OBJECT (chk2), "utfOptionName"); + return g_utf8_collate (t1, t2); +} + +/* Add a group of options: create title and layout widgets and then + add widgets for all the options in the group. */ +static void +xkb_options_add_group (XklConfigRegistry * config_registry, + XklConfigItem * config_item, GtkBuilder * dialog) +{ + GtkWidget *align, *vbox, *option_check; + gboolean allow_multiple_selection = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (config_item), + XCI_PROP_ALLOW_MULTIPLE_SELECTION)); + + GSList *expanders_list = + g_object_get_data (G_OBJECT (dialog), EXPANDERS_PROP); + + gchar *utf_group_name = xci_desc_to_utf8 (config_item); + gchar *titlemarkup = + g_strconcat ("", utf_group_name, "", NULL); + + current_expander = gtk_expander_new (titlemarkup); + gtk_expander_set_use_markup (GTK_EXPANDER (current_expander), + TRUE); + g_object_set_data_full (G_OBJECT (current_expander), + "utfGroupName", utf_group_name, g_free); + g_object_set_data_full (G_OBJECT (current_expander), "groupId", + g_strdup (config_item->name), g_free); + + g_free (titlemarkup); + align = gtk_alignment_new (0, 0, 1, 1); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 6, 12, 12, 0); + vbox = gtk_vbox_new (TRUE, 6); + gtk_container_add (GTK_CONTAINER (align), vbox); + gtk_container_add (GTK_CONTAINER (current_expander), align); + + current_multi_select = (gboolean) allow_multiple_selection; + current_radio_group = NULL; + current1st_level_id = config_item->name; + + option_checks_list = NULL; + + xkl_config_registry_foreach_option (config_registry, + config_item->name, + (ConfigItemProcessFunc) + xkb_options_add_option, + dialog); + /* sort it */ + option_checks_list = + g_slist_sort (option_checks_list, + (GCompareFunc) xkb_option_checks_compare); + while (option_checks_list) { + option_check = GTK_WIDGET (option_checks_list->data); + gtk_box_pack_start (GTK_BOX (vbox), option_check, TRUE, TRUE, 0); + option_checks_list = option_checks_list->next; + } + /* free it */ + g_slist_free (option_checks_list); + option_checks_list = NULL; + + xkb_options_expander_highlight (); + + expanders_list = g_slist_append (expanders_list, current_expander); + g_object_set_data (G_OBJECT (dialog), EXPANDERS_PROP, + expanders_list); + + g_signal_connect (current_expander, "focus-in-event", + G_CALLBACK (option_focused_cb), + WID ("options_scroll")); +} + +static gint +xkb_options_expanders_compare (GtkWidget * expander1, + GtkWidget * expander2) +{ + const gchar *t1 = + g_object_get_data (G_OBJECT (expander1), "utfGroupName"); + const gchar *t2 = + g_object_get_data (G_OBJECT (expander2), "utfGroupName"); + return g_utf8_collate (t1, t2); +} + +/* Create widgets to represent the options made available by the backend */ +void +xkb_options_load_options (GtkBuilder * dialog) +{ + GtkWidget *opts_vbox = WID ("options_vbox"); + GSList *expanders_list; + GtkWidget *expander; + + current1st_level_id = NULL; + current_none_radio = NULL; + current_multi_select = FALSE; + current_radio_group = NULL; + + /* fill the list */ + xkl_config_registry_foreach_option_group (config_registry, + (ConfigItemProcessFunc) + xkb_options_add_group, + dialog); + /* sort it */ + expanders_list = + g_object_get_data (G_OBJECT (dialog), EXPANDERS_PROP); + expanders_list = + g_slist_sort (expanders_list, + (GCompareFunc) xkb_options_expanders_compare); + g_object_set_data (G_OBJECT (dialog), EXPANDERS_PROP, + expanders_list); + while (expanders_list) { + expander = GTK_WIDGET (expanders_list->data); + gtk_box_pack_start (GTK_BOX (opts_vbox), expander, FALSE, + FALSE, 0); + expanders_list = expanders_list->next; + } + + gtk_widget_show_all (opts_vbox); +} + +static void +chooser_response_cb (GtkDialog * dialog, gint response, gpointer data) +{ + switch (response) { + case GTK_RESPONSE_HELP: + capplet_help (GTK_WINDOW (dialog), + "prefs-keyboard-layoutoptions"); + break; + case GTK_RESPONSE_CLOSE:{ + /* just cleanup */ + GSList *expanders_list = + g_object_get_data (G_OBJECT (dialog), + EXPANDERS_PROP); + g_object_set_data (G_OBJECT (dialog), + EXPANDERS_PROP, NULL); + g_slist_free (expanders_list); + + gtk_widget_destroy (GTK_WIDGET (dialog)); + chooser_dialog = NULL; + } + break; + } +} + +/* Create popup dialog */ +void +xkb_options_popup_dialog (GtkBuilder * dialog) +{ + GtkWidget *chooser; + + chooser_dialog = gtk_builder_new (); + gtk_builder_add_from_file (chooser_dialog, MATECC_UI_DIR + "/mate-keyboard-properties-options-dialog.ui", + NULL); + + chooser = CWID ("xkb_options_dialog"); + gtk_window_set_transient_for (GTK_WINDOW (chooser), + GTK_WINDOW (WID + ("keyboard_dialog"))); + xkb_options_load_options (chooser_dialog); + + g_signal_connect (chooser, "response", + G_CALLBACK (chooser_response_cb), dialog); + + gtk_dialog_run (GTK_DIALOG (chooser)); +} + +/* Update selected option counters for a group-bound expander */ +static void +xkb_options_update_option_counters (XklConfigRegistry * config_registry, + XklConfigItem * config_item) +{ + gchar *full_option_name = + g_strdup (matekbd_keyboard_config_merge_items + (current1st_level_id, config_item->name)); + gboolean current_state = + xkb_options_is_selected (full_option_name); + xkb_options_expander_selcounter_add (current_state); +} + +/* Respond to a change in the xkb mateconf settings */ +static void +xkb_options_update (MateConfClient * client, + guint cnxn_id, MateConfEntry * entry, GtkBuilder * dialog) +{ + /* Updating options is handled by mateconf notifies for each widget + This is here to avoid calling it N_OPTIONS times for each mateconf + change. */ + enable_disable_restoring (dialog); + + if (chooser_dialog != NULL) { + GSList *expanders_list = + g_object_get_data (G_OBJECT (chooser_dialog), + EXPANDERS_PROP); + while (expanders_list) { + current_expander = + GTK_WIDGET (expanders_list->data); + gchar *group_id = + g_object_get_data (G_OBJECT (current_expander), + "groupId"); + current1st_level_id = group_id; + xkb_options_expander_selcounter_reset (); + xkl_config_registry_foreach_option + (config_registry, group_id, + (ConfigItemProcessFunc) + xkb_options_update_option_counters, + current_expander); + xkb_options_expander_highlight (); + expanders_list = expanders_list->next; + } + } +} + +void +xkb_options_register_mateconf_listener (GtkBuilder * dialog) +{ + mateconf_client_notify_add (xkb_mateconf_client, + MATEKBD_KEYBOARD_CONFIG_KEY_OPTIONS, + (MateConfClientNotifyFunc) + xkb_options_update, dialog, NULL, NULL); +} diff --git a/capplets/keyboard/mate-keyboard-properties-xkbpv.c b/capplets/keyboard/mate-keyboard-properties-xkbpv.c new file mode 100644 index 00000000..6aec3070 --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties-xkbpv.c @@ -0,0 +1,136 @@ +/* -*- mode: c; style: linux -*- */ + +/* mate-keyboard-properties-xkbpv.c + * Copyright (C) 2003-2007 Sergey V. Udaltsov + * + * Written by: Sergey V. Udaltsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "capplet-util.h" +#include "mate-keyboard-properties-xkb.h" + +#ifdef HAVE_X11_EXTENSIONS_XKB_H +#include "X11/XKBlib.h" +/** + * BAD STYLE: Taken from xklavier_private_xkb.h + * Any ideas on architectural improvements are WELCOME + */ +extern gboolean xkl_xkb_config_native_prepare (XklEngine * engine, + const XklConfigRec * data, + XkbComponentNamesPtr + component_names); + +extern void xkl_xkb_config_native_cleanup (XklEngine * engine, + XkbComponentNamesPtr + component_names); + +/* */ +#endif + +static MatekbdKeyboardDrawingGroupLevel groupsLevels[] = + { {0, 1}, {0, 3}, {0, 0}, {0, 2} }; +static MatekbdKeyboardDrawingGroupLevel *pGroupsLevels[] = { + groupsLevels, groupsLevels + 1, groupsLevels + 2, groupsLevels + 3 +}; + +GtkWidget * +xkb_layout_preview_create_widget (GtkBuilder * chooserDialog) +{ + GtkWidget *kbdraw = matekbd_keyboard_drawing_new (); + + matekbd_keyboard_drawing_set_groups_levels (MATEKBD_KEYBOARD_DRAWING + (kbdraw), pGroupsLevels); + return kbdraw; +} + +void +xkb_layout_preview_update (GtkBuilder * chooser_dialog) +{ +#ifdef HAVE_X11_EXTENSIONS_XKB_H + GtkWidget *chooser = CWID ("xkb_layout_chooser"); + GtkWidget *kbdraw = + GTK_WIDGET (g_object_get_data (G_OBJECT (chooser), "kbdraw")); + gchar *id = xkb_layout_chooser_get_selected_id (chooser_dialog); + xkb_layout_preview_set_drawing_layout (kbdraw, id); + g_free (id); +#endif +} + +void +xkb_layout_preview_set_drawing_layout (GtkWidget * kbdraw, + const gchar * id) +{ +#ifdef HAVE_X11_EXTENSIONS_XKB_H + if (kbdraw != NULL) { + if (id != NULL) { + XklConfigRec *data; + char **p, *layout, *variant; + XkbComponentNamesRec component_names; + + data = xkl_config_rec_new (); + if (xkl_config_rec_get_from_server (data, engine)) { + if ((p = data->layouts) != NULL) + g_strfreev (data->layouts); + + if ((p = data->variants) != NULL) + g_strfreev (data->variants); + + data->layouts = g_new0 (char *, 2); + data->variants = g_new0 (char *, 2); + if (matekbd_keyboard_config_split_items + (id, &layout, &variant) + && variant != NULL) { + data->layouts[0] = + (layout == + NULL) ? NULL : + g_strdup (layout); + data->variants[0] = + (variant == + NULL) ? NULL : + g_strdup (variant); + } else { + data->layouts[0] = + (id == + NULL) ? NULL : g_strdup (id); + data->variants[0] = NULL; + } + + if (xkl_xkb_config_native_prepare + (engine, data, &component_names)) { + matekbd_keyboard_drawing_set_keyboard + (MATEKBD_KEYBOARD_DRAWING + (kbdraw), &component_names); + + xkl_xkb_config_native_cleanup + (engine, &component_names); + } + } + g_object_unref (G_OBJECT (data)); + } else + matekbd_keyboard_drawing_set_keyboard + (MATEKBD_KEYBOARD_DRAWING (kbdraw), NULL); + + } +#endif +} diff --git a/capplets/keyboard/mate-keyboard-properties.c b/capplets/keyboard/mate-keyboard-properties.c new file mode 100644 index 00000000..41c4558e --- /dev/null +++ b/capplets/keyboard/mate-keyboard-properties.c @@ -0,0 +1,265 @@ +/* -*- mode: c; style: linux -*- */ + +/* keyboard-properties.c + * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2001 Jonathan Blandford + * + * Written by: Bradford Hovinen + * Rachel Hestilow + * Jonathan Blandford + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" +#include "activate-settings-daemon.h" +#include "capplet-stock-icons.h" + +#include "mate-keyboard-properties-a11y.h" +#include "mate-keyboard-properties-xkb.h" + +enum { + RESPONSE_APPLY = 1, + RESPONSE_CLOSE +}; + +static GtkBuilder * +create_dialog (void) +{ + GtkBuilder *dialog; + GtkSizeGroup *size_group; + GtkWidget *image; + + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR + "/mate-keyboard-properties-dialog.ui", + NULL); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("repeat_slow_label")); + gtk_size_group_add_widget (size_group, WID ("delay_short_label")); + gtk_size_group_add_widget (size_group, WID ("blink_slow_label")); + g_object_unref (G_OBJECT (size_group)); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("repeat_fast_label")); + gtk_size_group_add_widget (size_group, WID ("delay_long_label")); + gtk_size_group_add_widget (size_group, WID ("blink_fast_label")); + g_object_unref (G_OBJECT (size_group)); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("repeat_delay_scale")); + gtk_size_group_add_widget (size_group, WID ("repeat_speed_scale")); + gtk_size_group_add_widget (size_group, WID ("cursor_blink_time_scale")); + g_object_unref (G_OBJECT (size_group)); + + image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (WID ("xkb_layouts_add")), image); + + image = gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (WID ("xkb_reset_to_defaults")), image); + + return dialog; +} + +static MateConfValue * +blink_from_widget (MateConfPropertyEditor * peditor, const MateConfValue * value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (new_value, + 2600 - mateconf_value_get_int (value)); + + return new_value; +} + +static MateConfValue * +blink_to_widget (MateConfPropertyEditor * peditor, const MateConfValue * value) +{ + MateConfValue *new_value; + gint current_rate; + + current_rate = mateconf_value_get_int (value); + new_value = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (new_value, + CLAMP (2600 - current_rate, 100, 2500)); + + return new_value; +} + +static void +dialog_response (GtkWidget * widget, + gint response_id, MateConfChangeSet * changeset) +{ + if (response_id == GTK_RESPONSE_HELP) + capplet_help (GTK_WINDOW (widget), "goscustperiph-2"); + else + gtk_main_quit (); +} + +static void +setup_dialog (GtkBuilder * dialog, MateConfChangeSet * changeset) +{ + GObject *peditor; + gchar *monitor; + + peditor = mateconf_peditor_new_boolean + (changeset, "/desktop/mate/peripherals/keyboard/repeat", + WID ("repeat_toggle"), NULL); + mateconf_peditor_widget_set_guard (MATECONF_PROPERTY_EDITOR (peditor), + WID ("repeat_table")); + + mateconf_peditor_new_numeric_range + (changeset, "/desktop/mate/peripherals/keyboard/delay", + WID ("repeat_delay_scale"), NULL); + + mateconf_peditor_new_numeric_range + (changeset, "/desktop/mate/peripherals/keyboard/rate", + WID ("repeat_speed_scale"), NULL); + + peditor = mateconf_peditor_new_boolean + (changeset, "/desktop/mate/interface/cursor_blink", + WID ("cursor_toggle"), NULL); + mateconf_peditor_widget_set_guard (MATECONF_PROPERTY_EDITOR (peditor), + WID ("cursor_hbox")); + mateconf_peditor_new_numeric_range (changeset, + "/desktop/mate/interface/cursor_blink_time", + WID ("cursor_blink_time_scale"), + "conv-to-widget-cb", + blink_to_widget, + "conv-from-widget-cb", + blink_from_widget, NULL); + + /* Ergonomics */ + monitor = g_find_program_in_path ("mate-typing-monitor"); + if (monitor != NULL) { + g_free (monitor); + + peditor = mateconf_peditor_new_boolean + (changeset, "/desktop/mate/typing_break/enabled", + WID ("break_enabled_toggle"), NULL); + mateconf_peditor_widget_set_guard (MATECONF_PROPERTY_EDITOR (peditor), + WID ("break_details_table")); + mateconf_peditor_new_numeric_range (changeset, + "/desktop/mate/typing_break/type_time", + WID ("break_enabled_spin"), NULL); + mateconf_peditor_new_numeric_range (changeset, + "/desktop/mate/typing_break/break_time", + WID ("break_interval_spin"), + NULL); + mateconf_peditor_new_boolean (changeset, + "/desktop/mate/typing_break/allow_postpone", + WID ("break_postponement_toggle"), + NULL); + + } else { + /* don't show the typing break tab if the daemon is not available */ + GtkNotebook *nb = GTK_NOTEBOOK (WID ("keyboard_notebook")); + gint tb_page = gtk_notebook_page_num (nb, WID ("break_enabled_toggle")); + gtk_notebook_remove_page (nb, tb_page); + } + + g_signal_connect (WID ("keyboard_dialog"), "response", + (GCallback) dialog_response, changeset); + + setup_xkb_tabs (dialog, changeset); + setup_a11y_tabs (dialog, changeset); +} + +int +main (int argc, char **argv) +{ + MateConfClient *client; + MateConfChangeSet *changeset; + GtkBuilder *dialog; + GOptionContext *context; + + static gboolean apply_only = FALSE; + static gboolean switch_to_typing_break_page = FALSE; + static gboolean switch_to_a11y_page = FALSE; + + static GOptionEntry cap_options[] = { + {"apply", 0, 0, G_OPTION_ARG_NONE, &apply_only, + N_ + ("Just apply settings and quit (compatibility only; now handled by daemon)"), + NULL}, + {"init-session-settings", 0, 0, G_OPTION_ARG_NONE, + &apply_only, + N_ + ("Just apply settings and quit (compatibility only; now handled by daemon)"), + NULL}, + {"typing-break", 0, 0, G_OPTION_ARG_NONE, + &switch_to_typing_break_page, + N_ + ("Start the page with the typing break settings showing"), + NULL}, + {"a11y", 0, 0, G_OPTION_ARG_NONE, + &switch_to_a11y_page, + N_ + ("Start the page with the accessibility settings showing"), + NULL}, + {NULL} + }; + + + context = g_option_context_new (_("- MATE Keyboard Preferences")); + g_option_context_add_main_entries (context, cap_options, + GETTEXT_PACKAGE); + + capplet_init (context, &argc, &argv); + + activate_settings_daemon (); + + client = mateconf_client_get_default (); + mateconf_client_add_dir (client, + "/desktop/mate/peripherals/keyboard", + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir (client, "/desktop/mate/interface", + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + g_object_unref (client); + + changeset = NULL; + dialog = create_dialog (); + setup_dialog (dialog, changeset); + if (switch_to_typing_break_page) { + gtk_notebook_set_current_page (GTK_NOTEBOOK + (WID + ("keyboard_notebook")), + 4); + } + else if (switch_to_a11y_page) { + gtk_notebook_set_current_page (GTK_NOTEBOOK + (WID + ("keyboard_notebook")), + 2); + + } + + capplet_set_icon (WID ("keyboard_dialog"), + "preferences-desktop-keyboard"); + gtk_widget_show (WID ("keyboard_dialog")); + gtk_main (); + + return 0; +} diff --git a/capplets/mouse/Makefile.am b/capplets/mouse/Makefile.am new file mode 100644 index 00000000..c60082a5 --- /dev/null +++ b/capplets/mouse/Makefile.am @@ -0,0 +1,35 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = mouse + +bin_PROGRAMS = mate-mouse-properties + +mate_mouse_properties_LDADD = $(MATECC_CAPPLETS_LIBS) +mate_mouse_properties_SOURCES = \ + mate-mouse-properties.c \ + mate-mouse-accessibility.c \ + mate-mouse-accessibility.h + +@INTLTOOL_DESKTOP_RULE@ + +pixmapdir = $(pkgdatadir)/pixmaps +pixmap_DATA = \ + double-click-on.png \ + double-click-off.png \ + double-click-maybe.png + +uidir = $(pkgdatadir)/ui +ui_DATA = mate-mouse-properties.ui + +desktopdir = $(datadir)/applications +Desktop_in_files = mate-settings-mouse.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_DATA_DIR="\"$(pkgdatadir)\"" \ + -DMATECC_UI_DIR="\"$(uidir)\"" +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) +EXTRA_DIST = $(ui_DATA) $(pixmap_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/mouse/double-click-maybe.png b/capplets/mouse/double-click-maybe.png new file mode 100644 index 00000000..6504e2e9 Binary files /dev/null and b/capplets/mouse/double-click-maybe.png differ diff --git a/capplets/mouse/double-click-off.png b/capplets/mouse/double-click-off.png new file mode 100644 index 00000000..ab428d13 Binary files /dev/null and b/capplets/mouse/double-click-off.png differ diff --git a/capplets/mouse/double-click-on.png b/capplets/mouse/double-click-on.png new file mode 100644 index 00000000..1ee9202e Binary files /dev/null and b/capplets/mouse/double-click-on.png differ diff --git a/capplets/mouse/mate-mouse-accessibility.c b/capplets/mouse/mate-mouse-accessibility.c new file mode 100644 index 00000000..a1c4277b --- /dev/null +++ b/capplets/mouse/mate-mouse-accessibility.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2007 Gerd Kohlberger + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" + +#define MT_MATECONF_HOME "/desktop/mate/accessibility/mouse" + +/* 5th entry in combo box */ +#define DIRECTION_DISABLE 4 + +enum { + CLICK_TYPE_SINGLE, + CLICK_TYPE_DOUBLE, + CLICK_TYPE_DRAG, + CLICK_TYPE_SECONDARY, + N_CLICK_TYPES +}; + +static void +update_mode_sensitivity (GtkBuilder *dialog, gint mode) +{ + gtk_widget_set_sensitive (WID ("box_ctw"), !mode); + gtk_widget_set_sensitive (WID ("box_gesture"), mode); +} + +/* check if a direction (gesture mode) is already in use */ +static gboolean +verify_setting (MateConfClient *client, gint value, gint type) +{ + gint i, ct[N_CLICK_TYPES]; + + ct[CLICK_TYPE_SINGLE] = + mateconf_client_get_int (client, + MT_MATECONF_HOME "/dwell_gesture_single", + NULL); + ct[CLICK_TYPE_DOUBLE] = + mateconf_client_get_int (client, + MT_MATECONF_HOME "/dwell_gesture_double", + NULL); + ct[CLICK_TYPE_DRAG] = + mateconf_client_get_int (client, + MT_MATECONF_HOME "/dwell_gesture_drag", + NULL); + ct[CLICK_TYPE_SECONDARY] = + mateconf_client_get_int (client, + MT_MATECONF_HOME "/dwell_gesture_secondary", + NULL); + + for (i = 0; i < N_CLICK_TYPES; ++i) { + if (i == type) + continue; + if (ct[i] == value) + return FALSE; + } + + return TRUE; +} + +static void +populate_gesture_combo (GtkWidget *combo) +{ + GtkListStore *model; + GtkTreeIter iter; + GtkCellRenderer *cr; + + model = gtk_list_store_new (1, G_TYPE_STRING); + + gtk_list_store_append (model, &iter); + /* Translators: this is the gesture to trigger/choose the click type. + Don't include the prefix "gesture|" in the translation. */ + gtk_list_store_set (model, &iter, 0, Q_("gesture|Move left"), -1); + + gtk_list_store_append (model, &iter); + /* Translators: this is the gesture to trigger/choose the click type. + Don't include the prefix "gesture|" in the translation. */ + gtk_list_store_set (model, &iter, 0, Q_("gesture|Move right"), -1); + + gtk_list_store_append (model, &iter); + /* Translators: this is the gesture to trigger/choose the click type. + Don't include the prefix "gesture|" in the translation. */ + gtk_list_store_set (model, &iter, 0, Q_("gesture|Move up"), -1); + + gtk_list_store_append (model, &iter); + /* Translators: this is the gesture to trigger/choose the click type. + Don't include the prefix "gesture|" in the translation. */ + gtk_list_store_set (model, &iter, 0, Q_("gesture|Move down"), -1); + + gtk_list_store_append (model, &iter); + /* Translators: this is the gesture to trigger/choose the click type. + Don't include the prefix "gesture|" in the translation. */ + gtk_list_store_set (model, &iter, 0, Q_("gesture|Disabled"), -1); + + gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (model)); + + cr = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cr, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cr, + "text", 0, + NULL); +} + +static void +delay_enable_toggled_cb (GtkWidget *checkbox, GtkBuilder *dialog) +{ + gtk_widget_set_sensitive (WID ("delay_box"), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox))); +} + +static void +dwell_enable_toggled_cb (GtkWidget *checkbox, GtkBuilder *dialog) +{ + gtk_widget_set_sensitive (WID ("dwell_box"), + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox))); +} + +static void +gesture_single (GtkComboBox *combo, gpointer data) +{ + if (!verify_setting (data, gtk_combo_box_get_active (combo), CLICK_TYPE_SINGLE)) + gtk_combo_box_set_active (combo, DIRECTION_DISABLE); +} + +static void +gesture_double (GtkComboBox *combo, gpointer data) +{ + if (!verify_setting (data, gtk_combo_box_get_active (combo), CLICK_TYPE_DOUBLE)) + gtk_combo_box_set_active (combo, DIRECTION_DISABLE); +} + +static void +gesture_drag (GtkComboBox *combo, gpointer data) +{ + if (!verify_setting (data, gtk_combo_box_get_active (combo), CLICK_TYPE_DRAG)) + gtk_combo_box_set_active (combo, DIRECTION_DISABLE); +} + +static void +gesture_secondary (GtkComboBox *combo, gpointer data) +{ + if (!verify_setting (data, gtk_combo_box_get_active (combo), CLICK_TYPE_SECONDARY)) + gtk_combo_box_set_active (combo, DIRECTION_DISABLE); +} + +static void +mateconf_value_changed (MateConfClient *client, + const gchar *key, + MateConfValue *value, + gpointer dialog) +{ + if (g_str_equal (key, MT_MATECONF_HOME "/dwell_mode")) + update_mode_sensitivity (dialog, mateconf_value_get_int (value)); +} + +void +setup_accessibility (GtkBuilder *dialog, MateConfClient *client) +{ + mateconf_client_add_dir (client, MT_MATECONF_HOME, + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + g_signal_connect (client, "value_changed", + G_CALLBACK (mateconf_value_changed), dialog); + + mateconf_peditor_new_boolean (NULL, MT_MATECONF_HOME "/dwell_enable", + WID ("dwell_enable"), NULL); + mateconf_peditor_new_boolean (NULL, MT_MATECONF_HOME "/delay_enable", + WID ("delay_enable"), NULL); + mateconf_peditor_new_boolean (NULL, MT_MATECONF_HOME "/dwell_show_ctw", + WID ("dwell_show_ctw"), NULL); + + mateconf_peditor_new_numeric_range (NULL, MT_MATECONF_HOME "/delay_time", + WID ("delay_time"), NULL); + mateconf_peditor_new_numeric_range (NULL, MT_MATECONF_HOME "/dwell_time", + WID ("dwell_time"), NULL); + mateconf_peditor_new_numeric_range (NULL, MT_MATECONF_HOME "/threshold", + WID ("threshold"), NULL); + + mateconf_peditor_new_select_radio (NULL, MT_MATECONF_HOME "/dwell_mode", + gtk_radio_button_get_group (GTK_RADIO_BUTTON (WID ("dwell_mode_ctw"))), + NULL); + update_mode_sensitivity (dialog, + mateconf_client_get_int (client, + MT_MATECONF_HOME "/dwell_mode", + NULL)); + + populate_gesture_combo (WID ("dwell_gest_single")); + mateconf_peditor_new_combo_box (NULL, MT_MATECONF_HOME "/dwell_gesture_single", + WID ("dwell_gest_single"), NULL); + g_signal_connect (WID ("dwell_gest_single"), "changed", + G_CALLBACK (gesture_single), client); + + populate_gesture_combo (WID ("dwell_gest_double")); + mateconf_peditor_new_combo_box (NULL, MT_MATECONF_HOME "/dwell_gesture_double", + WID ("dwell_gest_double"), NULL); + g_signal_connect (WID ("dwell_gest_double"), "changed", + G_CALLBACK (gesture_double), client); + + populate_gesture_combo (WID ("dwell_gest_drag")); + mateconf_peditor_new_combo_box (NULL, MT_MATECONF_HOME "/dwell_gesture_drag", + WID ("dwell_gest_drag"), NULL); + g_signal_connect (WID ("dwell_gest_drag"), "changed", + G_CALLBACK (gesture_drag), client); + + populate_gesture_combo (WID ("dwell_gest_secondary")); + mateconf_peditor_new_combo_box (NULL, MT_MATECONF_HOME "/dwell_gesture_secondary", + WID ("dwell_gest_secondary"), NULL); + g_signal_connect (WID ("dwell_gest_secondary"), "changed", + G_CALLBACK (gesture_secondary), client); + + g_signal_connect (WID ("delay_enable"), "toggled", + G_CALLBACK (delay_enable_toggled_cb), dialog); + delay_enable_toggled_cb (WID ("delay_enable"), dialog); + g_signal_connect (WID ("dwell_enable"), "toggled", + G_CALLBACK (dwell_enable_toggled_cb), dialog); + dwell_enable_toggled_cb (WID ("dwell_enable"), dialog); +} diff --git a/capplets/mouse/mate-mouse-accessibility.h b/capplets/mouse/mate-mouse-accessibility.h new file mode 100644 index 00000000..0fd4ad33 --- /dev/null +++ b/capplets/mouse/mate-mouse-accessibility.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Gerd Kohlberger + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MATE_MOUSE_A11Y_H +#define __MATE_MOUSE_A11Y_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void setup_accessibility (GtkBuilder *dialog, MateConfClient *client); + +#ifdef __cplusplus +} +#endif + +#endif /* __MATE_MOUSE_A11Y_H */ diff --git a/capplets/mouse/mate-mouse-properties.c b/capplets/mouse/mate-mouse-properties.c new file mode 100644 index 00000000..8e9824b9 --- /dev/null +++ b/capplets/mouse/mate-mouse-properties.c @@ -0,0 +1,647 @@ +/* -*- mode: c; style: linux -*- */ + +/* mouse-properties-capplet.c + * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001 Ximian, Inc. + * + * Written by: Jonathon Blandford , + * Bradford Hovinen , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" +#include "activate-settings-daemon.h" +#include "capplet-stock-icons.h" +#include "mate-mouse-accessibility.h" + +#include +#include +#include +#ifdef HAVE_XINPUT +#include +#include +#endif + +#ifdef HAVE_XCURSOR +#include +#endif + +enum +{ + DOUBLE_CLICK_TEST_OFF, + DOUBLE_CLICK_TEST_MAYBE, + DOUBLE_CLICK_TEST_ON +}; + +/* We use this in at least half a dozen places, so it makes sense just to + * define the macro */ + +#define DOUBLE_CLICK_KEY "/desktop/mate/peripherals/mouse/double_click" + +/* State in testing the double-click speed. Global for a great deal of + * convenience + */ +static gint double_click_state = DOUBLE_CLICK_TEST_OFF; + +/* normalization routines */ +/* All of our scales but double_click are on the range 1->10 as a result, we + * have a few routines to convert from whatever the mateconf key is to our range. + */ +static MateConfValue * +double_click_from_mateconf (MateConfPropertyEditor *peditor, const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + mateconf_value_set_int (new_value, CLAMP ((int) floor ((mateconf_value_get_int (value) + 50) / 100.0) * 100, 100, 1000)); + return new_value; +} + +static void +get_default_mouse_info (int *default_numerator, int *default_denominator, int *default_threshold) +{ + int numerator, denominator; + int threshold; + int tmp_num, tmp_den, tmp_threshold; + + /* Query X for the default value */ + XGetPointerControl (GDK_DISPLAY (), &numerator, &denominator, + &threshold); + XChangePointerControl (GDK_DISPLAY (), True, True, -1, -1, -1); + XGetPointerControl (GDK_DISPLAY (), &tmp_num, &tmp_den, &tmp_threshold); + XChangePointerControl (GDK_DISPLAY (), True, True, numerator, denominator, threshold); + + if (default_numerator) + *default_numerator = tmp_num; + + if (default_denominator) + *default_denominator = tmp_den; + + if (default_threshold) + *default_threshold = tmp_threshold; + +} + +static MateConfValue * +motion_acceleration_from_mateconf (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *new_value; + gfloat motion_acceleration; + + new_value = mateconf_value_new (MATECONF_VALUE_FLOAT); + + if (mateconf_value_get_float (value) == -1.0) { + int numerator, denominator; + + get_default_mouse_info (&numerator, &denominator, NULL); + + motion_acceleration = CLAMP ((gfloat)(numerator / denominator), 0.2, 6.0); + } + else { + motion_acceleration = CLAMP (mateconf_value_get_float (value), 0.2, 6.0); + } + + if (motion_acceleration >= 1) + mateconf_value_set_float (new_value, motion_acceleration + 4); + else + mateconf_value_set_float (new_value, motion_acceleration * 5); + + return new_value; +} + +static MateConfValue * +motion_acceleration_to_mateconf (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *new_value; + gfloat motion_acceleration; + + new_value = mateconf_value_new (MATECONF_VALUE_FLOAT); + motion_acceleration = CLAMP (mateconf_value_get_float (value), 1.0, 10.0); + + if (motion_acceleration < 5) + mateconf_value_set_float (new_value, motion_acceleration / 5.0); + else + mateconf_value_set_float (new_value, motion_acceleration - 4); + + return new_value; +} + +static MateConfValue * +threshold_from_mateconf (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_FLOAT); + + if (mateconf_value_get_int (value) == -1) { + int threshold; + + get_default_mouse_info (NULL, NULL, &threshold); + mateconf_value_set_float (new_value, CLAMP (threshold, 1, 10)); + } + else { + mateconf_value_set_float (new_value, CLAMP (mateconf_value_get_int (value), 1, 10)); + } + + return new_value; +} + +static MateConfValue * +drag_threshold_from_mateconf (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_FLOAT); + + mateconf_value_set_float (new_value, CLAMP (mateconf_value_get_int (value), 1, 10)); + + return new_value; +} + +/* Double Click handling */ + +struct test_data_t +{ + gint *timeout_id; + GtkWidget *image; +}; + +/* Timeout for the double click test */ + +static gboolean +test_maybe_timeout (struct test_data_t *data) +{ + double_click_state = DOUBLE_CLICK_TEST_OFF; + + gtk_image_set_from_stock (GTK_IMAGE (data->image), + MOUSE_DBLCLCK_OFF, mouse_capplet_dblclck_icon_get_size()); + + *data->timeout_id = 0; + + return FALSE; +} + +/* Callback issued when the user clicks the double click testing area. */ + +static gboolean +event_box_button_press_event (GtkWidget *widget, + GdkEventButton *event, + MateConfChangeSet *changeset) +{ + gint double_click_time; + MateConfValue *value; + static struct test_data_t data; + static gint test_on_timeout_id = 0; + static gint test_maybe_timeout_id = 0; + static guint32 double_click_timestamp = 0; + GtkWidget *image; + MateConfClient *client; + + if (event->type != GDK_BUTTON_PRESS) + return FALSE; + + image = g_object_get_data (G_OBJECT (widget), "image"); + + if (!(changeset && mateconf_change_set_check_value (changeset, DOUBLE_CLICK_KEY, &value))) { + client = mateconf_client_get_default(); + double_click_time = mateconf_client_get_int (client, DOUBLE_CLICK_KEY, NULL); + g_object_unref (client); + + } else + double_click_time = mateconf_value_get_int (value); + + if (test_maybe_timeout_id != 0) + g_source_remove (test_maybe_timeout_id); + if (test_on_timeout_id != 0) + g_source_remove (test_on_timeout_id); + + switch (double_click_state) { + case DOUBLE_CLICK_TEST_OFF: + double_click_state = DOUBLE_CLICK_TEST_MAYBE; + data.image = image; + data.timeout_id = &test_maybe_timeout_id; + test_maybe_timeout_id = g_timeout_add (double_click_time, (GtkFunction) test_maybe_timeout, &data); + break; + case DOUBLE_CLICK_TEST_MAYBE: + if (event->time - double_click_timestamp < double_click_time) { + double_click_state = DOUBLE_CLICK_TEST_ON; + data.image = image; + data.timeout_id = &test_on_timeout_id; + test_on_timeout_id = g_timeout_add (2500, (GtkFunction) test_maybe_timeout, &data); + } + break; + case DOUBLE_CLICK_TEST_ON: + double_click_state = DOUBLE_CLICK_TEST_OFF; + break; + } + + double_click_timestamp = event->time; + + switch (double_click_state) { + case DOUBLE_CLICK_TEST_ON: + gtk_image_set_from_stock (GTK_IMAGE (image), + MOUSE_DBLCLCK_ON, mouse_capplet_dblclck_icon_get_size()); + break; + case DOUBLE_CLICK_TEST_MAYBE: + gtk_image_set_from_stock (GTK_IMAGE (image), + MOUSE_DBLCLCK_MAYBE, mouse_capplet_dblclck_icon_get_size()); + break; + case DOUBLE_CLICK_TEST_OFF: + gtk_image_set_from_stock (GTK_IMAGE (image), + MOUSE_DBLCLCK_OFF, mouse_capplet_dblclck_icon_get_size()); + break; + } + + return TRUE; +} + +static void +orientation_radio_button_release_event (GtkWidget *widget, + GdkEventButton *event) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE); +} + +static MateConfValue * +left_handed_from_mateconf (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_INT); + + mateconf_value_set_int (new_value, mateconf_value_get_bool (value)); + + return new_value; +} + +static MateConfValue * +left_handed_to_mateconf (MateConfPropertyEditor *peditor, + const MateConfValue *value) +{ + MateConfValue *new_value; + + new_value = mateconf_value_new (MATECONF_VALUE_BOOL); + + mateconf_value_set_bool (new_value, mateconf_value_get_int (value) == 1); + + return new_value; +} + +static void +scrollmethod_changed_event (MateConfPropertyEditor *peditor, + const gchar *key, + const MateConfValue *value, + GtkBuilder *dialog) +{ + GtkToggleButton *disabled = GTK_TOGGLE_BUTTON (WID ("scroll_disabled_radio")); + + gtk_widget_set_sensitive (WID ("horiz_scroll_toggle"), + !gtk_toggle_button_get_active (disabled)); +} + +static void +synaptics_check_capabilities (GtkBuilder *dialog) +{ +#ifdef HAVE_XINPUT + int numdevices, i; + XDeviceInfo *devicelist; + Atom realtype, prop; + int realformat; + unsigned long nitems, bytes_after; + unsigned char *data; + + prop = XInternAtom (GDK_DISPLAY (), "Synaptics Capabilities", True); + if (!prop) + return; + + devicelist = XListInputDevices (GDK_DISPLAY (), &numdevices); + for (i = 0; i < numdevices; i++) { + if (devicelist[i].use != IsXExtensionPointer) + continue; + + gdk_error_trap_push (); + XDevice *device = XOpenDevice (GDK_DISPLAY (), + devicelist[i].id); + if (gdk_error_trap_pop ()) + continue; + + gdk_error_trap_push (); + if ((XGetDeviceProperty (GDK_DISPLAY (), device, prop, 0, 2, False, + XA_INTEGER, &realtype, &realformat, &nitems, + &bytes_after, &data) == Success) && (realtype != None)) { + /* Property data is booleans for has_left, has_middle, + * has_right, has_double, has_triple */ + if (!data[0]) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (WID ("tap_to_click_toggle")), TRUE); + gtk_widget_set_sensitive (WID ("tap_to_click_toggle"), FALSE); + } + + if (!data[3]) + gtk_widget_set_sensitive (WID ("scroll_twofinger_radio"), FALSE); + + XFree (data); + } + gdk_error_trap_pop (); + + XCloseDevice (GDK_DISPLAY (), device); + } + XFreeDeviceList (devicelist); +#endif +} + +static gboolean +find_synaptics (void) +{ + gboolean ret = FALSE; +#ifdef HAVE_XINPUT + int numdevices, i; + XDeviceInfo *devicelist; + Atom realtype, prop; + int realformat; + unsigned long nitems, bytes_after; + unsigned char *data; + XExtensionVersion *version; + + /* Input device properties require version 1.5 or higher */ + version = XGetExtensionVersion (GDK_DISPLAY (), "XInputExtension"); + if (!version->present || + (version->major_version * 1000 + version->minor_version) < 1005) { + XFree (version); + return False; + } + + prop = XInternAtom (GDK_DISPLAY (), "Synaptics Off", True); + if (!prop) + return False; + + devicelist = XListInputDevices (GDK_DISPLAY (), &numdevices); + for (i = 0; i < numdevices; i++) { + if (devicelist[i].use != IsXExtensionPointer) + continue; + + gdk_error_trap_push(); + XDevice *device = XOpenDevice (GDK_DISPLAY (), + devicelist[i].id); + if (gdk_error_trap_pop ()) + continue; + + gdk_error_trap_push (); + if ((XGetDeviceProperty (GDK_DISPLAY (), device, prop, 0, 1, False, + XA_INTEGER, &realtype, &realformat, &nitems, + &bytes_after, &data) == Success) && (realtype != None)) { + XFree (data); + ret = TRUE; + } + gdk_error_trap_pop (); + + XCloseDevice (GDK_DISPLAY (), device); + + if (ret) + break; + } + + XFree (version); + XFreeDeviceList (devicelist); +#endif + return ret; +} + +/* Set up the property editors in the dialog. */ +static void +setup_dialog (GtkBuilder *dialog, MateConfChangeSet *changeset) +{ + GtkRadioButton *radio; + GObject *peditor; + + /* Orientation radio buttons */ + radio = GTK_RADIO_BUTTON (WID ("left_handed_radio")); + peditor = mateconf_peditor_new_select_radio + (changeset, "/desktop/mate/peripherals/mouse/left_handed", gtk_radio_button_get_group (radio), + "conv-to-widget-cb", left_handed_from_mateconf, + "conv-from-widget-cb", left_handed_to_mateconf, + NULL); + /* explicitly connect to button-release so that you can change orientation with either button */ + g_signal_connect (WID ("right_handed_radio"), "button_release_event", + G_CALLBACK (orientation_radio_button_release_event), NULL); + g_signal_connect (WID ("left_handed_radio"), "button_release_event", + G_CALLBACK (orientation_radio_button_release_event), NULL); + + /* Locate pointer toggle */ + peditor = mateconf_peditor_new_boolean + (changeset, "/desktop/mate/peripherals/mouse/locate_pointer", WID ("locate_pointer_toggle"), NULL); + + /* Double-click time */ + peditor = mateconf_peditor_new_numeric_range + (changeset, DOUBLE_CLICK_KEY, WID ("delay_scale"), + "conv-to-widget-cb", double_click_from_mateconf, + NULL); + gtk_image_set_from_stock (GTK_IMAGE (WID ("double_click_image")), MOUSE_DBLCLCK_OFF, mouse_capplet_dblclck_icon_get_size ()); + g_object_set_data (G_OBJECT (WID ("double_click_eventbox")), "image", WID ("double_click_image")); + g_signal_connect (WID ("double_click_eventbox"), "button_press_event", + G_CALLBACK (event_box_button_press_event), changeset); + + /* speed */ + mateconf_peditor_new_numeric_range + (changeset, "/desktop/mate/peripherals/mouse/motion_acceleration", WID ("accel_scale"), + "conv-to-widget-cb", motion_acceleration_from_mateconf, + "conv-from-widget-cb", motion_acceleration_to_mateconf, + NULL); + + mateconf_peditor_new_numeric_range + (changeset, "/desktop/mate/peripherals/mouse/motion_threshold", WID ("sensitivity_scale"), + "conv-to-widget-cb", threshold_from_mateconf, + NULL); + + /* DnD threshold */ + mateconf_peditor_new_numeric_range + (changeset, "/desktop/mate/peripherals/mouse/drag_threshold", WID ("drag_threshold_scale"), + "conv-to-widget-cb", drag_threshold_from_mateconf, + NULL); + + /* Trackpad page */ + if (find_synaptics () == FALSE) + gtk_notebook_remove_page (GTK_NOTEBOOK (WID ("prefs_widget")), -1); + else { + mateconf_peditor_new_boolean + (changeset, "/desktop/mate/peripherals/touchpad/disable_while_typing", WID ("disable_w_typing_toggle"), NULL); + mateconf_peditor_new_boolean + (changeset, "/desktop/mate/peripherals/touchpad/tap_to_click", WID ("tap_to_click_toggle"), NULL); + mateconf_peditor_new_boolean + (changeset, "/desktop/mate/peripherals/touchpad/horiz_scroll_enabled", WID ("horiz_scroll_toggle"), NULL); + radio = GTK_RADIO_BUTTON (WID ("scroll_disabled_radio")); + peditor = mateconf_peditor_new_select_radio + (changeset, "/desktop/mate/peripherals/touchpad/scroll_method", gtk_radio_button_get_group (radio), + NULL); + + synaptics_check_capabilities (dialog); + scrollmethod_changed_event (MATECONF_PROPERTY_EDITOR (peditor), NULL, NULL, dialog); + g_signal_connect (peditor, "value-changed", + G_CALLBACK (scrollmethod_changed_event), dialog); + } + +} + +/* Construct the dialog */ + +static GtkBuilder * +create_dialog (void) +{ + GtkBuilder *dialog; + GtkSizeGroup *size_group; + GError *error = NULL; + + dialog = gtk_builder_new (); + gtk_builder_add_from_file (dialog, MATECC_UI_DIR "/mate-mouse-properties.ui", &error); + if (error != NULL) { + g_warning ("Error loading UI file: %s", error->message); + return NULL; + } + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("acceleration_label")); + gtk_size_group_add_widget (size_group, WID ("sensitivity_label")); + gtk_size_group_add_widget (size_group, WID ("threshold_label")); + gtk_size_group_add_widget (size_group, WID ("timeout_label")); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("acceleration_fast_label")); + gtk_size_group_add_widget (size_group, WID ("sensitivity_high_label")); + gtk_size_group_add_widget (size_group, WID ("threshold_large_label")); + gtk_size_group_add_widget (size_group, WID ("timeout_long_label")); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("acceleration_slow_label")); + gtk_size_group_add_widget (size_group, WID ("sensitivity_low_label")); + gtk_size_group_add_widget (size_group, WID ("threshold_small_label")); + gtk_size_group_add_widget (size_group, WID ("timeout_short_label")); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("simulated_delay_label")); + gtk_size_group_add_widget (size_group, WID ("dwell_delay_label")); + gtk_size_group_add_widget (size_group, WID ("dwell_threshold_label")); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("simulated_delay_short_label")); + gtk_size_group_add_widget (size_group, WID ("dwell_delay_short_label")); + gtk_size_group_add_widget (size_group, WID ("dwell_threshold_small_label")); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, WID ("simulated_delay_long_label")); + gtk_size_group_add_widget (size_group, WID ("dwell_delay_long_label")); + gtk_size_group_add_widget (size_group, WID ("dwell_threshold_large_label")); + + return dialog; +} + +/* Callback issued when a button is clicked on the dialog */ + +static void +dialog_response_cb (GtkDialog *dialog, gint response_id, MateConfChangeSet *changeset) +{ + if (response_id == GTK_RESPONSE_HELP) + capplet_help (GTK_WINDOW (dialog), + "goscustperiph-5"); + else + gtk_main_quit (); +} + +int +main (int argc, char **argv) +{ + MateConfClient *client; + GtkBuilder *dialog; + GtkWidget *dialog_win, *w; + gchar *start_page = NULL; + + GOptionContext *context; + GOptionEntry cap_options[] = { + {"show-page", 'p', G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_STRING, + &start_page, + /* TRANSLATORS: don't translate the terms in brackets */ + N_("Specify the name of the page to show (general|accessibility)"), + N_("page") }, + {NULL} + }; + + context = g_option_context_new (_("- MATE Mouse Preferences")); + g_option_context_add_main_entries (context, cap_options, GETTEXT_PACKAGE); + capplet_init (context, &argc, &argv); + + capplet_init_stock_icons (); + + activate_settings_daemon (); + + client = mateconf_client_get_default (); + mateconf_client_add_dir (client, "/desktop/mate/peripherals/mouse", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir (client, "/desktop/mate/peripherals/touchpad", MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + dialog = create_dialog (); + + if (dialog) { + setup_dialog (dialog, NULL); + setup_accessibility (dialog, client); + + dialog_win = WID ("mouse_properties_dialog"); + g_signal_connect (dialog_win, "response", + G_CALLBACK (dialog_response_cb), NULL); + + if (start_page != NULL) { + gchar *page_name; + + page_name = g_strconcat (start_page, "_vbox", NULL); + g_free (start_page); + + w = WID (page_name); + if (w != NULL) { + GtkNotebook *nb; + gint pindex; + + nb = GTK_NOTEBOOK (WID ("prefs_widget")); + pindex = gtk_notebook_page_num (nb, w); + if (pindex != -1) + gtk_notebook_set_current_page (nb, pindex); + } + g_free (page_name); + } + + capplet_set_icon (dialog_win, "input-mouse"); + gtk_widget_show (dialog_win); + + gtk_main (); + + g_object_unref (dialog); + } + + g_object_unref (client); + + return 0; +} diff --git a/capplets/mouse/mate-mouse-properties.ui b/capplets/mouse/mate-mouse-properties.ui new file mode 100644 index 00000000..b855ff11 --- /dev/null +++ b/capplets/mouse/mate-mouse-properties.ui @@ -0,0 +1,1707 @@ + + + + 10 + 1 + 1 + 1 + 0 + 6 + + + 10 + 1 + 1 + 1 + 0 + 1 + + + 10 + 1 + 1 + 1 + 0 + 1 + + + 1000 + 100 + 100 + 100 + 0 + 400 + + + 3 + 0.5 + 0.10000000000000001 + 0.10000000000000001 + 0 + 1.2 + + + 3 + 0.20000000000000001 + 0.10000000000000001 + 0.10000000000000001 + 0 + 1.2 + + + 30 + 0 + 1 + 1 + 0 + 15 + + + + + 5 + Mouse Preferences + dialog + False + + + True + vertical + 2 + + + True + True + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 18 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Mouse Orientation + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + _Right-handed + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + True + + + 0 + + + + + _Left-handed + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + True + right_handed_radio + + + 1 + + + + + + + 1 + + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Locate Pointer + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + Sh_ow position of pointer when the Control key is pressed + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + + + 1 + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Pointer Speed + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + True + 2 + 3 + 12 + 6 + + + True + 0 + _Acceleration: + True + center + accel_scale + + + GTK_FILL + GTK_FILL + + + + + True + 0 + _Sensitivity: + True + center + sensitivity_scale + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + 1 + Slow + center + + + + + + + False + 0 + + + + + True + True + discontinuous + adjustment1 + False + right + + + 1 + + + + + True + 0 + Fast + center + + + + + + + False + 2 + + + + + 1 + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + 1 + Low + center + + + + + + + False + 0 + + + + + True + True + discontinuous + adjustment2 + 0 + False + + + 1 + + + + + True + 0 + High + center + + + + + + + False + 2 + + + + + 1 + 3 + 1 + 2 + + + + + + + 1 + + + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Drag and Drop + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 12 + + + True + 12 + + + True + 0 + Thr_eshold: + True + center + drag_threshold_scale + + + False + False + 0 + + + + + True + 6 + + + True + 1 + Small + center + + + + + + + False + False + 0 + + + + + True + True + discontinuous + adjustment3 + 0 + False + + + + + + Cursor blinks speed + + + + + 1 + + + + + True + 0 + Large + + + + + + + False + False + 2 + + + + + 1 + + + + + 0 + + + + + + + 1 + + + + + False + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Double-Click Timeout + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 12 + + + True + 12 + + + True + 0 + _Timeout: + True + center + delay_scale + + + False + False + 0 + + + + + True + 6 + + + True + 1 + Short + + + + + + + False + False + 0 + + + + + True + True + discontinuous + adjustment4 + False + + + + + + Cursor blinks speed + + + + + 1 + + + + + True + 0 + Long + + + + + + + False + False + 2 + + + + + 1 + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + 180 + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + To test your double-click settings, try to double-click on the light bulb. + True + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-missing-image + + + + + 1 + + + + + 1 + + + + + + + 1 + + + + + False + 4 + + + + + + + True + General + center + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 18 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Simulated Secondary Click + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + _Trigger secondary click by holding down the primary button + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + 12 + + + True + 0 + _Delay: + True + center + delay_time + + + False + False + 0 + + + + + True + 6 + + + True + 1 + 0.4699999988079071 + Short + + + + + + + False + False + 0 + + + + + True + True + discontinuous + adjustment5 + False + + + + + + Cursor blinks speed + + + + + 1 + + + + + True + 0 + Long + + + + + + + False + False + 2 + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Dwell Click + + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + _Initiate click when stopping pointer movement + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + 2 + 3 + 12 + 6 + + + True + 0 + _Motion threshold: + True + center + threshold + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + D_elay: + True + center + dwell_time + + + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + 1 + Short + center + + + + + + + False + 0 + + + + + True + True + discontinuous + adjustment6 + False + right + + + 1 + + + + + True + 0 + Long + center + + + + + + + False + 2 + + + + + 1 + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + 1 + Small + center + + + + + + + False + 0 + + + + + True + True + discontinuous + adjustment7 + 0 + False + + + 1 + + + + + True + 0 + Large + center + + + + + + + False + 2 + + + + + 1 + 3 + 1 + 2 + + + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Choose type of click _beforehand + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + True + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Show click type _window + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-dialog-info + + + False + 0 + + + + + 320 + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + You can also use the Dwell Click panel applet to choose the click type. + True + + + + + + 1 + + + + + 1 + + + + + + + 1 + + + + + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + Choose type of click with mo_use gestures + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + True + dwell_mode_ctw + + + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 4 + 2 + 12 + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + 3 + 4 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + 2 + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Seco_ndary click: + True + dwell_gest_secondary + + + 3 + 4 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + D_rag click: + True + dwell_gest_drag + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + D_ouble click: + True + dwell_gest_double + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + _Single click: + True + dwell_gest_single + + + GTK_FILL + GTK_FILL + + + + + + + 1 + + + + + False + 1 + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + False + 1 + + + + + + + True + Accessibility + center + + + 1 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + vertical + 18 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + 0 + General + + + + + + False + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + 0 + + + + False + False + 0 + + + + + Disable _touchpad while typing + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + True + + + 1 + + + + + False + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + 0 + + + + False + False + 0 + + + + + Enable _mouse clicks with touchpad + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + True + + + 1 + + + + + False + False + 2 + + + + + False + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + 0 + Scrolling + + + + + + False + False + 0 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + 0 + + + + False + False + 0 + + + + + True + + + _Disabled + True + True + False + True + True + + + False + False + 0 + + + + + _Edge scrolling + True + True + False + True + True + scroll_disabled_radio + + + False + False + 1 + + + + + Two-_finger scrolling + True + True + False + True + True + scroll_disabled_radio + + + False + False + 2 + + + + + 1 + + + + + False + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + 0 + + + + False + False + 0 + + + + + Enable h_orizontal scrolling + True + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + 1 + + + + + False + False + 2 + + + + + False + False + 1 + + + + + + + True + Touchpad + center + + + 2 + False + + + + + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + helpbutton1 + closebutton1 + + + diff --git a/capplets/mouse/mate-settings-mouse.desktop.in.in b/capplets/mouse/mate-settings-mouse.desktop.in.in new file mode 100644 index 00000000..5fca5e02 --- /dev/null +++ b/capplets/mouse/mate-settings-mouse.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Mouse +_Comment=Set your mouse preferences +Exec=mate-mouse-properties +Icon=input-mouse +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;HardwareSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=mouse +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/network/Makefile.am b/capplets/network/Makefile.am new file mode 100644 index 00000000..823dfc48 --- /dev/null +++ b/capplets/network/Makefile.am @@ -0,0 +1,50 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = network + +bin_PROGRAMS = mate-network-properties + +mate_network_properties_SOURCES = mate-network-properties.c +mate_network_properties_LDADD = $(MATECC_CAPPLETS_LIBS) + +@INTLTOOL_DESKTOP_RULE@ + +uidir = $(pkgdatadir)/ui +dist_ui_DATA = mate-network-properties.ui + +icons16dir = $(datadir)/icons/hicolor/16x16/apps +dist_icons16_DATA = icons/16x16/mate-network-properties.png +icons22dir = $(datadir)/icons/hicolor/22x22/apps +dist_icons22_DATA = icons/22x22/mate-network-properties.png +icons24dir = $(datadir)/icons/hicolor/24x24/apps +dist_icons24_DATA = icons/24x24/mate-network-properties.png +icons32dir = $(datadir)/icons/hicolor/32x32/apps +dist_icons32_DATA = icons/32x32/mate-network-properties.png +icons48dir = $(datadir)/icons/hicolor/48x48/apps +dist_icons48_DATA = icons/48x48/mate-network-properties.png +iconssvgdir = $(datadir)/icons/hicolor/scalable/apps +dist_iconssvg_DATA = icons/scalable/mate-network-properties.svg + +desktopdir = $(datadir)/applications +desktop_in_files = mate-network-properties.desktop.in +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) + +INCLUDES = \ + $(MATECC_CAPPLETS_CFLAGS) \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DMATECC_UI_DIR="\"$(uidir)\"" + +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor +uninstall-hook: update-icon-cache +install-data-hook: update-icon-cache +update-icon-cache: + @-if test -z "$(DESTDIR)"; then \ + echo "Updating Gtk icon cache."; \ + $(gtk_update_icon_cache); \ + else \ + echo "*** Icon cache not updated. After install, run this:"; \ + echo "*** $(gtk_update_icon_cache)"; \ + fi + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(desktop_in_files) $(desktop_DATA) + +-include $(top_srcdir)/git.mk diff --git a/capplets/network/icons/16x16/mate-network-properties.png b/capplets/network/icons/16x16/mate-network-properties.png new file mode 100644 index 00000000..ebacb983 Binary files /dev/null and b/capplets/network/icons/16x16/mate-network-properties.png differ diff --git a/capplets/network/icons/22x22/mate-network-properties.png b/capplets/network/icons/22x22/mate-network-properties.png new file mode 100644 index 00000000..a5fb9e01 Binary files /dev/null and b/capplets/network/icons/22x22/mate-network-properties.png differ diff --git a/capplets/network/icons/24x24/mate-network-properties.png b/capplets/network/icons/24x24/mate-network-properties.png new file mode 100644 index 00000000..e743c831 Binary files /dev/null and b/capplets/network/icons/24x24/mate-network-properties.png differ diff --git a/capplets/network/icons/32x32/mate-network-properties.png b/capplets/network/icons/32x32/mate-network-properties.png new file mode 100644 index 00000000..826d99d8 Binary files /dev/null and b/capplets/network/icons/32x32/mate-network-properties.png differ diff --git a/capplets/network/icons/48x48/mate-network-properties.png b/capplets/network/icons/48x48/mate-network-properties.png new file mode 100644 index 00000000..e3a4ce28 Binary files /dev/null and b/capplets/network/icons/48x48/mate-network-properties.png differ diff --git a/capplets/network/icons/scalable/mate-network-properties.svg b/capplets/network/icons/scalable/mate-network-properties.svg new file mode 100644 index 00000000..71ef6762 --- /dev/null +++ b/capplets/network/icons/scalable/mate-network-properties.svg @@ -0,0 +1,628 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Network proxy + 16.02.2007 + + + Josef Vybíral + + + + + Josef Vybíral + + + http://blog.vybiral.info + + + network + proxy + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/capplets/network/mate-network-properties.c b/capplets/network/mate-network-properties.c new file mode 100644 index 00000000..3fcc04dc --- /dev/null +++ b/capplets/network/mate-network-properties.c @@ -0,0 +1,1397 @@ +/* mate-network-properties.c: network preferences capplet + * + * Copyright (C) 2002 Sun Microsystems Inc. + * + * Written by: Mark McLoughlin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" + +enum ProxyMode +{ + PROXYMODE_NONE, + PROXYMODE_MANUAL, + PROXYMODE_AUTO +}; + +static GEnumValue proxytype_values[] = { + { PROXYMODE_NONE, "PROXYMODE_NONE", "none"}, + { PROXYMODE_MANUAL, "PROXYMODE_MANUAL", "manual"}, + { PROXYMODE_AUTO, "PROXYMODE_AUTO", "auto"}, + { 0, NULL, NULL } +}; + +enum { + COL_NAME, + COL_STYLE +}; + +#define USE_PROXY_KEY "/system/http_proxy/use_http_proxy" +#define USE_SAME_PROXY_KEY "/system/http_proxy/use_same_proxy" +#define HTTP_PROXY_HOST_KEY "/system/http_proxy/host" +#define HTTP_PROXY_PORT_KEY "/system/http_proxy/port" +#define HTTP_USE_AUTH_KEY "/system/http_proxy/use_authentication" +#define HTTP_AUTH_USER_KEY "/system/http_proxy/authentication_user" +#define HTTP_AUTH_PASSWD_KEY "/system/http_proxy/authentication_password" +#define IGNORE_HOSTS_KEY "/system/http_proxy/ignore_hosts" +#define PROXY_MODE_KEY "/system/proxy/mode" +#define SECURE_PROXY_HOST_KEY "/system/proxy/secure_host" +#define OLD_SECURE_PROXY_HOST_KEY "/system/proxy/old_secure_host" +#define SECURE_PROXY_PORT_KEY "/system/proxy/secure_port" +#define OLD_SECURE_PROXY_PORT_KEY "/system/proxy/old_secure_port" +#define FTP_PROXY_HOST_KEY "/system/proxy/ftp_host" +#define OLD_FTP_PROXY_HOST_KEY "/system/proxy/old_ftp_host" +#define FTP_PROXY_PORT_KEY "/system/proxy/ftp_port" +#define OLD_FTP_PROXY_PORT_KEY "/system/proxy/old_ftp_port" +#define SOCKS_PROXY_HOST_KEY "/system/proxy/socks_host" +#define OLD_SOCKS_PROXY_HOST_KEY "/system/proxy/old_socks_host" +#define SOCKS_PROXY_PORT_KEY "/system/proxy/socks_port" +#define OLD_SOCKS_PROXY_PORT_KEY "/system/proxy/old_socks_port" +#define PROXY_AUTOCONFIG_URL_KEY "/system/proxy/autoconfig_url" + +#define LOCATION_DIR "/apps/control-center/network" +#define CURRENT_LOCATION "/apps/control-center/network/current_location" + +#define MATECC_GNP_UI_FILE (MATECC_UI_DIR "/mate-network-properties.ui") + +static GtkWidget *details_dialog = NULL; +static GSList *ignore_hosts = NULL; +static GtkTreeModel *model = NULL; + +static GtkTreeModel * +create_listmodel(void) +{ + GtkListStore *store; + + store = gtk_list_store_new(1, G_TYPE_STRING); + + return GTK_TREE_MODEL(store); +} + +static GtkTreeModel * +populate_listmodel(GtkListStore *store, GSList *list) +{ + GtkTreeIter iter; + GSList *pointer; + + gtk_list_store_clear(store); + + pointer = list; + while(pointer) + { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, (char *) pointer->data, -1); + pointer = g_slist_next(pointer); + } + + return GTK_TREE_MODEL(store); +} + +static GtkWidget * +config_treeview(GtkTreeView *tree, GtkTreeModel *model) +{ + GtkCellRenderer *renderer; + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), + -1, "Hosts", renderer, + "text", 0, NULL); + + gtk_tree_view_set_model(GTK_TREE_VIEW(tree), model); + + return GTK_WIDGET(tree); +} + +static GtkWidget* +_gtk_builder_get_widget (GtkBuilder *builder, const gchar *name) +{ + return GTK_WIDGET (gtk_builder_get_object (builder, name)); +} + + +static void +cb_add_url (GtkButton *button, gpointer data) +{ + GtkBuilder *builder = GTK_BUILDER (data); + gchar *new_url = NULL; + MateConfClient *client; + + new_url = g_strdup (gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder, "entry_url")))); + if (strlen (new_url) == 0) + return; + ignore_hosts = g_slist_append(ignore_hosts, new_url); + populate_listmodel(GTK_LIST_STORE(model), ignore_hosts); + gtk_entry_set_text(GTK_ENTRY (gtk_builder_get_object (builder, + "entry_url")), ""); + + client = mateconf_client_get_default (); + mateconf_client_set_list (client, IGNORE_HOSTS_KEY, MATECONF_VALUE_STRING, ignore_hosts, NULL); + g_object_unref (client); +} + +static void +cb_remove_url (GtkButton *button, gpointer data) +{ + GtkBuilder *builder = GTK_BUILDER (data); + GtkTreeSelection *selection; + GtkTreeIter iter; + MateConfClient *client; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gtk_builder_get_object (builder, "treeview_ignore_host"))); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gchar *url; + GSList *pointer; + + gtk_tree_model_get (model, &iter, 0, &url, -1); + + pointer = ignore_hosts; + while(pointer) + { + if(strcmp(url, (char *) pointer->data) == 0) + { + g_free (pointer->data); + ignore_hosts = g_slist_delete_link(ignore_hosts, pointer); + break; + } + pointer = g_slist_next(pointer); + } + + g_free(url); + populate_listmodel(GTK_LIST_STORE(model), ignore_hosts); + + client = mateconf_client_get_default (); + mateconf_client_set_list(client, IGNORE_HOSTS_KEY, MATECONF_VALUE_STRING, ignore_hosts, NULL); + g_object_unref (client); + } +} + +static void +cb_dialog_response (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) + capplet_help (GTK_WINDOW (dialog), + "goscustdesk-50"); + else if (response_id == GTK_RESPONSE_CLOSE || response_id == GTK_RESPONSE_DELETE_EVENT) + { + if (ignore_hosts) { + g_slist_foreach (ignore_hosts, (GFunc) g_free, NULL); + g_slist_free (ignore_hosts); + } + + gtk_main_quit (); + } +} + +static void +cb_details_dialog_response (GtkDialog *dialog, gint response_id) +{ + if (response_id == GTK_RESPONSE_HELP) + capplet_help (GTK_WINDOW (dialog), + "goscustdesk-50"); + else { + gtk_widget_destroy (GTK_WIDGET (dialog)); + details_dialog = NULL; + } +} + +static void +cb_use_auth_toggled (GtkToggleButton *toggle, + GtkWidget *table) +{ + gtk_widget_set_sensitive (table, gtk_toggle_button_get_active (toggle)); +} + +static void +cb_http_details_button_clicked (GtkWidget *button, + GtkWidget *parent) +{ + GtkBuilder *builder; + gchar *builder_widgets[] = { "details_dialog", NULL }; + GError *error = NULL; + GtkWidget *widget; + MateConfPropertyEditor *peditor; + + if (details_dialog != NULL) { + gtk_window_present (GTK_WINDOW (details_dialog)); + gtk_widget_grab_focus (details_dialog); + return; + } + + builder = gtk_builder_new (); + if (gtk_builder_add_objects_from_file (builder, MATECC_GNP_UI_FILE, + builder_widgets, &error) == 0) { + g_warning ("Could not load details dialog: %s", error->message); + g_error_free (error); + g_object_unref (builder); + return; + } + + details_dialog = widget = _gtk_builder_get_widget (builder, + "details_dialog"); + + gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (parent)); + + g_signal_connect (gtk_builder_get_object (builder, "use_auth_checkbutton"), + "toggled", + G_CALLBACK (cb_use_auth_toggled), + _gtk_builder_get_widget (builder, "auth_table")); + + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_boolean ( + NULL, HTTP_USE_AUTH_KEY, + _gtk_builder_get_widget (builder, "use_auth_checkbutton"), + NULL)); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, HTTP_AUTH_USER_KEY, + _gtk_builder_get_widget (builder, "username_entry"), + NULL)); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, HTTP_AUTH_PASSWD_KEY, + _gtk_builder_get_widget (builder, "password_entry"), + NULL)); + + g_signal_connect (widget, "response", + G_CALLBACK (cb_details_dialog_response), NULL); + + capplet_set_icon (widget, "mate-network-properties"); + + gtk_widget_show_all (widget); +} + +static gchar * +copy_location_create_key (const gchar *from, const gchar *what) +{ + if (from[0] == '\0') return g_strdup (what); + else return g_strconcat (from, what + strlen ("/system"), NULL); +} + +static void +copy_location (const gchar *from, const gchar *to, MateConfClient *client) +{ + int ti; + gboolean tb; + GSList *tl; + gchar *tstr, *dest, *src; + + if (from[0] != '\0' && !mateconf_client_dir_exists (client, from, NULL)) + return; + + /* USE_PROXY */ + dest = copy_location_create_key (to, USE_PROXY_KEY); + src = copy_location_create_key (from, USE_PROXY_KEY); + + tb = mateconf_client_get_bool (client, src, NULL); + mateconf_client_set_bool (client, dest, tb, NULL); + + g_free (dest); + g_free (src); + + /* USE_SAME_PROXY */ + dest = copy_location_create_key (to, USE_SAME_PROXY_KEY); + src = copy_location_create_key (from, USE_SAME_PROXY_KEY); + + tb = mateconf_client_get_bool (client, src, NULL); + mateconf_client_set_bool (client, dest, tb, NULL); + + g_free (dest); + g_free (src); + + /* HTTP_PROXY_HOST */ + dest = copy_location_create_key (to, HTTP_PROXY_HOST_KEY); + src = copy_location_create_key (from, HTTP_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* HTTP_PROXY_PORT */ + dest = copy_location_create_key (to, HTTP_PROXY_PORT_KEY); + src = copy_location_create_key (from, HTTP_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* HTTP_USE_AUTH */ + dest = copy_location_create_key (to, HTTP_USE_AUTH_KEY); + src = copy_location_create_key (from, HTTP_USE_AUTH_KEY); + + tb = mateconf_client_get_bool (client, src, NULL); + mateconf_client_set_bool (client, dest, tb, NULL); + + g_free (dest); + g_free (src); + + /* HTTP_AUTH_USER */ + dest = copy_location_create_key (to, HTTP_AUTH_USER_KEY); + src = copy_location_create_key (from, HTTP_AUTH_USER_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* HTTP_AUTH_PASSWD */ + dest = copy_location_create_key (to, HTTP_AUTH_PASSWD_KEY); + src = copy_location_create_key (from, HTTP_AUTH_PASSWD_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* IGNORE_HOSTS */ + dest = copy_location_create_key (to, IGNORE_HOSTS_KEY); + src = copy_location_create_key (from, IGNORE_HOSTS_KEY); + + tl = mateconf_client_get_list (client, src, MATECONF_VALUE_STRING, NULL); + mateconf_client_set_list (client, dest, MATECONF_VALUE_STRING, tl, NULL); + g_slist_foreach (tl, (GFunc) g_free, NULL); + g_slist_free (tl); + + g_free (dest); + g_free (src); + + /* PROXY_MODE */ + dest = copy_location_create_key (to, PROXY_MODE_KEY); + src = copy_location_create_key (from, PROXY_MODE_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* SECURE_PROXY_HOST */ + dest = copy_location_create_key (to, SECURE_PROXY_HOST_KEY); + src = copy_location_create_key (from, SECURE_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* OLD_SECURE_PROXY_HOST */ + dest = copy_location_create_key (to, OLD_SECURE_PROXY_HOST_KEY); + src = copy_location_create_key (from, OLD_SECURE_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* SECURE_PROXY_PORT */ + dest = copy_location_create_key (to, SECURE_PROXY_PORT_KEY); + src = copy_location_create_key (from, SECURE_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* OLD_SECURE_PROXY_PORT */ + dest = copy_location_create_key (to, OLD_SECURE_PROXY_PORT_KEY); + src = copy_location_create_key (from, OLD_SECURE_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* FTP_PROXY_HOST */ + dest = copy_location_create_key (to, FTP_PROXY_HOST_KEY); + src = copy_location_create_key (from, FTP_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* OLD_FTP_PROXY_HOST */ + dest = copy_location_create_key (to, OLD_FTP_PROXY_HOST_KEY); + src = copy_location_create_key (from, OLD_FTP_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* FTP_PROXY_PORT */ + dest = copy_location_create_key (to, FTP_PROXY_PORT_KEY); + src = copy_location_create_key (from, FTP_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* OLD_FTP_PROXY_PORT */ + dest = copy_location_create_key (to, OLD_FTP_PROXY_PORT_KEY); + src = copy_location_create_key (from, OLD_FTP_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* SOCKS_PROXY_HOST */ + dest = copy_location_create_key (to, SOCKS_PROXY_HOST_KEY); + src = copy_location_create_key (from, SOCKS_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* OLD_SOCKS_PROXY_HOST */ + dest = copy_location_create_key (to, OLD_SOCKS_PROXY_HOST_KEY); + src = copy_location_create_key (from, OLD_SOCKS_PROXY_HOST_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); + + /* SOCKS_PROXY_PORT */ + dest = copy_location_create_key (to, SOCKS_PROXY_PORT_KEY); + src = copy_location_create_key (from, SOCKS_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* OLD_SOCKS_PROXY_PORT */ + dest = copy_location_create_key (to, OLD_SOCKS_PROXY_PORT_KEY); + src = copy_location_create_key (from, OLD_SOCKS_PROXY_PORT_KEY); + + ti = mateconf_client_get_int (client, src, NULL); + mateconf_client_set_int (client, dest, ti, NULL); + + g_free (dest); + g_free (src); + + /* PROXY_AUTOCONFIG_URL */ + dest = copy_location_create_key (to, PROXY_AUTOCONFIG_URL_KEY); + src = copy_location_create_key (from, PROXY_AUTOCONFIG_URL_KEY); + + tstr = mateconf_client_get_string (client, src, NULL); + if (tstr != NULL) + { + mateconf_client_set_string (client, dest, tstr, NULL); + g_free (tstr); + } + + g_free (dest); + g_free (src); +} + +static gchar * +get_current_location (MateConfClient *client) +{ + gchar *result; + + result = mateconf_client_get_string (client, CURRENT_LOCATION, NULL); + + if (result == NULL || result[0] == '\0') + { + g_free (result); + result = g_strdup (_("Default")); + } + + return result; +} + +static gboolean +location_combo_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gchar *name; + gboolean ret; + + gtk_tree_model_get (model, iter, COL_NAME, &name, -1); + + ret = name == NULL || name[0] == '\0'; + + g_free (name); + + return ret; +} + +static void +update_locations (MateConfClient *client, + GtkBuilder *builder); + +static void +cb_location_changed (GtkWidget *location, + GtkBuilder *builder); + +static void +cb_current_location (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + GtkBuilder *builder) +{ + MateConfValue *value; + const gchar *newval; + + value = mateconf_entry_get_value (entry); + if (value == NULL) + return; + + newval = mateconf_value_get_string (value); + if (newval == NULL) + return; + + /* prevent the current settings from being saved by blocking + * the signal handler */ + g_signal_handlers_block_by_func (gtk_builder_get_object (builder, "location_combobox"), + cb_location_changed, builder); + update_locations (client, builder); + g_signal_handlers_unblock_by_func (gtk_builder_get_object (builder, "location_combobox"), + cb_location_changed, builder); +} + +static void +update_locations (MateConfClient *client, + GtkBuilder *builder) +{ + int i, select; + gchar *current; + GtkComboBox *location = GTK_COMBO_BOX (gtk_builder_get_object (builder, "location_combobox")); + GSList *list = mateconf_client_all_dirs (client, LOCATION_DIR, NULL); + GtkTreeIter titer; + GtkListStore *store; + GSList *iter, *last; + + store = GTK_LIST_STORE (gtk_combo_box_get_model (location)); + gtk_list_store_clear (store); + + current = get_current_location (client); + + list = g_slist_append (list, g_strconcat (LOCATION_DIR"/", current, NULL)); + list = g_slist_sort (list, (GCompareFunc) strcmp); + + select = -1; + + for (i = 0, iter = list, last = NULL; iter != NULL; last = iter, iter = g_slist_next (iter), ++i) + { + if (last == NULL || strcmp (last->data, iter->data) != 0) + { + gchar *locp, *key_name; + + locp = iter->data + strlen (LOCATION_DIR) + 1; + key_name = mateconf_unescape_key (locp, -1); + + gtk_list_store_append (store, &titer); + gtk_list_store_set (store, &titer, + COL_NAME, key_name, + COL_STYLE, PANGO_STYLE_NORMAL, -1); + + g_free (key_name); + + if (strcmp (locp, current) == 0) + select = i; + } + } + if (select == -1) + { + gtk_list_store_append (store, &titer); + gtk_list_store_set (store, &titer, + COL_NAME , current, + COL_STYLE, PANGO_STYLE_NORMAL, -1); + select = i++; + } + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "delete_button"), + i > 1); + + gtk_list_store_append (store, &titer); + gtk_list_store_set (store, &titer, + COL_NAME, NULL, + COL_STYLE, PANGO_STYLE_NORMAL, -1); + + gtk_list_store_append (store, &titer); + gtk_list_store_set (store, &titer, + COL_NAME, _("New Location..."), + COL_STYLE, PANGO_STYLE_ITALIC, -1); + + gtk_combo_box_set_row_separator_func (location, location_combo_separator, NULL, NULL); + gtk_combo_box_set_active (location, select); + g_free (current); + g_slist_foreach (list, (GFunc) mateconf_entry_free, NULL); + g_slist_free (list); +} + +static void +cb_location_new_text_changed (GtkEntry *entry, GtkBuilder *builder) +{ + gboolean exists; + gchar *current, *esc, *key; + const gchar *name; + MateConfClient *client; + + client = mateconf_client_get_default (); + + name = gtk_entry_get_text (entry); + if (name != NULL && name[0] != '\0') + { + esc = mateconf_escape_key (name, -1); + + key = g_strconcat (LOCATION_DIR "/", esc, NULL); + g_free (esc); + + current = get_current_location (client); + + exists = (strcmp (current, name) == 0) || + mateconf_client_dir_exists (client, key, NULL); + g_free (key); + } else exists = FALSE; + + g_object_unref (client); + + if (exists) + gtk_widget_show (_gtk_builder_get_widget (builder, + "error_label")); + else + gtk_widget_hide (_gtk_builder_get_widget (builder, + "error_label")); + + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "new_location"), + !exists); +} + +static void +location_new (GtkBuilder *capplet_builder, GtkWidget *parent) +{ + GtkBuilder *builder; + GError *error = NULL; + gchar *builder_widgets[] = { "location_new_dialog", + "new_location_btn_img", NULL }; + GtkWidget *askdialog; + const gchar *name; + int response; + MateConfClient *client; + + client = mateconf_client_get_default (); + + builder = gtk_builder_new (); + if (gtk_builder_add_objects_from_file (builder, MATECC_GNP_UI_FILE, + builder_widgets, &error) == 0) { + g_warning ("Could not load location dialog: %s", + error->message); + g_error_free (error); + g_object_unref (builder); + return; + } + + askdialog = _gtk_builder_get_widget (builder, "location_new_dialog"); + gtk_window_set_transient_for (GTK_WINDOW (askdialog), GTK_WINDOW (parent)); + g_signal_connect (askdialog, "response", + G_CALLBACK (gtk_widget_hide), NULL); + g_signal_connect (gtk_builder_get_object (builder, "text"), "changed", + G_CALLBACK (cb_location_new_text_changed), builder); + response = gtk_dialog_run (GTK_DIALOG (askdialog)); + name = gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder, "text"))); + g_object_unref (builder); + + if (response == GTK_RESPONSE_OK && name[0] != '\0') + { + gboolean exists; + gchar *current, *esc, *key; + esc = mateconf_escape_key (name, -1); + key = g_strconcat (LOCATION_DIR "/", esc, NULL); + g_free (esc); + + current = get_current_location (client); + + exists = (strcmp (current, name) == 0) || + mateconf_client_dir_exists (client, key, NULL); + + g_free (key); + + if (!exists) + { + esc = mateconf_escape_key (current, -1); + g_free (current); + key = g_strconcat (LOCATION_DIR "/", esc, NULL); + g_free (esc); + + copy_location ("", key, client); + g_free (key); + + mateconf_client_set_string (client, CURRENT_LOCATION, name, NULL); + update_locations (client, capplet_builder); + } + else + { + GtkWidget *err = gtk_message_dialog_new (GTK_WINDOW (askdialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Location already exists")); + gtk_dialog_run (GTK_DIALOG (err)); + gtk_widget_destroy (err); + + /* switch back to the currently selected location */ + mateconf_client_notify (client, CURRENT_LOCATION); + } + } + else + { + /* switch back to the currently selected location */ + mateconf_client_notify (client, CURRENT_LOCATION); + } + gtk_widget_destroy (askdialog); + g_object_unref (client); +} + +static void +cb_location_changed (GtkWidget *location, + GtkBuilder *builder) +{ + gchar *current; + gchar *name = gtk_combo_box_get_active_text (GTK_COMBO_BOX (location)); + MateConfClient *client; + + if (name == NULL) + return; + + client = mateconf_client_get_default (); + + current = get_current_location (client); + + if (strcmp (current, name) != 0) + { + if (strcmp (name, _("New Location...")) == 0) + { + location_new (builder, _gtk_builder_get_widget (builder, "network_dialog")); + } + else + { + gchar *key, *esc; + + /* save current settings */ + esc = mateconf_escape_key (current, -1); + key = g_strconcat (LOCATION_DIR "/", esc, NULL); + g_free (esc); + + copy_location ("", key, client); + g_free (key); + + /* load settings */ + esc = mateconf_escape_key (name, -1); + key = g_strconcat (LOCATION_DIR "/", esc, NULL); + g_free (esc); + + copy_location (key, "", client); + mateconf_client_recursive_unset (client, key, + MATECONF_UNSET_INCLUDING_SCHEMA_NAMES, NULL); + g_free (key); + + mateconf_client_set_string (client, CURRENT_LOCATION, name, NULL); + } + } + + g_free (current); + g_free (name); + g_object_unref (client); +} + +static void +cb_delete_button_clicked (GtkWidget *button, + GtkBuilder *builder) +{ + MateConfClient *client; + GtkComboBox *box = GTK_COMBO_BOX (gtk_builder_get_object (builder, + "location_combobox")); + int active = gtk_combo_box_get_active (box); + gchar *current, *key, *esc; + + /* prevent the current settings from being saved by blocking + * the signal handler */ + g_signal_handlers_block_by_func (box, cb_location_changed, builder); + gtk_combo_box_set_active (box, (active == 0) ? 1 : 0); + gtk_combo_box_remove_text (box, active); + g_signal_handlers_unblock_by_func (box, cb_location_changed, builder); + + /* set the new location */ + client = mateconf_client_get_default (); + current = gtk_combo_box_get_active_text (box); + + esc = mateconf_escape_key (current, -1); + key = g_strconcat (LOCATION_DIR "/", esc, NULL); + g_free (esc); + + copy_location (key, "", client); + mateconf_client_recursive_unset (client, key, + MATECONF_UNSET_INCLUDING_SCHEMA_NAMES, NULL); + mateconf_client_suggest_sync (client, NULL); + g_free (key); + + mateconf_client_set_string (client, CURRENT_LOCATION, current, NULL); + + g_free (current); + + g_object_unref (client); +} + +/* When using the same proxy for all protocols, updates every host_entry + * as the user types along */ +static void +synchronize_hosts (GtkEntry *widget, + GtkBuilder *builder) +{ + const gchar *hosts[] = { + "secure_host_entry", + "ftp_host_entry", + "socks_host_entry", + NULL }; + const gchar **host, *http_host; + + http_host = gtk_entry_get_text (widget); + + for (host = hosts; *host != NULL; ++host) + { + widget = GTK_ENTRY (gtk_builder_get_object (builder, *host)); + gtk_entry_set_text (widget, http_host); + } +} + +/* When using the same proxy for all protocols, copies the value of the + * http port to the other spinbuttons */ +static void +synchronize_ports (GtkSpinButton *widget, + GtkBuilder *builder) +{ + const gchar *ports[] = { + "secure_port_spinbutton", + "ftp_port_spinbutton", + "socks_port_spinbutton", + NULL }; + gdouble http_port; + const gchar **port; + + http_port = gtk_spin_button_get_value (widget); + + for (port = ports; *port != NULL; ++port) + { + widget = GTK_SPIN_BUTTON ( + gtk_builder_get_object (builder, *port)); + gtk_spin_button_set_value (widget, http_port); + } +} + +/* Synchronizes all hosts and ports */ +static void +synchronize_entries (GtkBuilder *builder) +{ + g_signal_connect ( + gtk_builder_get_object (builder, "http_host_entry"), + "changed", + G_CALLBACK (synchronize_hosts), + builder); + g_signal_connect ( + gtk_builder_get_object (builder, "http_port_spinbutton"), + "value-changed", + G_CALLBACK (synchronize_ports), + builder); +} + +/* Unsynchronize hosts and ports */ +static void +unsynchronize_entries (GtkBuilder *builder) +{ + g_signal_handlers_disconnect_by_func ( + gtk_builder_get_object (builder, "http_host_entry"), + synchronize_hosts, + builder); + g_signal_handlers_disconnect_by_func ( + gtk_builder_get_object (builder, "http_port_spinbutton"), + synchronize_ports, + builder); +} + +static void +cb_use_same_proxy_checkbutton_clicked (GtkWidget *checkbutton, + GtkBuilder *builder) +{ + MateConfClient *client; + gboolean same_proxy; + gchar *http_proxy; + gint http_port; + gchar *host; + + client = mateconf_client_get_default (); + same_proxy = mateconf_client_get_bool (client, USE_SAME_PROXY_KEY, NULL); + + http_proxy = mateconf_client_get_string (client, HTTP_PROXY_HOST_KEY, NULL); + http_port = mateconf_client_get_int (client, HTTP_PROXY_PORT_KEY, NULL); + + if (same_proxy) + { + /* Save the old values */ + host = mateconf_client_get_string (client, SECURE_PROXY_HOST_KEY, NULL); + mateconf_client_set_string (client, OLD_SECURE_PROXY_HOST_KEY, host, NULL); + mateconf_client_set_int (client, OLD_SECURE_PROXY_PORT_KEY, + mateconf_client_get_int (client, SECURE_PROXY_PORT_KEY, NULL), NULL); + g_free (host); + + host = mateconf_client_get_string (client, FTP_PROXY_HOST_KEY, NULL); + mateconf_client_set_string (client, OLD_FTP_PROXY_HOST_KEY, host, NULL); + mateconf_client_set_int (client, OLD_FTP_PROXY_PORT_KEY, + mateconf_client_get_int (client, FTP_PROXY_PORT_KEY, NULL), NULL); + g_free (host); + + host = mateconf_client_get_string (client, SOCKS_PROXY_HOST_KEY, NULL); + mateconf_client_set_string (client, OLD_SOCKS_PROXY_HOST_KEY, host, NULL); + mateconf_client_set_int (client, OLD_SOCKS_PROXY_PORT_KEY, + mateconf_client_get_int (client, SOCKS_PROXY_PORT_KEY, NULL), NULL); + g_free (host); + + /* Set the new values */ + mateconf_client_set_string (client, SECURE_PROXY_HOST_KEY, http_proxy, NULL); + mateconf_client_set_int (client, SECURE_PROXY_PORT_KEY, http_port, NULL); + + mateconf_client_set_string (client, FTP_PROXY_HOST_KEY, http_proxy, NULL); + mateconf_client_set_int (client, FTP_PROXY_PORT_KEY, http_port, NULL); + + mateconf_client_set_string (client, SOCKS_PROXY_HOST_KEY, http_proxy, NULL); + mateconf_client_set_int (client, SOCKS_PROXY_PORT_KEY, http_port, NULL); + + /* Synchronize entries */ + synchronize_entries (builder); + } + else + { + host = mateconf_client_get_string (client, OLD_SECURE_PROXY_HOST_KEY, NULL); + mateconf_client_set_string (client, SECURE_PROXY_HOST_KEY, host, NULL); + mateconf_client_set_int (client, SECURE_PROXY_PORT_KEY, + mateconf_client_get_int (client, OLD_SECURE_PROXY_PORT_KEY, NULL), NULL); + g_free (host); + + host = mateconf_client_get_string (client, OLD_FTP_PROXY_HOST_KEY, NULL); + mateconf_client_set_string (client, FTP_PROXY_HOST_KEY, host, NULL); + mateconf_client_set_int (client, FTP_PROXY_PORT_KEY, + mateconf_client_get_int (client, OLD_FTP_PROXY_PORT_KEY, NULL), NULL); + g_free (host); + + host = mateconf_client_get_string (client, OLD_SOCKS_PROXY_HOST_KEY, NULL); + mateconf_client_set_string (client, SOCKS_PROXY_HOST_KEY, host, NULL); + mateconf_client_set_int (client, SOCKS_PROXY_PORT_KEY, + mateconf_client_get_int (client, OLD_SOCKS_PROXY_PORT_KEY, NULL), NULL); + g_free (host); + + /* Hosts and ports should not be synchronized any more */ + unsynchronize_entries (builder); + } + + /* Set the proxy entries insensitive if we are using the same proxy for all */ + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "secure_host_entry"), + !same_proxy); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "secure_port_spinbutton"), + !same_proxy); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "ftp_host_entry"), + !same_proxy); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "ftp_port_spinbutton"), + !same_proxy); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "socks_host_entry"), + !same_proxy); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, + "socks_port_spinbutton"), + !same_proxy); + + g_object_unref (client); +} + +static gchar * +get_hostname_from_uri (const gchar *uri) +{ + const gchar *start, *end; + gchar *host; + + if (uri == NULL) + return NULL; + + /* skip the scheme part */ + start = strchr (uri, ':'); + if (start == NULL) + return NULL; + + /* forward until after the last '/' */ + do { + ++start; + } while (*start == '/'); + + if (*start == '\0') + return NULL; + + /* maybe we have a port? */ + end = strchr (start, ':'); + if (end == NULL) + end = strchr (start, '/'); + + if (end != NULL) + host = g_strndup (start, end - start); + else + host = g_strdup (start); + + return host; +} + +static MateConfValue * +extract_proxy_host (MateConfPropertyEditor *peditor, const MateConfValue *orig) +{ + char const *entered_text = mateconf_value_get_string (orig); + MateConfValue *res = NULL; + + if (entered_text != NULL) { + gchar *host = get_hostname_from_uri (entered_text); + + if (host != NULL) { + res = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (res, host); + g_free (host); + } + } + + return (res != NULL) ? res : mateconf_value_copy (orig); +} + +static void +proxy_mode_radiobutton_clicked_cb (GtkWidget *widget, + GtkBuilder *builder) +{ + GSList *mode_group; + int mode; + MateConfClient *client; + + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) + return; + + mode_group = g_slist_copy (gtk_radio_button_get_group + (GTK_RADIO_BUTTON (gtk_builder_get_object (builder, "none_radiobutton")))); + mode_group = g_slist_reverse (mode_group); + mode = g_slist_index (mode_group, widget); + g_slist_free (mode_group); + + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "manual_box"), + mode == PROXYMODE_MANUAL); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "same_proxy_checkbutton"), + mode == PROXYMODE_MANUAL); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "auto_box"), + mode == PROXYMODE_AUTO); + client = mateconf_client_get_default (); + mateconf_client_set_bool (client, USE_PROXY_KEY, + mode == PROXYMODE_AUTO || mode == PROXYMODE_MANUAL, NULL); + g_object_unref (client); +} + +static void +connect_sensitivity_signals (GtkBuilder *builder, GSList *mode_group) +{ + for (; mode_group != NULL; mode_group = mode_group->next) + { + g_signal_connect (G_OBJECT (mode_group->data), "clicked", + G_CALLBACK(proxy_mode_radiobutton_clicked_cb), + builder); + } +} + +static void +cb_ignore_hosts_mateconf_changed (MateConfClient *client, guint cnxn_id, + MateConfEntry *entry, gpointer user_data) +{ + g_slist_foreach (ignore_hosts, (GFunc) g_free, NULL); + g_slist_free (ignore_hosts); + + ignore_hosts = mateconf_client_get_list (client, IGNORE_HOSTS_KEY, + MATECONF_VALUE_STRING, NULL); + + populate_listmodel (GTK_LIST_STORE (model), ignore_hosts); +} + +static void +setup_dialog (GtkBuilder *builder) +{ + MateConfPropertyEditor *peditor; + GSList *mode_group; + GType mode_type = 0; + MateConfClient *client; + gint port_value; + GtkWidget *location_box, *same_proxy_toggle; + GtkCellRenderer *location_renderer; + GtkListStore *store; + + mode_type = g_enum_register_static ("NetworkPreferencesProxyType", + proxytype_values); + + client = mateconf_client_get_default (); + + /* Locations */ + location_box = _gtk_builder_get_widget (builder, "location_combobox"); + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); + gtk_combo_box_set_model (GTK_COMBO_BOX (location_box), GTK_TREE_MODEL (store)); + + update_locations (client, builder); + mateconf_client_add_dir (client, LOCATION_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_notify_add (client, CURRENT_LOCATION, (MateConfClientNotifyFunc) cb_current_location, builder, NULL, NULL); + + mateconf_client_notify_add (client, IGNORE_HOSTS_KEY, cb_ignore_hosts_mateconf_changed, NULL, NULL, NULL); + + g_signal_connect (location_box, "changed", G_CALLBACK (cb_location_changed), builder); + g_signal_connect (gtk_builder_get_object (builder, "delete_button"), "clicked", G_CALLBACK (cb_delete_button_clicked), builder); + + location_renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (location_box), location_renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (location_box), + location_renderer, + "text", COL_NAME, + "style", COL_STYLE, NULL); + + /* Mode */ + mode_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (gtk_builder_get_object (builder, "none_radiobutton"))); + connect_sensitivity_signals (builder, mode_group); + + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_select_radio_with_enum (NULL, + PROXY_MODE_KEY, mode_group, mode_type, + TRUE, NULL)); + + /* Use same proxy for all protocols */ + same_proxy_toggle = _gtk_builder_get_widget (builder, "same_proxy_checkbutton"); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_boolean (NULL, + USE_SAME_PROXY_KEY, same_proxy_toggle, NULL)); + + g_signal_connect (same_proxy_toggle, + "toggled", + G_CALLBACK (cb_use_same_proxy_checkbutton_clicked), + builder); + + /* Http */ + port_value = mateconf_client_get_int (client, HTTP_PROXY_PORT_KEY, NULL); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "http_port_spinbutton")), (gdouble) port_value); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, HTTP_PROXY_HOST_KEY, _gtk_builder_get_widget (builder, "http_host_entry"), + "conv-from-widget-cb", extract_proxy_host, + NULL)); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_integer ( + NULL, HTTP_PROXY_PORT_KEY, + _gtk_builder_get_widget (builder, "http_port_spinbutton"), + NULL)); + g_signal_connect (gtk_builder_get_object (builder, "details_button"), + "clicked", + G_CALLBACK (cb_http_details_button_clicked), + _gtk_builder_get_widget (builder, "network_dialog")); + + /* Secure */ + port_value = mateconf_client_get_int (client, SECURE_PROXY_PORT_KEY, NULL); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "secure_port_spinbutton")), (gdouble) port_value); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, SECURE_PROXY_HOST_KEY, + _gtk_builder_get_widget (builder, "secure_host_entry"), + "conv-from-widget-cb", extract_proxy_host, + NULL)); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_integer ( + NULL, SECURE_PROXY_PORT_KEY, + _gtk_builder_get_widget (builder, "secure_port_spinbutton"), + NULL)); + + /* Ftp */ + port_value = mateconf_client_get_int (client, FTP_PROXY_PORT_KEY, NULL); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "ftp_port_spinbutton")), (gdouble) port_value); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, FTP_PROXY_HOST_KEY, + _gtk_builder_get_widget (builder, "ftp_host_entry"), + "conv-from-widget-cb", extract_proxy_host, + NULL)); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_integer ( + NULL, FTP_PROXY_PORT_KEY, + _gtk_builder_get_widget (builder, "ftp_port_spinbutton"), + NULL)); + + /* Socks */ + port_value = mateconf_client_get_int (client, SOCKS_PROXY_PORT_KEY, NULL); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "socks_port_spinbutton")), (gdouble) port_value); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, SOCKS_PROXY_HOST_KEY, + _gtk_builder_get_widget (builder, "socks_host_entry"), + "conv-from-widget-cb", extract_proxy_host, + NULL)); + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_integer ( + NULL, SOCKS_PROXY_PORT_KEY, + _gtk_builder_get_widget (builder, "socks_port_spinbutton"), + NULL)); + + /* Set the proxy entries insensitive if we are using the same proxy for all, + and make sure they are all synchronized */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (same_proxy_toggle))) + { + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "secure_host_entry"), FALSE); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "secure_port_spinbutton"), FALSE); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "ftp_host_entry"), FALSE); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "ftp_port_spinbutton"), FALSE); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "socks_host_entry"), FALSE); + gtk_widget_set_sensitive (_gtk_builder_get_widget (builder, "socks_port_spinbutton"), FALSE); + + synchronize_entries (builder); + } + + /* Autoconfiguration */ + peditor = MATECONF_PROPERTY_EDITOR (mateconf_peditor_new_string ( + NULL, PROXY_AUTOCONFIG_URL_KEY, + _gtk_builder_get_widget (builder, "autoconfig_entry"), + NULL)); + + g_signal_connect (gtk_builder_get_object (builder, "network_dialog"), + "response", G_CALLBACK (cb_dialog_response), NULL); + + + ignore_hosts = mateconf_client_get_list(client, IGNORE_HOSTS_KEY, MATECONF_VALUE_STRING, NULL); + g_object_unref (client); + + model = create_listmodel(); + populate_listmodel(GTK_LIST_STORE(model), ignore_hosts); + config_treeview(GTK_TREE_VIEW(gtk_builder_get_object (builder, "treeview_ignore_host")), model); + + g_signal_connect (gtk_builder_get_object (builder, "button_add_url"), + "clicked", G_CALLBACK (cb_add_url), builder); + g_signal_connect (gtk_builder_get_object (builder, "entry_url"), + "activate", G_CALLBACK (cb_add_url), builder); + g_signal_connect (gtk_builder_get_object (builder, "button_remove_url"), + "clicked", G_CALLBACK (cb_remove_url), builder); +} + +int +main (int argc, char **argv) +{ + GtkBuilder *builder; + GError *error = NULL; + gchar *builder_widgets[] = {"network_dialog", "adjustment1", + "adjustment2", "adjustment3", "adjustment4", + "delete_button_img", NULL}; + MateConfClient *client; + GtkWidget *widget; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init (&argc, &argv); + + client = mateconf_client_get_default (); + mateconf_client_add_dir (client, "/system/http_proxy", + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + mateconf_client_add_dir (client, "/system/proxy", + MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + + builder = gtk_builder_new (); + if (gtk_builder_add_objects_from_file (builder, MATECC_GNP_UI_FILE, + builder_widgets, &error) == 0) { + g_warning ("Could not load main dialog: %s", + error->message); + g_error_free (error); + g_object_unref (builder); + g_object_unref (client); + return (EXIT_FAILURE); + } + + setup_dialog (builder); + widget = _gtk_builder_get_widget (builder, "network_dialog"); + capplet_set_icon (widget, "mate-network-properties"); + gtk_widget_show_all (widget); + gtk_main (); + + g_object_unref (builder); + g_object_unref (client); + + return 0; +} diff --git a/capplets/network/mate-network-properties.desktop.in.in b/capplets/network/mate-network-properties.desktop.in.in new file mode 100644 index 00000000..e16b3d09 --- /dev/null +++ b/capplets/network/mate-network-properties.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Network Proxy +_Comment=Set your network proxy preferences +Exec=mate-network-properties +Icon=mate-network-properties +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;X-MATE-NetworkSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=network preferences +X-MATE-Bugzilla-Version=@VERSION@ diff --git a/capplets/network/mate-network-properties.ui b/capplets/network/mate-network-properties.ui new file mode 100644 index 00000000..29c3c3e1 --- /dev/null +++ b/capplets/network/mate-network-properties.ui @@ -0,0 +1,1056 @@ + + + + + + 5 + Network Proxy Preferences + False + dialog + False + + + True + 2 + + + 0 + + + True + 3 + + + True + 1 + Location: + + + 0 + + + + + True + + + 1 + + + + + + + False + False + 1 + + + + + True + True + 5 + + + True + 12 + 18 + + + True + True + False + True + True + + + True + <b>Di_rect internet connection</b> + True + True + + + + + False + False + 0 + + + + + True + 18 + + + True + 6 + + + True + True + False + True + True + none_radiobutton + + + True + <b>_Manual proxy configuration</b> + True + True + + + + + False + False + 0 + + + + + _Use the same proxy for all protocols + True + False + True + False + True + True + + + False + False + 1 + + + + + True + False + + + True + + + + False + False + 0 + + + + + True + 4 + 5 + 12 + 6 + + + True + 0 + H_TTP proxy: + True + http_host_entry + + + GTK_FILL + + + + + + True + 0 + _Secure HTTP proxy: + True + secure_host_entry + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _FTP proxy: + True + ftp_host_entry + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + S_ocks host: + True + socks_host_entry + + + 3 + 4 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + + + 1 + 2 + 3 + 4 + + + + + + True + 0 + Port: + + + + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + Port: + + + + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + Port: + + + + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + Port: + + + + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + True + True + adjustment4 + 1 + + + + + + 3 + 4 + + + + + + True + True + adjustment3 + 1 + + + + + + 3 + 4 + 1 + 2 + + + + + + True + True + adjustment2 + 1 + + + + + + 3 + 4 + 2 + 3 + + + + + + True + True + adjustment1 + 1 + + + + + + 3 + 4 + 3 + 4 + + + + + + _Details + True + True + False + True + + + 4 + 5 + GTK_FILL + + + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 0 + + + + + True + 6 + + + True + True + False + True + 0.47 + True + none_radiobutton + + + True + <b>_Automatic proxy configuration</b> + True + True + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + False + 12 + + + True + Autoconfiguration _URL: + True + autoconfig_entry + + + False + False + 0 + + + + + True + True + + + 1 + + + + + 1 + + + + + 1 + + + + + False + False + 1 + + + + + 1 + + + + + + + True + Proxy Configuration + + + False + + + + + True + 12 + 6 + + + True + 0 + Ignore Host List + + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + 2 + 2 + 12 + 6 + + + True + True + + + + + + + + gtk-add + True + True + False + True + + + 1 + 2 + GTK_FILL + + + + + + True + True + automatic + automatic + in + + + True + True + False + + + + + 1 + 2 + GTK_FILL + + + + + True + 0 + 0 + 0 + + + True + + + gtk-remove + True + True + False + True + + + 0 + + + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + 1 + + + + + 1 + + + + + 1 + + + + + True + Ignored Hosts + + + 1 + False + + + + + 2 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + _Delete Location + True + False + True + False + delete_button_img + True + + + False + False + 1 + + + + + gtk-close + True + True + True + False + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + helpbutton1 + delete_button + closebutton1 + + + + 5 + HTTP Proxy Details + False + dialog + False + + + True + 2 + + + True + 5 + 6 + + + True + True + False + True + True + + + True + <b>_Use authentication</b> + True + True + + + + + False + False + 0 + + + + + True + + + True + + + + False + False + 0 + + + + + True + False + 2 + 2 + 12 + 6 + + + True + 0 + U_sername: + True + username_entry + + + GTK_FILL + + + + + + True + 0 + _Password: + True + password_entry + + + 1 + 2 + GTK_FILL + + + + + + True + True + False + + + 1 + 2 + 1 + 2 + + + + + + True + True + + + 1 + 2 + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + helpbutton2 + closebutton2 + + + + 6 + Create New Location + False + True + True + dialog + False + + + True + True + + + True + + + True + _Location name: + True + text + + + False + False + 1 + + + + + True + True + True + + + 6 + 2 + + + + + 1 + + + + + The location already exists. + + + 2 + + + + + True + end + + + C_reate + True + True + True + new_location_btn_img + True + + + False + False + 0 + + + + + gtk-cancel + True + True + True + True + + + False + False + 1 + + + + + False + False + end + 0 + + + + + + new_location + button2 + + + + 65535 + 1 + 10 + + + 65535 + 1 + 10 + + + 65535 + 1 + 10 + + + 65535 + 1 + 10 + + + True + gtk-add + + + True + gtk-delete + + diff --git a/capplets/windows/Makefile.am b/capplets/windows/Makefile.am new file mode 100644 index 00000000..a88ad37a --- /dev/null +++ b/capplets/windows/Makefile.am @@ -0,0 +1,32 @@ +# This is used in MATECC_CAPPLETS_CFLAGS +cappletname = window + +bin_PROGRAMS = mate-window-properties + +mate_window_properties_LDADD = $(MATECC_CAPPLETS_LIBS) \ + $(top_builddir)/libwindow-settings/libmate-window-settings.la + +mate_window_properties_SOURCES = \ + mate-window-properties.c + +@INTLTOOL_DESKTOP_RULE@ + +uidir = $(pkgdatadir)/ui +ui_DATA = mate-window-properties.ui + +desktopdir = $(datadir)/applications +Desktop_in_files = window-properties.desktop.in +desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) + +INCLUDES = $(MATECC_CAPPLETS_CFLAGS) \ + -I$(top_srcdir)/libwindow-settings \ + -DMATE_WINDOW_MANAGER_MODULE_PATH=\""$(libdir)/window-manager-settings"\" \ + -DUIDIR=\""$(uidir)"\" \ + -DMATELOCALEDIR="\"$(datadir)/locale\"" \ + -DPIXMAPDIR=\""$(pixmapdir)"\" + +CLEANFILES = $(MATECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) +EXTRA_DIST = $(ui_DATA) + + +-include $(top_srcdir)/git.mk diff --git a/capplets/windows/mate-window-properties.c b/capplets/windows/mate-window-properties.c new file mode 100644 index 00000000..74c8a207 --- /dev/null +++ b/capplets/windows/mate-window-properties.c @@ -0,0 +1,636 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ + +/* mate-window-properties.c + * Copyright (C) 2002 Seth Nickell + * Copyright (C) 2002 Red Hat, Inc. + * + * Written by: Seth Nickell + * Havoc Pennington + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "capplet-util.h" +#include "mateconf-property-editor.h" + +typedef struct +{ + int number; + char *name; + const char *value; /* machine-readable name for storing config */ + GtkWidget *radio; +} MouseClickModifier; + +static MateWindowManager *current_wm; /* may be NULL */ +static GtkWidget *dialog_win; +static GObject *focus_mode_checkbutton; +static GObject *autoraise_checkbutton; +static GObject *autoraise_delay_slider; +static GtkWidget *autoraise_delay_hbox; +static GObject *double_click_titlebar_optionmenu; +static GObject *alt_click_hbox; + +static MateWMSettings *settings; +static const MateWMDoubleClickAction *double_click_actions = NULL; +static int n_double_click_actions = 0; + +static MouseClickModifier *mouse_modifiers = NULL; +static int n_mouse_modifiers = 0; + +static void reload_mouse_modifiers (void); + +static void +mouse_focus_toggled_callback (GtkWidget *button, + void *data) +{ + MateWMSettings new_settings; + + new_settings.flags = MATE_WM_SETTING_MOUSE_FOCUS; + new_settings.focus_follows_mouse = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + + if (current_wm != NULL && new_settings.focus_follows_mouse != settings->focus_follows_mouse) + mate_window_manager_change_settings (current_wm, &new_settings); +} + +static void +autoraise_toggled_callback (GtkWidget *button, + void *data) +{ + MateWMSettings new_settings; + + new_settings.flags = MATE_WM_SETTING_AUTORAISE; + new_settings.autoraise = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + + if (current_wm != NULL && new_settings.autoraise != settings->autoraise) + mate_window_manager_change_settings (current_wm, &new_settings); + +} + +static void +autoraise_delay_value_changed_callback (GtkWidget *slider, + void *data) +{ + MateWMSettings new_settings; + + new_settings.flags = MATE_WM_SETTING_AUTORAISE_DELAY; + new_settings.autoraise_delay = + gtk_range_get_value (GTK_RANGE (slider)) * 1000; + + if (current_wm != NULL && new_settings.autoraise_delay != settings->autoraise_delay) + mate_window_manager_change_settings (current_wm, &new_settings); +} + +static void +double_click_titlebar_changed_callback (GtkWidget *optionmenu, + void *data) +{ + MateWMSettings new_settings; + + new_settings.flags = MATE_WM_SETTING_DOUBLE_CLICK_ACTION; + new_settings.double_click_action = + gtk_combo_box_get_active (GTK_COMBO_BOX (optionmenu)); + + if (current_wm != NULL && new_settings.double_click_action != settings->double_click_action) + mate_window_manager_change_settings (current_wm, &new_settings); +} + +static void +alt_click_radio_toggled_callback (GtkWidget *radio, + void *data) +{ + MateWMSettings new_settings; + gboolean active; + MouseClickModifier *modifier = data; + + new_settings.flags = MATE_WM_SETTING_MOUSE_MOVE_MODIFIER; + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio)); + + if (active && current_wm != NULL) { + new_settings.mouse_move_modifier = modifier->value; + + if ((settings->mouse_move_modifier == NULL) || + (strcmp (new_settings.mouse_move_modifier, + settings->mouse_move_modifier) != 0)) + mate_window_manager_change_settings (current_wm, &new_settings); + } +} + +static void +update_sensitivity (void) +{ + gtk_widget_set_sensitive (GTK_WIDGET (autoraise_checkbutton), + settings->focus_follows_mouse); + + gtk_widget_set_sensitive (autoraise_delay_hbox, + settings->focus_follows_mouse && settings->autoraise); + + gtk_widget_set_sensitive (GTK_WIDGET (double_click_titlebar_optionmenu), + n_double_click_actions > 1); + + /* disable the whole dialog while no WM is running, or + * a WM we don't understand is running. We should probably do + * something better. I don't want to just launch the config tool + * as we would on startup though, because then you'd get weirdness + * in the gap time between old and new WM. + */ + gtk_widget_set_sensitive (dialog_win, current_wm != NULL); +} + +static void +init_settings_struct (MateWMSettings *settings) +{ + /* Init fields that weren't initialized */ + if ((settings->flags & MATE_WM_SETTING_MOUSE_FOCUS) == 0) + settings->focus_follows_mouse = FALSE; + + if ((settings->flags & MATE_WM_SETTING_AUTORAISE) == 0) + settings->autoraise = FALSE; + + if ((settings->flags & MATE_WM_SETTING_AUTORAISE_DELAY) == 0) + settings->autoraise_delay = 1000; + + if ((settings->flags & MATE_WM_SETTING_MOUSE_MOVE_MODIFIER) == 0) + settings->mouse_move_modifier = "Super"; + + if ((settings->flags & MATE_WM_SETTING_DOUBLE_CLICK_ACTION) == 0) + settings->double_click_action = 0; +} + +static void +set_alt_click_value (const MateWMSettings *settings) +{ + gboolean match_found = FALSE; + int i; + + /* We look for a matching modifier and set it. */ + if (settings->mouse_move_modifier != NULL) { + for (i = 0; i < n_mouse_modifiers; i ++) + if (strcmp (mouse_modifiers[i].value, + settings->mouse_move_modifier) == 0) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mouse_modifiers[i].radio), + TRUE); + match_found = TRUE; + break; + } + } + + /* No matching modifier was found; we set all the toggle buttons to be + * insensitive. */ + for (i = 0; i < n_mouse_modifiers; i++) { + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (mouse_modifiers[i].radio), + ! match_found); + } +} + +static void +reload_settings (void) +{ + MateWMSettings new_settings; + + g_assert (n_mouse_modifiers > 0); + + if (current_wm != NULL) { + new_settings.flags = MATE_WM_SETTING_MOUSE_FOCUS | + MATE_WM_SETTING_AUTORAISE | + MATE_WM_SETTING_AUTORAISE_DELAY | + MATE_WM_SETTING_MOUSE_MOVE_MODIFIER | + MATE_WM_SETTING_DOUBLE_CLICK_ACTION; + + /* this will clear any flags that don't get filled in */ + mate_window_manager_get_settings (current_wm, &new_settings); + } else { + new_settings.flags = 0; + } + + init_settings_struct (&new_settings); + + if (new_settings.focus_follows_mouse != settings->focus_follows_mouse) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (focus_mode_checkbutton), + new_settings.focus_follows_mouse); + + if (new_settings.autoraise != settings->autoraise) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (autoraise_checkbutton), + new_settings.autoraise); + + if (new_settings.autoraise_delay != settings->autoraise_delay) + gtk_range_set_value (GTK_RANGE (autoraise_delay_slider), + new_settings.autoraise_delay / 1000.0); + + if (n_double_click_actions > 0 && + new_settings.double_click_action != settings->double_click_action) { + gtk_combo_box_set_active (GTK_COMBO_BOX (double_click_titlebar_optionmenu), + new_settings.double_click_action); + } + + if (settings->mouse_move_modifier == NULL || + new_settings.mouse_move_modifier == NULL || + strcmp (settings->mouse_move_modifier, + new_settings.mouse_move_modifier) != 0) { + set_alt_click_value (&new_settings); + } + + mate_wm_settings_free (settings); + settings = mate_wm_settings_copy (&new_settings); + + update_sensitivity (); +} + +static void +wm_settings_changed_callback (MateWindowManager *wm, + void *data) +{ + reload_settings (); +} + +static void +update_wm (GdkScreen *screen, + gboolean load_settings) +{ + int i; + + g_assert (n_mouse_modifiers > 0); + + if (current_wm != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (current_wm), + G_CALLBACK (wm_settings_changed_callback), + NULL); + current_wm = NULL; + double_click_actions = NULL; + n_double_click_actions = 0; + } + + current_wm = mate_wm_manager_get_current (screen); + + if (current_wm != NULL) { + g_signal_connect (G_OBJECT (current_wm), "settings_changed", + G_CALLBACK (wm_settings_changed_callback), NULL); + + mate_window_manager_get_double_click_actions (current_wm, + &double_click_actions, + &n_double_click_actions); + + } + + for (i = 0; i < n_double_click_actions; i++) { + gtk_combo_box_append_text (GTK_COMBO_BOX (double_click_titlebar_optionmenu), + double_click_actions[i].human_readable_name); + } + + if (load_settings) + reload_settings (); +} + +static void +wm_changed_callback (GdkScreen *screen, + void *data) +{ + update_wm (screen, TRUE); +} + +static void +response_cb (GtkWidget *dialog_win, + int response_id, + void *data) +{ + + if (response_id == GTK_RESPONSE_HELP) { + capplet_help (GTK_WINDOW (dialog_win), + "goscustdesk-58"); + } else { + gtk_widget_destroy (dialog_win); + } +} + +static void +try_spawn_config_tool (GdkScreen *screen) +{ + GError *error; + + error = NULL; + mate_wm_manager_spawn_config_tool_for_current (screen, &error); + + if (error != NULL) { + GtkWidget *no_tool_dialog; + char *str; + char *escaped; + + escaped = g_markup_escape_text (error->message, -1); + + str = g_strdup_printf ("%s\n\n%s", + _("Cannot start the preferences application for your window manager"), + escaped); + g_free (escaped); + + no_tool_dialog = + gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + " "); + gtk_window_set_title (GTK_WINDOW (no_tool_dialog), ""); + gtk_window_set_resizable (GTK_WINDOW (no_tool_dialog), FALSE); + + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (no_tool_dialog), str); + + g_free (str); + + gtk_dialog_run (GTK_DIALOG (no_tool_dialog)); + + gtk_widget_destroy (no_tool_dialog); + g_error_free (error); + + exit (1); + } + + /* exit, let the config tool handle it */ + exit (0); +} + +int +main (int argc, char **argv) +{ + GdkScreen *screen; + MateWMSettings new_settings; + GtkBuilder *builder; + GError *error = NULL; + int rc = 0; + int i; + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_init (&argc, &argv); + + mate_wm_manager_init (); + + screen = gdk_display_get_default_screen (gdk_display_get_default ()); + + current_wm = mate_wm_manager_get_current (screen); + + if (current_wm == NULL) { + try_spawn_config_tool (screen); + goto out; + } + + builder = gtk_builder_new (); + gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); + + if (gtk_builder_add_from_file (builder, UIDIR "/mate-window-properties.ui", &error) == 0) { + g_warning ("Could not parse UI file: %s", error->message); + g_error_free (error); + g_object_unref (builder); + rc = 1; + goto out; + } + + dialog_win = GTK_WIDGET (gtk_builder_get_object (builder, + "main-dialog")); + focus_mode_checkbutton = gtk_builder_get_object (builder, + "focus-mode-checkbutton"); + autoraise_checkbutton = gtk_builder_get_object (builder, + "autoraise-checkbutton"); + autoraise_delay_slider = gtk_builder_get_object (builder, + "autoraise-delay-slider"); + autoraise_delay_hbox = GTK_WIDGET (gtk_builder_get_object (builder, + "autoraise-delay-hbox")); + double_click_titlebar_optionmenu = gtk_builder_get_object (builder, + "double-click-titlebar-optionmenu"); + alt_click_hbox = gtk_builder_get_object (builder, "alt-click-box"); + + gtk_range_set_range (GTK_RANGE (autoraise_delay_slider), + 0, 10); + + gtk_range_set_increments (GTK_RANGE (autoraise_delay_slider), + 0.2, 1.0); + + new_settings.flags = 0; + init_settings_struct (&new_settings); + settings = mate_wm_settings_copy (&new_settings); + + reload_mouse_modifiers (); + update_wm (screen, FALSE); + + set_alt_click_value (&new_settings); + gtk_range_set_value (GTK_RANGE (autoraise_delay_slider), + new_settings.autoraise_delay / 1000.0); + gtk_combo_box_set_active (GTK_COMBO_BOX (double_click_titlebar_optionmenu), + new_settings.double_click_action); + + reload_settings (); /* must come before below signal connections */ + + g_signal_connect (G_OBJECT (dialog_win), "response", + G_CALLBACK (response_cb), NULL); + + g_signal_connect (G_OBJECT (dialog_win), "destroy", + G_CALLBACK (gtk_main_quit), NULL); + + + g_signal_connect (focus_mode_checkbutton, "toggled", + G_CALLBACK (mouse_focus_toggled_callback), NULL); + + g_signal_connect (autoraise_checkbutton, "toggled", + G_CALLBACK (autoraise_toggled_callback), NULL); + + g_signal_connect (autoraise_delay_slider, "value_changed", + G_CALLBACK (autoraise_delay_value_changed_callback), NULL); + + g_signal_connect (double_click_titlebar_optionmenu, "changed", + G_CALLBACK (double_click_titlebar_changed_callback), NULL); + + g_signal_connect (G_OBJECT (screen), "window_manager_changed", + G_CALLBACK (wm_changed_callback), NULL); + + i = 0; + while (i < n_mouse_modifiers) { + g_signal_connect (G_OBJECT (mouse_modifiers[i].radio), "toggled", + G_CALLBACK (alt_click_radio_toggled_callback), + &mouse_modifiers[i]); + ++i; + } + + capplet_set_icon (dialog_win, "preferences-system-windows"); + gtk_widget_show (dialog_win); + + gtk_main (); + + g_object_unref (builder); + +out: + return rc; +} + +#include +#include +#include + +static void +fill_radio (GtkRadioButton *group, + MouseClickModifier *modifier) +{ + modifier->radio = + gtk_radio_button_new_with_mnemonic_from_widget (group, + modifier->name); + gtk_box_pack_start (GTK_BOX (alt_click_hbox), + modifier->radio, FALSE, FALSE, 0); + + gtk_widget_show (modifier->radio); +} + +static void +reload_mouse_modifiers (void) +{ + XModifierKeymap *modmap; + KeySym *keymap; + int keysyms_per_keycode; + int map_size; + int i; + gboolean have_meta; + gboolean have_hyper; + gboolean have_super; + int min_keycode, max_keycode; + int mod_meta, mod_super, mod_hyper; + + XDisplayKeycodes (gdk_display, + &min_keycode, + &max_keycode); + + keymap = XGetKeyboardMapping (gdk_display, + min_keycode, + max_keycode - min_keycode, + &keysyms_per_keycode); + + modmap = XGetModifierMapping (gdk_display); + + have_super = FALSE; + have_meta = FALSE; + have_hyper = FALSE; + + /* there are 8 modifiers, and the first 3 are shift, shift lock, + * and control + */ + map_size = 8 * modmap->max_keypermod; + i = 3 * modmap->max_keypermod; + mod_meta = mod_super = mod_hyper = 0; + while (i < map_size) { + /* get the key code at this point in the map, + * see if its keysym is one we're interested in + */ + int keycode = modmap->modifiermap[i]; + + if (keycode >= min_keycode && + keycode <= max_keycode) { + int j = 0; + KeySym *syms = keymap + (keycode - min_keycode) * keysyms_per_keycode; + + while (j < keysyms_per_keycode) { + if (syms[j] == XK_Super_L || + syms[j] == XK_Super_R) + mod_super = i / modmap->max_keypermod; + else if (syms[j] == XK_Hyper_L || + syms[j] == XK_Hyper_R) + mod_hyper = i / modmap->max_keypermod; + else if ((syms[j] == XK_Meta_L || + syms[j] == XK_Meta_R)) + mod_meta = i / modmap->max_keypermod; + ++j; + } + } + + ++i; + } + + if ((1 << mod_meta) != Mod1Mask) + have_meta = TRUE; + if (mod_super != 0 && + mod_super != mod_meta) + have_super = TRUE; + if (mod_hyper != 0 && + mod_hyper != mod_meta && + mod_hyper != mod_super) + have_hyper = TRUE; + + XFreeModifiermap (modmap); + XFree (keymap); + + i = 0; + while (i < n_mouse_modifiers) { + g_free (mouse_modifiers[i].name); + if (mouse_modifiers[i].radio) + gtk_widget_destroy (mouse_modifiers[i].radio); + ++i; + } + g_free (mouse_modifiers); + mouse_modifiers = NULL; + + n_mouse_modifiers = 1; /* alt */ + if (have_super) + ++n_mouse_modifiers; + if (have_hyper) + ++n_mouse_modifiers; + if (have_meta) + ++n_mouse_modifiers; + + mouse_modifiers = g_new0 (MouseClickModifier, n_mouse_modifiers); + + i = 0; + + mouse_modifiers[i].number = i; + mouse_modifiers[i].name = g_strdup (_("_Alt")); + mouse_modifiers[i].value = "Alt"; + ++i; + + if (have_hyper) { + mouse_modifiers[i].number = i; + mouse_modifiers[i].name = g_strdup (_("H_yper")); + mouse_modifiers[i].value = "Hyper"; + ++i; + } + + if (have_super) { + mouse_modifiers[i].number = i; + mouse_modifiers[i].name = g_strdup (_("S_uper (or \"Windows logo\")")); + mouse_modifiers[i].value = "Super"; + ++i; + } + + if (have_meta) { + mouse_modifiers[i].number = i; + mouse_modifiers[i].name = g_strdup (_("_Meta")); + mouse_modifiers[i].value = "Meta"; + ++i; + } + + g_assert (i == n_mouse_modifiers); + + i = 0; + while (i < n_mouse_modifiers) { + fill_radio (i == 0 ? NULL : GTK_RADIO_BUTTON (mouse_modifiers[i-1].radio), + &mouse_modifiers[i]); + ++i; + } +} diff --git a/capplets/windows/mate-window-properties.ui b/capplets/windows/mate-window-properties.ui new file mode 100644 index 00000000..dee6e1aa --- /dev/null +++ b/capplets/windows/mate-window-properties.ui @@ -0,0 +1,387 @@ + + + + + + 5 + Window Preferences + False + dialog + False + + + True + 2 + + + True + 5 + 18 + + + True + 6 + + + True + 0 + Window Selection + + + + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + _Select windows when the mouse moves over them + True + True + False + True + True + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + _Raise selected windows after an interval + True + True + False + True + True + + + False + False + 0 + + + + + True + 6 + + + True + 0 + _Interval before raising: + True + autoraise-delay-slider + + + False + 0 + + + + + True + + + True + True + adjustment1 + right + + + 0 + + + + + True + 0 + 4 + seconds + + + False + False + 1 + + + + + 1 + + + + + 1 + + + + + + + False + False + 1 + + + + + + + False + False + 1 + + + + + False + False + 0 + + + + + True + 6 + + + True + 0 + Titlebar Action + + + + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + True + 12 + + + True + 0 + _Double-click titlebar to perform this action: + True + double-click-titlebar-optionmenu + + + False + 0 + + + + + True + liststore1 + + + + 0 + + + + + False + False + 1 + + + + + 0 + + + + + + + False + False + 1 + + + + + False + False + 1 + + + + + True + 6 + + + True + 0 + Movement Key + + + + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + True + 0 + To move a window, press-and-hold this key then grab the window: + True + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + + + + + + False + False + 1 + + + + + + + False + False + 1 + + + + + False + False + 2 + + + + + 1 + + + + + True + end + + + gtk-help + True + True + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + helpbutton1 + closebutton1 + + + + 7.7000000000000002 + 10 + 0.20000000000000001 + 1 + + + + + + + + diff --git a/capplets/windows/window-properties.desktop.in.in b/capplets/windows/window-properties.desktop.in.in new file mode 100644 index 00000000..afa170b3 --- /dev/null +++ b/capplets/windows/window-properties.desktop.in.in @@ -0,0 +1,14 @@ +[Desktop Entry] +_Name=Windows +_Comment=Set your window properties +Exec=mate-window-properties +Icon=preferences-system-windows +Terminal=false +Type=Application +StartupNotify=true +Categories=MATE;GTK;Settings;DesktopSettings; +OnlyShowIn=MATE; +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-control-center +X-MATE-Bugzilla-Component=Window preferences +X-MATE-Bugzilla-Version=@VERSION@ -- cgit v1.2.1